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