4use Wikimedia\ScopedCallback;
5use Wikimedia\TestingAccessWrapper;
18 return $this->createMock( IDatabase::class );
26 $expectedConnectionType =
null
28 $mock = $this->getMockBuilder( LoadBalancer::class )
29 ->disableOriginalConstructor()
31 if ( $expectedConnectionType !==
null ) {
32 $mock->expects( $this->
any() )
33 ->method(
'getConnectionRef' )
34 ->with( $expectedConnectionType )
35 ->will( $this->returnValue( $mockDb ) );
37 $mock->expects( $this->
any() )
38 ->method(
'getConnectionRef' )
39 ->will( $this->returnValue( $mockDb ) );
48 $mock = $this->getMockBuilder( HashBagOStuff::class )
49 ->disableOriginalConstructor()
50 ->setMethods( [
'get',
'set',
'delete',
'makeKey' ] )
52 $mock->expects( $this->
any() )
54 ->will( $this->returnCallback(
function () {
55 return implode(
':', func_get_args() );
64 $mock = $this->getMockBuilder( ReadOnlyMode::class )
65 ->disableOriginalConstructor()
67 $mock->expects( $this->
any() )
68 ->method(
'isReadOnly' )
69 ->will( $this->returnValue( $readOnly ) );
78 $mock = $this->createMock( User::class );
79 $mock->expects( $this->
any() )
81 ->will( $this->returnValue(
false ) );
82 $mock->expects( $this->
any() )
84 ->will( $this->returnValue( $id ) );
96 $fakeRow =
new stdClass();
97 foreach ( $rowValues as $valueName =>
$value ) {
98 $fakeRow->$valueName =
$value;
118 $mockDb->expects( $this->once() )
119 ->method(
'selectField' )
124 'wl_user' => $user->getId(),
126 $this->isType(
'string' )
128 ->will( $this->returnValue( 12 ) );
129 $mockDb->expects( $this->once() )
134 $this->isType(
'string' )
138 $mockCache->expects( $this->never() )->method(
'get' );
139 $mockCache->expects( $this->never() )->method(
'set' );
140 $mockCache->expects( $this->once() )
149 TestingAccessWrapper::newFromObject( $store )
150 ->cacheIndex = [ 0 => [
'F' => [ 7 =>
'RM-KEY', 9 =>
'KEEP-KEY' ] ] ];
152 $this->assertTrue( $store->clearUserWatchedItems( $user ) );
159 $mockDb->expects( $this->once() )
160 ->method(
'selectField' )
165 'wl_user' => $user->getId(),
167 $this->isType(
'string' )
169 ->will( $this->returnValue( 99999 ) );
172 $mockCache->expects( $this->never() )->method(
'get' );
173 $mockCache->expects( $this->never() )->method(
'set' );
174 $mockCache->expects( $this->never() )->method(
'delete' );
182 $this->assertFalse( $store->clearUserWatchedItems( $user ) );
189 $mockDb->expects( $this->exactly( 1 ) )
190 ->method(
'selectField' )
195 'wl_user' => $user->getId(),
197 $this->isType(
'string' )
199 ->will( $this->returnValue(
'12' ) );
202 $mockCache->expects( $this->never() )->method(
'get' );
203 $mockCache->expects( $this->never() )->method(
'set' );
204 $mockCache->expects( $this->never() )->method(
'delete' );
212 $this->assertEquals( 12, $store->countWatchedItems( $user ) );
216 $titleValue =
new TitleValue( 0,
'SomeDbKey' );
219 $mockDb->expects( $this->exactly( 1 ) )
220 ->method(
'selectField' )
225 'wl_namespace' => $titleValue->getNamespace(),
226 'wl_title' => $titleValue->getDBkey(),
228 $this->isType(
'string' )
230 ->will( $this->returnValue(
'7' ) );
233 $mockCache->expects( $this->never() )->method(
'get' );
234 $mockCache->expects( $this->never() )->method(
'set' );
235 $mockCache->expects( $this->never() )->method(
'delete' );
243 $this->assertEquals( 7, $store->countWatchers( $titleValue ) );
256 $this->
getFakeRow( [
'wl_title' =>
'SomeDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'100' ] ),
257 $this->
getFakeRow( [
'wl_title' =>
'OtherDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'300' ] ),
258 $this->
getFakeRow( [
'wl_title' =>
'AnotherDbKey',
'wl_namespace' =>
'1',
'watchers' =>
'500' ]
261 $mockDb->expects( $this->once() )
262 ->method(
'makeWhereFrom2d' )
264 [ [
'SomeDbKey' => 1,
'OtherDbKey' => 1 ], [
'AnotherDbKey' => 1 ] ],
265 $this->isType(
'string' ),
266 $this->isType(
'string' )
268 ->will( $this->returnValue(
'makeWhereFrom2d return value' ) );
269 $mockDb->expects( $this->once() )
273 [
'wl_title',
'wl_namespace',
'watchers' =>
'COUNT(*)' ],
274 [
'makeWhereFrom2d return value' ],
275 $this->isType(
'string' ),
277 'GROUP BY' => [
'wl_namespace',
'wl_title' ],
281 $this->returnValue( $dbResult )
285 $mockCache->expects( $this->never() )->method(
'get' );
286 $mockCache->expects( $this->never() )->method(
'set' );
287 $mockCache->expects( $this->never() )->method(
'delete' );
296 0 => [
'SomeDbKey' => 100,
'OtherDbKey' => 300 ],
297 1 => [
'AnotherDbKey' => 500 ],
299 $this->assertEquals( $expected, $store->countWatchersMultiple( $titleValues ) );
305 [
"50; DROP TABLE watchlist;\n--" ],
322 $this->
getFakeRow( [
'wl_title' =>
'SomeDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'100' ] ),
323 $this->
getFakeRow( [
'wl_title' =>
'OtherDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'300' ] ),
324 $this->
getFakeRow( [
'wl_title' =>
'AnotherDbKey',
'wl_namespace' =>
'1',
'watchers' =>
'500' ]
327 $mockDb->expects( $this->once() )
328 ->method(
'makeWhereFrom2d' )
330 [ [
'SomeDbKey' => 1,
'OtherDbKey' => 1 ], [
'AnotherDbKey' => 1 ] ],
331 $this->isType(
'string' ),
332 $this->isType(
'string' )
334 ->will( $this->returnValue(
'makeWhereFrom2d return value' ) );
335 $mockDb->expects( $this->once() )
339 [
'wl_title',
'wl_namespace',
'watchers' =>
'COUNT(*)' ],
340 [
'makeWhereFrom2d return value' ],
341 $this->isType(
'string' ),
343 'GROUP BY' => [
'wl_namespace',
'wl_title' ],
344 'HAVING' =>
'COUNT(*) >= 50',
348 $this->returnValue( $dbResult )
352 $mockCache->expects( $this->never() )->method(
'get' );
353 $mockCache->expects( $this->never() )->method(
'set' );
354 $mockCache->expects( $this->never() )->method(
'delete' );
363 0 => [
'SomeDbKey' => 100,
'OtherDbKey' => 300 ],
364 1 => [
'AnotherDbKey' => 500 ],
368 $store->countWatchersMultiple( $titleValues, [
'minimumWatchers' => $minWatchers ] )
373 $titleValue =
new TitleValue( 0,
'SomeDbKey' );
376 $mockDb->expects( $this->exactly( 1 ) )
377 ->method(
'selectField' )
382 'wl_namespace' => $titleValue->getNamespace(),
383 'wl_title' => $titleValue->getDBkey(),
384 'wl_notificationtimestamp >= \'TS111TS\' OR wl_notificationtimestamp IS NULL',
386 $this->isType(
'string' )
388 ->will( $this->returnValue(
'7' ) );
389 $mockDb->expects( $this->exactly( 1 ) )
390 ->method(
'addQuotes' )
391 ->will( $this->returnCallback(
function (
$value ) {
394 $mockDb->expects( $this->exactly( 1 ) )
395 ->method(
'timestamp' )
396 ->will( $this->returnCallback(
function (
$value ) {
397 return 'TS' .
$value .
'TS';
401 $mockCache->expects( $this->never() )->method(
'set' );
402 $mockCache->expects( $this->never() )->method(
'get' );
403 $mockCache->expects( $this->never() )->method(
'delete' );
411 $this->assertEquals( 7, $store->countVisitingWatchers( $titleValue,
'111' ) );
415 $titleValuesWithThresholds = [
418 [
new TitleValue( 1,
'AnotherDbKey' ),
'123' ],
422 $this->
getFakeRow( [
'wl_title' =>
'SomeDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'100' ] ),
423 $this->
getFakeRow( [
'wl_title' =>
'OtherDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'300' ] ),
425 [
'wl_title' =>
'AnotherDbKey',
'wl_namespace' =>
'1',
'watchers' =>
'500' ]
429 $mockDb->expects( $this->exactly( 2 * 3 ) )
430 ->method(
'addQuotes' )
431 ->will( $this->returnCallback(
function (
$value ) {
434 $mockDb->expects( $this->exactly( 3 ) )
435 ->method(
'timestamp' )
436 ->will( $this->returnCallback(
function (
$value ) {
437 return 'TS' .
$value .
'TS';
439 $mockDb->expects( $this->
any() )
440 ->method(
'makeList' )
442 $this->isType(
'array' ),
443 $this->isType(
'int' )
445 ->will( $this->returnCallback(
function ( $a, $conj ) {
446 $sqlConj = $conj ===
LIST_AND ?
' AND ' :
' OR ';
447 return implode( $sqlConj, array_map(
function (
$s ) {
448 return '(' .
$s .
')';
452 $mockDb->expects( $this->never() )
453 ->method(
'makeWhereFrom2d' );
456 '((wl_namespace = 0) AND (' .
457 "(((wl_title = 'SomeDbKey') AND (" .
458 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
460 "(wl_title = 'OtherDbKey') AND (" .
461 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
463 ') OR ((wl_namespace = 1) AND (' .
464 "(((wl_title = 'AnotherDbKey') AND (".
465 "(wl_notificationtimestamp >= 'TS123TS') OR (wl_notificationtimestamp IS NULL)" .
467 $mockDb->expects( $this->once() )
471 [
'wl_namespace',
'wl_title',
'watchers' =>
'COUNT(*)' ],
473 $this->isType(
'string' ),
475 'GROUP BY' => [
'wl_namespace',
'wl_title' ],
479 $this->returnValue( $dbResult )
483 $mockCache->expects( $this->never() )->method(
'get' );
484 $mockCache->expects( $this->never() )->method(
'set' );
485 $mockCache->expects( $this->never() )->method(
'delete' );
494 0 => [
'SomeDbKey' => 100,
'OtherDbKey' => 300 ],
495 1 => [
'AnotherDbKey' => 500 ],
499 $store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
504 $titleValuesWithThresholds = [
507 [
new TitleValue( 1,
'AnotherDbKey' ),
'123' ],
508 [
new TitleValue( 0,
'SomeNotExisitingDbKey' ), null ],
509 [
new TitleValue( 0,
'OtherNotExisitingDbKey' ), null ],
513 $this->
getFakeRow( [
'wl_title' =>
'SomeDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'100' ] ),
514 $this->
getFakeRow( [
'wl_title' =>
'OtherDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'300' ] ),
516 [
'wl_title' =>
'AnotherDbKey',
'wl_namespace' =>
'1',
'watchers' =>
'500' ]
519 [
'wl_title' =>
'SomeNotExisitingDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'100' ]
522 [
'wl_title' =>
'OtherNotExisitingDbKey',
'wl_namespace' =>
'0',
'watchers' =>
'200' ]
526 $mockDb->expects( $this->exactly( 2 * 3 ) )
527 ->method(
'addQuotes' )
528 ->will( $this->returnCallback(
function (
$value ) {
531 $mockDb->expects( $this->exactly( 3 ) )
532 ->method(
'timestamp' )
533 ->will( $this->returnCallback(
function (
$value ) {
534 return 'TS' .
$value .
'TS';
536 $mockDb->expects( $this->
any() )
537 ->method(
'makeList' )
539 $this->isType(
'array' ),
540 $this->isType(
'int' )
542 ->will( $this->returnCallback(
function ( $a, $conj ) {
543 $sqlConj = $conj ===
LIST_AND ?
' AND ' :
' OR ';
544 return implode( $sqlConj, array_map(
function (
$s ) {
545 return '(' .
$s .
')';
549 $mockDb->expects( $this->once() )
550 ->method(
'makeWhereFrom2d' )
552 [ [
'SomeNotExisitingDbKey' => 1,
'OtherNotExisitingDbKey' => 1 ] ],
553 $this->isType(
'string' ),
554 $this->isType(
'string' )
556 ->
will( $this->returnValue(
'makeWhereFrom2d return value' ) );
559 '((wl_namespace = 0) AND (' .
560 "(((wl_title = 'SomeDbKey') AND (" .
561 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
563 "(wl_title = 'OtherDbKey') AND (" .
564 "(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
566 ') OR ((wl_namespace = 1) AND (' .
567 "(((wl_title = 'AnotherDbKey') AND (".
568 "(wl_notificationtimestamp >= 'TS123TS') OR (wl_notificationtimestamp IS NULL)" .
571 '(makeWhereFrom2d return value)';
572 $mockDb->expects( $this->once() )
576 [
'wl_namespace',
'wl_title',
'watchers' =>
'COUNT(*)' ],
578 $this->isType(
'string' ),
580 'GROUP BY' => [
'wl_namespace',
'wl_title' ],
584 $this->returnValue( $dbResult )
588 $mockCache->expects( $this->never() )->method(
'get' );
589 $mockCache->expects( $this->never() )->method(
'set' );
590 $mockCache->expects( $this->never() )->method(
'delete' );
600 'SomeDbKey' => 100,
'OtherDbKey' => 300,
601 'SomeNotExisitingDbKey' => 100,
'OtherNotExisitingDbKey' => 200
603 1 => [
'AnotherDbKey' => 500 ],
607 $store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
615 $titleValuesWithThresholds = [
618 [
new TitleValue( 1,
'AnotherDbKey' ),
'123' ],
622 $mockDb->expects( $this->
any() )
623 ->method(
'makeList' )
624 ->will( $this->returnValue(
'makeList return value' ) );
625 $mockDb->expects( $this->once() )
629 [
'wl_namespace',
'wl_title',
'watchers' =>
'COUNT(*)' ],
630 'makeList return value',
631 $this->isType(
'string' ),
633 'GROUP BY' => [
'wl_namespace',
'wl_title' ],
634 'HAVING' =>
'COUNT(*) >= 50',
638 $this->returnValue( [] )
642 $mockCache->expects( $this->never() )->method(
'get' );
643 $mockCache->expects( $this->never() )->method(
'set' );
644 $mockCache->expects( $this->never() )->method(
'delete' );
653 0 => [
'SomeDbKey' => 0,
'OtherDbKey' => 0 ],
654 1 => [
'AnotherDbKey' => 0 ],
658 $store->countVisitingWatchersMultiple( $titleValuesWithThresholds, $minWatchers )
666 $mockDb->expects( $this->exactly( 1 ) )
667 ->method(
'selectRowCount' )
672 "wl_notificationtimestamp IS NOT NULL",
675 $this->isType(
'string' )
677 ->will( $this->returnValue(
'9' ) );
680 $mockCache->expects( $this->never() )->method(
'set' );
681 $mockCache->expects( $this->never() )->method(
'get' );
682 $mockCache->expects( $this->never() )->method(
'delete' );
690 $this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
700 $mockDb->expects( $this->exactly( 1 ) )
701 ->method(
'selectRowCount' )
706 "wl_notificationtimestamp IS NOT NULL",
709 $this->isType(
'string' ),
712 ->will( $this->returnValue(
'50' ) );
715 $mockCache->expects( $this->never() )->method(
'set' );
716 $mockCache->expects( $this->never() )->method(
'get' );
717 $mockCache->expects( $this->never() )->method(
'delete' );
727 $store->countUnreadNotifications( $user, $limit )
738 $mockDb->expects( $this->exactly( 1 ) )
739 ->method(
'selectRowCount' )
744 "wl_notificationtimestamp IS NOT NULL",
747 $this->isType(
'string' ),
750 ->will( $this->returnValue(
'9' ) );
753 $mockCache->expects( $this->never() )->method(
'set' );
754 $mockCache->expects( $this->never() )->method(
'get' );
755 $mockCache->expects( $this->never() )->method(
'delete' );
765 $store->countUnreadNotifications( $user, $limit )
771 $mockDb->expects( $this->once() )
777 'wl_notificationtimestamp',
781 'wl_title' =>
'Old_Title',
783 'WatchedItemStore::duplicateEntry',
786 ->will( $this->returnValue(
new FakeResultWrapper( [] ) ) );
794 $store->duplicateEntry(
795 Title::newFromText(
'Old_Title' ),
796 Title::newFromText(
'New_Title' )
802 $this->
getFakeRow( [
'wl_user' =>
'1',
'wl_notificationtimestamp' =>
'20151212010101' ] ),
803 $this->
getFakeRow( [
'wl_user' =>
'2',
'wl_notificationtimestamp' =>
null ] ),
807 $mockDb->expects( $this->at( 0 ) )
813 'wl_notificationtimestamp',
817 'wl_title' =>
'Old_Title',
820 ->will( $this->returnValue(
new FakeResultWrapper( $fakeRows ) ) );
821 $mockDb->expects( $this->at( 1 ) )
822 ->method(
'replace' )
825 [ [
'wl_user',
'wl_namespace',
'wl_title' ] ],
830 'wl_title' =>
'New_Title',
831 'wl_notificationtimestamp' =>
'20151212010101',
836 'wl_title' =>
'New_Title',
837 'wl_notificationtimestamp' =>
null,
840 $this->isType(
'string' )
844 $mockCache->expects( $this->never() )->method(
'get' );
845 $mockCache->expects( $this->never() )->method(
'delete' );
853 $store->duplicateEntry(
854 Title::newFromText(
'Old_Title' ),
855 Title::newFromText(
'New_Title' )
861 $mockDb->expects( $this->at( 0 ) )
867 'wl_notificationtimestamp',
871 'wl_title' =>
'Old_Title',
874 ->will( $this->returnValue(
new FakeResultWrapper( [] ) ) );
875 $mockDb->expects( $this->at( 1 ) )
881 'wl_notificationtimestamp',
885 'wl_title' =>
'Old_Title',
888 ->will( $this->returnValue(
new FakeResultWrapper( [] ) ) );
891 $mockCache->expects( $this->never() )->method(
'get' );
892 $mockCache->expects( $this->never() )->method(
'delete' );
900 $store->duplicateAllAssociatedEntries(
901 Title::newFromText(
'Old_Title' ),
902 Title::newFromText(
'New_Title' )
908 [ Title::newFromText(
'Old_Title' ), Title::newFromText(
'New_Title' ) ],
921 $this->
getFakeRow( [
'wl_user' =>
'1',
'wl_notificationtimestamp' =>
'20151212010101' ] ),
925 $mockDb->expects( $this->at( 0 ) )
931 'wl_notificationtimestamp',
934 'wl_namespace' => $oldTarget->getNamespace(),
935 'wl_title' => $oldTarget->getDBkey(),
938 ->will( $this->returnValue(
new FakeResultWrapper( $fakeRows ) ) );
939 $mockDb->expects( $this->at( 1 ) )
940 ->method(
'replace' )
943 [ [
'wl_user',
'wl_namespace',
'wl_title' ] ],
947 'wl_namespace' => $newTarget->getNamespace(),
948 'wl_title' => $newTarget->getDBkey(),
949 'wl_notificationtimestamp' =>
'20151212010101',
952 $this->isType(
'string' )
954 $mockDb->expects( $this->at( 2 ) )
960 'wl_notificationtimestamp',
963 'wl_namespace' => $oldTarget->getNamespace() + 1,
964 'wl_title' => $oldTarget->getDBkey(),
967 ->will( $this->returnValue(
new FakeResultWrapper( $fakeRows ) ) );
968 $mockDb->expects( $this->at( 3 ) )
969 ->method(
'replace' )
972 [ [
'wl_user',
'wl_namespace',
'wl_title' ] ],
976 'wl_namespace' => $newTarget->getNamespace() + 1,
977 'wl_title' => $newTarget->getDBkey(),
978 'wl_notificationtimestamp' =>
'20151212010101',
981 $this->isType(
'string' )
985 $mockCache->expects( $this->never() )->method(
'get' );
986 $mockCache->expects( $this->never() )->method(
'delete' );
994 $store->duplicateAllAssociatedEntries(
1002 $mockDb->expects( $this->once() )
1003 ->method(
'insert' )
1009 'wl_namespace' => 0,
1010 'wl_title' =>
'Some_Page',
1011 'wl_notificationtimestamp' =>
null,
1017 $mockCache->expects( $this->once() )
1018 ->method(
'delete' )
1019 ->with(
'0:Some_Page:1' );
1029 Title::newFromText(
'Some_Page' )
1035 $mockDb->expects( $this->never() )
1036 ->method(
'insert' );
1039 $mockCache->expects( $this->never() )
1040 ->method(
'delete' );
1050 Title::newFromText(
'Some_Page' )
1062 $store->addWatchBatchForUser(
1063 $this->getMockNonAnonUserWithId( 1 ),
1071 $mockDb->expects( $this->once() )
1072 ->method(
'insert' )
1078 'wl_namespace' => 0,
1079 'wl_title' =>
'Some_Page',
1080 'wl_notificationtimestamp' =>
null,
1084 'wl_namespace' => 1,
1085 'wl_title' =>
'Some_Page',
1086 'wl_notificationtimestamp' =>
null,
1092 $mockCache->expects( $this->exactly( 2 ) )
1093 ->method(
'delete' );
1094 $mockCache->expects( $this->at( 1 ) )
1095 ->method(
'delete' )
1096 ->with(
'0:Some_Page:1' );
1097 $mockCache->expects( $this->at( 3 ) )
1098 ->method(
'delete' )
1099 ->with(
'1:Some_Page:1' );
1110 $store->addWatchBatchForUser(
1119 $mockDb->expects( $this->never() )
1120 ->method(
'insert' );
1123 $mockCache->expects( $this->never() )
1124 ->method(
'delete' );
1133 $store->addWatchBatchForUser(
1134 $this->getAnonUser(),
1143 $mockDb->expects( $this->never() )
1144 ->method(
'insert' );
1147 $mockCache->expects( $this->never() )
1148 ->method(
'delete' );
1157 $store->addWatchBatchForUser( $user, [] )
1163 $mockDb->expects( $this->once() )
1164 ->method(
'selectRow' )
1167 'wl_notificationtimestamp',
1170 'wl_namespace' => 0,
1171 'wl_title' =>
'SomeDbKey',
1174 ->will( $this->returnValue(
1175 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'20151212010101' ] )
1179 $mockCache->expects( $this->once() )
1191 $watchedItem = $store->loadWatchedItem(
1195 $this->assertInstanceOf( WatchedItem::class, $watchedItem );
1196 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
1197 $this->assertEquals(
'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
1198 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
1203 $mockDb->expects( $this->once() )
1204 ->method(
'selectRow' )
1207 'wl_notificationtimestamp',
1210 'wl_namespace' => 0,
1211 'wl_title' =>
'SomeDbKey',
1214 ->will( $this->returnValue( [] ) );
1217 $mockCache->expects( $this->never() )->method(
'get' );
1218 $mockCache->expects( $this->never() )->method(
'delete' );
1227 $store->loadWatchedItem(
1228 $this->getMockNonAnonUserWithId( 1 ),
1236 $mockDb->expects( $this->never() )
1237 ->method(
'selectRow' );
1240 $mockCache->expects( $this->never() )->method(
'get' );
1241 $mockCache->expects( $this->never() )->method(
'delete' );
1250 $store->loadWatchedItem(
1251 $this->getAnonUser(),
1259 $mockDb->expects( $this->once() )
1260 ->method(
'delete' )
1265 'wl_namespace' => 0,
1266 'wl_title' =>
'SomeDbKey',
1269 $mockDb->expects( $this->once() )
1270 ->method(
'affectedRows' )
1271 ->will( $this->returnValue( 1 ) );
1274 $mockCache->expects( $this->never() )->method(
'get' );
1275 $mockCache->expects( $this->once() )
1276 ->method(
'delete' )
1277 ->with(
'0:SomeDbKey:1' );
1286 $store->removeWatch(
1287 $this->getMockNonAnonUserWithId( 1 ),
1295 $mockDb->expects( $this->once() )
1296 ->method(
'delete' )
1301 'wl_namespace' => 0,
1302 'wl_title' =>
'SomeDbKey',
1305 $mockDb->expects( $this->once() )
1306 ->method(
'affectedRows' )
1307 ->will( $this->returnValue( 0 ) );
1310 $mockCache->expects( $this->never() )->method(
'get' );
1311 $mockCache->expects( $this->once() )
1312 ->method(
'delete' )
1313 ->with(
'0:SomeDbKey:1' );
1322 $store->removeWatch(
1323 $this->getMockNonAnonUserWithId( 1 ),
1331 $mockDb->expects( $this->never() )
1332 ->method(
'delete' );
1335 $mockCache->expects( $this->never() )->method(
'get' );
1336 $mockCache->expects( $this->never() )
1337 ->method(
'delete' );
1346 $store->removeWatch(
1347 $this->getAnonUser(),
1355 $mockDb->expects( $this->once() )
1356 ->method(
'selectRow' )
1359 'wl_notificationtimestamp',
1362 'wl_namespace' => 0,
1363 'wl_title' =>
'SomeDbKey',
1366 ->will( $this->returnValue(
1367 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'20151212010101' ] )
1371 $mockCache->expects( $this->never() )->method(
'delete' );
1372 $mockCache->expects( $this->once() )
1377 ->will( $this->returnValue(
null ) );
1378 $mockCache->expects( $this->once() )
1390 $watchedItem = $store->getWatchedItem(
1394 $this->assertInstanceOf( WatchedItem::class, $watchedItem );
1395 $this->assertEquals( 1, $watchedItem->getUser()->getId() );
1396 $this->assertEquals(
'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
1397 $this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
1402 $mockDb->expects( $this->never() )
1403 ->method(
'selectRow' );
1406 $linkTarget =
new TitleValue( 0,
'SomeDbKey' );
1407 $cachedItem =
new WatchedItem( $mockUser, $linkTarget,
'20151212010101' );
1410 $mockCache->expects( $this->never() )->method(
'delete' );
1411 $mockCache->expects( $this->never() )->method(
'set' );
1412 $mockCache->expects( $this->once() )
1417 ->will( $this->returnValue( $cachedItem ) );
1425 $this->assertEquals(
1427 $store->getWatchedItem(
1436 $mockDb->expects( $this->once() )
1437 ->method(
'selectRow' )
1440 'wl_notificationtimestamp',
1443 'wl_namespace' => 0,
1444 'wl_title' =>
'SomeDbKey',
1447 ->will( $this->returnValue( [] ) );
1450 $mockCache->expects( $this->never() )->method(
'set' );
1451 $mockCache->expects( $this->never() )->method(
'delete' );
1452 $mockCache->expects( $this->once() )
1454 ->with(
'0:SomeDbKey:1' )
1455 ->will( $this->returnValue(
false ) );
1464 $store->getWatchedItem(
1465 $this->getMockNonAnonUserWithId( 1 ),
1473 $mockDb->expects( $this->never() )
1474 ->method(
'selectRow' );
1477 $mockCache->expects( $this->never() )->method(
'set' );
1478 $mockCache->expects( $this->never() )->method(
'get' );
1479 $mockCache->expects( $this->never() )->method(
'delete' );
1488 $store->getWatchedItem(
1489 $this->getAnonUser(),
1497 $mockDb->expects( $this->once() )
1498 ->method(
'select' )
1501 [
'wl_namespace',
'wl_title',
'wl_notificationtimestamp' ],
1504 ->will( $this->returnValue( [
1506 'wl_namespace' => 0,
1507 'wl_title' =>
'Foo1',
1508 'wl_notificationtimestamp' =>
'20151212010101',
1511 'wl_namespace' => 1,
1512 'wl_title' =>
'Foo2',
1513 'wl_notificationtimestamp' =>
null,
1518 $mockCache->expects( $this->never() )->method(
'delete' );
1519 $mockCache->expects( $this->never() )->method(
'get' );
1520 $mockCache->expects( $this->never() )->method(
'set' );
1529 $watchedItems = $store->getWatchedItemsForUser( $user );
1531 $this->assertInternalType(
'array', $watchedItems );
1532 $this->assertCount( 2, $watchedItems );
1533 foreach ( $watchedItems as $watchedItem ) {
1534 $this->assertInstanceOf( WatchedItem::class, $watchedItem );
1536 $this->assertEquals(
1540 $this->assertEquals(
1562 $mockDb->expects( $this->once() )
1563 ->method(
'select' )
1566 [
'wl_namespace',
'wl_title',
'wl_notificationtimestamp' ],
1568 $this->isType(
'string' ),
1569 [
'ORDER BY' => [
'wl_namespace ASC',
'wl_title ASC' ] ]
1571 ->will( $this->returnValue( [] ) );
1579 $watchedItems = $store->getWatchedItemsForUser(
1583 $this->assertEquals( [], $watchedItems );
1593 $this->setExpectedException( InvalidArgumentException::class );
1594 $store->getWatchedItemsForUser(
1602 $mockDb->expects( $this->once() )
1603 ->method(
'selectRow' )
1606 'wl_notificationtimestamp',
1609 'wl_namespace' => 0,
1610 'wl_title' =>
'SomeDbKey',
1613 ->will( $this->returnValue(
1614 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'20151212010101' ] )
1618 $mockCache->expects( $this->never() )->method(
'delete' );
1619 $mockCache->expects( $this->once() )
1621 ->with(
'0:SomeDbKey:1' )
1622 ->will( $this->returnValue(
false ) );
1623 $mockCache->expects( $this->once() )
1637 $this->getMockNonAnonUserWithId( 1 ),
1645 $mockDb->expects( $this->once() )
1646 ->method(
'selectRow' )
1649 'wl_notificationtimestamp',
1652 'wl_namespace' => 0,
1653 'wl_title' =>
'SomeDbKey',
1656 ->will( $this->returnValue( [] ) );
1659 $mockCache->expects( $this->never() )->method(
'set' );
1660 $mockCache->expects( $this->never() )->method(
'delete' );
1661 $mockCache->expects( $this->once() )
1663 ->with(
'0:SomeDbKey:1' )
1664 ->will( $this->returnValue(
false ) );
1674 $this->getMockNonAnonUserWithId( 1 ),
1682 $mockDb->expects( $this->never() )
1683 ->method(
'selectRow' );
1686 $mockCache->expects( $this->never() )->method(
'set' );
1687 $mockCache->expects( $this->never() )->method(
'get' );
1688 $mockCache->expects( $this->never() )->method(
'delete' );
1698 $this->getAnonUser(),
1713 'wl_namespace' =>
'0',
1714 'wl_title' =>
'SomeDbKey',
1715 'wl_notificationtimestamp' =>
'20151212010101',
1719 'wl_namespace' =>
'1',
1720 'wl_title' =>
'AnotherDbKey',
1721 'wl_notificationtimestamp' =>
null,
1726 $mockDb->expects( $this->once() )
1727 ->method(
'makeWhereFrom2d' )
1729 [ [
'SomeDbKey' => 1 ], [
'AnotherDbKey' => 1 ] ],
1730 $this->isType(
'string' ),
1731 $this->isType(
'string' )
1733 ->will( $this->returnValue(
'makeWhereFrom2d return value' ) );
1734 $mockDb->expects( $this->once() )
1735 ->method(
'select' )
1738 [
'wl_namespace',
'wl_title',
'wl_notificationtimestamp' ],
1740 'makeWhereFrom2d return value',
1743 $this->isType(
'string' )
1745 ->will( $this->returnValue( $dbResult ) );
1748 $mockCache->expects( $this->exactly( 2 ) )
1751 [
'0:SomeDbKey:1' ],
1752 [
'1:AnotherDbKey:1' ]
1754 ->will( $this->returnValue(
null ) );
1755 $mockCache->expects( $this->never() )->method(
'set' );
1756 $mockCache->expects( $this->never() )->method(
'delete' );
1764 $this->assertEquals(
1766 0 => [
'SomeDbKey' =>
'20151212010101', ],
1767 1 => [
'AnotherDbKey' =>
null, ],
1769 $store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
1780 $mockDb->expects( $this->once() )
1781 ->method(
'makeWhereFrom2d' )
1783 [ [
'OtherDbKey' => 1 ] ],
1784 $this->isType(
'string' ),
1785 $this->isType(
'string' )
1787 ->will( $this->returnValue(
'makeWhereFrom2d return value' ) );
1788 $mockDb->expects( $this->once() )
1789 ->method(
'select' )
1792 [
'wl_namespace',
'wl_title',
'wl_notificationtimestamp' ],
1794 'makeWhereFrom2d return value',
1797 $this->isType(
'string' )
1799 ->will( $this->returnValue( $this->
getFakeRow( [] ) ) );
1802 $mockCache->expects( $this->once() )
1804 ->with(
'0:OtherDbKey:1' )
1805 ->will( $this->returnValue(
null ) );
1806 $mockCache->expects( $this->never() )->method(
'set' );
1807 $mockCache->expects( $this->never() )->method(
'delete' );
1815 $this->assertEquals(
1817 0 => [
'OtherDbKey' =>
false, ],
1819 $store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
1830 $cachedItem =
new WatchedItem( $user, $targets[0],
'20151212010101' );
1834 $mockDb->expects( $this->once() )
1835 ->method(
'makeWhereFrom2d' )
1837 [ 1 => [
'AnotherDbKey' => 1 ] ],
1838 $this->isType(
'string' ),
1839 $this->isType(
'string' )
1841 ->will( $this->returnValue(
'makeWhereFrom2d return value' ) );
1842 $mockDb->expects( $this->once() )
1843 ->method(
'select' )
1846 [
'wl_namespace',
'wl_title',
'wl_notificationtimestamp' ],
1848 'makeWhereFrom2d return value',
1851 $this->isType(
'string' )
1853 ->will( $this->returnValue( [
1855 [
'wl_namespace' =>
'1',
'wl_title' =>
'AnotherDbKey',
'wl_notificationtimestamp' =>
null, ]
1860 $mockCache->expects( $this->at( 1 ) )
1862 ->with(
'0:SomeDbKey:1' )
1863 ->will( $this->returnValue( $cachedItem ) );
1864 $mockCache->expects( $this->at( 3 ) )
1866 ->with(
'1:AnotherDbKey:1' )
1867 ->will( $this->returnValue(
null ) );
1868 $mockCache->expects( $this->never() )->method(
'set' );
1869 $mockCache->expects( $this->never() )->method(
'delete' );
1877 $this->assertEquals(
1879 0 => [
'SomeDbKey' =>
'20151212010101', ],
1880 1 => [
'AnotherDbKey' =>
null, ],
1882 $store->getNotificationTimestampsBatch( $user, $targets )
1894 new WatchedItem( $user, $targets[0],
'20151212010101' ),
1898 $mockDb->expects( $this->never() )->method( $this->
anything() );
1901 $mockCache->expects( $this->at( 1 ) )
1903 ->with(
'0:SomeDbKey:1' )
1904 ->will( $this->returnValue( $cachedItems[0] ) );
1905 $mockCache->expects( $this->at( 3 ) )
1907 ->with(
'1:AnotherDbKey:1' )
1908 ->will( $this->returnValue( $cachedItems[1] ) );
1909 $mockCache->expects( $this->never() )->method(
'set' );
1910 $mockCache->expects( $this->never() )->method(
'delete' );
1918 $this->assertEquals(
1920 0 => [
'SomeDbKey' =>
'20151212010101', ],
1921 1 => [
'AnotherDbKey' =>
null, ],
1923 $store->getNotificationTimestampsBatch( $user, $targets )
1934 $mockDb->expects( $this->never() )->method( $this->
anything() );
1937 $mockCache->expects( $this->never() )->method( $this->
anything() );
1945 $this->assertEquals(
1947 0 => [
'SomeDbKey' =>
false, ],
1948 1 => [
'AnotherDbKey' =>
false, ],
1950 $store->getNotificationTimestampsBatch( $this->getAnonUser(), $targets )
1956 $mockDb->expects( $this->never() )
1957 ->method(
'selectRow' );
1960 $mockCache->expects( $this->never() )->method(
'get' );
1961 $mockCache->expects( $this->never() )->method(
'set' );
1962 $mockCache->expects( $this->never() )->method(
'delete' );
1971 $store->resetNotificationTimestamp(
1972 $this->getAnonUser(),
1973 Title::newFromText(
'SomeDbKey' )
1980 $mockDb->expects( $this->once() )
1981 ->method(
'selectRow' )
1984 'wl_notificationtimestamp',
1987 'wl_namespace' => 0,
1988 'wl_title' =>
'SomeDbKey',
1991 ->will( $this->returnValue( [] ) );
1994 $mockCache->expects( $this->never() )->method(
'get' );
1995 $mockCache->expects( $this->never() )->method(
'set' );
1996 $mockCache->expects( $this->never() )->method(
'delete' );
2005 $store->resetNotificationTimestamp(
2006 $this->getMockNonAnonUserWithId( 1 ),
2007 Title::newFromText(
'SomeDbKey' )
2014 $title = Title::newFromText(
'SomeDbKey' );
2017 $mockDb->expects( $this->once() )
2018 ->method(
'selectRow' )
2021 'wl_notificationtimestamp',
2024 'wl_namespace' => 0,
2025 'wl_title' =>
'SomeDbKey',
2028 ->will( $this->returnValue(
2029 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'20151212010101' ] )
2033 $mockCache->expects( $this->never() )->method(
'get' );
2034 $mockCache->expects( $this->once() )
2038 $this->isInstanceOf( WatchedItem::class )
2040 $mockCache->expects( $this->once() )
2041 ->method(
'delete' )
2042 ->with(
'0:SomeDbKey:1' );
2051 $callableCallCounter = 0;
2052 $mockCallback =
function ( $callable ) use ( &$callableCallCounter ) {
2053 $callableCallCounter++;
2054 $this->assertInternalType(
'callable', $callable );
2056 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
2059 $store->resetNotificationTimestamp(
2064 $this->assertEquals( 1, $callableCallCounter );
2066 ScopedCallback::consume( $scopedOverride );
2071 $title = Title::newFromText(
'SomeDbKey' );
2074 $mockDb->expects( $this->never() )
2075 ->method(
'selectRow' );
2078 $mockCache->expects( $this->never() )->method(
'get' );
2079 $mockCache->expects( $this->never() )->method(
'set' );
2080 $mockCache->expects( $this->once() )
2081 ->method(
'delete' )
2082 ->with(
'0:SomeDbKey:1' );
2091 $callableCallCounter = 0;
2092 $mockCallback =
function ( $callable ) use ( &$callableCallCounter ) {
2093 $callableCallCounter++;
2094 $this->assertInternalType(
'callable', $callable );
2096 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
2099 $store->resetNotificationTimestamp(
2105 $this->assertEquals( 1, $callableCallCounter );
2107 ScopedCallback::consume( $scopedOverride );
2117 $title = $this->createMock( Title::class );
2118 $title->expects( $this->
any() )
2119 ->method(
'getText' )
2120 ->will( $this->returnValue( str_replace(
'_',
' ', $text ) ) );
2121 $title->expects( $this->
any() )
2122 ->method(
'getDbKey' )
2123 ->will( $this->returnValue( str_replace(
'_',
' ', $text ) ) );
2124 $title->expects( $this->
any() )
2125 ->method(
'getNamespace' )
2126 ->will( $this->returnValue( $ns ) );
2134 callable $notificationTimestampCondition
2136 $this->assertInternalType(
'callable', $callback );
2138 $callbackReflector =
new ReflectionFunction( $callback );
2139 $vars = $callbackReflector->getStaticVariables();
2140 $this->assertArrayHasKey(
'job',
$vars );
2141 $this->assertInstanceOf( ActivityUpdateJob::class,
$vars[
'job'] );
2145 $this->assertEquals( $expectedTitle->getDBkey(),
$job->getTitle()->getDBkey() );
2146 $this->assertEquals( $expectedTitle->getNamespace(),
$job->getTitle()->getNamespace() );
2148 $jobParams =
$job->getParams();
2149 $this->assertArrayHasKey(
'type', $jobParams );
2150 $this->assertEquals(
'updateWatchlistNotification', $jobParams[
'type'] );
2151 $this->assertArrayHasKey(
'userid', $jobParams );
2152 $this->assertEquals( $expectedUserId, $jobParams[
'userid'] );
2153 $this->assertArrayHasKey(
'notifTime', $jobParams );
2154 $this->assertTrue( $notificationTimestampCondition( $jobParams[
'notifTime'] ) );
2161 $title->expects( $this->once() )
2162 ->method(
'getNextRevisionID' )
2164 ->will( $this->returnValue(
false ) );
2167 $mockDb->expects( $this->never() )
2168 ->method(
'selectRow' );
2171 $mockCache->expects( $this->never() )->method(
'get' );
2172 $mockCache->expects( $this->never() )->method(
'set' );
2173 $mockCache->expects( $this->once() )
2174 ->method(
'delete' )
2175 ->with(
'0:SomeTitle:1' );
2183 $callableCallCounter = 0;
2184 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
2185 function ( $callable ) use ( &$callableCallCounter, $title, $user ) {
2186 $callableCallCounter++;
2192 return $time ===
null;
2199 $store->resetNotificationTimestamp(
2206 $this->assertEquals( 1, $callableCallCounter );
2208 ScopedCallback::consume( $scopedOverride );
2215 $title->expects( $this->once() )
2216 ->method(
'getNextRevisionID' )
2218 ->will( $this->returnValue( 33 ) );
2221 $mockDb->expects( $this->once() )
2222 ->method(
'selectRow' )
2225 'wl_notificationtimestamp',
2228 'wl_namespace' => 0,
2229 'wl_title' =>
'SomeDbKey',
2232 ->will( $this->returnValue(
2233 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'20151212010101' ] )
2237 $mockCache->expects( $this->never() )->method(
'get' );
2238 $mockCache->expects( $this->once() )
2240 ->with(
'0:SomeDbKey:1', $this->isType(
'object' ) );
2241 $mockCache->expects( $this->once() )
2242 ->method(
'delete' )
2243 ->with(
'0:SomeDbKey:1' );
2251 $addUpdateCallCounter = 0;
2252 $scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
2253 function ( $callable ) use ( &$addUpdateCallCounter, $title, $user ) {
2254 $addUpdateCallCounter++;
2260 return $time !==
null &&
$time >
'20151212010101';
2266 $getTimestampCallCounter = 0;
2267 $scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
2268 function ( $titleParam, $oldidParam )
use ( &$getTimestampCallCounter, $title, $oldid ) {
2269 $getTimestampCallCounter++;
2270 $this->assertEquals( $title, $titleParam );
2271 $this->assertEquals( $oldid, $oldidParam );
2276 $store->resetNotificationTimestamp(
2283 $this->assertEquals( 1, $addUpdateCallCounter );
2284 $this->assertEquals( 1, $getTimestampCallCounter );
2286 ScopedCallback::consume( $scopedOverrideDeferred );
2287 ScopedCallback::consume( $scopedOverrideRevision );
2294 $title->expects( $this->once() )
2295 ->method(
'getNextRevisionID' )
2297 ->will( $this->returnValue( 33 ) );
2300 $mockDb->expects( $this->once() )
2301 ->method(
'selectRow' )
2304 'wl_notificationtimestamp',
2307 'wl_namespace' => 0,
2308 'wl_title' =>
'SomeDbKey',
2311 ->will( $this->returnValue(
false ) );
2314 $mockCache->expects( $this->never() )->method(
'get' );
2315 $mockCache->expects( $this->never() )->method(
'set' );
2316 $mockCache->expects( $this->once() )
2317 ->method(
'delete' )
2318 ->with(
'0:SomeDbKey:1' );
2326 $callableCallCounter = 0;
2327 $scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
2328 function ( $callable ) use ( &$callableCallCounter, $title, $user ) {
2329 $callableCallCounter++;
2335 return $time ===
null;
2342 $store->resetNotificationTimestamp(
2349 $this->assertEquals( 1, $callableCallCounter );
2351 ScopedCallback::consume( $scopedOverride );
2358 $title->expects( $this->once() )
2359 ->method(
'getNextRevisionID' )
2361 ->will( $this->returnValue( 33 ) );
2364 $mockDb->expects( $this->once() )
2365 ->method(
'selectRow' )
2368 'wl_notificationtimestamp',
2371 'wl_namespace' => 0,
2372 'wl_title' =>
'SomeDbKey',
2375 ->will( $this->returnValue(
2376 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'30151212010101' ] )
2380 $mockCache->expects( $this->never() )->method(
'get' );
2381 $mockCache->expects( $this->once() )
2383 ->with(
'0:SomeDbKey:1', $this->isType(
'object' ) );
2384 $mockCache->expects( $this->once() )
2385 ->method(
'delete' )
2386 ->with(
'0:SomeDbKey:1' );
2394 $addUpdateCallCounter = 0;
2395 $scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
2396 function ( $callable ) use ( &$addUpdateCallCounter, $title, $user ) {
2397 $addUpdateCallCounter++;
2403 return $time ===
'30151212010101';
2409 $getTimestampCallCounter = 0;
2410 $scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
2411 function ( $titleParam, $oldidParam )
use ( &$getTimestampCallCounter, $title, $oldid ) {
2412 $getTimestampCallCounter++;
2413 $this->assertEquals( $title, $titleParam );
2414 $this->assertEquals( $oldid, $oldidParam );
2419 $store->resetNotificationTimestamp(
2426 $this->assertEquals( 1, $addUpdateCallCounter );
2427 $this->assertEquals( 1, $getTimestampCallCounter );
2429 ScopedCallback::consume( $scopedOverrideDeferred );
2430 ScopedCallback::consume( $scopedOverrideRevision );
2437 $title->expects( $this->once() )
2438 ->method(
'getNextRevisionID' )
2440 ->will( $this->returnValue( 33 ) );
2443 $mockDb->expects( $this->once() )
2444 ->method(
'selectRow' )
2447 'wl_notificationtimestamp',
2450 'wl_namespace' => 0,
2451 'wl_title' =>
'SomeDbKey',
2454 ->will( $this->returnValue(
2455 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'30151212010101' ] )
2459 $mockCache->expects( $this->never() )->method(
'get' );
2460 $mockCache->expects( $this->once() )
2462 ->with(
'0:SomeDbKey:1', $this->isType(
'object' ) );
2463 $mockCache->expects( $this->once() )
2464 ->method(
'delete' )
2465 ->with(
'0:SomeDbKey:1' );
2473 $addUpdateCallCounter = 0;
2474 $scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
2475 function ( $callable ) use ( &$addUpdateCallCounter, $title, $user ) {
2476 $addUpdateCallCounter++;
2482 return $time ===
false;
2488 $getTimestampCallCounter = 0;
2489 $scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
2490 function ( $titleParam, $oldidParam )
use ( &$getTimestampCallCounter, $title, $oldid ) {
2491 $getTimestampCallCounter++;
2492 $this->assertEquals( $title, $titleParam );
2493 $this->assertEquals( $oldid, $oldidParam );
2498 $store->resetNotificationTimestamp(
2505 $this->assertEquals( 1, $addUpdateCallCounter );
2506 $this->assertEquals( 1, $getTimestampCallCounter );
2508 ScopedCallback::consume( $scopedOverrideDeferred );
2509 ScopedCallback::consume( $scopedOverrideRevision );
2518 $this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(),
'' ) );
2523 $timestamp =
'20100101010101';
2526 $mockDb->expects( $this->once() )
2527 ->method(
'update' )
2530 [
'wl_notificationtimestamp' =>
'TS' . $timestamp .
'TS' ],
2533 ->will( $this->returnValue(
true ) );
2534 $mockDb->expects( $this->exactly( 1 ) )
2535 ->method(
'timestamp' )
2536 ->will( $this->returnCallback(
function (
$value ) {
2537 return 'TS' .
$value .
'TS';
2547 $store->setNotificationTimestampsForUser( $user, $timestamp )
2556 $mockDb->expects( $this->once() )
2557 ->method(
'update' )
2560 [
'wl_notificationtimestamp' =>
null ],
2563 ->will( $this->returnValue(
true ) );
2564 $mockDb->expects( $this->exactly( 0 ) )
2565 ->method(
'timestamp' )
2566 ->will( $this->returnCallback(
function (
$value ) {
2567 return 'TS' .
$value .
'TS';
2577 $store->setNotificationTimestampsForUser( $user, $timestamp )
2583 $timestamp =
'20100101010101';
2587 $mockDb->expects( $this->once() )
2588 ->method(
'update' )
2591 [
'wl_notificationtimestamp' =>
'TS' . $timestamp .
'TS' ],
2592 [
'wl_user' => 1, 0 =>
'makeWhereFrom2d return value' ]
2594 ->will( $this->returnValue(
true ) );
2595 $mockDb->expects( $this->exactly( 1 ) )
2596 ->method(
'timestamp' )
2597 ->will( $this->returnCallback(
function (
$value ) {
2598 return 'TS' .
$value .
'TS';
2600 $mockDb->expects( $this->once() )
2601 ->method(
'makeWhereFrom2d' )
2603 [ [
'Foo' => 1,
'Bar' => 1 ] ],
2604 $this->isType(
'string' ),
2605 $this->isType(
'string' )
2607 ->will( $this->returnValue(
'makeWhereFrom2d return value' ) );
2616 $store->setNotificationTimestampsForUser( $user, $timestamp, $targets )
2622 $mockDb->expects( $this->once() )
2623 ->method(
'selectFieldValues' )
2629 'wl_namespace' => 0,
2630 'wl_title' =>
'SomeDbKey',
2631 'wl_notificationtimestamp IS NULL'
2634 ->will( $this->returnValue( [
'2',
'3' ] ) );
2635 $mockDb->expects( $this->once() )
2636 ->method(
'update' )
2639 [
'wl_notificationtimestamp' =>
null ],
2641 'wl_user' => [ 2, 3 ],
2642 'wl_namespace' => 0,
2643 'wl_title' =>
'SomeDbKey',
2648 $mockCache->expects( $this->never() )->method(
'set' );
2649 $mockCache->expects( $this->never() )->method(
'get' );
2650 $mockCache->expects( $this->never() )->method(
'delete' );
2658 $this->assertEquals(
2660 $store->updateNotificationTimestamp(
2661 $this->getMockNonAnonUserWithId( 1 ),
2670 $mockDb->expects( $this->once() )
2671 ->method(
'selectFieldValues' )
2677 'wl_namespace' => 0,
2678 'wl_title' =>
'SomeDbKey',
2679 'wl_notificationtimestamp IS NULL'
2683 $this->returnValue( [] )
2685 $mockDb->expects( $this->never() )
2686 ->method(
'update' );
2689 $mockCache->expects( $this->never() )->method(
'set' );
2690 $mockCache->expects( $this->never() )->method(
'get' );
2691 $mockCache->expects( $this->never() )->method(
'delete' );
2699 $watchers = $store->updateNotificationTimestamp(
2704 $this->assertInternalType(
'array', $watchers );
2705 $this->assertEmpty( $watchers );
2710 $titleValue =
new TitleValue( 0,
'SomeDbKey' );
2713 $mockDb->expects( $this->once() )
2714 ->method(
'selectRow' )
2715 ->will( $this->returnValue(
2716 $this->
getFakeRow( [
'wl_notificationtimestamp' =>
'20151212010101' ] )
2718 $mockDb->expects( $this->once() )
2719 ->method(
'selectFieldValues' )
2721 $this->returnValue( [
'2',
'3' ] )
2723 $mockDb->expects( $this->once() )
2724 ->method(
'update' );
2727 $mockCache->expects( $this->once() )
2729 ->with(
'0:SomeDbKey:1', $this->isType(
'object' ) );
2730 $mockCache->expects( $this->once() )
2732 ->with(
'0:SomeDbKey:1' );
2733 $mockCache->expects( $this->once() )
2734 ->method(
'delete' )
2735 ->with(
'0:SomeDbKey:1' );
2744 $store->getWatchedItem( $user, $titleValue );
2746 $store->updateNotificationTimestamp(
</td >< td > &</td >< td > t want your writing to be edited mercilessly and redistributed at will
they could even be mouse clicks or menu items whatever suits your program You should also get your if any
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Simple store for keeping values in an associative array for the current process.
A service class for fetching the wiki's current read-only mode.
Represents a page (or page fragment) title within MediaWiki.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
testLoadWatchedItem_anonymousUser()
testLoadWatchedItem_noItem()
testCountWatchersMultiple_withMinimumWatchers( $minWatchers)
provideIntWithDbUnsafeVersion
testDuplicateAllAssociatedEntries_somethingToDuplicate(LinkTarget $oldTarget, LinkTarget $newTarget)
provideLinkTargetPairs
testIsWatchedItem_existingItem()
testGetNotificationTimestampsBatch_cachedItem()
getMockTitle( $text, $ns=0)
testDuplicateEntry_somethingToDuplicate()
testSetNotificationTimestampsForUser_nullTimestamp()
testGetWatchedItem_noItem()
getFakeRow(array $rowValues)
testCountVisitingWatchers()
testGetNotificationTimestampsBatch_anonymousUser()
testCountUnreadNotifications_withUnreadLimit_overLimit( $limit)
provideIntWithDbUnsafeVersion
testCountUnreadNotifications()
testIsWatchedItem_anonymousUser()
testGetWatchedItem_cachedItem()
testCountUnreadNotifications_withUnreadLimit_underLimit( $limit)
provideIntWithDbUnsafeVersion
testGetNotificationTimestampsBatch_notWatchedTarget()
testUpdateNotificationTimestamp_noWatchers()
testAddWatchBatchForUser_nonAnonymousUser()
testAddWatch_anonymousUser()
testCountVisitingWatchersMultiple_withMissingTargets()
testCountWatchersMultiple()
testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced()
testRemoveWatch_anonymousUser()
testSetNotificationTimestampsForUser_allRows()
testResetNotificationTimestamp_item()
testGetNotificationTimestampsBatch_allItemsCached()
testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced()
testDuplicateAllAssociatedEntries_nothingToDuplicate()
testResetNotificationTimestamp_noItem()
testUpdateNotificationTimestamp_watchersExist()
testAddWatchBatchForUser_anonymousUsersAreSkipped()
getMockNonAnonUserWithId( $id)
testAddWatchBatchReturnsTrue_whenGivenEmptyList()
getMockLoadBalancer( $mockDb, $expectedConnectionType=null)
testAddWatch_nonAnonymousUser()
testSetNotificationTimestampsForUser_specificTargets()
testResetNotificationTimestamp_anonymousUser()
testSetNotificationTimestampsForUser_anonUser()
testUpdateNotificationTimestamp_clearsCachedItems()
testGetWatchedItemsForUser_badSortOptionThrowsException()
testResetNotificationTimestamp_notWatchedPageForced()
testGetNotificationTimestampsBatch()
testGetWatchedItemsForUser_optionsAndEmptyResult( $forWrite, $dbType)
provideDbTypes
verifyCallbackJob( $callback, LinkTarget $expectedTitle, $expectedUserId, callable $notificationTimestampCondition)
testClearWatchedItems_tooManyItemsWatched()
testGetWatchedItem_anonymousUser()
getMockReadOnlyMode( $readOnly=false)
testRemoveWatch_existingItem()
testDuplicateEntry_nothingToDuplicate()
testLoadWatchedItem_existingItem()
testCountVisitingWatchersMultiple()
newWatchedItemStore(LoadBalancer $loadBalancer, HashBagOStuff $cache, ReadOnlyMode $readOnlyMode)
testResetNotificationTimestamp_noItemForced()
testResetNotificationTimestamp_futureNotificationTimestampForced()
testGetWatchedItem_existingItem()
testResetNotificationTimestamp_futureNotificationTimestampNotForced()
testAddWatchBatchForUser_readOnlyDBReturnsFalse()
testGetWatchedItemsForUser()
provideIntWithDbUnsafeVersion()
testIsWatchedItem_noItem()
testCountVisitingWatchersMultiple_withMinimumWatchers( $minWatchers)
provideIntWithDbUnsafeVersion
Storage layer class for WatchedItems.
Representation of a pair of user and title for watchlist entries.
when a variable name is used in a function
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same so they can t rely on Unix and must forbid reads to even standard directories like tmp lest users read each others files We cannot assume that the user has the ability to install or run any programs not written as web accessible PHP scripts Since anything that works on cheap shared hosting will work if you have shell or root access MediaWiki s design is based around catering to the lowest common denominator Although we support higher end setups as the way many things work by default is tailored toward shared hosting These defaults are unconventional from the point of view of and they certainly aren t ideal for someone who s installing MediaWiki as MediaWiki does not conform to normal Unix filesystem layout Hopefully we ll offer direct support for standard layouts in the but for now *any change to the location of files is unsupported *Moving things and leaving symlinks will *probably *not break anything
static configuration should be added through ResourceLoaderGetConfigVars instead & $vars
see documentation in includes Linker php for Linker::makeImageLink & $time
namespace and then decline to actually register it file or subcat img or subcat $title
if(count( $args)< 1) $job