Move countVisitingWatchers to WatchedItemStore
[lhc/web/wiklou.git] / tests / phpunit / includes / WatchedItemStoreUnitTest.php
1 <?php
2
3 /**
4 * @author Addshore
5 *
6 * @covers WatchedItemStore
7 */
8 class WatchedItemStoreUnitTest extends PHPUnit_Framework_TestCase {
9
10 /**
11 * @return PHPUnit_Framework_MockObject_MockObject|IDatabase
12 */
13 private function getMockDb() {
14 return $this->getMock( IDatabase::class );
15 }
16
17 /**
18 * @return PHPUnit_Framework_MockObject_MockObject|LoadBalancer
19 */
20 private function getMockLoadBalancer( $mockDb ) {
21 $mock = $this->getMockBuilder( LoadBalancer::class )
22 ->disableOriginalConstructor()
23 ->getMock();
24 $mock->expects( $this->any() )
25 ->method( 'getConnection' )
26 ->will( $this->returnValue( $mockDb ) );
27 $mock->expects( $this->any() )
28 ->method( 'getReadOnlyReason' )
29 ->will( $this->returnValue( false ) );
30 return $mock;
31 }
32
33 /**
34 * @return PHPUnit_Framework_MockObject_MockObject|HashBagOStuff
35 */
36 private function getMockCache() {
37 $mock = $this->getMockBuilder( HashBagOStuff::class )
38 ->disableOriginalConstructor()
39 ->getMock();
40 $mock->expects( $this->any() )
41 ->method( 'makeKey' )
42 ->will( $this->returnCallback( function() {
43 return implode( ':', func_get_args() );
44 } ) );
45 return $mock;
46 }
47
48 /**
49 * @param int $id
50 * @return PHPUnit_Framework_MockObject_MockObject|User
51 */
52 private function getMockNonAnonUserWithId( $id ) {
53 $mock = $this->getMock( User::class );
54 $mock->expects( $this->any() )
55 ->method( 'isAnon' )
56 ->will( $this->returnValue( false ) );
57 $mock->expects( $this->any() )
58 ->method( 'getId' )
59 ->will( $this->returnValue( $id ) );
60 return $mock;
61 }
62
63 /**
64 * @return User
65 */
66 private function getAnonUser() {
67 return User::newFromName( 'Anon_User' );
68 }
69
70 private function getFakeRow( array $rowValues ) {
71 $fakeRow = new stdClass();
72 foreach ( $rowValues as $valueName => $value ) {
73 $fakeRow->$valueName = $value;
74 }
75 return $fakeRow;
76 }
77
78 public function testGetDefaultInstance() {
79 $instanceOne = WatchedItemStore::getDefaultInstance();
80 $instanceTwo = WatchedItemStore::getDefaultInstance();
81 $this->assertSame( $instanceOne, $instanceTwo );
82 }
83
84 public function testCountWatchers() {
85 $titleValue = new TitleValue( 0, 'SomeDbKey' );
86
87 $mockDb = $this->getMockDb();
88 $mockDb->expects( $this->exactly( 1 ) )
89 ->method( 'selectField' )
90 ->with(
91 'watchlist',
92 'COUNT(*)',
93 [
94 'wl_namespace' => $titleValue->getNamespace(),
95 'wl_title' => $titleValue->getDBkey(),
96 ],
97 $this->isType( 'string' )
98 )
99 ->will( $this->returnValue( 7 ) );
100
101 $mockCache = $this->getMockCache();
102 $mockCache->expects( $this->never() )->method( 'get' );
103 $mockCache->expects( $this->never() )->method( 'set' );
104 $mockCache->expects( $this->never() )->method( 'delete' );
105
106 $store = new WatchedItemStore(
107 $this->getMockLoadBalancer( $mockDb ),
108 $mockCache
109 );
110
111 $this->assertEquals( 7, $store->countWatchers( $titleValue ) );
112 }
113
114 public function testCountWatchersMultiple() {
115 $titleValues = [
116 new TitleValue( 0, 'SomeDbKey' ),
117 new TitleValue( 0, 'OtherDbKey' ),
118 new TitleValue( 1, 'AnotherDbKey' ),
119 ];
120
121 $mockDb = $this->getMockDb();
122
123 $dbResult = [
124 $this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
125 $this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
126 $this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ]
127 ),
128 ];
129 $mockDb->expects( $this->once() )
130 ->method( 'makeWhereFrom2d' )
131 ->with(
132 [ [ 'SomeDbKey' => 1, 'OtherDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
133 $this->isType( 'string' ),
134 $this->isType( 'string' )
135 )
136 ->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
137 $mockDb->expects( $this->once() )
138 ->method( 'select' )
139 ->with(
140 'watchlist',
141 [ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
142 [ 'makeWhereFrom2d return value' ],
143 $this->isType( 'string' ),
144 [
145 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
146 ]
147 )
148 ->will(
149 $this->returnValue( $dbResult )
150 );
151
152 $mockCache = $this->getMockCache();
153 $mockCache->expects( $this->never() )->method( 'get' );
154 $mockCache->expects( $this->never() )->method( 'set' );
155 $mockCache->expects( $this->never() )->method( 'delete' );
156
157 $store = new WatchedItemStore(
158 $this->getMockLoadBalancer( $mockDb ),
159 $mockCache
160 );
161
162 $expected = [
163 0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
164 1 => [ 'AnotherDbKey' => 500 ],
165 ];
166 $this->assertEquals( $expected, $store->countWatchersMultiple( $titleValues ) );
167 }
168
169 public function provideMinimumWatchers() {
170 return [
171 [ 50 ],
172 [ "50; DROP TABLE watchlist;\n--" ],
173 ];
174 }
175
176 /**
177 * @dataProvider provideMinimumWatchers
178 */
179 public function testCountWatchersMultiple_withMinimumWatchers( $minWatchers ) {
180 $titleValues = [
181 new TitleValue( 0, 'SomeDbKey' ),
182 new TitleValue( 0, 'OtherDbKey' ),
183 new TitleValue( 1, 'AnotherDbKey' ),
184 ];
185
186 $mockDb = $this->getMockDb();
187
188 $dbResult = [
189 $this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
190 $this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
191 $this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ]
192 ),
193 ];
194 $mockDb->expects( $this->once() )
195 ->method( 'makeWhereFrom2d' )
196 ->with(
197 [ [ 'SomeDbKey' => 1, 'OtherDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
198 $this->isType( 'string' ),
199 $this->isType( 'string' )
200 )
201 ->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
202 $mockDb->expects( $this->once() )
203 ->method( 'select' )
204 ->with(
205 'watchlist',
206 [ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
207 [ 'makeWhereFrom2d return value' ],
208 $this->isType( 'string' ),
209 [
210 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
211 'HAVING' => 'COUNT(*) >= 50',
212 ]
213 )
214 ->will(
215 $this->returnValue( $dbResult )
216 );
217
218 $mockCache = $this->getMockCache();
219 $mockCache->expects( $this->never() )->method( 'get' );
220 $mockCache->expects( $this->never() )->method( 'set' );
221 $mockCache->expects( $this->never() )->method( 'delete' );
222
223 $store = new WatchedItemStore(
224 $this->getMockLoadBalancer( $mockDb ),
225 $mockCache
226 );
227
228 $expected = [
229 0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
230 1 => [ 'AnotherDbKey' => 500 ],
231 ];
232 $this->assertEquals(
233 $expected,
234 $store->countWatchersMultiple( $titleValues, [ 'minimumWatchers' => $minWatchers ] )
235 );
236 }
237
238 public function testCountVisitingWatchers() {
239 $titleValue = new TitleValue( 0, 'SomeDbKey' );
240
241 $mockDb = $this->getMockDb();
242 $mockDb->expects( $this->exactly( 1 ) )
243 ->method( 'selectField' )
244 ->with(
245 'watchlist',
246 'COUNT(*)',
247 [
248 'wl_namespace' => $titleValue->getNamespace(),
249 'wl_title' => $titleValue->getDBkey(),
250 'wl_notificationtimestamp >= \'TS111TS\' OR wl_notificationtimestamp IS NULL',
251 ],
252 $this->isType( 'string' )
253 )
254 ->will( $this->returnValue( 7 ) );
255 $mockDb->expects( $this->exactly( 1 ) )
256 ->method( 'addQuotes' )
257 ->will( $this->returnCallback( function( $value ) {
258 return "'$value'";
259 } ) );
260 $mockDb->expects( $this->exactly( 1 ) )
261 ->method( 'timestamp' )
262 ->will( $this->returnCallback( function( $value ) {
263 return 'TS' . $value . 'TS';
264 } ) );
265
266 $mockCache = $this->getMockCache();
267 $mockCache->expects( $this->never() )->method( 'set' );
268 $mockCache->expects( $this->never() )->method( 'get' );
269 $mockCache->expects( $this->never() )->method( 'delete' );
270
271 $store = new WatchedItemStore(
272 $this->getMockLoadBalancer( $mockDb ),
273 $mockCache
274 );
275
276 $this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
277 }
278
279 public function testDuplicateEntry_nothingToDuplicate() {
280 $mockDb = $this->getMockDb();
281 $mockDb->expects( $this->once() )
282 ->method( 'select' )
283 ->with(
284 'watchlist',
285 [
286 'wl_user',
287 'wl_notificationtimestamp',
288 ],
289 [
290 'wl_namespace' => 0,
291 'wl_title' => 'Old_Title',
292 ],
293 'WatchedItemStore::duplicateEntry',
294 [ 'FOR UPDATE' ]
295 )
296 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
297
298 $store = new WatchedItemStore(
299 $this->getMockLoadBalancer( $mockDb ),
300 $this->getMockCache()
301 );
302
303 $store->duplicateEntry(
304 Title::newFromText( 'Old_Title' ),
305 Title::newFromText( 'New_Title' )
306 );
307 }
308
309 public function testDuplicateEntry_somethingToDuplicate() {
310 $fakeRows = [
311 $this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
312 $this->getFakeRow( [ 'wl_user' => 2, 'wl_notificationtimestamp' => null ] ),
313 ];
314
315 $mockDb = $this->getMockDb();
316 $mockDb->expects( $this->at( 0 ) )
317 ->method( 'select' )
318 ->with(
319 'watchlist',
320 [
321 'wl_user',
322 'wl_notificationtimestamp',
323 ],
324 [
325 'wl_namespace' => 0,
326 'wl_title' => 'Old_Title',
327 ]
328 )
329 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
330 $mockDb->expects( $this->at( 1 ) )
331 ->method( 'replace' )
332 ->with(
333 'watchlist',
334 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
335 [
336 [
337 'wl_user' => 1,
338 'wl_namespace' => 0,
339 'wl_title' => 'New_Title',
340 'wl_notificationtimestamp' => '20151212010101',
341 ],
342 [
343 'wl_user' => 2,
344 'wl_namespace' => 0,
345 'wl_title' => 'New_Title',
346 'wl_notificationtimestamp' => null,
347 ],
348 ],
349 $this->isType( 'string' )
350 );
351
352 $mockCache = $this->getMockCache();
353 $mockCache->expects( $this->never() )->method( 'get' );
354 $mockCache->expects( $this->never() )->method( 'delete' );
355
356 $store = new WatchedItemStore(
357 $this->getMockLoadBalancer( $mockDb ),
358 $mockCache
359 );
360
361 $store->duplicateEntry(
362 Title::newFromText( 'Old_Title' ),
363 Title::newFromText( 'New_Title' )
364 );
365 }
366
367 public function testDuplicateAllAssociatedEntries_nothingToDuplicate() {
368 $mockDb = $this->getMockDb();
369 $mockDb->expects( $this->at( 0 ) )
370 ->method( 'select' )
371 ->with(
372 'watchlist',
373 [
374 'wl_user',
375 'wl_notificationtimestamp',
376 ],
377 [
378 'wl_namespace' => 0,
379 'wl_title' => 'Old_Title',
380 ]
381 )
382 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
383 $mockDb->expects( $this->at( 1 ) )
384 ->method( 'select' )
385 ->with(
386 'watchlist',
387 [
388 'wl_user',
389 'wl_notificationtimestamp',
390 ],
391 [
392 'wl_namespace' => 1,
393 'wl_title' => 'Old_Title',
394 ]
395 )
396 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
397
398 $mockCache = $this->getMockCache();
399 $mockCache->expects( $this->never() )->method( 'get' );
400 $mockCache->expects( $this->never() )->method( 'delete' );
401
402 $store = new WatchedItemStore(
403 $this->getMockLoadBalancer( $mockDb ),
404 $mockCache
405 );
406
407 $store->duplicateAllAssociatedEntries(
408 Title::newFromText( 'Old_Title' ),
409 Title::newFromText( 'New_Title' )
410 );
411 }
412
413 public function testDuplicateAllAssociatedEntries_somethingToDuplicate() {
414 $fakeRows = [
415 $this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
416 ];
417
418 $mockDb = $this->getMockDb();
419 $mockDb->expects( $this->at( 0 ) )
420 ->method( 'select' )
421 ->with(
422 'watchlist',
423 [
424 'wl_user',
425 'wl_notificationtimestamp',
426 ],
427 [
428 'wl_namespace' => 0,
429 'wl_title' => 'Old_Title',
430 ]
431 )
432 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
433 $mockDb->expects( $this->at( 1 ) )
434 ->method( 'replace' )
435 ->with(
436 'watchlist',
437 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
438 [
439 [
440 'wl_user' => 1,
441 'wl_namespace' => 0,
442 'wl_title' => 'New_Title',
443 'wl_notificationtimestamp' => '20151212010101',
444 ],
445 ],
446 $this->isType( 'string' )
447 );
448 $mockDb->expects( $this->at( 2 ) )
449 ->method( 'select' )
450 ->with(
451 'watchlist',
452 [
453 'wl_user',
454 'wl_notificationtimestamp',
455 ],
456 [
457 'wl_namespace' => 1,
458 'wl_title' => 'Old_Title',
459 ]
460 )
461 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
462 $mockDb->expects( $this->at( 3 ) )
463 ->method( 'replace' )
464 ->with(
465 'watchlist',
466 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
467 [
468 [
469 'wl_user' => 1,
470 'wl_namespace' => 1,
471 'wl_title' => 'New_Title',
472 'wl_notificationtimestamp' => '20151212010101',
473 ],
474 ],
475 $this->isType( 'string' )
476 );
477
478 $mockCache = $this->getMockCache();
479 $mockCache->expects( $this->never() )->method( 'get' );
480 $mockCache->expects( $this->never() )->method( 'delete' );
481
482 $store = new WatchedItemStore(
483 $this->getMockLoadBalancer( $mockDb ),
484 $mockCache
485 );
486
487 $store->duplicateAllAssociatedEntries(
488 Title::newFromText( 'Old_Title' ),
489 Title::newFromText( 'New_Title' )
490 );
491 }
492
493 public function testAddWatch_nonAnonymousUser() {
494 $mockDb = $this->getMockDb();
495 $mockDb->expects( $this->once() )
496 ->method( 'insert' )
497 ->with(
498 'watchlist',
499 [
500 [
501 'wl_user' => 1,
502 'wl_namespace' => 0,
503 'wl_title' => 'Some_Page',
504 'wl_notificationtimestamp' => null,
505 ]
506 ]
507 );
508
509 $mockCache = $this->getMockCache();
510 $mockCache->expects( $this->once() )
511 ->method( 'delete' )
512 ->with( '0:Some_Page:1' );
513
514 $store = new WatchedItemStore(
515 $this->getMockLoadBalancer( $mockDb ),
516 $mockCache
517 );
518
519 $store->addWatch(
520 $this->getMockNonAnonUserWithId( 1 ),
521 Title::newFromText( 'Some_Page' )
522 );
523 }
524
525 public function testAddWatch_anonymousUser() {
526 $mockDb = $this->getMockDb();
527 $mockDb->expects( $this->never() )
528 ->method( 'insert' );
529
530 $mockCache = $this->getMockCache();
531 $mockCache->expects( $this->never() )
532 ->method( 'delete' );
533
534 $store = new WatchedItemStore(
535 $this->getMockLoadBalancer( $mockDb ),
536 $mockCache
537 );
538
539 $store->addWatch(
540 $this->getAnonUser(),
541 Title::newFromText( 'Some_Page' )
542 );
543 }
544
545 public function testAddWatchBatch_nonAnonymousUser() {
546 $mockDb = $this->getMockDb();
547 $mockDb->expects( $this->once() )
548 ->method( 'insert' )
549 ->with(
550 'watchlist',
551 [
552 [
553 'wl_user' => 1,
554 'wl_namespace' => 0,
555 'wl_title' => 'Some_Page',
556 'wl_notificationtimestamp' => null,
557 ],
558 [
559 'wl_user' => 1,
560 'wl_namespace' => 1,
561 'wl_title' => 'Some_Page',
562 'wl_notificationtimestamp' => null,
563 ]
564 ]
565 );
566
567 $mockCache = $this->getMockCache();
568 $mockCache->expects( $this->exactly( 2 ) )
569 ->method( 'delete' );
570 $mockCache->expects( $this->at( 1 ) )
571 ->method( 'delete' )
572 ->with( '0:Some_Page:1' );
573 $mockCache->expects( $this->at( 3 ) )
574 ->method( 'delete' )
575 ->with( '1:Some_Page:1' );
576
577 $store = new WatchedItemStore(
578 $this->getMockLoadBalancer( $mockDb ),
579 $mockCache
580 );
581
582 $mockUser = $this->getMockNonAnonUserWithId( 1 );
583
584 $this->assertTrue(
585 $store->addWatchBatch(
586 [
587 [ $mockUser, new TitleValue( 0, 'Some_Page' ) ],
588 [ $mockUser, new TitleValue( 1, 'Some_Page' ) ],
589 ]
590 )
591 );
592 }
593
594 public function testAddWatchBatch_anonymousUserCombinationsAreSkipped() {
595 $mockDb = $this->getMockDb();
596 $mockDb->expects( $this->once() )
597 ->method( 'insert' )
598 ->with(
599 'watchlist',
600 [
601 [
602 'wl_user' => 1,
603 'wl_namespace' => 0,
604 'wl_title' => 'Some_Page',
605 'wl_notificationtimestamp' => null,
606 ]
607 ]
608 );
609
610 $mockCache = $this->getMockCache();
611 $mockCache->expects( $this->once() )
612 ->method( 'delete' )
613 ->with( '0:Some_Page:1' );
614
615 $store = new WatchedItemStore(
616 $this->getMockLoadBalancer( $mockDb ),
617 $mockCache
618 );
619
620 $this->assertTrue(
621 $store->addWatchBatch(
622 [
623 [ $this->getMockNonAnonUserWithId( 1 ), new TitleValue( 0, 'Some_Page' ) ],
624 [ $this->getAnonUser(), new TitleValue( 0, 'Other_Page' ) ],
625 ]
626 )
627 );
628 }
629
630 public function testAddWatchBatchReturnsFalse_whenOnlyGivenAnonymousUserCombinations() {
631 $mockDb = $this->getMockDb();
632 $mockDb->expects( $this->never() )
633 ->method( 'insert' );
634
635 $mockCache = $this->getMockCache();
636 $mockCache->expects( $this->never() )
637 ->method( 'delete' );
638
639 $store = new WatchedItemStore(
640 $this->getMockLoadBalancer( $mockDb ),
641 $mockCache
642 );
643
644 $anonUser = $this->getAnonUser();
645 $this->assertFalse(
646 $store->addWatchBatch(
647 [
648 [ $anonUser, new TitleValue( 0, 'Some_Page' ) ],
649 [ $anonUser, new TitleValue( 1, 'Other_Page' ) ],
650 ]
651 )
652 );
653 }
654
655 public function testAddWatchBatchReturnsFalse_whenGivenEmptyList() {
656 $mockDb = $this->getMockDb();
657 $mockDb->expects( $this->never() )
658 ->method( 'insert' );
659
660 $mockCache = $this->getMockCache();
661 $mockCache->expects( $this->never() )
662 ->method( 'delete' );
663
664 $store = new WatchedItemStore(
665 $this->getMockLoadBalancer( $mockDb ),
666 $mockCache
667 );
668
669 $this->assertFalse(
670 $store->addWatchBatch( [] )
671 );
672 }
673
674 public function testLoadWatchedItem_existingItem() {
675 $mockDb = $this->getMockDb();
676 $mockDb->expects( $this->once() )
677 ->method( 'selectRow' )
678 ->with(
679 'watchlist',
680 'wl_notificationtimestamp',
681 [
682 'wl_user' => 1,
683 'wl_namespace' => 0,
684 'wl_title' => 'SomeDbKey',
685 ]
686 )
687 ->will( $this->returnValue(
688 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
689 ) );
690
691 $mockCache = $this->getMockCache();
692 $mockCache->expects( $this->once() )
693 ->method( 'set' )
694 ->with(
695 '0:SomeDbKey:1'
696 );
697
698 $store = new WatchedItemStore(
699 $this->getMockLoadBalancer( $mockDb ),
700 $mockCache
701 );
702
703 $watchedItem = $store->loadWatchedItem(
704 $this->getMockNonAnonUserWithId( 1 ),
705 new TitleValue( 0, 'SomeDbKey' )
706 );
707 $this->assertInstanceOf( 'WatchedItem', $watchedItem );
708 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
709 $this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
710 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
711 }
712
713 public function testLoadWatchedItem_noItem() {
714 $mockDb = $this->getMockDb();
715 $mockDb->expects( $this->once() )
716 ->method( 'selectRow' )
717 ->with(
718 'watchlist',
719 'wl_notificationtimestamp',
720 [
721 'wl_user' => 1,
722 'wl_namespace' => 0,
723 'wl_title' => 'SomeDbKey',
724 ]
725 )
726 ->will( $this->returnValue( [] ) );
727
728 $mockCache = $this->getMockCache();
729 $mockCache->expects( $this->never() )->method( 'get' );
730 $mockCache->expects( $this->never() )->method( 'delete' );
731
732 $store = new WatchedItemStore(
733 $this->getMockLoadBalancer( $mockDb ),
734 $mockCache
735 );
736
737 $this->assertFalse(
738 $store->loadWatchedItem(
739 $this->getMockNonAnonUserWithId( 1 ),
740 new TitleValue( 0, 'SomeDbKey' )
741 )
742 );
743 }
744
745 public function testLoadWatchedItem_anonymousUser() {
746 $mockDb = $this->getMockDb();
747 $mockDb->expects( $this->never() )
748 ->method( 'selectRow' );
749
750 $mockCache = $this->getMockCache();
751 $mockCache->expects( $this->never() )->method( 'get' );
752 $mockCache->expects( $this->never() )->method( 'delete' );
753
754 $store = new WatchedItemStore(
755 $this->getMockLoadBalancer( $mockDb ),
756 $mockCache
757 );
758
759 $this->assertFalse(
760 $store->loadWatchedItem(
761 $this->getAnonUser(),
762 new TitleValue( 0, 'SomeDbKey' )
763 )
764 );
765 }
766
767 public function testRemoveWatch_existingItem() {
768 $mockDb = $this->getMockDb();
769 $mockDb->expects( $this->once() )
770 ->method( 'delete' )
771 ->with(
772 'watchlist',
773 [
774 'wl_user' => 1,
775 'wl_namespace' => 0,
776 'wl_title' => 'SomeDbKey',
777 ]
778 );
779 $mockDb->expects( $this->once() )
780 ->method( 'affectedRows' )
781 ->will( $this->returnValue( 1 ) );
782
783 $mockCache = $this->getMockCache();
784 $mockCache->expects( $this->never() )->method( 'get' );
785 $mockCache->expects( $this->once() )
786 ->method( 'delete' )
787 ->with( '0:SomeDbKey:1' );
788
789 $store = new WatchedItemStore(
790 $this->getMockLoadBalancer( $mockDb ),
791 $mockCache
792 );
793
794 $this->assertTrue(
795 $store->removeWatch(
796 $this->getMockNonAnonUserWithId( 1 ),
797 new TitleValue( 0, 'SomeDbKey' )
798 )
799 );
800 }
801
802 public function testRemoveWatch_noItem() {
803 $mockDb = $this->getMockDb();
804 $mockDb->expects( $this->once() )
805 ->method( 'delete' )
806 ->with(
807 'watchlist',
808 [
809 'wl_user' => 1,
810 'wl_namespace' => 0,
811 'wl_title' => 'SomeDbKey',
812 ]
813 );
814 $mockDb->expects( $this->once() )
815 ->method( 'affectedRows' )
816 ->will( $this->returnValue( 0 ) );
817
818 $mockCache = $this->getMockCache();
819 $mockCache->expects( $this->never() )->method( 'get' );
820 $mockCache->expects( $this->once() )
821 ->method( 'delete' )
822 ->with( '0:SomeDbKey:1' );
823
824 $store = new WatchedItemStore(
825 $this->getMockLoadBalancer( $mockDb ),
826 $mockCache
827 );
828
829 $this->assertFalse(
830 $store->removeWatch(
831 $this->getMockNonAnonUserWithId( 1 ),
832 new TitleValue( 0, 'SomeDbKey' )
833 )
834 );
835 }
836
837 public function testRemoveWatch_anonymousUser() {
838 $mockDb = $this->getMockDb();
839 $mockDb->expects( $this->never() )
840 ->method( 'delete' );
841
842 $mockCache = $this->getMockCache();
843 $mockCache->expects( $this->never() )->method( 'get' );
844 $mockCache->expects( $this->never() )
845 ->method( 'delete' );
846
847 $store = new WatchedItemStore(
848 $this->getMockLoadBalancer( $mockDb ),
849 $mockCache
850 );
851
852 $this->assertFalse(
853 $store->removeWatch(
854 $this->getAnonUser(),
855 new TitleValue( 0, 'SomeDbKey' )
856 )
857 );
858 }
859
860 public function testGetWatchedItem_existingItem() {
861 $mockDb = $this->getMockDb();
862 $mockDb->expects( $this->once() )
863 ->method( 'selectRow' )
864 ->with(
865 'watchlist',
866 'wl_notificationtimestamp',
867 [
868 'wl_user' => 1,
869 'wl_namespace' => 0,
870 'wl_title' => 'SomeDbKey',
871 ]
872 )
873 ->will( $this->returnValue(
874 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
875 ) );
876
877 $mockCache = $this->getMockCache();
878 $mockCache->expects( $this->never() )->method( 'delete' );
879 $mockCache->expects( $this->once() )
880 ->method( 'get' )
881 ->with(
882 '0:SomeDbKey:1'
883 )
884 ->will( $this->returnValue( null ) );
885 $mockCache->expects( $this->once() )
886 ->method( 'set' )
887 ->with(
888 '0:SomeDbKey:1'
889 );
890
891 $store = new WatchedItemStore(
892 $this->getMockLoadBalancer( $mockDb ),
893 $mockCache
894 );
895
896 $watchedItem = $store->getWatchedItem(
897 $this->getMockNonAnonUserWithId( 1 ),
898 new TitleValue( 0, 'SomeDbKey' )
899 );
900 $this->assertInstanceOf( 'WatchedItem', $watchedItem );
901 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
902 $this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
903 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
904 }
905
906 public function testGetWatchedItem_cachedItem() {
907 $mockDb = $this->getMockDb();
908 $mockDb->expects( $this->never() )
909 ->method( 'selectRow' );
910
911 $mockUser = $this->getMockNonAnonUserWithId( 1 );
912 $linkTarget = new TitleValue( 0, 'SomeDbKey' );
913 $cachedItem = new WatchedItem( $mockUser, $linkTarget, '20151212010101' );
914
915 $mockCache = $this->getMockCache();
916 $mockCache->expects( $this->never() )->method( 'delete' );
917 $mockCache->expects( $this->never() )->method( 'set' );
918 $mockCache->expects( $this->once() )
919 ->method( 'get' )
920 ->with(
921 '0:SomeDbKey:1'
922 )
923 ->will( $this->returnValue( $cachedItem ) );
924
925 $store = new WatchedItemStore(
926 $this->getMockLoadBalancer( $mockDb ),
927 $mockCache
928 );
929
930 $this->assertEquals(
931 $cachedItem,
932 $store->getWatchedItem(
933 $mockUser,
934 $linkTarget
935 )
936 );
937 }
938
939 public function testGetWatchedItem_noItem() {
940 $mockDb = $this->getMockDb();
941 $mockDb->expects( $this->once() )
942 ->method( 'selectRow' )
943 ->with(
944 'watchlist',
945 'wl_notificationtimestamp',
946 [
947 'wl_user' => 1,
948 'wl_namespace' => 0,
949 'wl_title' => 'SomeDbKey',
950 ]
951 )
952 ->will( $this->returnValue( [] ) );
953
954 $mockCache = $this->getMockCache();
955 $mockCache->expects( $this->never() )->method( 'set' );
956 $mockCache->expects( $this->never() )->method( 'delete' );
957 $mockCache->expects( $this->once() )
958 ->method( 'get' )
959 ->with( '0:SomeDbKey:1' )
960 ->will( $this->returnValue( false ) );
961
962 $store = new WatchedItemStore(
963 $this->getMockLoadBalancer( $mockDb ),
964 $mockCache
965 );
966
967 $this->assertFalse(
968 $store->getWatchedItem(
969 $this->getMockNonAnonUserWithId( 1 ),
970 new TitleValue( 0, 'SomeDbKey' )
971 )
972 );
973 }
974
975 public function testGetWatchedItem_anonymousUser() {
976 $mockDb = $this->getMockDb();
977 $mockDb->expects( $this->never() )
978 ->method( 'selectRow' );
979
980 $mockCache = $this->getMockCache();
981 $mockCache->expects( $this->never() )->method( 'set' );
982 $mockCache->expects( $this->never() )->method( 'get' );
983 $mockCache->expects( $this->never() )->method( 'delete' );
984
985 $store = new WatchedItemStore(
986 $this->getMockLoadBalancer( $mockDb ),
987 $mockCache
988 );
989
990 $this->assertFalse(
991 $store->getWatchedItem(
992 $this->getAnonUser(),
993 new TitleValue( 0, 'SomeDbKey' )
994 )
995 );
996 }
997
998 public function testIsWatchedItem_existingItem() {
999 $mockDb = $this->getMockDb();
1000 $mockDb->expects( $this->once() )
1001 ->method( 'selectRow' )
1002 ->with(
1003 'watchlist',
1004 'wl_notificationtimestamp',
1005 [
1006 'wl_user' => 1,
1007 'wl_namespace' => 0,
1008 'wl_title' => 'SomeDbKey',
1009 ]
1010 )
1011 ->will( $this->returnValue(
1012 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1013 ) );
1014
1015 $mockCache = $this->getMockCache();
1016 $mockCache->expects( $this->never() )->method( 'delete' );
1017 $mockCache->expects( $this->once() )
1018 ->method( 'get' )
1019 ->with( '0:SomeDbKey:1' )
1020 ->will( $this->returnValue( false ) );
1021 $mockCache->expects( $this->once() )
1022 ->method( 'set' )
1023 ->with(
1024 '0:SomeDbKey:1'
1025 );
1026
1027 $store = new WatchedItemStore(
1028 $this->getMockLoadBalancer( $mockDb ),
1029 $mockCache
1030 );
1031
1032 $this->assertTrue(
1033 $store->isWatched(
1034 $this->getMockNonAnonUserWithId( 1 ),
1035 new TitleValue( 0, 'SomeDbKey' )
1036 )
1037 );
1038 }
1039
1040 public function testIsWatchedItem_noItem() {
1041 $mockDb = $this->getMockDb();
1042 $mockDb->expects( $this->once() )
1043 ->method( 'selectRow' )
1044 ->with(
1045 'watchlist',
1046 'wl_notificationtimestamp',
1047 [
1048 'wl_user' => 1,
1049 'wl_namespace' => 0,
1050 'wl_title' => 'SomeDbKey',
1051 ]
1052 )
1053 ->will( $this->returnValue( [] ) );
1054
1055 $mockCache = $this->getMockCache();
1056 $mockCache->expects( $this->never() )->method( 'set' );
1057 $mockCache->expects( $this->never() )->method( 'delete' );
1058 $mockCache->expects( $this->once() )
1059 ->method( 'get' )
1060 ->with( '0:SomeDbKey:1' )
1061 ->will( $this->returnValue( false ) );
1062
1063 $store = new WatchedItemStore(
1064 $this->getMockLoadBalancer( $mockDb ),
1065 $mockCache
1066 );
1067
1068 $this->assertFalse(
1069 $store->isWatched(
1070 $this->getMockNonAnonUserWithId( 1 ),
1071 new TitleValue( 0, 'SomeDbKey' )
1072 )
1073 );
1074 }
1075
1076 public function testIsWatchedItem_anonymousUser() {
1077 $mockDb = $this->getMockDb();
1078 $mockDb->expects( $this->never() )
1079 ->method( 'selectRow' );
1080
1081 $mockCache = $this->getMockCache();
1082 $mockCache->expects( $this->never() )->method( 'set' );
1083 $mockCache->expects( $this->never() )->method( 'get' );
1084 $mockCache->expects( $this->never() )->method( 'delete' );
1085
1086 $store = new WatchedItemStore(
1087 $this->getMockLoadBalancer( $mockDb ),
1088 $mockCache
1089 );
1090
1091 $this->assertFalse(
1092 $store->isWatched(
1093 $this->getAnonUser(),
1094 new TitleValue( 0, 'SomeDbKey' )
1095 )
1096 );
1097 }
1098
1099 public function testResetNotificationTimestamp_anonymousUser() {
1100 $mockDb = $this->getMockDb();
1101 $mockDb->expects( $this->never() )
1102 ->method( 'selectRow' );
1103
1104 $mockCache = $this->getMockCache();
1105 $mockCache->expects( $this->never() )->method( 'get' );
1106 $mockCache->expects( $this->never() )->method( 'set' );
1107 $mockCache->expects( $this->never() )->method( 'delete' );
1108
1109 $store = new WatchedItemStore(
1110 $this->getMockLoadBalancer( $mockDb ),
1111 $mockCache
1112 );
1113
1114 $this->assertFalse(
1115 $store->resetNotificationTimestamp(
1116 $this->getAnonUser(),
1117 Title::newFromText( 'SomeDbKey' )
1118 )
1119 );
1120 }
1121
1122 public function testResetNotificationTimestamp_noItem() {
1123 $mockDb = $this->getMockDb();
1124 $mockDb->expects( $this->once() )
1125 ->method( 'selectRow' )
1126 ->with(
1127 'watchlist',
1128 'wl_notificationtimestamp',
1129 [
1130 'wl_user' => 1,
1131 'wl_namespace' => 0,
1132 'wl_title' => 'SomeDbKey',
1133 ]
1134 )
1135 ->will( $this->returnValue( [] ) );
1136
1137 $mockCache = $this->getMockCache();
1138 $mockCache->expects( $this->never() )->method( 'get' );
1139 $mockCache->expects( $this->never() )->method( 'set' );
1140 $mockCache->expects( $this->never() )->method( 'delete' );
1141
1142 $store = new WatchedItemStore(
1143 $this->getMockLoadBalancer( $mockDb ),
1144 $mockCache
1145 );
1146
1147 $this->assertFalse(
1148 $store->resetNotificationTimestamp(
1149 $this->getMockNonAnonUserWithId( 1 ),
1150 Title::newFromText( 'SomeDbKey' )
1151 )
1152 );
1153 }
1154
1155 public function testResetNotificationTimestamp_item() {
1156 $user = $this->getMockNonAnonUserWithId( 1 );
1157 $title = Title::newFromText( 'SomeDbKey' );
1158
1159 $mockDb = $this->getMockDb();
1160 $mockDb->expects( $this->once() )
1161 ->method( 'selectRow' )
1162 ->with(
1163 'watchlist',
1164 'wl_notificationtimestamp',
1165 [
1166 'wl_user' => 1,
1167 'wl_namespace' => 0,
1168 'wl_title' => 'SomeDbKey',
1169 ]
1170 )
1171 ->will( $this->returnValue(
1172 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1173 ) );
1174
1175 $mockCache = $this->getMockCache();
1176 $mockCache->expects( $this->never() )->method( 'get' );
1177 $mockCache->expects( $this->once() )
1178 ->method( 'set' )
1179 ->with(
1180 '0:SomeDbKey:1',
1181 $this->isInstanceOf( WatchedItem::class )
1182 );
1183 $mockCache->expects( $this->once() )
1184 ->method( 'delete' )
1185 ->with( '0:SomeDbKey:1' );
1186
1187 $store = new WatchedItemStore(
1188 $this->getMockLoadBalancer( $mockDb ),
1189 $mockCache
1190 );
1191
1192 // Note: This does not actually assert the job is correct
1193 $callableCallCounter = 0;
1194 $mockCallback = function( $callable ) use ( &$callableCallCounter ) {
1195 $callableCallCounter++;
1196 $this->assertInternalType( 'callable', $callable );
1197 };
1198 $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
1199
1200 $this->assertTrue(
1201 $store->resetNotificationTimestamp(
1202 $user,
1203 $title
1204 )
1205 );
1206 $this->assertEquals( 1, $callableCallCounter );
1207 }
1208
1209 public function testResetNotificationTimestamp_noItemForced() {
1210 $user = $this->getMockNonAnonUserWithId( 1 );
1211 $title = Title::newFromText( 'SomeDbKey' );
1212
1213 $mockDb = $this->getMockDb();
1214 $mockDb->expects( $this->never() )
1215 ->method( 'selectRow' );
1216
1217 $mockCache = $this->getMockCache();
1218 $mockDb->expects( $this->never() )
1219 ->method( 'get' );
1220 $mockDb->expects( $this->never() )
1221 ->method( 'set' );
1222 $mockDb->expects( $this->never() )
1223 ->method( 'delete' );
1224
1225 $store = new WatchedItemStore(
1226 $this->getMockLoadBalancer( $mockDb ),
1227 $mockCache
1228 );
1229
1230 // Note: This does not actually assert the job is correct
1231 $callableCallCounter = 0;
1232 $mockCallback = function( $callable ) use ( &$callableCallCounter ) {
1233 $callableCallCounter++;
1234 $this->assertInternalType( 'callable', $callable );
1235 };
1236 $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
1237
1238 $this->assertTrue(
1239 $store->resetNotificationTimestamp(
1240 $user,
1241 $title,
1242 'force'
1243 )
1244 );
1245 $this->assertEquals( 1, $callableCallCounter );
1246 }
1247
1248 /**
1249 * @param $text
1250 * @param int $ns
1251 *
1252 * @return PHPUnit_Framework_MockObject_MockObject|Title
1253 */
1254 private function getMockTitle( $text, $ns = 0 ) {
1255 $title = $this->getMock( Title::class );
1256 $title->expects( $this->any() )
1257 ->method( 'getText' )
1258 ->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
1259 $title->expects( $this->any() )
1260 ->method( 'getDbKey' )
1261 ->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
1262 $title->expects( $this->any() )
1263 ->method( 'getNamespace' )
1264 ->will( $this->returnValue( $ns ) );
1265 return $title;
1266 }
1267
1268 public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced() {
1269 $user = $this->getMockNonAnonUserWithId( 1 );
1270 $oldid = 22;
1271 $title = $this->getMockTitle( 'SomeTitle' );
1272 $title->expects( $this->once() )
1273 ->method( 'getNextRevisionID' )
1274 ->with( $oldid )
1275 ->will( $this->returnValue( false ) );
1276
1277 $mockDb = $this->getMockDb();
1278 $mockDb->expects( $this->never() )
1279 ->method( 'selectRow' );
1280
1281 $mockCache = $this->getMockCache();
1282 $mockDb->expects( $this->never() )
1283 ->method( 'get' );
1284 $mockDb->expects( $this->never() )
1285 ->method( 'set' );
1286 $mockDb->expects( $this->never() )
1287 ->method( 'delete' );
1288
1289 $store = new WatchedItemStore(
1290 $this->getMockLoadBalancer( $mockDb ),
1291 $mockCache
1292 );
1293
1294 // Note: This does not actually assert the job is correct
1295 $callableCallCounter = 0;
1296 $store->overrideDeferredUpdatesAddCallableUpdateCallback(
1297 function( $callable ) use ( &$callableCallCounter ) {
1298 $callableCallCounter++;
1299 $this->assertInternalType( 'callable', $callable );
1300 }
1301 );
1302
1303 $this->assertTrue(
1304 $store->resetNotificationTimestamp(
1305 $user,
1306 $title,
1307 'force',
1308 $oldid
1309 )
1310 );
1311 $this->assertEquals( 1, $callableCallCounter );
1312 }
1313
1314 public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced() {
1315 $user = $this->getMockNonAnonUserWithId( 1 );
1316 $oldid = 22;
1317 $title = $this->getMockTitle( 'SomeDbKey' );
1318 $title->expects( $this->once() )
1319 ->method( 'getNextRevisionID' )
1320 ->with( $oldid )
1321 ->will( $this->returnValue( 33 ) );
1322
1323 $mockDb = $this->getMockDb();
1324 $mockDb->expects( $this->once() )
1325 ->method( 'selectRow' )
1326 ->with(
1327 'watchlist',
1328 'wl_notificationtimestamp',
1329 [
1330 'wl_user' => 1,
1331 'wl_namespace' => 0,
1332 'wl_title' => 'SomeDbKey',
1333 ]
1334 )
1335 ->will( $this->returnValue(
1336 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1337 ) );
1338
1339 $mockCache = $this->getMockCache();
1340 $mockDb->expects( $this->never() )
1341 ->method( 'get' );
1342 $mockDb->expects( $this->never() )
1343 ->method( 'set' );
1344 $mockDb->expects( $this->never() )
1345 ->method( 'delete' );
1346
1347 $store = new WatchedItemStore(
1348 $this->getMockLoadBalancer( $mockDb ),
1349 $mockCache
1350 );
1351
1352 // Note: This does not actually assert the job is correct
1353 $addUpdateCallCounter = 0;
1354 $store->overrideDeferredUpdatesAddCallableUpdateCallback(
1355 function( $callable ) use ( &$addUpdateCallCounter ) {
1356 $addUpdateCallCounter++;
1357 $this->assertInternalType( 'callable', $callable );
1358 }
1359 );
1360
1361 $getTimestampCallCounter = 0;
1362 $store->overrideRevisionGetTimestampFromIdCallback(
1363 function( $titleParam, $oldidParam ) use ( &$getTimestampCallCounter, $title, $oldid ) {
1364 $getTimestampCallCounter++;
1365 $this->assertEquals( $title, $titleParam );
1366 $this->assertEquals( $oldid, $oldidParam );
1367 }
1368 );
1369
1370 $this->assertTrue(
1371 $store->resetNotificationTimestamp(
1372 $user,
1373 $title,
1374 'force',
1375 $oldid
1376 )
1377 );
1378 $this->assertEquals( 1, $addUpdateCallCounter );
1379 $this->assertEquals( 1, $getTimestampCallCounter );
1380 }
1381
1382 public function testUpdateNotificationTimestamp_watchersExist() {
1383 $mockDb = $this->getMockDb();
1384 $mockDb->expects( $this->once() )
1385 ->method( 'select' )
1386 ->with(
1387 [ 'watchlist' ],
1388 [ 'wl_user' ],
1389 [
1390 'wl_user != 1',
1391 'wl_namespace' => 0,
1392 'wl_title' => 'SomeDbKey',
1393 'wl_notificationtimestamp IS NULL'
1394 ]
1395 )
1396 ->will(
1397 $this->returnValue( [
1398 $this->getFakeRow( [ 'wl_user' => '2' ] ),
1399 $this->getFakeRow( [ 'wl_user' => '3' ] )
1400 ] )
1401 );
1402 $mockDb->expects( $this->once() )
1403 ->method( 'onTransactionIdle' )
1404 ->with( $this->isType( 'callable' ) )
1405 ->will( $this->returnCallback( function( $callable ) {
1406 $callable();
1407 } ) );
1408 $mockDb->expects( $this->once() )
1409 ->method( 'update' )
1410 ->with(
1411 'watchlist',
1412 [ 'wl_notificationtimestamp' => null ],
1413 [
1414 'wl_user' => [ 2, 3 ],
1415 'wl_namespace' => 0,
1416 'wl_title' => 'SomeDbKey',
1417 ]
1418 );
1419
1420 $mockCache = $this->getMockCache();
1421 $mockCache->expects( $this->never() )->method( 'set' );
1422 $mockCache->expects( $this->never() )->method( 'get' );
1423 $mockCache->expects( $this->never() )->method( 'delete' );
1424
1425 $store = new WatchedItemStore(
1426 $this->getMockLoadBalancer( $mockDb ),
1427 $mockCache
1428 );
1429
1430 $this->assertEquals(
1431 [ 2, 3 ],
1432 $store->updateNotificationTimestamp(
1433 $this->getMockNonAnonUserWithId( 1 ),
1434 new TitleValue( 0, 'SomeDbKey' ),
1435 '20151212010101'
1436 )
1437 );
1438 }
1439
1440 public function testUpdateNotificationTimestamp_noWatchers() {
1441 $mockDb = $this->getMockDb();
1442 $mockDb->expects( $this->once() )
1443 ->method( 'select' )
1444 ->with(
1445 [ 'watchlist' ],
1446 [ 'wl_user' ],
1447 [
1448 'wl_user != 1',
1449 'wl_namespace' => 0,
1450 'wl_title' => 'SomeDbKey',
1451 'wl_notificationtimestamp IS NULL'
1452 ]
1453 )
1454 ->will(
1455 $this->returnValue( [] )
1456 );
1457 $mockDb->expects( $this->never() )
1458 ->method( 'onTransactionIdle' );
1459 $mockDb->expects( $this->never() )
1460 ->method( 'update' );
1461
1462 $mockCache = $this->getMockCache();
1463 $mockCache->expects( $this->never() )->method( 'set' );
1464 $mockCache->expects( $this->never() )->method( 'get' );
1465 $mockCache->expects( $this->never() )->method( 'delete' );
1466
1467 $store = new WatchedItemStore(
1468 $this->getMockLoadBalancer( $mockDb ),
1469 $mockCache
1470 );
1471
1472 $watchers = $store->updateNotificationTimestamp(
1473 $this->getMockNonAnonUserWithId( 1 ),
1474 new TitleValue( 0, 'SomeDbKey' ),
1475 '20151212010101'
1476 );
1477 $this->assertInternalType( 'array', $watchers );
1478 $this->assertEmpty( $watchers );
1479 }
1480
1481 public function testUpdateNotificationTimestamp_clearsCachedItems() {
1482 $user = $this->getMockNonAnonUserWithId( 1 );
1483 $titleValue = new TitleValue( 0, 'SomeDbKey' );
1484
1485 $mockDb = $this->getMockDb();
1486 $mockDb->expects( $this->once() )
1487 ->method( 'selectRow' )
1488 ->will( $this->returnValue(
1489 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1490 ) );
1491 $mockDb->expects( $this->once() )
1492 ->method( 'select' )
1493 ->will(
1494 $this->returnValue( [
1495 $this->getFakeRow( [ 'wl_user' => '2' ] ),
1496 $this->getFakeRow( [ 'wl_user' => '3' ] )
1497 ] )
1498 );
1499 $mockDb->expects( $this->once() )
1500 ->method( 'onTransactionIdle' )
1501 ->with( $this->isType( 'callable' ) )
1502 ->will( $this->returnCallback( function( $callable ) {
1503 $callable();
1504 } ) );
1505 $mockDb->expects( $this->once() )
1506 ->method( 'update' );
1507
1508 $mockCache = $this->getMockCache();
1509 $mockCache->expects( $this->once() )
1510 ->method( 'set' )
1511 ->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
1512 $mockCache->expects( $this->once() )
1513 ->method( 'get' )
1514 ->with( '0:SomeDbKey:1' );
1515 $mockCache->expects( $this->once() )
1516 ->method( 'delete' )
1517 ->with( '0:SomeDbKey:1' );
1518
1519 $store = new WatchedItemStore(
1520 $this->getMockLoadBalancer( $mockDb ),
1521 $mockCache
1522 );
1523
1524 // This will add the item to the cache
1525 $store->getWatchedItem( $user, $titleValue );
1526
1527 $store->updateNotificationTimestamp(
1528 $this->getMockNonAnonUserWithId( 1 ),
1529 $titleValue,
1530 '20151212010101'
1531 );
1532 }
1533
1534 }