6 * @covers WatchedItemStore
8 class WatchedItemStoreUnitTest
extends PHPUnit_Framework_TestCase
{
11 * @return PHPUnit_Framework_MockObject_MockObject|IDatabase
13 private function getMockDb() {
14 return $this->getMock( IDatabase
::class );
18 * @return PHPUnit_Framework_MockObject_MockObject|LoadBalancer
20 private function getMockLoadBalancer( $mockDb ) {
21 $mock = $this->getMockBuilder( LoadBalancer
::class )
22 ->disableOriginalConstructor()
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 ) );
34 * @return PHPUnit_Framework_MockObject_MockObject|HashBagOStuff
36 private function getMockCache() {
37 $mock = $this->getMockBuilder( HashBagOStuff
::class )
38 ->disableOriginalConstructor()
40 $mock->expects( $this->any() )
42 ->will( $this->returnCallback( function() {
43 return implode( ':', func_get_args() );
50 * @return PHPUnit_Framework_MockObject_MockObject|User
52 private function getMockNonAnonUserWithId( $id ) {
53 $mock = $this->getMock( User
::class );
54 $mock->expects( $this->any() )
56 ->will( $this->returnValue( false ) );
57 $mock->expects( $this->any() )
59 ->will( $this->returnValue( $id ) );
66 private function getAnonUser() {
67 return User
::newFromName( 'Anon_User' );
70 private function getFakeRow( array $rowValues ) {
71 $fakeRow = new stdClass();
72 foreach ( $rowValues as $valueName => $value ) {
73 $fakeRow->$valueName = $value;
78 public function testGetDefaultInstance() {
79 $instanceOne = WatchedItemStore
::getDefaultInstance();
80 $instanceTwo = WatchedItemStore
::getDefaultInstance();
81 $this->assertSame( $instanceOne, $instanceTwo );
84 public function testDuplicateEntry_nothingToDuplicate() {
85 $mockDb = $this->getMockDb();
86 $mockDb->expects( $this->once() )
92 'wl_notificationtimestamp',
96 'wl_title' => 'Old_Title',
98 'WatchedItemStore::duplicateEntry',
101 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
103 $store = new WatchedItemStore(
104 $this->getMockLoadBalancer( $mockDb ),
105 $this->getMockCache()
108 $store->duplicateEntry(
109 Title
::newFromText( 'Old_Title' ),
110 Title
::newFromText( 'New_Title' )
114 public function testDuplicateEntry_somethingToDuplicate() {
116 $this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
117 $this->getFakeRow( [ 'wl_user' => 2, 'wl_notificationtimestamp' => null ] ),
120 $mockDb = $this->getMockDb();
121 $mockDb->expects( $this->at( 0 ) )
127 'wl_notificationtimestamp',
131 'wl_title' => 'Old_Title',
134 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
135 $mockDb->expects( $this->at( 1 ) )
136 ->method( 'replace' )
139 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
144 'wl_title' => 'New_Title',
145 'wl_notificationtimestamp' => '20151212010101',
150 'wl_title' => 'New_Title',
151 'wl_notificationtimestamp' => null,
154 $this->isType( 'string' )
157 $mockCache = $this->getMockCache();
158 $mockCache->expects( $this->never() )->method( 'get' );
159 $mockCache->expects( $this->never() )->method( 'delete' );
161 $store = new WatchedItemStore(
162 $this->getMockLoadBalancer( $mockDb ),
166 $store->duplicateEntry(
167 Title
::newFromText( 'Old_Title' ),
168 Title
::newFromText( 'New_Title' )
172 public function testDuplicateAllAssociatedEntries_nothingToDuplicate() {
173 $mockDb = $this->getMockDb();
174 $mockDb->expects( $this->at( 0 ) )
180 'wl_notificationtimestamp',
184 'wl_title' => 'Old_Title',
187 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
188 $mockDb->expects( $this->at( 1 ) )
194 'wl_notificationtimestamp',
198 'wl_title' => 'Old_Title',
201 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
203 $mockCache = $this->getMockCache();
204 $mockCache->expects( $this->never() )->method( 'get' );
205 $mockCache->expects( $this->never() )->method( 'delete' );
207 $store = new WatchedItemStore(
208 $this->getMockLoadBalancer( $mockDb ),
212 $store->duplicateAllAssociatedEntries(
213 Title
::newFromText( 'Old_Title' ),
214 Title
::newFromText( 'New_Title' )
218 public function testDuplicateAllAssociatedEntries_somethingToDuplicate() {
220 $this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
223 $mockDb = $this->getMockDb();
224 $mockDb->expects( $this->at( 0 ) )
230 'wl_notificationtimestamp',
234 'wl_title' => 'Old_Title',
237 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
238 $mockDb->expects( $this->at( 1 ) )
239 ->method( 'replace' )
242 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
247 'wl_title' => 'New_Title',
248 'wl_notificationtimestamp' => '20151212010101',
251 $this->isType( 'string' )
253 $mockDb->expects( $this->at( 2 ) )
259 'wl_notificationtimestamp',
263 'wl_title' => 'Old_Title',
266 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
267 $mockDb->expects( $this->at( 3 ) )
268 ->method( 'replace' )
271 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
276 'wl_title' => 'New_Title',
277 'wl_notificationtimestamp' => '20151212010101',
280 $this->isType( 'string' )
283 $mockCache = $this->getMockCache();
284 $mockCache->expects( $this->never() )->method( 'get' );
285 $mockCache->expects( $this->never() )->method( 'delete' );
287 $store = new WatchedItemStore(
288 $this->getMockLoadBalancer( $mockDb ),
292 $store->duplicateAllAssociatedEntries(
293 Title
::newFromText( 'Old_Title' ),
294 Title
::newFromText( 'New_Title' )
298 public function testAddWatch_nonAnonymousUser() {
299 $mockDb = $this->getMockDb();
300 $mockDb->expects( $this->once() )
308 'wl_title' => 'Some_Page',
309 'wl_notificationtimestamp' => null,
314 $mockCache = $this->getMockCache();
315 $mockCache->expects( $this->once() )
317 ->with( '0:Some_Page:1' );
319 $store = new WatchedItemStore(
320 $this->getMockLoadBalancer( $mockDb ),
325 $this->getMockNonAnonUserWithId( 1 ),
326 Title
::newFromText( 'Some_Page' )
330 public function testAddWatch_anonymousUser() {
331 $mockDb = $this->getMockDb();
332 $mockDb->expects( $this->never() )
333 ->method( 'insert' );
335 $mockCache = $this->getMockCache();
336 $mockCache->expects( $this->never() )
337 ->method( 'delete' );
339 $store = new WatchedItemStore(
340 $this->getMockLoadBalancer( $mockDb ),
345 $this->getAnonUser(),
346 Title
::newFromText( 'Some_Page' )
350 public function testAddWatchBatch_nonAnonymousUser() {
351 $mockDb = $this->getMockDb();
352 $mockDb->expects( $this->once() )
360 'wl_title' => 'Some_Page',
361 'wl_notificationtimestamp' => null,
366 'wl_title' => 'Some_Page',
367 'wl_notificationtimestamp' => null,
372 $mockCache = $this->getMockCache();
373 $mockCache->expects( $this->exactly( 2 ) )
374 ->method( 'delete' );
375 $mockCache->expects( $this->at( 1 ) )
377 ->with( '0:Some_Page:1' );
378 $mockCache->expects( $this->at( 3 ) )
380 ->with( '1:Some_Page:1' );
382 $store = new WatchedItemStore(
383 $this->getMockLoadBalancer( $mockDb ),
387 $mockUser = $this->getMockNonAnonUserWithId( 1 );
390 $store->addWatchBatch(
392 [ $mockUser, new TitleValue( 0, 'Some_Page' ) ],
393 [ $mockUser, new TitleValue( 1, 'Some_Page' ) ],
399 public function testAddWatchBatch_anonymousUserCombinationsAreSkipped() {
400 $mockDb = $this->getMockDb();
401 $mockDb->expects( $this->once() )
409 'wl_title' => 'Some_Page',
410 'wl_notificationtimestamp' => null,
415 $mockCache = $this->getMockCache();
416 $mockCache->expects( $this->once() )
418 ->with( '0:Some_Page:1' );
420 $store = new WatchedItemStore(
421 $this->getMockLoadBalancer( $mockDb ),
426 $store->addWatchBatch(
428 [ $this->getMockNonAnonUserWithId( 1 ), new TitleValue( 0, 'Some_Page' ) ],
429 [ $this->getAnonUser(), new TitleValue( 0, 'Other_Page' ) ],
435 public function testAddWatchBatchReturnsFalse_whenOnlyGivenAnonymousUserCombinations() {
436 $mockDb = $this->getMockDb();
437 $mockDb->expects( $this->never() )
438 ->method( 'insert' );
440 $mockCache = $this->getMockCache();
441 $mockCache->expects( $this->never() )
442 ->method( 'delete' );
444 $store = new WatchedItemStore(
445 $this->getMockLoadBalancer( $mockDb ),
449 $anonUser = $this->getAnonUser();
451 $store->addWatchBatch(
453 [ $anonUser, new TitleValue( 0, 'Some_Page' ) ],
454 [ $anonUser, new TitleValue( 1, 'Other_Page' ) ],
460 public function testAddWatchBatchReturnsFalse_whenGivenEmptyList() {
461 $mockDb = $this->getMockDb();
462 $mockDb->expects( $this->never() )
463 ->method( 'insert' );
465 $mockCache = $this->getMockCache();
466 $mockCache->expects( $this->never() )
467 ->method( 'delete' );
469 $store = new WatchedItemStore(
470 $this->getMockLoadBalancer( $mockDb ),
475 $store->addWatchBatch( [] )
479 public function testLoadWatchedItem_existingItem() {
480 $mockDb = $this->getMockDb();
481 $mockDb->expects( $this->once() )
482 ->method( 'selectRow' )
485 'wl_notificationtimestamp',
489 'wl_title' => 'SomeDbKey',
492 ->will( $this->returnValue(
493 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
496 $mockCache = $this->getMockCache();
497 $mockCache->expects( $this->once() )
503 $store = new WatchedItemStore(
504 $this->getMockLoadBalancer( $mockDb ),
508 $watchedItem = $store->loadWatchedItem(
509 $this->getMockNonAnonUserWithId( 1 ),
510 new TitleValue( 0, 'SomeDbKey' )
512 $this->assertInstanceOf( 'WatchedItem', $watchedItem );
513 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
514 $this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
515 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
518 public function testLoadWatchedItem_noItem() {
519 $mockDb = $this->getMockDb();
520 $mockDb->expects( $this->once() )
521 ->method( 'selectRow' )
524 'wl_notificationtimestamp',
528 'wl_title' => 'SomeDbKey',
531 ->will( $this->returnValue( [] ) );
533 $mockCache = $this->getMockCache();
534 $mockCache->expects( $this->never() )->method( 'get' );
535 $mockCache->expects( $this->never() )->method( 'delete' );
537 $store = new WatchedItemStore(
538 $this->getMockLoadBalancer( $mockDb ),
543 $store->loadWatchedItem(
544 $this->getMockNonAnonUserWithId( 1 ),
545 new TitleValue( 0, 'SomeDbKey' )
550 public function testLoadWatchedItem_anonymousUser() {
551 $mockDb = $this->getMockDb();
552 $mockDb->expects( $this->never() )
553 ->method( 'selectRow' );
555 $mockCache = $this->getMockCache();
556 $mockCache->expects( $this->never() )->method( 'get' );
557 $mockCache->expects( $this->never() )->method( 'delete' );
559 $store = new WatchedItemStore(
560 $this->getMockLoadBalancer( $mockDb ),
565 $store->loadWatchedItem(
566 $this->getAnonUser(),
567 new TitleValue( 0, 'SomeDbKey' )
572 public function testRemoveWatch_existingItem() {
573 $mockDb = $this->getMockDb();
574 $mockDb->expects( $this->once() )
581 'wl_title' => 'SomeDbKey',
584 $mockDb->expects( $this->once() )
585 ->method( 'affectedRows' )
586 ->will( $this->returnValue( 1 ) );
588 $mockCache = $this->getMockCache();
589 $mockCache->expects( $this->never() )->method( 'get' );
590 $mockCache->expects( $this->once() )
592 ->with( '0:SomeDbKey:1' );
594 $store = new WatchedItemStore(
595 $this->getMockLoadBalancer( $mockDb ),
601 $this->getMockNonAnonUserWithId( 1 ),
602 new TitleValue( 0, 'SomeDbKey' )
607 public function testRemoveWatch_noItem() {
608 $mockDb = $this->getMockDb();
609 $mockDb->expects( $this->once() )
616 'wl_title' => 'SomeDbKey',
619 $mockDb->expects( $this->once() )
620 ->method( 'affectedRows' )
621 ->will( $this->returnValue( 0 ) );
623 $mockCache = $this->getMockCache();
624 $mockCache->expects( $this->never() )->method( 'get' );
625 $mockCache->expects( $this->once() )
627 ->with( '0:SomeDbKey:1' );
629 $store = new WatchedItemStore(
630 $this->getMockLoadBalancer( $mockDb ),
636 $this->getMockNonAnonUserWithId( 1 ),
637 new TitleValue( 0, 'SomeDbKey' )
642 public function testRemoveWatch_anonymousUser() {
643 $mockDb = $this->getMockDb();
644 $mockDb->expects( $this->never() )
645 ->method( 'delete' );
647 $mockCache = $this->getMockCache();
648 $mockCache->expects( $this->never() )->method( 'get' );
649 $mockCache->expects( $this->never() )
650 ->method( 'delete' );
652 $store = new WatchedItemStore(
653 $this->getMockLoadBalancer( $mockDb ),
659 $this->getAnonUser(),
660 new TitleValue( 0, 'SomeDbKey' )
665 public function testGetWatchedItem_existingItem() {
666 $mockDb = $this->getMockDb();
667 $mockDb->expects( $this->once() )
668 ->method( 'selectRow' )
671 'wl_notificationtimestamp',
675 'wl_title' => 'SomeDbKey',
678 ->will( $this->returnValue(
679 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
682 $mockCache = $this->getMockCache();
683 $mockCache->expects( $this->never() )->method( 'delete' );
684 $mockCache->expects( $this->once() )
689 ->will( $this->returnValue( null ) );
690 $mockCache->expects( $this->once() )
696 $store = new WatchedItemStore(
697 $this->getMockLoadBalancer( $mockDb ),
701 $watchedItem = $store->getWatchedItem(
702 $this->getMockNonAnonUserWithId( 1 ),
703 new TitleValue( 0, 'SomeDbKey' )
705 $this->assertInstanceOf( 'WatchedItem', $watchedItem );
706 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
707 $this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
708 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
711 public function testGetWatchedItem_cachedItem() {
712 $mockDb = $this->getMockDb();
713 $mockDb->expects( $this->never() )
714 ->method( 'selectRow' );
716 $mockUser = $this->getMockNonAnonUserWithId( 1 );
717 $linkTarget = new TitleValue( 0, 'SomeDbKey' );
718 $cachedItem = new WatchedItem( $mockUser, $linkTarget, '20151212010101' );
720 $mockCache = $this->getMockCache();
721 $mockCache->expects( $this->never() )->method( 'delete' );
722 $mockCache->expects( $this->never() )->method( 'set' );
723 $mockCache->expects( $this->once() )
728 ->will( $this->returnValue( $cachedItem ) );
730 $store = new WatchedItemStore(
731 $this->getMockLoadBalancer( $mockDb ),
737 $store->getWatchedItem(
744 public function testGetWatchedItem_noItem() {
745 $mockDb = $this->getMockDb();
746 $mockDb->expects( $this->once() )
747 ->method( 'selectRow' )
750 'wl_notificationtimestamp',
754 'wl_title' => 'SomeDbKey',
757 ->will( $this->returnValue( [] ) );
759 $mockCache = $this->getMockCache();
760 $mockCache->expects( $this->never() )->method( 'set' );
761 $mockCache->expects( $this->never() )->method( 'delete' );
762 $mockCache->expects( $this->once() )
764 ->with( '0:SomeDbKey:1' )
765 ->will( $this->returnValue( false ) );
767 $store = new WatchedItemStore(
768 $this->getMockLoadBalancer( $mockDb ),
773 $store->getWatchedItem(
774 $this->getMockNonAnonUserWithId( 1 ),
775 new TitleValue( 0, 'SomeDbKey' )
780 public function testGetWatchedItem_anonymousUser() {
781 $mockDb = $this->getMockDb();
782 $mockDb->expects( $this->never() )
783 ->method( 'selectRow' );
785 $mockCache = $this->getMockCache();
786 $mockCache->expects( $this->never() )->method( 'set' );
787 $mockCache->expects( $this->never() )->method( 'get' );
788 $mockCache->expects( $this->never() )->method( 'delete' );
790 $store = new WatchedItemStore(
791 $this->getMockLoadBalancer( $mockDb ),
796 $store->getWatchedItem(
797 $this->getAnonUser(),
798 new TitleValue( 0, 'SomeDbKey' )
803 public function testIsWatchedItem_existingItem() {
804 $mockDb = $this->getMockDb();
805 $mockDb->expects( $this->once() )
806 ->method( 'selectRow' )
809 'wl_notificationtimestamp',
813 'wl_title' => 'SomeDbKey',
816 ->will( $this->returnValue(
817 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
820 $mockCache = $this->getMockCache();
821 $mockCache->expects( $this->never() )->method( 'delete' );
822 $mockCache->expects( $this->once() )
824 ->with( '0:SomeDbKey:1' )
825 ->will( $this->returnValue( false ) );
826 $mockCache->expects( $this->once() )
832 $store = new WatchedItemStore(
833 $this->getMockLoadBalancer( $mockDb ),
839 $this->getMockNonAnonUserWithId( 1 ),
840 new TitleValue( 0, 'SomeDbKey' )
845 public function testIsWatchedItem_noItem() {
846 $mockDb = $this->getMockDb();
847 $mockDb->expects( $this->once() )
848 ->method( 'selectRow' )
851 'wl_notificationtimestamp',
855 'wl_title' => 'SomeDbKey',
858 ->will( $this->returnValue( [] ) );
860 $mockCache = $this->getMockCache();
861 $mockCache->expects( $this->never() )->method( 'set' );
862 $mockCache->expects( $this->never() )->method( 'delete' );
863 $mockCache->expects( $this->once() )
865 ->with( '0:SomeDbKey:1' )
866 ->will( $this->returnValue( false ) );
868 $store = new WatchedItemStore(
869 $this->getMockLoadBalancer( $mockDb ),
875 $this->getMockNonAnonUserWithId( 1 ),
876 new TitleValue( 0, 'SomeDbKey' )
881 public function testIsWatchedItem_anonymousUser() {
882 $mockDb = $this->getMockDb();
883 $mockDb->expects( $this->never() )
884 ->method( 'selectRow' );
886 $mockCache = $this->getMockCache();
887 $mockCache->expects( $this->never() )->method( 'set' );
888 $mockCache->expects( $this->never() )->method( 'get' );
889 $mockCache->expects( $this->never() )->method( 'delete' );
891 $store = new WatchedItemStore(
892 $this->getMockLoadBalancer( $mockDb ),
898 $this->getAnonUser(),
899 new TitleValue( 0, 'SomeDbKey' )
904 public function testResetNotificationTimestamp_anonymousUser() {
905 $mockDb = $this->getMockDb();
906 $mockDb->expects( $this->never() )
907 ->method( 'selectRow' );
909 $mockCache = $this->getMockCache();
910 $mockCache->expects( $this->never() )->method( 'get' );
911 $mockCache->expects( $this->never() )->method( 'set' );
912 $mockCache->expects( $this->never() )->method( 'delete' );
914 $store = new WatchedItemStore(
915 $this->getMockLoadBalancer( $mockDb ),
920 $store->resetNotificationTimestamp(
921 $this->getAnonUser(),
922 Title
::newFromText( 'SomeDbKey' )
927 public function testResetNotificationTimestamp_noItem() {
928 $mockDb = $this->getMockDb();
929 $mockDb->expects( $this->once() )
930 ->method( 'selectRow' )
933 'wl_notificationtimestamp',
937 'wl_title' => 'SomeDbKey',
940 ->will( $this->returnValue( [] ) );
942 $mockCache = $this->getMockCache();
943 $mockCache->expects( $this->never() )->method( 'get' );
944 $mockCache->expects( $this->never() )->method( 'set' );
945 $mockCache->expects( $this->never() )->method( 'delete' );
947 $store = new WatchedItemStore(
948 $this->getMockLoadBalancer( $mockDb ),
953 $store->resetNotificationTimestamp(
954 $this->getMockNonAnonUserWithId( 1 ),
955 Title
::newFromText( 'SomeDbKey' )
960 public function testResetNotificationTimestamp_item() {
961 $user = $this->getMockNonAnonUserWithId( 1 );
962 $title = Title
::newFromText( 'SomeDbKey' );
964 $mockDb = $this->getMockDb();
965 $mockDb->expects( $this->once() )
966 ->method( 'selectRow' )
969 'wl_notificationtimestamp',
973 'wl_title' => 'SomeDbKey',
976 ->will( $this->returnValue(
977 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
980 $mockCache = $this->getMockCache();
981 $mockCache->expects( $this->never() )->method( 'get' );
982 $mockCache->expects( $this->once() )
986 $this->isInstanceOf( WatchedItem
::class )
988 $mockCache->expects( $this->once() )
990 ->with( '0:SomeDbKey:1' );
992 $store = new WatchedItemStore(
993 $this->getMockLoadBalancer( $mockDb ),
997 // Note: This does not actually assert the job is correct
998 $callableCallCounter = 0;
999 $mockCallback = function( $callable ) use ( &$callableCallCounter ) {
1000 $callableCallCounter++
;
1001 $this->assertInternalType( 'callable', $callable );
1003 $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
1006 $store->resetNotificationTimestamp(
1011 $this->assertEquals( 1, $callableCallCounter );
1014 public function testResetNotificationTimestamp_noItemForced() {
1015 $user = $this->getMockNonAnonUserWithId( 1 );
1016 $title = Title
::newFromText( 'SomeDbKey' );
1018 $mockDb = $this->getMockDb();
1019 $mockDb->expects( $this->never() )
1020 ->method( 'selectRow' );
1022 $mockCache = $this->getMockCache();
1023 $mockDb->expects( $this->never() )
1025 $mockDb->expects( $this->never() )
1027 $mockDb->expects( $this->never() )
1028 ->method( 'delete' );
1030 $store = new WatchedItemStore(
1031 $this->getMockLoadBalancer( $mockDb ),
1035 // Note: This does not actually assert the job is correct
1036 $callableCallCounter = 0;
1037 $mockCallback = function( $callable ) use ( &$callableCallCounter ) {
1038 $callableCallCounter++
;
1039 $this->assertInternalType( 'callable', $callable );
1041 $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
1044 $store->resetNotificationTimestamp(
1050 $this->assertEquals( 1, $callableCallCounter );
1057 * @return PHPUnit_Framework_MockObject_MockObject|Title
1059 private function getMockTitle( $text, $ns = 0 ) {
1060 $title = $this->getMock( Title
::class );
1061 $title->expects( $this->any() )
1062 ->method( 'getText' )
1063 ->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
1064 $title->expects( $this->any() )
1065 ->method( 'getDbKey' )
1066 ->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
1067 $title->expects( $this->any() )
1068 ->method( 'getNamespace' )
1069 ->will( $this->returnValue( $ns ) );
1073 public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced() {
1074 $user = $this->getMockNonAnonUserWithId( 1 );
1076 $title = $this->getMockTitle( 'SomeTitle' );
1077 $title->expects( $this->once() )
1078 ->method( 'getNextRevisionID' )
1080 ->will( $this->returnValue( false ) );
1082 $mockDb = $this->getMockDb();
1083 $mockDb->expects( $this->never() )
1084 ->method( 'selectRow' );
1086 $mockCache = $this->getMockCache();
1087 $mockDb->expects( $this->never() )
1089 $mockDb->expects( $this->never() )
1091 $mockDb->expects( $this->never() )
1092 ->method( 'delete' );
1094 $store = new WatchedItemStore(
1095 $this->getMockLoadBalancer( $mockDb ),
1099 // Note: This does not actually assert the job is correct
1100 $callableCallCounter = 0;
1101 $store->overrideDeferredUpdatesAddCallableUpdateCallback(
1102 function( $callable ) use ( &$callableCallCounter ) {
1103 $callableCallCounter++
;
1104 $this->assertInternalType( 'callable', $callable );
1109 $store->resetNotificationTimestamp(
1116 $this->assertEquals( 1, $callableCallCounter );
1119 public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced() {
1120 $user = $this->getMockNonAnonUserWithId( 1 );
1122 $title = $this->getMockTitle( 'SomeDbKey' );
1123 $title->expects( $this->once() )
1124 ->method( 'getNextRevisionID' )
1126 ->will( $this->returnValue( 33 ) );
1128 $mockDb = $this->getMockDb();
1129 $mockDb->expects( $this->once() )
1130 ->method( 'selectRow' )
1133 'wl_notificationtimestamp',
1136 'wl_namespace' => 0,
1137 'wl_title' => 'SomeDbKey',
1140 ->will( $this->returnValue(
1141 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1144 $mockCache = $this->getMockCache();
1145 $mockDb->expects( $this->never() )
1147 $mockDb->expects( $this->never() )
1149 $mockDb->expects( $this->never() )
1150 ->method( 'delete' );
1152 $store = new WatchedItemStore(
1153 $this->getMockLoadBalancer( $mockDb ),
1157 // Note: This does not actually assert the job is correct
1158 $addUpdateCallCounter = 0;
1159 $store->overrideDeferredUpdatesAddCallableUpdateCallback(
1160 function( $callable ) use ( &$addUpdateCallCounter ) {
1161 $addUpdateCallCounter++
;
1162 $this->assertInternalType( 'callable', $callable );
1166 $getTimestampCallCounter = 0;
1167 $store->overrideRevisionGetTimestampFromIdCallback(
1168 function( $titleParam, $oldidParam ) use ( &$getTimestampCallCounter, $title, $oldid ) {
1169 $getTimestampCallCounter++
;
1170 $this->assertEquals( $title, $titleParam );
1171 $this->assertEquals( $oldid, $oldidParam );
1176 $store->resetNotificationTimestamp(
1183 $this->assertEquals( 1, $addUpdateCallCounter );
1184 $this->assertEquals( 1, $getTimestampCallCounter );
1187 public function testUpdateNotificationTimestamp_watchersExist() {
1188 $mockDb = $this->getMockDb();
1189 $mockDb->expects( $this->once() )
1190 ->method( 'select' )
1196 'wl_namespace' => 0,
1197 'wl_title' => 'SomeDbKey',
1198 'wl_notificationtimestamp IS NULL'
1202 $this->returnValue( [
1203 $this->getFakeRow( [ 'wl_user' => '2' ] ),
1204 $this->getFakeRow( [ 'wl_user' => '3' ] )
1207 $mockDb->expects( $this->once() )
1208 ->method( 'onTransactionIdle' )
1209 ->with( $this->isType( 'callable' ) )
1210 ->will( $this->returnCallback( function( $callable ) {
1213 $mockDb->expects( $this->once() )
1214 ->method( 'update' )
1217 [ 'wl_notificationtimestamp' => null ],
1219 'wl_user' => [ 2, 3 ],
1220 'wl_namespace' => 0,
1221 'wl_title' => 'SomeDbKey',
1225 $mockCache = $this->getMockCache();
1226 $mockCache->expects( $this->never() )->method( 'set' );
1227 $mockCache->expects( $this->never() )->method( 'get' );
1228 $mockCache->expects( $this->never() )->method( 'delete' );
1230 $store = new WatchedItemStore(
1231 $this->getMockLoadBalancer( $mockDb ),
1235 $this->assertEquals(
1237 $store->updateNotificationTimestamp(
1238 $this->getMockNonAnonUserWithId( 1 ),
1239 new TitleValue( 0, 'SomeDbKey' ),
1245 public function testUpdateNotificationTimestamp_noWatchers() {
1246 $mockDb = $this->getMockDb();
1247 $mockDb->expects( $this->once() )
1248 ->method( 'select' )
1254 'wl_namespace' => 0,
1255 'wl_title' => 'SomeDbKey',
1256 'wl_notificationtimestamp IS NULL'
1260 $this->returnValue( [] )
1262 $mockDb->expects( $this->never() )
1263 ->method( 'onTransactionIdle' );
1264 $mockDb->expects( $this->never() )
1265 ->method( 'update' );
1267 $mockCache = $this->getMockCache();
1268 $mockCache->expects( $this->never() )->method( 'set' );
1269 $mockCache->expects( $this->never() )->method( 'get' );
1270 $mockCache->expects( $this->never() )->method( 'delete' );
1272 $store = new WatchedItemStore(
1273 $this->getMockLoadBalancer( $mockDb ),
1277 $watchers = $store->updateNotificationTimestamp(
1278 $this->getMockNonAnonUserWithId( 1 ),
1279 new TitleValue( 0, 'SomeDbKey' ),
1282 $this->assertInternalType( 'array', $watchers );
1283 $this->assertEmpty( $watchers );
1286 public function testUpdateNotificationTimestamp_clearsCachedItems() {
1287 $user = $this->getMockNonAnonUserWithId( 1 );
1288 $titleValue = new TitleValue( 0, 'SomeDbKey' );
1290 $mockDb = $this->getMockDb();
1291 $mockDb->expects( $this->once() )
1292 ->method( 'selectRow' )
1293 ->will( $this->returnValue(
1294 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1296 $mockDb->expects( $this->once() )
1297 ->method( 'select' )
1299 $this->returnValue( [
1300 $this->getFakeRow( [ 'wl_user' => '2' ] ),
1301 $this->getFakeRow( [ 'wl_user' => '3' ] )
1304 $mockDb->expects( $this->once() )
1305 ->method( 'onTransactionIdle' )
1306 ->with( $this->isType( 'callable' ) )
1307 ->will( $this->returnCallback( function( $callable ) {
1310 $mockDb->expects( $this->once() )
1311 ->method( 'update' );
1313 $mockCache = $this->getMockCache();
1314 $mockCache->expects( $this->once() )
1316 ->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
1317 $mockCache->expects( $this->once() )
1319 ->with( '0:SomeDbKey:1' );
1320 $mockCache->expects( $this->once() )
1321 ->method( 'delete' )
1322 ->with( '0:SomeDbKey:1' );
1324 $store = new WatchedItemStore(
1325 $this->getMockLoadBalancer( $mockDb ),
1329 // This will add the item to the cache
1330 $store->getWatchedItem( $user, $titleValue );
1332 $store->updateNotificationTimestamp(
1333 $this->getMockNonAnonUserWithId( 1 ),