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 testOverrideDefaultInstance() {
85 $instance = WatchedItemStore
::getDefaultInstance();
86 $scopedOverride = $instance->overrideDefaultInstance( null );
88 $this->assertNotSame( $instance, WatchedItemStore
::getDefaultInstance() );
90 unset( $scopedOverride );
92 $this->assertSame( $instance, WatchedItemStore
::getDefaultInstance() );
95 public function testCountWatchedItems() {
96 $user = $this->getMockNonAnonUserWithId( 1 );
98 $mockDb = $this->getMockDb();
99 $mockDb->expects( $this->exactly( 1 ) )
100 ->method( 'selectField' )
105 'wl_user' => $user->getId(),
107 $this->isType( 'string' )
109 ->will( $this->returnValue( 12 ) );
111 $mockCache = $this->getMockCache();
112 $mockCache->expects( $this->never() )->method( 'get' );
113 $mockCache->expects( $this->never() )->method( 'set' );
114 $mockCache->expects( $this->never() )->method( 'delete' );
116 $store = new WatchedItemStore(
117 $this->getMockLoadBalancer( $mockDb ),
121 $this->assertEquals( 12, $store->countWatchedItems( $user ) );
124 public function testCountWatchers() {
125 $titleValue = new TitleValue( 0, 'SomeDbKey' );
127 $mockDb = $this->getMockDb();
128 $mockDb->expects( $this->exactly( 1 ) )
129 ->method( 'selectField' )
134 'wl_namespace' => $titleValue->getNamespace(),
135 'wl_title' => $titleValue->getDBkey(),
137 $this->isType( 'string' )
139 ->will( $this->returnValue( 7 ) );
141 $mockCache = $this->getMockCache();
142 $mockCache->expects( $this->never() )->method( 'get' );
143 $mockCache->expects( $this->never() )->method( 'set' );
144 $mockCache->expects( $this->never() )->method( 'delete' );
146 $store = new WatchedItemStore(
147 $this->getMockLoadBalancer( $mockDb ),
151 $this->assertEquals( 7, $store->countWatchers( $titleValue ) );
154 public function testCountWatchersMultiple() {
156 new TitleValue( 0, 'SomeDbKey' ),
157 new TitleValue( 0, 'OtherDbKey' ),
158 new TitleValue( 1, 'AnotherDbKey' ),
161 $mockDb = $this->getMockDb();
164 $this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
165 $this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
166 $this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ]
169 $mockDb->expects( $this->once() )
170 ->method( 'makeWhereFrom2d' )
172 [ [ 'SomeDbKey' => 1, 'OtherDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
173 $this->isType( 'string' ),
174 $this->isType( 'string' )
176 ->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
177 $mockDb->expects( $this->once() )
181 [ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
182 [ 'makeWhereFrom2d return value' ],
183 $this->isType( 'string' ),
185 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
189 $this->returnValue( $dbResult )
192 $mockCache = $this->getMockCache();
193 $mockCache->expects( $this->never() )->method( 'get' );
194 $mockCache->expects( $this->never() )->method( 'set' );
195 $mockCache->expects( $this->never() )->method( 'delete' );
197 $store = new WatchedItemStore(
198 $this->getMockLoadBalancer( $mockDb ),
203 0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
204 1 => [ 'AnotherDbKey' => 500 ],
206 $this->assertEquals( $expected, $store->countWatchersMultiple( $titleValues ) );
209 public function provideIntWithDbUnsafeVersion() {
212 [ "50; DROP TABLE watchlist;\n--" ],
217 * @dataProvider provideIntWithDbUnsafeVersion
219 public function testCountWatchersMultiple_withMinimumWatchers( $minWatchers ) {
221 new TitleValue( 0, 'SomeDbKey' ),
222 new TitleValue( 0, 'OtherDbKey' ),
223 new TitleValue( 1, 'AnotherDbKey' ),
226 $mockDb = $this->getMockDb();
229 $this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
230 $this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
231 $this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ]
234 $mockDb->expects( $this->once() )
235 ->method( 'makeWhereFrom2d' )
237 [ [ 'SomeDbKey' => 1, 'OtherDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
238 $this->isType( 'string' ),
239 $this->isType( 'string' )
241 ->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
242 $mockDb->expects( $this->once() )
246 [ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
247 [ 'makeWhereFrom2d return value' ],
248 $this->isType( 'string' ),
250 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
251 'HAVING' => 'COUNT(*) >= 50',
255 $this->returnValue( $dbResult )
258 $mockCache = $this->getMockCache();
259 $mockCache->expects( $this->never() )->method( 'get' );
260 $mockCache->expects( $this->never() )->method( 'set' );
261 $mockCache->expects( $this->never() )->method( 'delete' );
263 $store = new WatchedItemStore(
264 $this->getMockLoadBalancer( $mockDb ),
269 0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
270 1 => [ 'AnotherDbKey' => 500 ],
274 $store->countWatchersMultiple( $titleValues, [ 'minimumWatchers' => $minWatchers ] )
278 public function testCountVisitingWatchers() {
279 $titleValue = new TitleValue( 0, 'SomeDbKey' );
281 $mockDb = $this->getMockDb();
282 $mockDb->expects( $this->exactly( 1 ) )
283 ->method( 'selectField' )
288 'wl_namespace' => $titleValue->getNamespace(),
289 'wl_title' => $titleValue->getDBkey(),
290 'wl_notificationtimestamp >= \'TS111TS\' OR wl_notificationtimestamp IS NULL',
292 $this->isType( 'string' )
294 ->will( $this->returnValue( 7 ) );
295 $mockDb->expects( $this->exactly( 1 ) )
296 ->method( 'addQuotes' )
297 ->will( $this->returnCallback( function( $value ) {
300 $mockDb->expects( $this->exactly( 1 ) )
301 ->method( 'timestamp' )
302 ->will( $this->returnCallback( function( $value ) {
303 return 'TS' . $value . 'TS';
306 $mockCache = $this->getMockCache();
307 $mockCache->expects( $this->never() )->method( 'set' );
308 $mockCache->expects( $this->never() )->method( 'get' );
309 $mockCache->expects( $this->never() )->method( 'delete' );
311 $store = new WatchedItemStore(
312 $this->getMockLoadBalancer( $mockDb ),
316 $this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
319 public function testCountVisitingWatchersMultiple() {
320 $titleValuesWithThresholds = [
321 [ new TitleValue( 0, 'SomeDbKey' ), '111' ],
322 [ new TitleValue( 0, 'OtherDbKey' ), '111' ],
323 [ new TitleValue( 1, 'AnotherDbKey' ), '123' ],
327 $this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
328 $this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
329 $this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ] ),
331 $mockDb = $this->getMockDb();
332 $mockDb->expects( $this->exactly( 2 * 3 ) )
333 ->method( 'addQuotes' )
334 ->will( $this->returnCallback( function( $value ) {
337 $mockDb->expects( $this->exactly( 3 ) )
338 ->method( 'timestamp' )
339 ->will( $this->returnCallback( function( $value ) {
340 return 'TS' . $value . 'TS';
342 $mockDb->expects( $this->any() )
343 ->method( 'makeList' )
345 $this->isType( 'array' ),
346 $this->isType( 'int' )
348 ->will( $this->returnCallback( function( $a, $conj ) {
349 $sqlConj = $conj === LIST_AND ?
' AND ' : ' OR ';
350 return join( $sqlConj, array_map( function( $s ) {
351 return '(' . $s . ')';
355 $mockDb->expects( $this->never() )
356 ->method( 'makeWhereFrom2d' );
359 '((wl_namespace = 0) AND (' .
360 "(((wl_title = 'SomeDbKey') AND (" .
361 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
363 "(wl_title = 'OtherDbKey') AND (" .
364 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
366 ') OR ((wl_namespace = 1) AND (' .
367 "(((wl_title = 'AnotherDbKey') AND (".
368 "(wl_notificationtimestamp >= 'TS123TS') OR (wl_notificationtimestamp IS NULL)" .
370 $mockDb->expects( $this->once() )
374 [ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
376 $this->isType( 'string' ),
378 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
382 $this->returnValue( $dbResult )
385 $mockCache = $this->getMockCache();
386 $mockCache->expects( $this->never() )->method( 'get' );
387 $mockCache->expects( $this->never() )->method( 'set' );
388 $mockCache->expects( $this->never() )->method( 'delete' );
390 $store = new WatchedItemStore(
391 $this->getMockLoadBalancer( $mockDb ),
396 0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
397 1 => [ 'AnotherDbKey' => 500 ],
401 $store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
405 public function testCountVisitingWatchersMultiple_withMissingTargets() {
406 $titleValuesWithThresholds = [
407 [ new TitleValue( 0, 'SomeDbKey' ), '111' ],
408 [ new TitleValue( 0, 'OtherDbKey' ), '111' ],
409 [ new TitleValue( 1, 'AnotherDbKey' ), '123' ],
410 [ new TitleValue( 0, 'SomeNotExisitingDbKey' ), null ],
411 [ new TitleValue( 0, 'OtherNotExisitingDbKey' ), null ],
415 $this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
416 $this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
417 $this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ] ),
419 [ 'wl_title' => 'SomeNotExisitingDbKey', 'wl_namespace' => 0, 'watchers' => 100 ]
422 [ 'wl_title' => 'OtherNotExisitingDbKey', 'wl_namespace' => 0, 'watchers' => 200 ]
425 $mockDb = $this->getMockDb();
426 $mockDb->expects( $this->exactly( 2 * 3 ) )
427 ->method( 'addQuotes' )
428 ->will( $this->returnCallback( function( $value ) {
431 $mockDb->expects( $this->exactly( 3 ) )
432 ->method( 'timestamp' )
433 ->will( $this->returnCallback( function( $value ) {
434 return 'TS' . $value . 'TS';
436 $mockDb->expects( $this->any() )
437 ->method( 'makeList' )
439 $this->isType( 'array' ),
440 $this->isType( 'int' )
442 ->will( $this->returnCallback( function( $a, $conj ) {
443 $sqlConj = $conj === LIST_AND ?
' AND ' : ' OR ';
444 return join( $sqlConj, array_map( function( $s ) {
445 return '(' . $s . ')';
449 $mockDb->expects( $this->once() )
450 ->method( 'makeWhereFrom2d' )
452 [ [ 'SomeNotExisitingDbKey' => 1, 'OtherNotExisitingDbKey' => 1 ] ],
453 $this->isType( 'string' ),
454 $this->isType( 'string' )
456 ->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
459 '((wl_namespace = 0) AND (' .
460 "(((wl_title = 'SomeDbKey') AND (" .
461 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
463 "(wl_title = 'OtherDbKey') AND (" .
464 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
466 ') OR ((wl_namespace = 1) AND (' .
467 "(((wl_title = 'AnotherDbKey') AND (".
468 "(wl_notificationtimestamp >= 'TS123TS') OR (wl_notificationtimestamp IS NULL)" .
471 '(makeWhereFrom2d return value)';
472 $mockDb->expects( $this->once() )
476 [ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
478 $this->isType( 'string' ),
480 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
484 $this->returnValue( $dbResult )
487 $mockCache = $this->getMockCache();
488 $mockCache->expects( $this->never() )->method( 'get' );
489 $mockCache->expects( $this->never() )->method( 'set' );
490 $mockCache->expects( $this->never() )->method( 'delete' );
492 $store = new WatchedItemStore(
493 $this->getMockLoadBalancer( $mockDb ),
499 'SomeDbKey' => 100, 'OtherDbKey' => 300,
500 'SomeNotExisitingDbKey' => 100, 'OtherNotExisitingDbKey' => 200
502 1 => [ 'AnotherDbKey' => 500 ],
506 $store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
511 * @dataProvider provideIntWithDbUnsafeVersion
513 public function testCountVisitingWatchersMultiple_withMinimumWatchers( $minWatchers ) {
514 $titleValuesWithThresholds = [
515 [ new TitleValue( 0, 'SomeDbKey' ), '111' ],
516 [ new TitleValue( 0, 'OtherDbKey' ), '111' ],
517 [ new TitleValue( 1, 'AnotherDbKey' ), '123' ],
520 $mockDb = $this->getMockDb();
521 $mockDb->expects( $this->any() )
522 ->method( 'makeList' )
523 ->will( $this->returnValue( 'makeList return value' ) );
524 $mockDb->expects( $this->once() )
528 [ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
529 'makeList return value',
530 $this->isType( 'string' ),
532 'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
533 'HAVING' => 'COUNT(*) >= 50',
537 $this->returnValue( [] )
540 $mockCache = $this->getMockCache();
541 $mockCache->expects( $this->never() )->method( 'get' );
542 $mockCache->expects( $this->never() )->method( 'set' );
543 $mockCache->expects( $this->never() )->method( 'delete' );
545 $store = new WatchedItemStore(
546 $this->getMockLoadBalancer( $mockDb ),
551 0 => [ 'SomeDbKey' => 0, 'OtherDbKey' => 0 ],
552 1 => [ 'AnotherDbKey' => 0 ],
556 $store->countVisitingWatchersMultiple( $titleValuesWithThresholds, $minWatchers )
560 public function testCountUnreadNotifications() {
561 $user = $this->getMockNonAnonUserWithId( 1 );
563 $mockDb = $this->getMockDb();
564 $mockDb->expects( $this->exactly( 1 ) )
565 ->method( 'selectRowCount' )
570 "wl_notificationtimestamp IS NOT NULL",
573 $this->isType( 'string' )
575 ->will( $this->returnValue( 9 ) );
577 $mockCache = $this->getMockCache();
578 $mockCache->expects( $this->never() )->method( 'set' );
579 $mockCache->expects( $this->never() )->method( 'get' );
580 $mockCache->expects( $this->never() )->method( 'delete' );
582 $store = new WatchedItemStore(
583 $this->getMockLoadBalancer( $mockDb ),
587 $this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
591 * @dataProvider provideIntWithDbUnsafeVersion
593 public function testCountUnreadNotifications_withUnreadLimit_overLimit( $limit ) {
594 $user = $this->getMockNonAnonUserWithId( 1 );
596 $mockDb = $this->getMockDb();
597 $mockDb->expects( $this->exactly( 1 ) )
598 ->method( 'selectRowCount' )
603 "wl_notificationtimestamp IS NOT NULL",
606 $this->isType( 'string' ),
609 ->will( $this->returnValue( 50 ) );
611 $mockCache = $this->getMockCache();
612 $mockCache->expects( $this->never() )->method( 'set' );
613 $mockCache->expects( $this->never() )->method( 'get' );
614 $mockCache->expects( $this->never() )->method( 'delete' );
616 $store = new WatchedItemStore(
617 $this->getMockLoadBalancer( $mockDb ),
623 $store->countUnreadNotifications( $user, $limit )
628 * @dataProvider provideIntWithDbUnsafeVersion
630 public function testCountUnreadNotifications_withUnreadLimit_underLimit( $limit ) {
631 $user = $this->getMockNonAnonUserWithId( 1 );
633 $mockDb = $this->getMockDb();
634 $mockDb->expects( $this->exactly( 1 ) )
635 ->method( 'selectRowCount' )
640 "wl_notificationtimestamp IS NOT NULL",
643 $this->isType( 'string' ),
646 ->will( $this->returnValue( 9 ) );
648 $mockCache = $this->getMockCache();
649 $mockCache->expects( $this->never() )->method( 'set' );
650 $mockCache->expects( $this->never() )->method( 'get' );
651 $mockCache->expects( $this->never() )->method( 'delete' );
653 $store = new WatchedItemStore(
654 $this->getMockLoadBalancer( $mockDb ),
660 $store->countUnreadNotifications( $user, $limit )
664 public function testDuplicateEntry_nothingToDuplicate() {
665 $mockDb = $this->getMockDb();
666 $mockDb->expects( $this->once() )
672 'wl_notificationtimestamp',
676 'wl_title' => 'Old_Title',
678 'WatchedItemStore::duplicateEntry',
681 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
683 $store = new WatchedItemStore(
684 $this->getMockLoadBalancer( $mockDb ),
685 $this->getMockCache()
688 $store->duplicateEntry(
689 Title
::newFromText( 'Old_Title' ),
690 Title
::newFromText( 'New_Title' )
694 public function testDuplicateEntry_somethingToDuplicate() {
696 $this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
697 $this->getFakeRow( [ 'wl_user' => 2, 'wl_notificationtimestamp' => null ] ),
700 $mockDb = $this->getMockDb();
701 $mockDb->expects( $this->at( 0 ) )
707 'wl_notificationtimestamp',
711 'wl_title' => 'Old_Title',
714 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
715 $mockDb->expects( $this->at( 1 ) )
716 ->method( 'replace' )
719 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
724 'wl_title' => 'New_Title',
725 'wl_notificationtimestamp' => '20151212010101',
730 'wl_title' => 'New_Title',
731 'wl_notificationtimestamp' => null,
734 $this->isType( 'string' )
737 $mockCache = $this->getMockCache();
738 $mockCache->expects( $this->never() )->method( 'get' );
739 $mockCache->expects( $this->never() )->method( 'delete' );
741 $store = new WatchedItemStore(
742 $this->getMockLoadBalancer( $mockDb ),
746 $store->duplicateEntry(
747 Title
::newFromText( 'Old_Title' ),
748 Title
::newFromText( 'New_Title' )
752 public function testDuplicateAllAssociatedEntries_nothingToDuplicate() {
753 $mockDb = $this->getMockDb();
754 $mockDb->expects( $this->at( 0 ) )
760 'wl_notificationtimestamp',
764 'wl_title' => 'Old_Title',
767 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
768 $mockDb->expects( $this->at( 1 ) )
774 'wl_notificationtimestamp',
778 'wl_title' => 'Old_Title',
781 ->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
783 $mockCache = $this->getMockCache();
784 $mockCache->expects( $this->never() )->method( 'get' );
785 $mockCache->expects( $this->never() )->method( 'delete' );
787 $store = new WatchedItemStore(
788 $this->getMockLoadBalancer( $mockDb ),
792 $store->duplicateAllAssociatedEntries(
793 Title
::newFromText( 'Old_Title' ),
794 Title
::newFromText( 'New_Title' )
798 public function testDuplicateAllAssociatedEntries_somethingToDuplicate() {
800 $this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
803 $mockDb = $this->getMockDb();
804 $mockDb->expects( $this->at( 0 ) )
810 'wl_notificationtimestamp',
814 'wl_title' => 'Old_Title',
817 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
818 $mockDb->expects( $this->at( 1 ) )
819 ->method( 'replace' )
822 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
827 'wl_title' => 'New_Title',
828 'wl_notificationtimestamp' => '20151212010101',
831 $this->isType( 'string' )
833 $mockDb->expects( $this->at( 2 ) )
839 'wl_notificationtimestamp',
843 'wl_title' => 'Old_Title',
846 ->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
847 $mockDb->expects( $this->at( 3 ) )
848 ->method( 'replace' )
851 [ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
856 'wl_title' => 'New_Title',
857 'wl_notificationtimestamp' => '20151212010101',
860 $this->isType( 'string' )
863 $mockCache = $this->getMockCache();
864 $mockCache->expects( $this->never() )->method( 'get' );
865 $mockCache->expects( $this->never() )->method( 'delete' );
867 $store = new WatchedItemStore(
868 $this->getMockLoadBalancer( $mockDb ),
872 $store->duplicateAllAssociatedEntries(
873 Title
::newFromText( 'Old_Title' ),
874 Title
::newFromText( 'New_Title' )
878 public function testAddWatch_nonAnonymousUser() {
879 $mockDb = $this->getMockDb();
880 $mockDb->expects( $this->once() )
888 'wl_title' => 'Some_Page',
889 'wl_notificationtimestamp' => null,
894 $mockCache = $this->getMockCache();
895 $mockCache->expects( $this->once() )
897 ->with( '0:Some_Page:1' );
899 $store = new WatchedItemStore(
900 $this->getMockLoadBalancer( $mockDb ),
905 $this->getMockNonAnonUserWithId( 1 ),
906 Title
::newFromText( 'Some_Page' )
910 public function testAddWatch_anonymousUser() {
911 $mockDb = $this->getMockDb();
912 $mockDb->expects( $this->never() )
913 ->method( 'insert' );
915 $mockCache = $this->getMockCache();
916 $mockCache->expects( $this->never() )
917 ->method( 'delete' );
919 $store = new WatchedItemStore(
920 $this->getMockLoadBalancer( $mockDb ),
925 $this->getAnonUser(),
926 Title
::newFromText( 'Some_Page' )
930 public function testAddWatchBatch_nonAnonymousUser() {
931 $mockDb = $this->getMockDb();
932 $mockDb->expects( $this->once() )
940 'wl_title' => 'Some_Page',
941 'wl_notificationtimestamp' => null,
946 'wl_title' => 'Some_Page',
947 'wl_notificationtimestamp' => null,
952 $mockCache = $this->getMockCache();
953 $mockCache->expects( $this->exactly( 2 ) )
954 ->method( 'delete' );
955 $mockCache->expects( $this->at( 1 ) )
957 ->with( '0:Some_Page:1' );
958 $mockCache->expects( $this->at( 3 ) )
960 ->with( '1:Some_Page:1' );
962 $store = new WatchedItemStore(
963 $this->getMockLoadBalancer( $mockDb ),
967 $mockUser = $this->getMockNonAnonUserWithId( 1 );
970 $store->addWatchBatch(
972 [ $mockUser, new TitleValue( 0, 'Some_Page' ) ],
973 [ $mockUser, new TitleValue( 1, 'Some_Page' ) ],
979 public function testAddWatchBatch_anonymousUserCombinationsAreSkipped() {
980 $mockDb = $this->getMockDb();
981 $mockDb->expects( $this->once() )
989 'wl_title' => 'Some_Page',
990 'wl_notificationtimestamp' => null,
995 $mockCache = $this->getMockCache();
996 $mockCache->expects( $this->once() )
998 ->with( '0:Some_Page:1' );
1000 $store = new WatchedItemStore(
1001 $this->getMockLoadBalancer( $mockDb ),
1006 $store->addWatchBatch(
1008 [ $this->getMockNonAnonUserWithId( 1 ), new TitleValue( 0, 'Some_Page' ) ],
1009 [ $this->getAnonUser(), new TitleValue( 0, 'Other_Page' ) ],
1015 public function testAddWatchBatchReturnsFalse_whenOnlyGivenAnonymousUserCombinations() {
1016 $mockDb = $this->getMockDb();
1017 $mockDb->expects( $this->never() )
1018 ->method( 'insert' );
1020 $mockCache = $this->getMockCache();
1021 $mockCache->expects( $this->never() )
1022 ->method( 'delete' );
1024 $store = new WatchedItemStore(
1025 $this->getMockLoadBalancer( $mockDb ),
1029 $anonUser = $this->getAnonUser();
1031 $store->addWatchBatch(
1033 [ $anonUser, new TitleValue( 0, 'Some_Page' ) ],
1034 [ $anonUser, new TitleValue( 1, 'Other_Page' ) ],
1040 public function testAddWatchBatchReturnsFalse_whenGivenEmptyList() {
1041 $mockDb = $this->getMockDb();
1042 $mockDb->expects( $this->never() )
1043 ->method( 'insert' );
1045 $mockCache = $this->getMockCache();
1046 $mockCache->expects( $this->never() )
1047 ->method( 'delete' );
1049 $store = new WatchedItemStore(
1050 $this->getMockLoadBalancer( $mockDb ),
1055 $store->addWatchBatch( [] )
1059 public function testLoadWatchedItem_existingItem() {
1060 $mockDb = $this->getMockDb();
1061 $mockDb->expects( $this->once() )
1062 ->method( 'selectRow' )
1065 'wl_notificationtimestamp',
1068 'wl_namespace' => 0,
1069 'wl_title' => 'SomeDbKey',
1072 ->will( $this->returnValue(
1073 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1076 $mockCache = $this->getMockCache();
1077 $mockCache->expects( $this->once() )
1083 $store = new WatchedItemStore(
1084 $this->getMockLoadBalancer( $mockDb ),
1088 $watchedItem = $store->loadWatchedItem(
1089 $this->getMockNonAnonUserWithId( 1 ),
1090 new TitleValue( 0, 'SomeDbKey' )
1092 $this->assertInstanceOf( 'WatchedItem', $watchedItem );
1093 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
1094 $this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
1095 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
1098 public function testLoadWatchedItem_noItem() {
1099 $mockDb = $this->getMockDb();
1100 $mockDb->expects( $this->once() )
1101 ->method( 'selectRow' )
1104 'wl_notificationtimestamp',
1107 'wl_namespace' => 0,
1108 'wl_title' => 'SomeDbKey',
1111 ->will( $this->returnValue( [] ) );
1113 $mockCache = $this->getMockCache();
1114 $mockCache->expects( $this->never() )->method( 'get' );
1115 $mockCache->expects( $this->never() )->method( 'delete' );
1117 $store = new WatchedItemStore(
1118 $this->getMockLoadBalancer( $mockDb ),
1123 $store->loadWatchedItem(
1124 $this->getMockNonAnonUserWithId( 1 ),
1125 new TitleValue( 0, 'SomeDbKey' )
1130 public function testLoadWatchedItem_anonymousUser() {
1131 $mockDb = $this->getMockDb();
1132 $mockDb->expects( $this->never() )
1133 ->method( 'selectRow' );
1135 $mockCache = $this->getMockCache();
1136 $mockCache->expects( $this->never() )->method( 'get' );
1137 $mockCache->expects( $this->never() )->method( 'delete' );
1139 $store = new WatchedItemStore(
1140 $this->getMockLoadBalancer( $mockDb ),
1145 $store->loadWatchedItem(
1146 $this->getAnonUser(),
1147 new TitleValue( 0, 'SomeDbKey' )
1152 public function testRemoveWatch_existingItem() {
1153 $mockDb = $this->getMockDb();
1154 $mockDb->expects( $this->once() )
1155 ->method( 'delete' )
1160 'wl_namespace' => 0,
1161 'wl_title' => 'SomeDbKey',
1164 $mockDb->expects( $this->once() )
1165 ->method( 'affectedRows' )
1166 ->will( $this->returnValue( 1 ) );
1168 $mockCache = $this->getMockCache();
1169 $mockCache->expects( $this->never() )->method( 'get' );
1170 $mockCache->expects( $this->once() )
1171 ->method( 'delete' )
1172 ->with( '0:SomeDbKey:1' );
1174 $store = new WatchedItemStore(
1175 $this->getMockLoadBalancer( $mockDb ),
1180 $store->removeWatch(
1181 $this->getMockNonAnonUserWithId( 1 ),
1182 new TitleValue( 0, 'SomeDbKey' )
1187 public function testRemoveWatch_noItem() {
1188 $mockDb = $this->getMockDb();
1189 $mockDb->expects( $this->once() )
1190 ->method( 'delete' )
1195 'wl_namespace' => 0,
1196 'wl_title' => 'SomeDbKey',
1199 $mockDb->expects( $this->once() )
1200 ->method( 'affectedRows' )
1201 ->will( $this->returnValue( 0 ) );
1203 $mockCache = $this->getMockCache();
1204 $mockCache->expects( $this->never() )->method( 'get' );
1205 $mockCache->expects( $this->once() )
1206 ->method( 'delete' )
1207 ->with( '0:SomeDbKey:1' );
1209 $store = new WatchedItemStore(
1210 $this->getMockLoadBalancer( $mockDb ),
1215 $store->removeWatch(
1216 $this->getMockNonAnonUserWithId( 1 ),
1217 new TitleValue( 0, 'SomeDbKey' )
1222 public function testRemoveWatch_anonymousUser() {
1223 $mockDb = $this->getMockDb();
1224 $mockDb->expects( $this->never() )
1225 ->method( 'delete' );
1227 $mockCache = $this->getMockCache();
1228 $mockCache->expects( $this->never() )->method( 'get' );
1229 $mockCache->expects( $this->never() )
1230 ->method( 'delete' );
1232 $store = new WatchedItemStore(
1233 $this->getMockLoadBalancer( $mockDb ),
1238 $store->removeWatch(
1239 $this->getAnonUser(),
1240 new TitleValue( 0, 'SomeDbKey' )
1245 public function testGetWatchedItem_existingItem() {
1246 $mockDb = $this->getMockDb();
1247 $mockDb->expects( $this->once() )
1248 ->method( 'selectRow' )
1251 'wl_notificationtimestamp',
1254 'wl_namespace' => 0,
1255 'wl_title' => 'SomeDbKey',
1258 ->will( $this->returnValue(
1259 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1262 $mockCache = $this->getMockCache();
1263 $mockCache->expects( $this->never() )->method( 'delete' );
1264 $mockCache->expects( $this->once() )
1269 ->will( $this->returnValue( null ) );
1270 $mockCache->expects( $this->once() )
1276 $store = new WatchedItemStore(
1277 $this->getMockLoadBalancer( $mockDb ),
1281 $watchedItem = $store->getWatchedItem(
1282 $this->getMockNonAnonUserWithId( 1 ),
1283 new TitleValue( 0, 'SomeDbKey' )
1285 $this->assertInstanceOf( 'WatchedItem', $watchedItem );
1286 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
1287 $this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
1288 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
1291 public function testGetWatchedItem_cachedItem() {
1292 $mockDb = $this->getMockDb();
1293 $mockDb->expects( $this->never() )
1294 ->method( 'selectRow' );
1296 $mockUser = $this->getMockNonAnonUserWithId( 1 );
1297 $linkTarget = new TitleValue( 0, 'SomeDbKey' );
1298 $cachedItem = new WatchedItem( $mockUser, $linkTarget, '20151212010101' );
1300 $mockCache = $this->getMockCache();
1301 $mockCache->expects( $this->never() )->method( 'delete' );
1302 $mockCache->expects( $this->never() )->method( 'set' );
1303 $mockCache->expects( $this->once() )
1308 ->will( $this->returnValue( $cachedItem ) );
1310 $store = new WatchedItemStore(
1311 $this->getMockLoadBalancer( $mockDb ),
1315 $this->assertEquals(
1317 $store->getWatchedItem(
1324 public function testGetWatchedItem_noItem() {
1325 $mockDb = $this->getMockDb();
1326 $mockDb->expects( $this->once() )
1327 ->method( 'selectRow' )
1330 'wl_notificationtimestamp',
1333 'wl_namespace' => 0,
1334 'wl_title' => 'SomeDbKey',
1337 ->will( $this->returnValue( [] ) );
1339 $mockCache = $this->getMockCache();
1340 $mockCache->expects( $this->never() )->method( 'set' );
1341 $mockCache->expects( $this->never() )->method( 'delete' );
1342 $mockCache->expects( $this->once() )
1344 ->with( '0:SomeDbKey:1' )
1345 ->will( $this->returnValue( false ) );
1347 $store = new WatchedItemStore(
1348 $this->getMockLoadBalancer( $mockDb ),
1353 $store->getWatchedItem(
1354 $this->getMockNonAnonUserWithId( 1 ),
1355 new TitleValue( 0, 'SomeDbKey' )
1360 public function testGetWatchedItem_anonymousUser() {
1361 $mockDb = $this->getMockDb();
1362 $mockDb->expects( $this->never() )
1363 ->method( 'selectRow' );
1365 $mockCache = $this->getMockCache();
1366 $mockCache->expects( $this->never() )->method( 'set' );
1367 $mockCache->expects( $this->never() )->method( 'get' );
1368 $mockCache->expects( $this->never() )->method( 'delete' );
1370 $store = new WatchedItemStore(
1371 $this->getMockLoadBalancer( $mockDb ),
1376 $store->getWatchedItem(
1377 $this->getAnonUser(),
1378 new TitleValue( 0, 'SomeDbKey' )
1383 public function testIsWatchedItem_existingItem() {
1384 $mockDb = $this->getMockDb();
1385 $mockDb->expects( $this->once() )
1386 ->method( 'selectRow' )
1389 'wl_notificationtimestamp',
1392 'wl_namespace' => 0,
1393 'wl_title' => 'SomeDbKey',
1396 ->will( $this->returnValue(
1397 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1400 $mockCache = $this->getMockCache();
1401 $mockCache->expects( $this->never() )->method( 'delete' );
1402 $mockCache->expects( $this->once() )
1404 ->with( '0:SomeDbKey:1' )
1405 ->will( $this->returnValue( false ) );
1406 $mockCache->expects( $this->once() )
1412 $store = new WatchedItemStore(
1413 $this->getMockLoadBalancer( $mockDb ),
1419 $this->getMockNonAnonUserWithId( 1 ),
1420 new TitleValue( 0, 'SomeDbKey' )
1425 public function testIsWatchedItem_noItem() {
1426 $mockDb = $this->getMockDb();
1427 $mockDb->expects( $this->once() )
1428 ->method( 'selectRow' )
1431 'wl_notificationtimestamp',
1434 'wl_namespace' => 0,
1435 'wl_title' => 'SomeDbKey',
1438 ->will( $this->returnValue( [] ) );
1440 $mockCache = $this->getMockCache();
1441 $mockCache->expects( $this->never() )->method( 'set' );
1442 $mockCache->expects( $this->never() )->method( 'delete' );
1443 $mockCache->expects( $this->once() )
1445 ->with( '0:SomeDbKey:1' )
1446 ->will( $this->returnValue( false ) );
1448 $store = new WatchedItemStore(
1449 $this->getMockLoadBalancer( $mockDb ),
1455 $this->getMockNonAnonUserWithId( 1 ),
1456 new TitleValue( 0, 'SomeDbKey' )
1461 public function testIsWatchedItem_anonymousUser() {
1462 $mockDb = $this->getMockDb();
1463 $mockDb->expects( $this->never() )
1464 ->method( 'selectRow' );
1466 $mockCache = $this->getMockCache();
1467 $mockCache->expects( $this->never() )->method( 'set' );
1468 $mockCache->expects( $this->never() )->method( 'get' );
1469 $mockCache->expects( $this->never() )->method( 'delete' );
1471 $store = new WatchedItemStore(
1472 $this->getMockLoadBalancer( $mockDb ),
1478 $this->getAnonUser(),
1479 new TitleValue( 0, 'SomeDbKey' )
1484 public function testResetNotificationTimestamp_anonymousUser() {
1485 $mockDb = $this->getMockDb();
1486 $mockDb->expects( $this->never() )
1487 ->method( 'selectRow' );
1489 $mockCache = $this->getMockCache();
1490 $mockCache->expects( $this->never() )->method( 'get' );
1491 $mockCache->expects( $this->never() )->method( 'set' );
1492 $mockCache->expects( $this->never() )->method( 'delete' );
1494 $store = new WatchedItemStore(
1495 $this->getMockLoadBalancer( $mockDb ),
1500 $store->resetNotificationTimestamp(
1501 $this->getAnonUser(),
1502 Title
::newFromText( 'SomeDbKey' )
1507 public function testResetNotificationTimestamp_noItem() {
1508 $mockDb = $this->getMockDb();
1509 $mockDb->expects( $this->once() )
1510 ->method( 'selectRow' )
1513 'wl_notificationtimestamp',
1516 'wl_namespace' => 0,
1517 'wl_title' => 'SomeDbKey',
1520 ->will( $this->returnValue( [] ) );
1522 $mockCache = $this->getMockCache();
1523 $mockCache->expects( $this->never() )->method( 'get' );
1524 $mockCache->expects( $this->never() )->method( 'set' );
1525 $mockCache->expects( $this->never() )->method( 'delete' );
1527 $store = new WatchedItemStore(
1528 $this->getMockLoadBalancer( $mockDb ),
1533 $store->resetNotificationTimestamp(
1534 $this->getMockNonAnonUserWithId( 1 ),
1535 Title
::newFromText( 'SomeDbKey' )
1540 public function testResetNotificationTimestamp_item() {
1541 $user = $this->getMockNonAnonUserWithId( 1 );
1542 $title = Title
::newFromText( 'SomeDbKey' );
1544 $mockDb = $this->getMockDb();
1545 $mockDb->expects( $this->once() )
1546 ->method( 'selectRow' )
1549 'wl_notificationtimestamp',
1552 'wl_namespace' => 0,
1553 'wl_title' => 'SomeDbKey',
1556 ->will( $this->returnValue(
1557 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1560 $mockCache = $this->getMockCache();
1561 $mockCache->expects( $this->never() )->method( 'get' );
1562 $mockCache->expects( $this->once() )
1566 $this->isInstanceOf( WatchedItem
::class )
1568 $mockCache->expects( $this->once() )
1569 ->method( 'delete' )
1570 ->with( '0:SomeDbKey:1' );
1572 $store = new WatchedItemStore(
1573 $this->getMockLoadBalancer( $mockDb ),
1577 // Note: This does not actually assert the job is correct
1578 $callableCallCounter = 0;
1579 $mockCallback = function( $callable ) use ( &$callableCallCounter ) {
1580 $callableCallCounter++
;
1581 $this->assertInternalType( 'callable', $callable );
1583 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
1586 $store->resetNotificationTimestamp(
1591 $this->assertEquals( 1, $callableCallCounter );
1593 ScopedCallback
::consume( $scopedOverride );
1596 public function testResetNotificationTimestamp_noItemForced() {
1597 $user = $this->getMockNonAnonUserWithId( 1 );
1598 $title = Title
::newFromText( 'SomeDbKey' );
1600 $mockDb = $this->getMockDb();
1601 $mockDb->expects( $this->never() )
1602 ->method( 'selectRow' );
1604 $mockCache = $this->getMockCache();
1605 $mockDb->expects( $this->never() )
1607 $mockDb->expects( $this->never() )
1609 $mockDb->expects( $this->never() )
1610 ->method( 'delete' );
1612 $store = new WatchedItemStore(
1613 $this->getMockLoadBalancer( $mockDb ),
1617 // Note: This does not actually assert the job is correct
1618 $callableCallCounter = 0;
1619 $mockCallback = function( $callable ) use ( &$callableCallCounter ) {
1620 $callableCallCounter++
;
1621 $this->assertInternalType( 'callable', $callable );
1623 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
1626 $store->resetNotificationTimestamp(
1632 $this->assertEquals( 1, $callableCallCounter );
1634 ScopedCallback
::consume( $scopedOverride );
1641 * @return PHPUnit_Framework_MockObject_MockObject|Title
1643 private function getMockTitle( $text, $ns = 0 ) {
1644 $title = $this->getMock( Title
::class );
1645 $title->expects( $this->any() )
1646 ->method( 'getText' )
1647 ->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
1648 $title->expects( $this->any() )
1649 ->method( 'getDbKey' )
1650 ->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
1651 $title->expects( $this->any() )
1652 ->method( 'getNamespace' )
1653 ->will( $this->returnValue( $ns ) );
1657 public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced() {
1658 $user = $this->getMockNonAnonUserWithId( 1 );
1660 $title = $this->getMockTitle( 'SomeTitle' );
1661 $title->expects( $this->once() )
1662 ->method( 'getNextRevisionID' )
1664 ->will( $this->returnValue( false ) );
1666 $mockDb = $this->getMockDb();
1667 $mockDb->expects( $this->never() )
1668 ->method( 'selectRow' );
1670 $mockCache = $this->getMockCache();
1671 $mockDb->expects( $this->never() )
1673 $mockDb->expects( $this->never() )
1675 $mockDb->expects( $this->never() )
1676 ->method( 'delete' );
1678 $store = new WatchedItemStore(
1679 $this->getMockLoadBalancer( $mockDb ),
1683 // Note: This does not actually assert the job is correct
1684 $callableCallCounter = 0;
1685 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
1686 function( $callable ) use ( &$callableCallCounter ) {
1687 $callableCallCounter++
;
1688 $this->assertInternalType( 'callable', $callable );
1693 $store->resetNotificationTimestamp(
1700 $this->assertEquals( 1, $callableCallCounter );
1702 ScopedCallback
::consume( $scopedOverride );
1705 public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced() {
1706 $user = $this->getMockNonAnonUserWithId( 1 );
1708 $title = $this->getMockTitle( 'SomeDbKey' );
1709 $title->expects( $this->once() )
1710 ->method( 'getNextRevisionID' )
1712 ->will( $this->returnValue( 33 ) );
1714 $mockDb = $this->getMockDb();
1715 $mockDb->expects( $this->once() )
1716 ->method( 'selectRow' )
1719 'wl_notificationtimestamp',
1722 'wl_namespace' => 0,
1723 'wl_title' => 'SomeDbKey',
1726 ->will( $this->returnValue(
1727 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1730 $mockCache = $this->getMockCache();
1731 $mockDb->expects( $this->never() )
1733 $mockDb->expects( $this->never() )
1735 $mockDb->expects( $this->never() )
1736 ->method( 'delete' );
1738 $store = new WatchedItemStore(
1739 $this->getMockLoadBalancer( $mockDb ),
1743 // Note: This does not actually assert the job is correct
1744 $addUpdateCallCounter = 0;
1745 $scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
1746 function( $callable ) use ( &$addUpdateCallCounter ) {
1747 $addUpdateCallCounter++
;
1748 $this->assertInternalType( 'callable', $callable );
1752 $getTimestampCallCounter = 0;
1753 $scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
1754 function( $titleParam, $oldidParam ) use ( &$getTimestampCallCounter, $title, $oldid ) {
1755 $getTimestampCallCounter++
;
1756 $this->assertEquals( $title, $titleParam );
1757 $this->assertEquals( $oldid, $oldidParam );
1762 $store->resetNotificationTimestamp(
1769 $this->assertEquals( 1, $addUpdateCallCounter );
1770 $this->assertEquals( 1, $getTimestampCallCounter );
1772 ScopedCallback
::consume( $scopedOverrideDeferred );
1773 ScopedCallback
::consume( $scopedOverrideRevision );
1776 public function testUpdateNotificationTimestamp_watchersExist() {
1777 $mockDb = $this->getMockDb();
1778 $mockDb->expects( $this->once() )
1779 ->method( 'select' )
1785 'wl_namespace' => 0,
1786 'wl_title' => 'SomeDbKey',
1787 'wl_notificationtimestamp IS NULL'
1791 $this->returnValue( [
1792 $this->getFakeRow( [ 'wl_user' => '2' ] ),
1793 $this->getFakeRow( [ 'wl_user' => '3' ] )
1796 $mockDb->expects( $this->once() )
1797 ->method( 'onTransactionIdle' )
1798 ->with( $this->isType( 'callable' ) )
1799 ->will( $this->returnCallback( function( $callable ) {
1802 $mockDb->expects( $this->once() )
1803 ->method( 'update' )
1806 [ 'wl_notificationtimestamp' => null ],
1808 'wl_user' => [ 2, 3 ],
1809 'wl_namespace' => 0,
1810 'wl_title' => 'SomeDbKey',
1814 $mockCache = $this->getMockCache();
1815 $mockCache->expects( $this->never() )->method( 'set' );
1816 $mockCache->expects( $this->never() )->method( 'get' );
1817 $mockCache->expects( $this->never() )->method( 'delete' );
1819 $store = new WatchedItemStore(
1820 $this->getMockLoadBalancer( $mockDb ),
1824 $this->assertEquals(
1826 $store->updateNotificationTimestamp(
1827 $this->getMockNonAnonUserWithId( 1 ),
1828 new TitleValue( 0, 'SomeDbKey' ),
1834 public function testUpdateNotificationTimestamp_noWatchers() {
1835 $mockDb = $this->getMockDb();
1836 $mockDb->expects( $this->once() )
1837 ->method( 'select' )
1843 'wl_namespace' => 0,
1844 'wl_title' => 'SomeDbKey',
1845 'wl_notificationtimestamp IS NULL'
1849 $this->returnValue( [] )
1851 $mockDb->expects( $this->never() )
1852 ->method( 'onTransactionIdle' );
1853 $mockDb->expects( $this->never() )
1854 ->method( 'update' );
1856 $mockCache = $this->getMockCache();
1857 $mockCache->expects( $this->never() )->method( 'set' );
1858 $mockCache->expects( $this->never() )->method( 'get' );
1859 $mockCache->expects( $this->never() )->method( 'delete' );
1861 $store = new WatchedItemStore(
1862 $this->getMockLoadBalancer( $mockDb ),
1866 $watchers = $store->updateNotificationTimestamp(
1867 $this->getMockNonAnonUserWithId( 1 ),
1868 new TitleValue( 0, 'SomeDbKey' ),
1871 $this->assertInternalType( 'array', $watchers );
1872 $this->assertEmpty( $watchers );
1875 public function testUpdateNotificationTimestamp_clearsCachedItems() {
1876 $user = $this->getMockNonAnonUserWithId( 1 );
1877 $titleValue = new TitleValue( 0, 'SomeDbKey' );
1879 $mockDb = $this->getMockDb();
1880 $mockDb->expects( $this->once() )
1881 ->method( 'selectRow' )
1882 ->will( $this->returnValue(
1883 $this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
1885 $mockDb->expects( $this->once() )
1886 ->method( 'select' )
1888 $this->returnValue( [
1889 $this->getFakeRow( [ 'wl_user' => '2' ] ),
1890 $this->getFakeRow( [ 'wl_user' => '3' ] )
1893 $mockDb->expects( $this->once() )
1894 ->method( 'onTransactionIdle' )
1895 ->with( $this->isType( 'callable' ) )
1896 ->will( $this->returnCallback( function( $callable ) {
1899 $mockDb->expects( $this->once() )
1900 ->method( 'update' );
1902 $mockCache = $this->getMockCache();
1903 $mockCache->expects( $this->once() )
1905 ->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
1906 $mockCache->expects( $this->once() )
1908 ->with( '0:SomeDbKey:1' );
1909 $mockCache->expects( $this->once() )
1910 ->method( 'delete' )
1911 ->with( '0:SomeDbKey:1' );
1913 $store = new WatchedItemStore(
1914 $this->getMockLoadBalancer( $mockDb ),
1918 // This will add the item to the cache
1919 $store->getWatchedItem( $user, $titleValue );
1921 $store->updateNotificationTimestamp(
1922 $this->getMockNonAnonUserWithId( 1 ),