4 * @covers WatchedItemQueryService
6 class WatchedItemQueryServiceUnitTest
extends PHPUnit_Framework_TestCase
{
9 * @return PHPUnit_Framework_MockObject_MockObject|DatabaseBase
11 private function getMockDb() {
12 $mock = $this->getMockBuilder( DatabaseBase
::class )
13 ->disableOriginalConstructor()
16 $mock->expects( $this->any() )
17 ->method( 'makeList' )
19 $this->isType( 'array' ),
20 $this->isType( 'int' )
22 ->will( $this->returnCallback( function( $a, $conj ) {
23 $sqlConj = $conj === LIST_AND ?
' AND ' : ' OR ';
24 return join( $sqlConj, array_map( function( $s ) {
25 return '(' . $s . ')';
30 $mock->expects( $this->any() )
31 ->method( 'addQuotes' )
32 ->will( $this->returnCallback( function( $value ) {
36 $mock->expects( $this->any() )
37 ->method( 'timestamp' )
38 ->will( $this->returnArgument( 0 ) );
40 $mock->expects( $this->any() )
42 ->willReturnCallback( function( $a, $b ) {
51 * @return PHPUnit_Framework_MockObject_MockObject|LoadBalancer
53 private function getMockLoadBalancer( $mockDb ) {
54 $mock = $this->getMockBuilder( LoadBalancer
::class )
55 ->disableOriginalConstructor()
57 $mock->expects( $this->any() )
58 ->method( 'getConnection' )
60 ->will( $this->returnValue( $mockDb ) );
66 * @return PHPUnit_Framework_MockObject_MockObject|User
68 private function getMockNonAnonUserWithId( $id ) {
69 $mock = $this->getMock( User
::class );
70 $mock->expects( $this->any() )
72 ->will( $this->returnValue( false ) );
73 $mock->expects( $this->any() )
75 ->will( $this->returnValue( $id ) );
81 * @return PHPUnit_Framework_MockObject_MockObject|User
83 private function getMockUnrestrictedNonAnonUserWithId( $id ) {
84 $mock = $this->getMockNonAnonUserWithId( $id );
85 $mock->expects( $this->any() )
86 ->method( 'isAllowed' )
87 ->will( $this->returnValue( true ) );
88 $mock->expects( $this->any() )
89 ->method( 'isAllowedAny' )
90 ->will( $this->returnValue( true ) );
91 $mock->expects( $this->any() )
92 ->method( 'useRCPatrol' )
93 ->will( $this->returnValue( true ) );
99 * @param string $notAllowedAction
100 * @return PHPUnit_Framework_MockObject_MockObject|User
102 private function getMockNonAnonUserWithIdAndRestrictedPermissions( $id, $notAllowedAction ) {
103 $mock = $this->getMockNonAnonUserWithId( $id );
105 $mock->expects( $this->any() )
106 ->method( 'isAllowed' )
107 ->will( $this->returnCallback( function( $action ) use ( $notAllowedAction ) {
108 return $action !== $notAllowedAction;
110 $mock->expects( $this->any() )
111 ->method( 'isAllowedAny' )
112 ->will( $this->returnCallback( function() use ( $notAllowedAction ) {
113 $actions = func_get_args();
114 return !in_array( $notAllowedAction, $actions );
122 * @return PHPUnit_Framework_MockObject_MockObject|User
124 private function getMockNonAnonUserWithIdAndNoPatrolRights( $id ) {
125 $mock = $this->getMockNonAnonUserWithId( $id );
127 $mock->expects( $this->any() )
128 ->method( 'isAllowed' )
129 ->will( $this->returnValue( true ) );
130 $mock->expects( $this->any() )
131 ->method( 'isAllowedAny' )
132 ->will( $this->returnValue( true ) );
134 $mock->expects( $this->any() )
135 ->method( 'useRCPatrol' )
136 ->will( $this->returnValue( false ) );
137 $mock->expects( $this->any() )
138 ->method( 'useNPPatrol' )
139 ->will( $this->returnValue( false ) );
144 private function getFakeRow( array $rowValues ) {
145 $fakeRow = new stdClass();
146 foreach ( $rowValues as $valueName => $value ) {
147 $fakeRow->$valueName = $value;
152 public function testGetWatchedItemsWithRecentChangeInfo() {
153 $mockDb = $this->getMockDb();
154 $mockDb->expects( $this->once() )
157 [ 'recentchanges', 'watchlist', 'page' ],
165 'wl_notificationtimestamp',
172 '(rc_this_oldid=page_latest) OR (rc_type=3)',
174 $this->isType( 'string' ),
180 'wl_namespace=rc_namespace',
190 ->will( $this->returnValue( [
194 'rc_title' => 'Foo1',
195 'rc_timestamp' => '20151212010101',
198 'wl_notificationtimestamp' => '20151212010101',
203 'rc_title' => 'Foo2',
204 'rc_timestamp' => '20151212010102',
207 'wl_notificationtimestamp' => null,
211 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
212 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
214 $items = $queryService->getWatchedItemsWithRecentChangeInfo( $user );
216 $this->assertInternalType( 'array', $items );
217 $this->assertCount( 2, $items );
219 foreach ( $items as list( $watchedItem, $recentChangeInfo ) ) {
220 $this->assertInstanceOf( WatchedItem
::class, $watchedItem );
221 $this->assertInternalType( 'array', $recentChangeInfo );
225 new WatchedItem( $user, new TitleValue( 0, 'Foo1' ), '20151212010101' ),
232 'rc_title' => 'Foo1',
233 'rc_timestamp' => '20151212010101',
241 new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
248 'rc_title' => 'Foo2',
249 'rc_timestamp' => '20151212010102',
257 public function getWatchedItemsWithRecentChangeInfoOptionsProvider() {
260 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_FLAGS
] ],
261 [ 'rc_type', 'rc_minor', 'rc_bot' ],
266 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_USER
] ],
272 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_USER_ID
] ],
278 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_COMMENT
] ],
284 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_PATROL_INFO
] ],
285 [ 'rc_patrolled', 'rc_log_type' ],
290 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_SIZES
] ],
291 [ 'rc_old_len', 'rc_new_len' ],
296 [ 'includeFields' => [ WatchedItemQueryService
::INCLUDE_LOG_INFO
] ],
297 [ 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ],
302 [ 'namespaceIds' => [ 0, 1 ] ],
304 [ 'wl_namespace' => [ 0, 1 ] ],
308 [ 'namespaceIds' => [ 0, "1; DROP TABLE watchlist;\n--" ] ],
310 [ 'wl_namespace' => [ 0, 1 ] ],
314 [ 'rcTypes' => [ RC_EDIT
, RC_NEW
] ],
316 [ 'rc_type' => [ RC_EDIT
, RC_NEW
] ],
320 [ 'dir' => WatchedItemQueryService
::DIR_OLDER
],
323 [ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ]
326 [ 'dir' => WatchedItemQueryService
::DIR_NEWER
],
329 [ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ]
332 [ 'dir' => WatchedItemQueryService
::DIR_OLDER
, 'start' => '20151212010101' ],
334 [ "rc_timestamp <= '20151212010101'" ],
335 [ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ]
338 [ 'dir' => WatchedItemQueryService
::DIR_OLDER
, 'end' => '20151212010101' ],
340 [ "rc_timestamp >= '20151212010101'" ],
341 [ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ]
345 'dir' => WatchedItemQueryService
::DIR_OLDER
,
346 'start' => '20151212020101',
347 'end' => '20151212010101'
350 [ "rc_timestamp <= '20151212020101'", "rc_timestamp >= '20151212010101'" ],
351 [ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ]
354 [ 'dir' => WatchedItemQueryService
::DIR_NEWER
, 'start' => '20151212010101' ],
356 [ "rc_timestamp >= '20151212010101'" ],
357 [ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ]
360 [ 'dir' => WatchedItemQueryService
::DIR_NEWER
, 'end' => '20151212010101' ],
362 [ "rc_timestamp <= '20151212010101'" ],
363 [ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ]
367 'dir' => WatchedItemQueryService
::DIR_NEWER
,
368 'start' => '20151212010101',
369 'end' => '20151212020101'
372 [ "rc_timestamp >= '20151212010101'", "rc_timestamp <= '20151212020101'" ],
373 [ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ]
382 [ 'limit' => "10; DROP TABLE watchlist;\n--" ],
388 [ 'filters' => [ WatchedItemQueryService
::FILTER_MINOR
] ],
394 [ 'filters' => [ WatchedItemQueryService
::FILTER_NOT_MINOR
] ],
400 [ 'filters' => [ WatchedItemQueryService
::FILTER_BOT
] ],
406 [ 'filters' => [ WatchedItemQueryService
::FILTER_NOT_BOT
] ],
412 [ 'filters' => [ WatchedItemQueryService
::FILTER_ANON
] ],
418 [ 'filters' => [ WatchedItemQueryService
::FILTER_NOT_ANON
] ],
424 [ 'filters' => [ WatchedItemQueryService
::FILTER_PATROLLED
] ],
426 [ 'rc_patrolled != 0' ],
430 [ 'filters' => [ WatchedItemQueryService
::FILTER_NOT_PATROLLED
] ],
432 [ 'rc_patrolled = 0' ],
436 [ 'filters' => [ WatchedItemQueryService
::FILTER_UNREAD
] ],
438 [ 'rc_timestamp >= wl_notificationtimestamp' ],
442 [ 'filters' => [ WatchedItemQueryService
::FILTER_NOT_UNREAD
] ],
444 [ 'wl_notificationtimestamp IS NULL OR rc_timestamp < wl_notificationtimestamp' ],
448 [ 'onlyByUser' => 'SomeOtherUser' ],
450 [ 'rc_user_text' => 'SomeOtherUser' ],
454 [ 'notByUser' => 'SomeOtherUser' ],
456 [ "rc_user_text != 'SomeOtherUser'" ],
460 [ 'startFrom' => [ '20151212010101', 123 ], 'dir' => WatchedItemQueryService
::DIR_OLDER
],
463 "(rc_timestamp < '20151212010101') OR ((rc_timestamp = '20151212010101') AND (rc_id <= 123))"
465 [ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
468 [ 'startFrom' => [ '20151212010101', 123 ], 'dir' => WatchedItemQueryService
::DIR_NEWER
],
471 "(rc_timestamp > '20151212010101') OR ((rc_timestamp = '20151212010101') AND (rc_id >= 123))"
473 [ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ],
477 'startFrom' => [ '20151212010101', "123; DROP TABLE watchlist;\n--" ],
478 'dir' => WatchedItemQueryService
::DIR_OLDER
482 "(rc_timestamp < '20151212010101') OR ((rc_timestamp = '20151212010101') AND (rc_id <= 123))"
484 [ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
490 * @dataProvider getWatchedItemsWithRecentChangeInfoOptionsProvider
492 public function testGetWatchedItemsWithRecentChangeInfo_optionsAndEmptyResult(
494 array $expectedExtraFields,
495 array $expectedExtraConds,
496 array $expectedDbOptions
498 $expectedFields = array_merge(
506 'wl_notificationtimestamp',
514 $expectedConds = array_merge(
515 [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)', ],
519 $mockDb = $this->getMockDb();
520 $mockDb->expects( $this->once() )
523 [ 'recentchanges', 'watchlist', 'page' ],
526 $this->isType( 'string' ),
532 'wl_namespace=rc_namespace',
542 ->will( $this->returnValue( [] ) );
544 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
545 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
547 $items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options );
549 $this->assertEmpty( $items );
552 public function filterPatrolledOptionProvider() {
554 [ WatchedItemQueryService
::FILTER_PATROLLED
],
555 [ WatchedItemQueryService
::FILTER_NOT_PATROLLED
],
560 * @dataProvider filterPatrolledOptionProvider
562 public function testGetWatchedItemsWithRecentChangeInfo_filterPatrolledAndUserWithNoPatrolRights(
565 $mockDb = $this->getMockDb();
566 $mockDb->expects( $this->once() )
569 [ 'recentchanges', 'watchlist', 'page' ],
570 $this->isType( 'array' ),
571 [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ],
572 $this->isType( 'string' ),
573 $this->isType( 'array' ),
574 $this->isType( 'array' )
576 ->will( $this->returnValue( [] ) );
578 $user = $this->getMockNonAnonUserWithIdAndNoPatrolRights( 1 );
580 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
581 $items = $queryService->getWatchedItemsWithRecentChangeInfo(
583 [ 'filters' => [ $filtersOption ] ]
586 $this->assertEmpty( $items );
589 public function mysqlIndexOptimizationProvider() {
594 [ "rc_timestamp > ''" ],
598 [ 'start' => '20151212010101', 'dir' => WatchedItemQueryService
::DIR_OLDER
],
599 [ "rc_timestamp <= '20151212010101'" ],
603 [ 'end' => '20151212010101', 'dir' => WatchedItemQueryService
::DIR_OLDER
],
604 [ "rc_timestamp >= '20151212010101'" ],
615 * @dataProvider mysqlIndexOptimizationProvider
617 public function testGetWatchedItemsWithRecentChangeInfo_mysqlIndexOptimization(
620 array $expectedExtraConds
622 $commonConds = [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ];
623 $conds = array_merge( $commonConds, $expectedExtraConds );
625 $mockDb = $this->getMockDb();
626 $mockDb->expects( $this->once() )
629 [ 'recentchanges', 'watchlist', 'page' ],
630 $this->isType( 'array' ),
632 $this->isType( 'string' ),
633 $this->isType( 'array' ),
634 $this->isType( 'array' )
636 ->will( $this->returnValue( [] ) );
637 $mockDb->expects( $this->any() )
638 ->method( 'getType' )
639 ->will( $this->returnValue( $dbType ) );
641 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
642 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
644 $items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options );
646 $this->assertEmpty( $items );
649 public function userPermissionRelatedExtraChecksProvider() {
655 '(rc_type != ' . RC_LOG
. ') OR ((rc_deleted & ' . LogPage
::DELETED_ACTION
. ') != ' .
656 LogPage
::DELETED_ACTION
. ')'
663 '(rc_type != ' . RC_LOG
. ') OR (' .
664 '(rc_deleted & ' . ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ') != ' .
665 ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ')'
672 '(rc_type != ' . RC_LOG
. ') OR (' .
673 '(rc_deleted & ' . ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ') != ' .
674 ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ')'
678 [ 'onlyByUser' => 'SomeOtherUser' ],
681 'rc_user_text' => 'SomeOtherUser',
682 '(rc_deleted & ' . Revision
::DELETED_USER
. ') != ' . Revision
::DELETED_USER
,
683 '(rc_type != ' . RC_LOG
. ') OR ((rc_deleted & ' . LogPage
::DELETED_ACTION
. ') != ' .
684 LogPage
::DELETED_ACTION
. ')'
688 [ 'onlyByUser' => 'SomeOtherUser' ],
691 'rc_user_text' => 'SomeOtherUser',
692 '(rc_deleted & ' . ( Revision
::DELETED_USER | Revision
::DELETED_RESTRICTED
) . ') != ' .
693 ( Revision
::DELETED_USER | Revision
::DELETED_RESTRICTED
),
694 '(rc_type != ' . RC_LOG
. ') OR (' .
695 '(rc_deleted & ' . ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ') != ' .
696 ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ')'
700 [ 'onlyByUser' => 'SomeOtherUser' ],
703 'rc_user_text' => 'SomeOtherUser',
704 '(rc_deleted & ' . ( Revision
::DELETED_USER | Revision
::DELETED_RESTRICTED
) . ') != ' .
705 ( Revision
::DELETED_USER | Revision
::DELETED_RESTRICTED
),
706 '(rc_type != ' . RC_LOG
. ') OR (' .
707 '(rc_deleted & ' . ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ') != ' .
708 ( LogPage
::DELETED_ACTION | LogPage
::DELETED_RESTRICTED
) . ')'
715 * @dataProvider userPermissionRelatedExtraChecksProvider
717 public function testGetWatchedItemsWithRecentChangeInfo_userPermissionRelatedExtraChecks(
720 array $expectedExtraConds
722 $commonConds = [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ];
723 $conds = array_merge( $commonConds, $expectedExtraConds );
725 $mockDb = $this->getMockDb();
726 $mockDb->expects( $this->once() )
729 [ 'recentchanges', 'watchlist', 'page' ],
730 $this->isType( 'array' ),
732 $this->isType( 'string' ),
733 $this->isType( 'array' ),
734 $this->isType( 'array' )
736 ->will( $this->returnValue( [] ) );
738 $user = $this->getMockNonAnonUserWithIdAndRestrictedPermissions( 1, $notAllowedAction );
740 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
741 $items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options );
743 $this->assertEmpty( $items );
746 public function testGetWatchedItemsWithRecentChangeInfo_allRevisionsOptionAndEmptyResult() {
747 $mockDb = $this->getMockDb();
748 $mockDb->expects( $this->once() )
751 [ 'recentchanges', 'watchlist' ],
759 'wl_notificationtimestamp',
766 $this->isType( 'string' ),
772 'wl_namespace=rc_namespace',
778 ->will( $this->returnValue( [] ) );
780 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
781 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
783 $items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, [ 'allRevisions' => true ] );
785 $this->assertEmpty( $items );
788 public function getWatchedItemsWithRecentChangeInfoInvalidOptionsProvider() {
791 [ 'rcTypes' => [ 1337 ] ],
792 'Bad value for parameter $options[\'rcTypes\']',
795 [ 'rcTypes' => [ 'edit' ] ],
796 'Bad value for parameter $options[\'rcTypes\']',
799 [ 'rcTypes' => [ RC_EDIT
, 1337 ] ],
800 'Bad value for parameter $options[\'rcTypes\']',
804 'Bad value for parameter $options[\'dir\']',
807 [ 'start' => '20151212010101' ],
808 'Bad value for parameter $options[\'dir\']: must be provided',
811 [ 'end' => '20151212010101' ],
812 'Bad value for parameter $options[\'dir\']: must be provided',
815 [ 'startFrom' => [ '20151212010101', 123 ] ],
816 'Bad value for parameter $options[\'dir\']: must be provided',
819 [ 'dir' => WatchedItemQueryService
::DIR_OLDER
, 'startFrom' => '20151212010101' ],
820 'Bad value for parameter $options[\'startFrom\']: must be a two-element array',
823 [ 'dir' => WatchedItemQueryService
::DIR_OLDER
, 'startFrom' => [ '20151212010101' ] ],
824 'Bad value for parameter $options[\'startFrom\']: must be a two-element array',
828 'dir' => WatchedItemQueryService
::DIR_OLDER
,
829 'startFrom' => [ '20151212010101', 123, 'foo' ]
831 'Bad value for parameter $options[\'startFrom\']: must be a two-element array',
834 [ 'watchlistOwner' => $this->getMockUnrestrictedNonAnonUserWithId( 2 ) ],
835 'Bad value for parameter $options[\'watchlistOwnerToken\']',
838 [ 'watchlistOwner' => 'Other User', 'watchlistOwnerToken' => 'some-token' ],
839 'Bad value for parameter $options[\'watchlistOwner\']',
845 * @dataProvider getWatchedItemsWithRecentChangeInfoInvalidOptionsProvider
847 public function testGetWatchedItemsWithRecentChangeInfo_invalidOptions(
849 $expectedInExceptionMessage
851 $mockDb = $this->getMockDb();
852 $mockDb->expects( $this->never() )
853 ->method( $this->anything() );
855 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
856 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
858 $this->setExpectedException( InvalidArgumentException
::class, $expectedInExceptionMessage );
859 $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options );
862 public function testGetWatchedItemsWithRecentChangeInfo_usedInGeneratorOptionAndEmptyResult() {
863 $mockDb = $this->getMockDb();
864 $mockDb->expects( $this->once() )
867 [ 'recentchanges', 'watchlist', 'page' ],
875 'wl_notificationtimestamp',
878 [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ],
879 $this->isType( 'string' ),
885 'wl_namespace=rc_namespace',
895 ->will( $this->returnValue( [] ) );
897 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
898 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
900 $items = $queryService->getWatchedItemsWithRecentChangeInfo(
902 [ 'usedInGenerator' => true ]
905 $this->assertEmpty( $items );
908 public function testGetWatchedItemsWithRecentChangeInfo_usedInGeneratorAllRevisionsOptions() {
909 $mockDb = $this->getMockDb();
910 $mockDb->expects( $this->once() )
913 [ 'recentchanges', 'watchlist' ],
921 'wl_notificationtimestamp',
925 $this->isType( 'string' ),
931 'wl_namespace=rc_namespace',
937 ->will( $this->returnValue( [] ) );
939 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
940 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
942 $items = $queryService->getWatchedItemsWithRecentChangeInfo(
944 [ 'usedInGenerator' => true, 'allRevisions' => true, ]
947 $this->assertEmpty( $items );
950 public function testGetWatchedItemsWithRecentChangeInfo_watchlistOwnerOptionAndEmptyResult() {
951 $mockDb = $this->getMockDb();
952 $mockDb->expects( $this->once() )
955 $this->isType( 'array' ),
956 $this->isType( 'array' ),
959 '(rc_this_oldid=page_latest) OR (rc_type=3)',
961 $this->isType( 'string' ),
962 $this->isType( 'array' ),
963 $this->isType( 'array' )
965 ->will( $this->returnValue( [] ) );
967 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
968 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
969 $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2 );
970 $otherUser->expects( $this->once() )
971 ->method( 'getOption' )
972 ->with( 'watchlisttoken' )
973 ->willReturn( '0123456789abcdef' );
975 $items = $queryService->getWatchedItemsWithRecentChangeInfo(
977 [ 'watchlistOwner' => $otherUser, 'watchlistOwnerToken' => '0123456789abcdef' ]
980 $this->assertEmpty( $items );
983 public function invalidWatchlistTokenProvider() {
991 * @dataProvider invalidWatchlistTokenProvider
993 public function testGetWatchedItemsWithRecentChangeInfo_watchlistOwnerAndInvalidToken( $token ) {
994 $mockDb = $this->getMockDb();
995 $mockDb->expects( $this->never() )
996 ->method( $this->anything() );
998 $queryService = new WatchedItemQueryService( $this->getMockLoadBalancer( $mockDb ) );
999 $user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
1000 $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2 );
1001 $otherUser->expects( $this->once() )
1002 ->method( 'getOption' )
1003 ->with( 'watchlisttoken' )
1004 ->willReturn( '0123456789abcdef' );
1006 $this->setExpectedException( UsageException
::class, 'Incorrect watchlist token provided' );
1007 $queryService->getWatchedItemsWithRecentChangeInfo(
1009 [ 'watchlistOwner' => $otherUser, 'watchlistOwnerToken' => $token ]