2016-04-19 09:34:31 +00:00
|
|
|
<?php
|
|
|
|
|
|
2020-07-03 00:20:38 +00:00
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2019-04-28 11:07:18 +00:00
|
|
|
use MediaWiki\User\UserIdentityValue;
|
2022-04-22 12:46:10 +00:00
|
|
|
use MediaWiki\User\UserOptionsLookup;
|
2019-10-06 04:54:59 +00:00
|
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
2021-08-31 17:38:46 +00:00
|
|
|
use Wikimedia\Rdbms\DBConnRef;
|
2019-05-01 15:31:13 +00:00
|
|
|
use Wikimedia\Rdbms\IDatabase;
|
2018-01-26 19:17:27 +00:00
|
|
|
use Wikimedia\Rdbms\LoadBalancer;
|
2017-04-19 19:37:35 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
|
|
2016-04-19 09:34:31 +00:00
|
|
|
/**
|
|
|
|
|
* @covers WatchedItemQueryService
|
|
|
|
|
*/
|
2021-01-10 02:19:44 +00:00
|
|
|
class WatchedItemQueryServiceUnitTest extends MediaWikiUnitTestCase {
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @return MockObject&CommentStore
|
2018-01-26 19:17:27 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockCommentStore() {
|
2021-01-10 02:19:44 +00:00
|
|
|
$mockStore = $this->createMock( CommentStore::class );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockStore->method( 'getFields' )
|
2018-01-24 23:41:01 +00:00
|
|
|
->willReturn( [ 'commentstore' => 'fields' ] );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockStore->method( 'getJoin' )
|
2018-01-24 23:41:01 +00:00
|
|
|
->willReturn( [
|
|
|
|
|
'tables' => [ 'commentstore' => 'table' ],
|
|
|
|
|
'fields' => [ 'commentstore' => 'field' ],
|
|
|
|
|
'joins' => [ 'commentstore' => 'join' ],
|
|
|
|
|
] );
|
2018-01-26 19:17:27 +00:00
|
|
|
return $mockStore;
|
|
|
|
|
}
|
2018-01-24 23:41:01 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @param DBConnRef $mockDb
|
2022-04-22 12:46:10 +00:00
|
|
|
* @param UserOptionsLookup|null $userOptionsLookup
|
2018-01-26 19:17:27 +00:00
|
|
|
* @return WatchedItemQueryService
|
|
|
|
|
*/
|
2022-04-22 12:46:10 +00:00
|
|
|
private function newService(
|
|
|
|
|
DBConnRef $mockDb,
|
|
|
|
|
UserOptionsLookup $userOptionsLookup = null
|
|
|
|
|
) {
|
2018-01-26 19:17:27 +00:00
|
|
|
return new WatchedItemQueryService(
|
|
|
|
|
$this->getMockLoadBalancer( $mockDb ),
|
2017-09-12 17:12:29 +00:00
|
|
|
$this->getMockCommentStore(),
|
2019-09-13 20:39:50 +00:00
|
|
|
$this->getMockWatchedItemStore(),
|
Hooks::run() call site migration
Migrate all callers of Hooks::run() to use the new
HookContainer/HookRunner system.
General principles:
* Use DI if it is already used. We're not changing the way state is
managed in this patch.
* HookContainer is always injected, not HookRunner. HookContainer
is a service, it's a more generic interface, it is the only
thing that provides isRegistered() which is needed in some cases,
and a HookRunner can be efficiently constructed from it
(confirmed by benchmark). Because HookContainer is needed
for object construction, it is also needed by all factories.
* "Ask your friendly local base class". Big hierarchies like
SpecialPage and ApiBase have getHookContainer() and getHookRunner()
methods in the base class, and classes that extend that base class
are not expected to know or care where the base class gets its
HookContainer from.
* ProtectedHookAccessorTrait provides protected getHookContainer() and
getHookRunner() methods, getting them from the global service
container. The point of this is to ease migration to DI by ensuring
that call sites ask their local friendly base class rather than
getting a HookRunner from the service container directly.
* Private $this->hookRunner. In some smaller classes where accessor
methods did not seem warranted, there is a private HookRunner property
which is accessed directly. Very rarely (two cases), there is a
protected property, for consistency with code that conventionally
assumes protected=private, but in cases where the class might actually
be overridden, a protected accessor is preferred over a protected
property.
* The last resort: Hooks::runner(). Mostly for static, file-scope and
global code. In a few cases it was used for objects with broken
construction schemes, out of horror or laziness.
Constructors with new required arguments:
* AuthManager
* BadFileLookup
* BlockManager
* ClassicInterwikiLookup
* ContentHandlerFactory
* ContentSecurityPolicy
* DefaultOptionsManager
* DerivedPageDataUpdater
* FullSearchResultWidget
* HtmlCacheUpdater
* LanguageFactory
* LanguageNameUtils
* LinkRenderer
* LinkRendererFactory
* LocalisationCache
* MagicWordFactory
* MessageCache
* NamespaceInfo
* PageEditStash
* PageHandlerFactory
* PageUpdater
* ParserFactory
* PermissionManager
* RevisionStore
* RevisionStoreFactory
* SearchEngineConfig
* SearchEngineFactory
* SearchFormWidget
* SearchNearMatcher
* SessionBackend
* SpecialPageFactory
* UserNameUtils
* UserOptionsManager
* WatchedItemQueryService
* WatchedItemStore
Constructors with new optional arguments:
* DefaultPreferencesFactory
* Language
* LinkHolderArray
* MovePage
* Parser
* ParserCache
* PasswordReset
* Router
setHookContainer() now required after construction:
* AuthenticationProvider
* ResourceLoaderModule
* SearchEngine
Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
|
|
|
$this->createHookContainer(),
|
2022-04-22 12:46:10 +00:00
|
|
|
$userOptionsLookup ?? $this->createMock( UserOptionsLookup::class ),
|
2020-03-06 06:03:44 +00:00
|
|
|
false
|
2018-01-26 19:17:27 +00:00
|
|
|
);
|
2018-01-24 23:41:01 +00:00
|
|
|
}
|
|
|
|
|
|
2016-04-19 09:34:31 +00:00
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @return MockObject&DBConnRef
|
2016-04-19 09:34:31 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockDb() {
|
2021-08-31 17:38:46 +00:00
|
|
|
$mock = $this->createMock( DBConnRef::class );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'makeList' )
|
2016-04-19 09:34:31 +00:00
|
|
|
->with(
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'int' )
|
|
|
|
|
)
|
2021-08-31 17:38:46 +00:00
|
|
|
->willReturnCallback( static function ( $a, $conj ) {
|
2016-04-19 09:34:31 +00:00
|
|
|
$sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
|
2017-09-12 17:12:29 +00:00
|
|
|
$conds = [];
|
|
|
|
|
foreach ( $a as $k => $v ) {
|
|
|
|
|
if ( is_int( $k ) ) {
|
|
|
|
|
$conds[] = "($v)";
|
|
|
|
|
} elseif ( is_array( $v ) ) {
|
|
|
|
|
$conds[] = "($k IN ('" . implode( "','", $v ) . "'))";
|
|
|
|
|
} else {
|
|
|
|
|
$conds[] = "($k = '$v')";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return implode( $sqlConj, $conds );
|
2021-08-31 17:38:46 +00:00
|
|
|
} );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2022-09-16 12:44:18 +00:00
|
|
|
$mock->method( 'buildComparison' )
|
|
|
|
|
->with(
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'array' )
|
|
|
|
|
)
|
|
|
|
|
->willReturnCallback( static function ( string $op, array $conds ) {
|
|
|
|
|
$sql = '';
|
|
|
|
|
foreach ( array_reverse( $conds ) as $field => $value ) {
|
|
|
|
|
if ( $sql === '' ) {
|
|
|
|
|
$sql = "$field $op '$value'";
|
|
|
|
|
$op = rtrim( $op, '=' );
|
|
|
|
|
} else {
|
|
|
|
|
$sql = "$field $op '$value' OR ($field = '$value' AND ($sql))";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return $sql;
|
|
|
|
|
} );
|
|
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'addQuotes' )
|
2022-06-05 23:39:02 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-04-19 09:34:31 +00:00
|
|
|
return "'$value'";
|
2022-06-05 23:39:02 +00:00
|
|
|
} );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'timestamp' )
|
2022-11-12 18:57:12 +00:00
|
|
|
->willReturnArgument( 0 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'bitAnd' )
|
2021-02-07 13:10:36 +00:00
|
|
|
->willReturnCallback( static function ( $a, $b ) {
|
2016-04-19 09:34:31 +00:00
|
|
|
return "($a & $b)";
|
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @param DBConnRef $mockDb
|
2021-01-14 08:20:36 +00:00
|
|
|
* @return LoadBalancer
|
2016-04-19 09:34:31 +00:00
|
|
|
*/
|
2021-08-31 17:38:46 +00:00
|
|
|
private function getMockLoadBalancer( DBConnRef $mockDb ) {
|
2021-01-10 02:19:44 +00:00
|
|
|
$mock = $this->createMock( LoadBalancer::class );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'getConnectionRef' )
|
2017-08-04 18:53:34 +00:00
|
|
|
->with( DB_REPLICA )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $mockDb );
|
2016-04-19 09:34:31 +00:00
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-03 00:21:36 +00:00
|
|
|
/**
|
2019-10-06 04:54:59 +00:00
|
|
|
* @return MockObject|WatchedItemStore
|
2018-03-03 00:21:36 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockWatchedItemStore() {
|
2021-01-10 02:19:44 +00:00
|
|
|
$mock = $this->createMock( WatchedItemStore::class );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'getLatestNotificationTimestamp' )
|
2022-06-06 00:27:48 +00:00
|
|
|
->willReturnArgument( 0 );
|
2018-03-03 00:21:36 +00:00
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-19 09:34:31 +00:00
|
|
|
/**
|
|
|
|
|
* @param int $id
|
2021-05-26 09:23:30 +00:00
|
|
|
* @param bool $canPatrol result for User::useRCPatrol() and User::useNPPatrol()
|
|
|
|
|
* @param string|null $notAllowedAction for permission checks, the user has all other rights
|
2019-04-28 11:07:18 +00:00
|
|
|
* @param string[] $extraMethods Extra methods that are expected might be called
|
2019-10-06 04:54:59 +00:00
|
|
|
* @return MockObject|User
|
2016-04-19 09:34:31 +00:00
|
|
|
*/
|
2021-05-26 09:23:30 +00:00
|
|
|
private function getMockUserWithId(
|
|
|
|
|
$id,
|
|
|
|
|
bool $canPatrol = true,
|
|
|
|
|
$notAllowedAction = null,
|
|
|
|
|
array $extraMethods = []
|
|
|
|
|
) {
|
|
|
|
|
$methods = array_merge(
|
|
|
|
|
[ 'isRegistered', 'getId', 'useRCPatrol', 'useNPPatrol', 'isAllowed', 'isAllowedAny' ],
|
|
|
|
|
$extraMethods
|
|
|
|
|
);
|
2021-01-10 02:19:44 +00:00
|
|
|
$mock = $this->createNoOpMock(
|
|
|
|
|
User::class,
|
2021-05-26 09:23:30 +00:00
|
|
|
$methods
|
2021-01-10 02:19:44 +00:00
|
|
|
);
|
2019-04-28 11:07:18 +00:00
|
|
|
$mock->method( 'isRegistered' )->willReturn( true );
|
|
|
|
|
$mock->method( 'getId' )->willReturn( $id );
|
2021-05-26 09:23:30 +00:00
|
|
|
$mock->method( 'useRCPatrol' )->willReturn( $canPatrol );
|
|
|
|
|
$mock->method( 'useNPPatrol' )->willReturn( $canPatrol );
|
|
|
|
|
$mock->method( 'isAllowed' )->willReturnCallback(
|
|
|
|
|
static function ( $permission ) use ( $notAllowedAction ) {
|
|
|
|
|
return $permission !== $notAllowedAction;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
$mock->method( 'isAllowedAny' )->willReturnCallback(
|
|
|
|
|
static function ( ...$permissions ) use ( $notAllowedAction ) {
|
|
|
|
|
return !in_array( $notAllowedAction, $permissions );
|
|
|
|
|
}
|
|
|
|
|
);
|
2016-04-19 09:34:31 +00:00
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist', 'page' ],
|
|
|
|
|
[
|
|
|
|
|
'rc_id',
|
|
|
|
|
'rc_namespace',
|
|
|
|
|
'rc_title',
|
|
|
|
|
'rc_timestamp',
|
|
|
|
|
'rc_type',
|
|
|
|
|
'rc_deleted',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
'rc_cur_id',
|
|
|
|
|
'rc_this_oldid',
|
|
|
|
|
'rc_last_oldid',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'(rc_this_oldid=page_latest) OR (rc_type=3)',
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' ),
|
2016-10-11 20:17:22 +00:00
|
|
|
[
|
|
|
|
|
'LIMIT' => 3,
|
|
|
|
|
],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'watchlist' => [
|
2019-03-06 17:17:27 +00:00
|
|
|
'JOIN',
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace=rc_namespace',
|
|
|
|
|
'wl_title=rc_title'
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'page' => [
|
|
|
|
|
'LEFT JOIN',
|
|
|
|
|
'rc_cur_id=page_id',
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-10 02:19:44 +00:00
|
|
|
(object)[
|
2016-04-19 09:34:31 +00:00
|
|
|
'rc_id' => 1,
|
|
|
|
|
'rc_namespace' => 0,
|
|
|
|
|
'rc_title' => 'Foo1',
|
|
|
|
|
'rc_timestamp' => '20151212010101',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2016-04-19 09:34:31 +00:00
|
|
|
'rc_id' => 2,
|
|
|
|
|
'rc_namespace' => 1,
|
|
|
|
|
'rc_title' => 'Foo2',
|
|
|
|
|
'rc_timestamp' => '20151212010102',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2016-10-11 20:17:22 +00:00
|
|
|
'rc_id' => 3,
|
|
|
|
|
'rc_namespace' => 1,
|
|
|
|
|
'rc_title' => 'Foo3',
|
|
|
|
|
'rc_timestamp' => '20151212010103',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2016-10-11 20:17:22 +00:00
|
|
|
$startFrom = null;
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo(
|
|
|
|
|
$user, [ 'limit' => 2 ], $startFrom
|
|
|
|
|
);
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
$this->assertCount( 2, $items );
|
|
|
|
|
|
2022-10-21 04:32:38 +00:00
|
|
|
foreach ( $items as [ $watchedItem, $recentChangeInfo ] ) {
|
2016-04-19 09:34:31 +00:00
|
|
|
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $recentChangeInfo );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 0, 'Foo1' ), '20151212010101' ),
|
|
|
|
|
$items[0][0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
'rc_id' => 1,
|
|
|
|
|
'rc_namespace' => 0,
|
|
|
|
|
'rc_title' => 'Foo1',
|
|
|
|
|
'rc_timestamp' => '20151212010101',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
],
|
|
|
|
|
$items[0][1]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
|
|
|
|
|
$items[1][0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
'rc_id' => 2,
|
|
|
|
|
'rc_namespace' => 1,
|
|
|
|
|
'rc_title' => 'Foo2',
|
|
|
|
|
'rc_timestamp' => '20151212010102',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
],
|
|
|
|
|
$items[1][1]
|
|
|
|
|
);
|
2016-10-11 20:17:22 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( [ '20151212010103', 3 ], $startFrom );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_extension() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist', 'page', 'extension_dummy_table' ],
|
|
|
|
|
[
|
|
|
|
|
'rc_id',
|
|
|
|
|
'rc_namespace',
|
|
|
|
|
'rc_title',
|
|
|
|
|
'rc_timestamp',
|
|
|
|
|
'rc_type',
|
|
|
|
|
'rc_deleted',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
'rc_cur_id',
|
|
|
|
|
'rc_this_oldid',
|
|
|
|
|
'rc_last_oldid',
|
|
|
|
|
'extension_dummy_field',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'(rc_this_oldid=page_latest) OR (rc_type=3)',
|
|
|
|
|
'extension_dummy_cond',
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[
|
|
|
|
|
'extension_dummy_option',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'watchlist' => [
|
2019-03-06 17:17:27 +00:00
|
|
|
'JOIN',
|
2016-10-11 20:17:22 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace=rc_namespace',
|
|
|
|
|
'wl_title=rc_title'
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'page' => [
|
|
|
|
|
'LEFT JOIN',
|
|
|
|
|
'rc_cur_id=page_id',
|
|
|
|
|
],
|
|
|
|
|
'extension_dummy_join_cond' => [],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-10 02:19:44 +00:00
|
|
|
(object)[
|
2016-10-11 20:17:22 +00:00
|
|
|
'rc_id' => 1,
|
|
|
|
|
'rc_namespace' => 0,
|
|
|
|
|
'rc_title' => 'Foo1',
|
|
|
|
|
'rc_timestamp' => '20151212010101',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2016-10-11 20:17:22 +00:00
|
|
|
'rc_id' => 2,
|
|
|
|
|
'rc_namespace' => 1,
|
|
|
|
|
'rc_title' => 'Foo2',
|
|
|
|
|
'rc_timestamp' => '20151212010102',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-10-11 20:17:22 +00:00
|
|
|
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-10-11 20:17:22 +00:00
|
|
|
|
2022-07-14 12:42:07 +00:00
|
|
|
$mockExtension = $this->createMock( WatchedItemQueryServiceExtension::class );
|
2016-10-11 20:17:22 +00:00
|
|
|
$mockExtension->expects( $this->once() )
|
|
|
|
|
->method( 'modifyWatchedItemsWithRCInfoQuery' )
|
|
|
|
|
->with(
|
|
|
|
|
$this->identicalTo( $user ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isInstanceOf( IDatabase::class ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' )
|
|
|
|
|
)
|
2022-06-05 23:39:02 +00:00
|
|
|
->willReturnCallback( static function (
|
2016-10-11 20:17:22 +00:00
|
|
|
$user, $options, $db, &$tables, &$fields, &$conds, &$dbOptions, &$joinConds
|
|
|
|
|
) {
|
|
|
|
|
$tables[] = 'extension_dummy_table';
|
|
|
|
|
$fields[] = 'extension_dummy_field';
|
|
|
|
|
$conds[] = 'extension_dummy_cond';
|
|
|
|
|
$dbOptions[] = 'extension_dummy_option';
|
|
|
|
|
$joinConds['extension_dummy_join_cond'] = [];
|
2022-06-05 23:39:02 +00:00
|
|
|
} );
|
2016-10-11 20:17:22 +00:00
|
|
|
$mockExtension->expects( $this->once() )
|
|
|
|
|
->method( 'modifyWatchedItemsWithRCInfo' )
|
|
|
|
|
->with(
|
|
|
|
|
$this->identicalTo( $user ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isInstanceOf( IDatabase::class ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->anything(),
|
|
|
|
|
$this->anything() // Can't test for null here, PHPUnit applies this after the callback
|
|
|
|
|
)
|
2022-06-05 23:39:02 +00:00
|
|
|
->willReturnCallback( function ( $user, $options, $db, &$items, $res, &$startFrom ) {
|
2016-10-11 20:17:22 +00:00
|
|
|
foreach ( $items as $i => &$item ) {
|
|
|
|
|
$item[1]['extension_dummy_field'] = $i;
|
|
|
|
|
}
|
|
|
|
|
unset( $item );
|
|
|
|
|
|
|
|
|
|
$this->assertNull( $startFrom );
|
|
|
|
|
$startFrom = [ '20160203123456', 42 ];
|
2022-06-05 23:39:02 +00:00
|
|
|
} );
|
2016-10-11 20:17:22 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2016-10-11 20:17:22 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $queryService )->extensions = [ $mockExtension ];
|
|
|
|
|
|
|
|
|
|
$startFrom = null;
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo(
|
|
|
|
|
$user, [], $startFrom
|
|
|
|
|
);
|
|
|
|
|
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $items );
|
2016-10-11 20:17:22 +00:00
|
|
|
$this->assertCount( 2, $items );
|
|
|
|
|
|
2022-10-21 04:32:38 +00:00
|
|
|
foreach ( $items as [ $watchedItem, $recentChangeInfo ] ) {
|
2016-10-11 20:17:22 +00:00
|
|
|
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $recentChangeInfo );
|
2016-10-11 20:17:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 0, 'Foo1' ), '20151212010101' ),
|
|
|
|
|
$items[0][0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
'rc_id' => 1,
|
|
|
|
|
'rc_namespace' => 0,
|
|
|
|
|
'rc_title' => 'Foo1',
|
|
|
|
|
'rc_timestamp' => '20151212010101',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'extension_dummy_field' => 0,
|
|
|
|
|
],
|
|
|
|
|
$items[0][1]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
|
|
|
|
|
$items[1][0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
'rc_id' => 2,
|
|
|
|
|
'rc_namespace' => 1,
|
|
|
|
|
'rc_title' => 'Foo2',
|
|
|
|
|
'rc_timestamp' => '20151212010102',
|
|
|
|
|
'rc_type' => RC_NEW,
|
|
|
|
|
'rc_deleted' => 0,
|
|
|
|
|
'extension_dummy_field' => 1,
|
|
|
|
|
],
|
|
|
|
|
$items[1][1]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals( [ '20160203123456', 42 ], $startFrom );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getWatchedItemsWithRecentChangeInfoOptionsProvider() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_FLAGS ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_type', 'rc_minor', 'rc_bot' ],
|
|
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_USER ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
|
|
|
|
[ 'rc_user_text' => 'watchlist_actor.actor_name' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_USER_ID ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
|
|
|
|
[ 'rc_user' => 'watchlist_actor.actor_user' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2017-06-06 17:39:14 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_COMMENT ] ],
|
|
|
|
|
null,
|
2018-01-24 23:41:01 +00:00
|
|
|
[ 'commentstore' => 'table' ],
|
|
|
|
|
[ 'commentstore' => 'field' ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
|
|
|
|
[],
|
2018-01-24 23:41:01 +00:00
|
|
|
[ 'commentstore' => 'join' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_PATROL_INFO ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_patrolled', 'rc_log_type' ],
|
|
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_SIZES ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_old_len', 'rc_new_len' ],
|
|
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'includeFields' => [ WatchedItemQueryService::INCLUDE_LOG_INFO ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ],
|
|
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'namespaceIds' => [ 0, 1 ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'wl_namespace' => [ 0, 1 ] ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'namespaceIds' => [ 0, "1; DROP TABLE watchlist;\n--" ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'wl_namespace' => [ 0, 1 ] ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'rcTypes' => [ RC_EDIT, RC_NEW ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_type' => [ RC_EDIT, RC_NEW ] ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
|
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_NEWER ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
|
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER, 'start' => '20151212010101' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ "rc_timestamp <= '20151212010101'" ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER, 'end' => '20151212010101' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ "rc_timestamp >= '20151212010101'" ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'dir' => WatchedItemQueryService::DIR_OLDER,
|
|
|
|
|
'start' => '20151212020101',
|
|
|
|
|
'end' => '20151212010101'
|
|
|
|
|
],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ "rc_timestamp <= '20151212020101'", "rc_timestamp >= '20151212010101'" ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_NEWER, 'start' => '20151212010101' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ "rc_timestamp >= '20151212010101'" ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_NEWER, 'end' => '20151212010101' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ "rc_timestamp <= '20151212010101'" ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'dir' => WatchedItemQueryService::DIR_NEWER,
|
|
|
|
|
'start' => '20151212010101',
|
|
|
|
|
'end' => '20151212020101'
|
|
|
|
|
],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ "rc_timestamp >= '20151212010101'", "rc_timestamp <= '20151212020101'" ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ],
|
|
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'limit' => 10 ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'LIMIT' => 11 ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'limit' => "10; DROP TABLE watchlist;\n--" ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'LIMIT' => 11 ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_MINOR ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_minor != 0' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_NOT_MINOR ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_minor = 0' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_BOT ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_bot != 0' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_NOT_BOT ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_bot = 0' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_ANON ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor.actor_user IS NULL' ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_NOT_ANON ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor.actor_user IS NOT NULL' ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_PATROLLED ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_patrolled != 0' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_NOT_PATROLLED ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2018-04-13 21:36:34 +00:00
|
|
|
[ 'rc_patrolled' => 0 ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_UNREAD ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'rc_timestamp >= wl_notificationtimestamp' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filters' => [ WatchedItemQueryService::FILTER_NOT_UNREAD ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[ 'wl_notificationtimestamp IS NULL OR rc_timestamp < wl_notificationtimestamp' ],
|
|
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'onlyByUser' => 'SomeOtherUser' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor.actor_name' => 'SomeOtherUser' ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'notByUser' => 'SomeOtherUser' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor.actor_name<>\'SomeOtherUser\'' ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
[ '20151212010101', 123 ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
2022-09-16 12:44:18 +00:00
|
|
|
"rc_timestamp < '20151212010101' OR (rc_timestamp = '20151212010101' AND (rc_id <= '123'))"
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_NEWER ],
|
|
|
|
|
[ '20151212010101', 123 ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
2022-09-16 12:44:18 +00:00
|
|
|
"rc_timestamp > '20151212010101' OR (rc_timestamp = '20151212010101' AND (rc_id >= '123'))"
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp', 'rc_id' ] ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
[ '20151212010101', "123; DROP TABLE watchlist;\n--" ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
2022-09-16 12:44:18 +00:00
|
|
|
"rc_timestamp < '20151212010101' OR (rc_timestamp = '20151212010101' AND (rc_id <= '123'))"
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[ 'ORDER BY' => [ 'rc_timestamp DESC', 'rc_id DESC' ] ],
|
2017-06-06 17:39:14 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider getWatchedItemsWithRecentChangeInfoOptionsProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_optionsAndEmptyResult(
|
|
|
|
|
array $options,
|
2016-10-11 20:17:22 +00:00
|
|
|
$startFrom,
|
2017-06-06 17:39:14 +00:00
|
|
|
array $expectedExtraTables,
|
2016-04-19 09:34:31 +00:00
|
|
|
array $expectedExtraFields,
|
|
|
|
|
array $expectedExtraConds,
|
2017-06-06 17:39:14 +00:00
|
|
|
array $expectedDbOptions,
|
2018-01-24 23:41:01 +00:00
|
|
|
array $expectedExtraJoinConds
|
2016-04-19 09:34:31 +00:00
|
|
|
) {
|
2017-06-06 17:39:14 +00:00
|
|
|
$expectedTables = array_merge( [ 'recentchanges', 'watchlist', 'page' ], $expectedExtraTables );
|
2016-04-19 09:34:31 +00:00
|
|
|
$expectedFields = array_merge(
|
|
|
|
|
[
|
|
|
|
|
'rc_id',
|
|
|
|
|
'rc_namespace',
|
|
|
|
|
'rc_title',
|
|
|
|
|
'rc_timestamp',
|
|
|
|
|
'rc_type',
|
|
|
|
|
'rc_deleted',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
|
|
|
|
|
'rc_cur_id',
|
|
|
|
|
'rc_this_oldid',
|
|
|
|
|
'rc_last_oldid',
|
|
|
|
|
],
|
|
|
|
|
$expectedExtraFields
|
|
|
|
|
);
|
|
|
|
|
$expectedConds = array_merge(
|
|
|
|
|
[ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)', ],
|
|
|
|
|
$expectedExtraConds
|
|
|
|
|
);
|
2017-06-06 17:39:14 +00:00
|
|
|
$expectedJoinConds = array_merge(
|
|
|
|
|
[
|
|
|
|
|
'watchlist' => [
|
2019-03-06 17:17:27 +00:00
|
|
|
'JOIN',
|
2017-06-06 17:39:14 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace=rc_namespace',
|
|
|
|
|
'wl_title=rc_title'
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'page' => [
|
|
|
|
|
'LEFT JOIN',
|
|
|
|
|
'rc_cur_id=page_id',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
$expectedExtraJoinConds
|
|
|
|
|
);
|
2016-04-19 09:34:31 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2017-06-06 17:39:14 +00:00
|
|
|
$expectedTables,
|
2016-04-19 09:34:31 +00:00
|
|
|
$expectedFields,
|
|
|
|
|
$expectedConds,
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$expectedDbOptions,
|
2017-06-06 17:39:14 +00:00
|
|
|
$expectedJoinConds
|
2016-04-19 09:34:31 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2016-10-11 20:17:22 +00:00
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options, $startFrom );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-10-11 20:17:22 +00:00
|
|
|
$this->assertNull( $startFrom );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function filterPatrolledOptionProvider() {
|
|
|
|
|
return [
|
2021-01-10 02:19:44 +00:00
|
|
|
'Patrolled' => [ WatchedItemQueryService::FILTER_PATROLLED ],
|
|
|
|
|
'Not patrolled' => [ WatchedItemQueryService::FILTER_NOT_PATROLLED ],
|
2016-04-19 09:34:31 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider filterPatrolledOptionProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_filterPatrolledAndUserWithNoPatrolRights(
|
|
|
|
|
$filtersOption
|
|
|
|
|
) {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist', 'page' ],
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
[ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1, false );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2016-04-19 09:34:31 +00:00
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo(
|
|
|
|
|
$user,
|
|
|
|
|
[ 'filters' => [ $filtersOption ] ]
|
|
|
|
|
);
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function mysqlIndexOptimizationProvider() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
'mysql',
|
|
|
|
|
[],
|
|
|
|
|
[ "rc_timestamp > ''" ],
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'mysql',
|
|
|
|
|
[ 'start' => '20151212010101', 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
[ "rc_timestamp <= '20151212010101'" ],
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'mysql',
|
|
|
|
|
[ 'end' => '20151212010101', 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
[ "rc_timestamp >= '20151212010101'" ],
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'postgres',
|
|
|
|
|
[],
|
|
|
|
|
[],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider mysqlIndexOptimizationProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_mysqlIndexOptimization(
|
|
|
|
|
$dbType,
|
|
|
|
|
array $options,
|
|
|
|
|
array $expectedExtraConds
|
|
|
|
|
) {
|
|
|
|
|
$commonConds = [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ];
|
|
|
|
|
$conds = array_merge( $commonConds, $expectedExtraConds );
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist', 'page' ],
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$conds,
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
|
|
|
|
$mockDb->method( 'getType' )
|
|
|
|
|
->willReturn( $dbType );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options );
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function userPermissionRelatedExtraChecksProvider() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[],
|
|
|
|
|
'deletedhistory',
|
2017-09-12 17:12:29 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'(rc_type != ' . RC_LOG . ') OR ((rc_deleted & ' . LogPage::DELETED_ACTION . ') != ' .
|
|
|
|
|
LogPage::DELETED_ACTION . ')'
|
|
|
|
|
],
|
2017-09-12 17:12:29 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[],
|
|
|
|
|
'suppressrevision',
|
2017-09-12 17:12:29 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'(rc_type != ' . RC_LOG . ') OR (' .
|
|
|
|
|
'(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
|
|
|
|
|
( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
|
|
|
|
|
],
|
2017-09-12 17:12:29 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[],
|
|
|
|
|
'viewsuppressed',
|
2017-09-12 17:12:29 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'(rc_type != ' . RC_LOG . ') OR (' .
|
|
|
|
|
'(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
|
|
|
|
|
( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
|
|
|
|
|
],
|
2017-09-12 17:12:29 +00:00
|
|
|
[],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'onlyByUser' => 'SomeOtherUser' ],
|
|
|
|
|
'deletedhistory',
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
2021-04-19 00:57:23 +00:00
|
|
|
'watchlist_actor.actor_name' => 'SomeOtherUser',
|
2020-07-03 00:20:38 +00:00
|
|
|
'(rc_deleted & ' . RevisionRecord::DELETED_USER . ') != ' . RevisionRecord::DELETED_USER,
|
2016-04-19 09:34:31 +00:00
|
|
|
'(rc_type != ' . RC_LOG . ') OR ((rc_deleted & ' . LogPage::DELETED_ACTION . ') != ' .
|
|
|
|
|
LogPage::DELETED_ACTION . ')'
|
|
|
|
|
],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'onlyByUser' => 'SomeOtherUser' ],
|
|
|
|
|
'suppressrevision',
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
2021-04-19 00:57:23 +00:00
|
|
|
'watchlist_actor.actor_name' => 'SomeOtherUser',
|
2020-07-03 00:20:38 +00:00
|
|
|
'(rc_deleted & ' . ( RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED ) . ') != ' .
|
|
|
|
|
( RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED ),
|
2016-04-19 09:34:31 +00:00
|
|
|
'(rc_type != ' . RC_LOG . ') OR (' .
|
|
|
|
|
'(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
|
|
|
|
|
( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
|
|
|
|
|
],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'onlyByUser' => 'SomeOtherUser' ],
|
|
|
|
|
'viewsuppressed',
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => 'actor' ],
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
2021-04-19 00:57:23 +00:00
|
|
|
'watchlist_actor.actor_name' => 'SomeOtherUser',
|
2020-07-03 00:20:38 +00:00
|
|
|
'(rc_deleted & ' . ( RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED ) . ') != ' .
|
|
|
|
|
( RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED ),
|
2016-04-19 09:34:31 +00:00
|
|
|
'(rc_type != ' . RC_LOG . ') OR (' .
|
|
|
|
|
'(rc_deleted & ' . ( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ') != ' .
|
|
|
|
|
( LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED ) . ')'
|
|
|
|
|
],
|
2021-04-19 00:57:23 +00:00
|
|
|
[ 'watchlist_actor' => [ 'JOIN', 'actor_id=rc_actor' ] ],
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider userPermissionRelatedExtraChecksProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_userPermissionRelatedExtraChecks(
|
|
|
|
|
array $options,
|
|
|
|
|
$notAllowedAction,
|
2017-09-12 17:12:29 +00:00
|
|
|
array $expectedExtraTables,
|
|
|
|
|
array $expectedExtraConds,
|
|
|
|
|
array $expectedExtraJoins
|
2016-04-19 09:34:31 +00:00
|
|
|
) {
|
|
|
|
|
$commonConds = [ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ];
|
|
|
|
|
$conds = array_merge( $commonConds, $expectedExtraConds );
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2017-09-12 17:12:29 +00:00
|
|
|
array_merge( [ 'recentchanges', 'watchlist', 'page' ], $expectedExtraTables ),
|
2016-04-19 09:34:31 +00:00
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$conds,
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'array' ),
|
2017-09-12 17:12:29 +00:00
|
|
|
array_merge( [
|
2019-03-06 17:17:27 +00:00
|
|
|
'watchlist' => [ 'JOIN', [ 'wl_namespace=rc_namespace', 'wl_title=rc_title' ] ],
|
2017-09-12 17:12:29 +00:00
|
|
|
'page' => [ 'LEFT JOIN', 'rc_cur_id=page_id' ],
|
|
|
|
|
], $expectedExtraJoins )
|
2016-04-19 09:34:31 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1, false, $notAllowedAction );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-05-26 09:23:30 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2016-04-19 09:34:31 +00:00
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, $options );
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_allRevisionsOptionAndEmptyResult() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist' ],
|
|
|
|
|
[
|
|
|
|
|
'rc_id',
|
|
|
|
|
'rc_namespace',
|
|
|
|
|
'rc_title',
|
|
|
|
|
'rc_timestamp',
|
|
|
|
|
'rc_type',
|
|
|
|
|
'rc_deleted',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
|
|
|
|
|
'rc_cur_id',
|
|
|
|
|
'rc_this_oldid',
|
|
|
|
|
'rc_last_oldid',
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_user' => 1, ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[],
|
|
|
|
|
[
|
|
|
|
|
'watchlist' => [
|
2019-03-06 17:17:27 +00:00
|
|
|
'JOIN',
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace=rc_namespace',
|
|
|
|
|
'wl_title=rc_title'
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo( $user, [ 'allRevisions' => true ] );
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getWatchedItemsWithRecentChangeInfoInvalidOptionsProvider() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[ 'rcTypes' => [ 1337 ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'rcTypes\']',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'rcTypes' => [ 'edit' ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'rcTypes\']',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'rcTypes' => [ RC_EDIT, 1337 ] ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'rcTypes\']',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'dir' => 'foo' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'dir\']',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'start' => '20151212010101' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'dir\']: must be provided',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'end' => '20151212010101' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'dir\']: must be provided',
|
|
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[],
|
|
|
|
|
[ '20151212010101', 123 ],
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'dir\']: must be provided',
|
|
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
'20151212010101',
|
|
|
|
|
'Bad value for parameter $startFrom: must be a two-element array',
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
[ '20151212010101' ],
|
|
|
|
|
'Bad value for parameter $startFrom: must be a two-element array',
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
2016-10-11 20:17:22 +00:00
|
|
|
[ 'dir' => WatchedItemQueryService::DIR_OLDER ],
|
|
|
|
|
[ '20151212010101', 123, 'foo' ],
|
|
|
|
|
'Bad value for parameter $startFrom: must be a two-element array',
|
2016-04-19 09:34:31 +00:00
|
|
|
],
|
|
|
|
|
[
|
2021-05-26 09:23:30 +00:00
|
|
|
[ 'watchlistOwner' => $this->getMockUserWithId( 2 ) ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'watchlistOwnerToken\']',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'watchlistOwner' => 'Other User', 'watchlistOwnerToken' => 'some-token' ],
|
2016-10-11 20:17:22 +00:00
|
|
|
null,
|
2016-04-19 09:34:31 +00:00
|
|
|
'Bad value for parameter $options[\'watchlistOwner\']',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider getWatchedItemsWithRecentChangeInfoInvalidOptionsProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_invalidOptions(
|
|
|
|
|
array $options,
|
2016-10-11 20:17:22 +00:00
|
|
|
$startFrom,
|
2016-04-19 09:34:31 +00:00
|
|
|
$expectedInExceptionMessage
|
|
|
|
|
) {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( $this->anything() );
|
|
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2019-10-06 09:52:39 +00:00
|
|
|
$this->expectException( InvalidArgumentException::class );
|
|
|
|
|
$this->expectExceptionMessage( $expectedInExceptionMessage );
|
2016-10-11 20:17:22 +00:00
|
|
|
$queryService->getWatchedItemsWithRecentChangeInfo( $user, $options, $startFrom );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_usedInGeneratorOptionAndEmptyResult() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist', 'page' ],
|
|
|
|
|
[
|
|
|
|
|
'rc_id',
|
|
|
|
|
'rc_namespace',
|
|
|
|
|
'rc_title',
|
|
|
|
|
'rc_timestamp',
|
|
|
|
|
'rc_type',
|
|
|
|
|
'rc_deleted',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
'rc_cur_id',
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_user' => 1, '(rc_this_oldid=page_latest) OR (rc_type=3)' ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[],
|
|
|
|
|
[
|
|
|
|
|
'watchlist' => [
|
2019-03-06 17:17:27 +00:00
|
|
|
'JOIN',
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace=rc_namespace',
|
|
|
|
|
'wl_title=rc_title'
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'page' => [
|
|
|
|
|
'LEFT JOIN',
|
|
|
|
|
'rc_cur_id=page_id',
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo(
|
|
|
|
|
$user,
|
|
|
|
|
[ 'usedInGenerator' => true ]
|
|
|
|
|
);
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_usedInGeneratorAllRevisionsOptions() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'recentchanges', 'watchlist' ],
|
|
|
|
|
[
|
|
|
|
|
'rc_id',
|
|
|
|
|
'rc_namespace',
|
|
|
|
|
'rc_title',
|
|
|
|
|
'rc_timestamp',
|
|
|
|
|
'rc_type',
|
|
|
|
|
'rc_deleted',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
'rc_this_oldid',
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_user' => 1 ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[],
|
|
|
|
|
[
|
|
|
|
|
'watchlist' => [
|
2019-03-06 17:17:27 +00:00
|
|
|
'JOIN',
|
2016-04-19 09:34:31 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace=rc_namespace',
|
|
|
|
|
'wl_title=rc_title'
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo(
|
|
|
|
|
$user,
|
|
|
|
|
[ 'usedInGenerator' => true, 'allRevisions' => true, ]
|
|
|
|
|
);
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsWithRecentChangeInfo_watchlistOwnerOptionAndEmptyResult() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 2,
|
|
|
|
|
'(rc_this_oldid=page_latest) OR (rc_type=3)',
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'array' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2022-04-22 12:46:10 +00:00
|
|
|
$otherUser = $this->getMockUserWithId( 2, true );
|
|
|
|
|
$userOptionsLookup = $this->createMock( UserOptionsLookup::class );
|
|
|
|
|
$userOptionsLookup->expects( $this->once() )
|
2016-04-19 09:34:31 +00:00
|
|
|
->method( 'getOption' )
|
2022-04-22 12:46:10 +00:00
|
|
|
->with( $otherUser, 'watchlisttoken' )
|
2016-04-19 09:34:31 +00:00
|
|
|
->willReturn( '0123456789abcdef' );
|
2022-04-22 12:46:10 +00:00
|
|
|
$queryService = $this->newService( $mockDb, $userOptionsLookup );
|
2016-04-19 09:34:31 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsWithRecentChangeInfo(
|
|
|
|
|
$user,
|
|
|
|
|
[ 'watchlistOwner' => $otherUser, 'watchlistOwnerToken' => '0123456789abcdef' ]
|
|
|
|
|
);
|
|
|
|
|
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
2016-06-17 11:11:05 +00:00
|
|
|
public function testGetWatchedItemsForUser() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
|
|
|
|
|
[ 'wl_user' => 1 ]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-10 02:19:44 +00:00
|
|
|
(object)[
|
2016-06-17 11:11:05 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Foo1',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2016-06-17 11:11:05 +00:00
|
|
|
'wl_namespace' => 1,
|
|
|
|
|
'wl_title' => 'Foo2',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-10 02:19:44 +00:00
|
|
|
],
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsForUser( $user );
|
|
|
|
|
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $items );
|
2016-06-17 11:11:05 +00:00
|
|
|
$this->assertCount( 2, $items );
|
|
|
|
|
$this->assertContainsOnlyInstancesOf( WatchedItem::class, $items );
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 0, 'Foo1' ), '20151212010101' ),
|
|
|
|
|
$items[0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
|
|
|
|
|
$items[1]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function provideGetWatchedItemsForUserOptions() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[ 'namespaceIds' => [ 0, 1 ], ],
|
|
|
|
|
[ 'wl_namespace' => [ 0, 1 ], ],
|
|
|
|
|
[]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'sort' => WatchedItemQueryService::SORT_ASC, ],
|
|
|
|
|
[],
|
|
|
|
|
[ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'namespaceIds' => [ 0 ],
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_ASC,
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_namespace' => [ 0 ], ],
|
|
|
|
|
[ 'ORDER BY' => 'wl_title ASC' ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'limit' => 10 ],
|
|
|
|
|
[],
|
|
|
|
|
[ 'LIMIT' => 10 ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'namespaceIds' => [ 0, "1; DROP TABLE watchlist;\n--" ],
|
|
|
|
|
'limit' => "10; DROP TABLE watchlist;\n--",
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_namespace' => [ 0, 1 ], ],
|
|
|
|
|
[ 'LIMIT' => 10 ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filter' => WatchedItemQueryService::FILTER_CHANGED ],
|
|
|
|
|
[ 'wl_notificationtimestamp IS NOT NULL' ],
|
|
|
|
|
[]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filter' => WatchedItemQueryService::FILTER_NOT_CHANGED ],
|
|
|
|
|
[ 'wl_notificationtimestamp IS NULL' ],
|
|
|
|
|
[]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'sort' => WatchedItemQueryService::SORT_DESC, ],
|
|
|
|
|
[],
|
|
|
|
|
[ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'namespaceIds' => [ 0 ],
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_DESC,
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_namespace' => [ 0 ], ],
|
|
|
|
|
[ 'ORDER BY' => 'wl_title DESC' ]
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideGetWatchedItemsForUserOptions
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsForUser_optionsAndEmptyResult(
|
|
|
|
|
array $options,
|
|
|
|
|
array $expectedConds,
|
|
|
|
|
array $expectedDbOptions
|
|
|
|
|
) {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
|
|
|
|
$expectedConds = array_merge( [ 'wl_user' => 1 ], $expectedConds );
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
|
|
|
|
|
$expectedConds,
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$expectedDbOptions
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsForUser( $user, $options );
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-06-17 11:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function provideGetWatchedItemsForUser_fromUntilStartFromOptions() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'from' => new TitleValue( 0, 'SomeDbKey' ),
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_ASC
|
|
|
|
|
],
|
2022-09-16 12:44:18 +00:00
|
|
|
[ "wl_namespace > '0' OR (wl_namespace = '0' AND (wl_title >= 'SomeDbKey'))", ],
|
2016-06-17 11:11:05 +00:00
|
|
|
[ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'from' => new TitleValue( 0, 'SomeDbKey' ),
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_DESC,
|
|
|
|
|
],
|
2022-09-16 12:44:18 +00:00
|
|
|
[ "wl_namespace < '0' OR (wl_namespace = '0' AND (wl_title <= 'SomeDbKey'))", ],
|
2016-06-17 11:11:05 +00:00
|
|
|
[ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'until' => new TitleValue( 0, 'SomeDbKey' ),
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_ASC
|
|
|
|
|
],
|
2022-09-16 12:44:18 +00:00
|
|
|
[ "wl_namespace < '0' OR (wl_namespace = '0' AND (wl_title <= 'SomeDbKey'))", ],
|
2016-06-17 11:11:05 +00:00
|
|
|
[ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'until' => new TitleValue( 0, 'SomeDbKey' ),
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_DESC
|
|
|
|
|
],
|
2022-09-16 12:44:18 +00:00
|
|
|
[ "wl_namespace > '0' OR (wl_namespace = '0' AND (wl_title >= 'SomeDbKey'))", ],
|
2016-06-17 11:11:05 +00:00
|
|
|
[ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'from' => new TitleValue( 0, 'AnotherDbKey' ),
|
|
|
|
|
'until' => new TitleValue( 0, 'SomeOtherDbKey' ),
|
|
|
|
|
'startFrom' => new TitleValue( 0, 'SomeDbKey' ),
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_ASC
|
|
|
|
|
],
|
|
|
|
|
[
|
2022-09-16 12:44:18 +00:00
|
|
|
"wl_namespace > '0' OR (wl_namespace = '0' AND (wl_title >= 'AnotherDbKey'))",
|
|
|
|
|
"wl_namespace < '0' OR (wl_namespace = '0' AND (wl_title <= 'SomeOtherDbKey'))",
|
|
|
|
|
"wl_namespace > '0' OR (wl_namespace = '0' AND (wl_title >= 'SomeDbKey'))",
|
2016-06-17 11:11:05 +00:00
|
|
|
],
|
|
|
|
|
[ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'from' => new TitleValue( 0, 'SomeOtherDbKey' ),
|
|
|
|
|
'until' => new TitleValue( 0, 'AnotherDbKey' ),
|
|
|
|
|
'startFrom' => new TitleValue( 0, 'SomeDbKey' ),
|
|
|
|
|
'sort' => WatchedItemQueryService::SORT_DESC
|
|
|
|
|
],
|
|
|
|
|
[
|
2022-09-16 12:44:18 +00:00
|
|
|
"wl_namespace < '0' OR (wl_namespace = '0' AND (wl_title <= 'SomeOtherDbKey'))",
|
|
|
|
|
"wl_namespace > '0' OR (wl_namespace = '0' AND (wl_title >= 'AnotherDbKey'))",
|
|
|
|
|
"wl_namespace < '0' OR (wl_namespace = '0' AND (wl_title <= 'SomeDbKey'))",
|
2016-06-17 11:11:05 +00:00
|
|
|
],
|
|
|
|
|
[ 'ORDER BY' => [ 'wl_namespace DESC', 'wl_title DESC' ] ]
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideGetWatchedItemsForUser_fromUntilStartFromOptions
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsForUser_fromUntilStartFromOptions(
|
|
|
|
|
array $options,
|
|
|
|
|
array $expectedConds,
|
|
|
|
|
array $expectedDbOptions
|
|
|
|
|
) {
|
2021-05-26 09:23:30 +00:00
|
|
|
$user = $this->getMockUserWithId( 1 );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
|
|
|
|
$expectedConds = array_merge( [ 'wl_user' => 1 ], $expectedConds );
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockDb->method( 'addQuotes' )
|
2022-06-05 23:39:02 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-06-17 11:11:05 +00:00
|
|
|
return "'$value'";
|
2022-06-05 23:39:02 +00:00
|
|
|
} );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockDb->method( 'makeList' )
|
2016-06-17 11:11:05 +00:00
|
|
|
->with(
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'int' )
|
|
|
|
|
)
|
2022-06-05 23:39:02 +00:00
|
|
|
->willReturnCallback( static function ( $a, $conj ) {
|
2016-06-17 11:11:05 +00:00
|
|
|
$sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
|
2021-02-06 19:40:52 +00:00
|
|
|
return implode( $sqlConj, array_map( static function ( $s ) {
|
2016-06-17 11:11:05 +00:00
|
|
|
return '(' . $s . ')';
|
|
|
|
|
}, $a
|
|
|
|
|
) );
|
2022-06-05 23:39:02 +00:00
|
|
|
} );
|
2016-06-17 11:11:05 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
|
|
|
|
|
$expectedConds,
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$expectedDbOptions
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
|
|
|
|
$items = $queryService->getWatchedItemsForUser( $user, $options );
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-06-17 11:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getWatchedItemsForUserInvalidOptionsProvider() {
|
|
|
|
|
return [
|
|
|
|
|
[
|
|
|
|
|
[ 'sort' => 'foo' ],
|
|
|
|
|
'Bad value for parameter $options[\'sort\']'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'filter' => 'foo' ],
|
|
|
|
|
'Bad value for parameter $options[\'filter\']'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'from' => new TitleValue( 0, 'SomeDbKey' ), ],
|
|
|
|
|
'Bad value for parameter $options[\'sort\']: must be provided'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'until' => new TitleValue( 0, 'SomeDbKey' ), ],
|
|
|
|
|
'Bad value for parameter $options[\'sort\']: must be provided'
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[ 'startFrom' => new TitleValue( 0, 'SomeDbKey' ), ],
|
|
|
|
|
'Bad value for parameter $options[\'sort\']: must be provided'
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider getWatchedItemsForUserInvalidOptionsProvider
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsForUser_invalidOptionThrowsException(
|
|
|
|
|
array $options,
|
|
|
|
|
$expectedInExceptionMessage
|
|
|
|
|
) {
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $this->getMockDb() );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
2019-10-06 09:52:39 +00:00
|
|
|
$this->expectException( InvalidArgumentException::class );
|
|
|
|
|
$this->expectExceptionMessage( $expectedInExceptionMessage );
|
2021-05-26 09:23:30 +00:00
|
|
|
$queryService->getWatchedItemsForUser( $this->getMockUserWithId( 1 ), $options );
|
2016-06-17 11:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testGetWatchedItemsForUser_userNotAllowedToViewWatchlist() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( $this->anything() );
|
|
|
|
|
|
2018-01-26 19:17:27 +00:00
|
|
|
$queryService = $this->newService( $mockDb );
|
2016-06-17 11:11:05 +00:00
|
|
|
|
2019-04-28 11:07:18 +00:00
|
|
|
$items = $queryService->getWatchedItemsForUser(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ) );
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $items );
|
2016-06-17 11:11:05 +00:00
|
|
|
}
|
|
|
|
|
|
2016-04-19 09:34:31 +00:00
|
|
|
}
|