2016-02-01 11:53:01 +00:00
|
|
|
<?php
|
2019-05-28 14:04:23 +00:00
|
|
|
|
2020-08-07 21:37:23 +00:00
|
|
|
use MediaWiki\Cache\LinkBatchFactory;
|
2020-04-20 22:22:27 +00:00
|
|
|
use MediaWiki\Config\ServiceOptions;
|
2022-04-21 19:35:30 +00:00
|
|
|
use MediaWiki\Linker\LinksMigration;
|
2016-04-20 08:29:21 +00:00
|
|
|
use MediaWiki\Linker\LinkTarget;
|
2022-01-27 23:32:56 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
2022-05-28 06:56:01 +00:00
|
|
|
use MediaWiki\Page\PageIdentity;
|
2021-03-25 03:08:22 +00:00
|
|
|
use MediaWiki\Page\PageIdentityValue;
|
2019-04-29 17:25:13 +00:00
|
|
|
use MediaWiki\Revision\RevisionLookup;
|
|
|
|
|
use MediaWiki\Revision\RevisionRecord;
|
2021-05-03 20:38:48 +00:00
|
|
|
use MediaWiki\Tests\Unit\DummyServicesTrait;
|
2019-04-28 11:07:18 +00:00
|
|
|
use MediaWiki\User\UserIdentityValue;
|
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;
|
2022-05-27 20:38:32 +00:00
|
|
|
use Wikimedia\Rdbms\FakeResultWrapper;
|
2022-10-06 20:25:11 +00:00
|
|
|
use Wikimedia\Rdbms\IResultWrapper;
|
2018-10-11 19:11:43 +00:00
|
|
|
use Wikimedia\Rdbms\LBFactory;
|
2022-08-12 11:20:33 +00:00
|
|
|
use Wikimedia\Rdbms\SelectQueryBuilder;
|
2016-03-15 00:36:02 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @author Addshore
|
2021-03-21 20:00:41 +00:00
|
|
|
* @author DannyS712
|
2022-04-08 16:47:27 +00:00
|
|
|
* @todo This test should become unittest again once LinksMigration is done (T300222)
|
2016-02-01 11:53:01 +00:00
|
|
|
*
|
|
|
|
|
* @covers WatchedItemStore
|
|
|
|
|
*/
|
2022-04-08 16:47:27 +00:00
|
|
|
class WatchedItemStoreUnitTest extends MediaWikiIntegrationTestCase {
|
2021-05-03 20:38:48 +00:00
|
|
|
use DummyServicesTrait;
|
2021-03-25 03:08:22 +00:00
|
|
|
use MockTitleTrait;
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @return MockObject&DBConnRef
|
2016-02-01 11:53:01 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockDb() {
|
2022-08-12 11:20:33 +00:00
|
|
|
$mock = $this->createMock( DBConnRef::class );
|
|
|
|
|
$mock->method( 'newSelectQueryBuilder' )
|
|
|
|
|
->willReturn( new SelectQueryBuilder( $mock ), new SelectQueryBuilder( $mock ), new SelectQueryBuilder( $mock ) );
|
|
|
|
|
return $mock;
|
2016-02-01 11:53:01 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-11 19:11:43 +00:00
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @param DBConnRef $mockDb
|
2021-01-16 19:44:17 +00:00
|
|
|
* @param string|null $expectedConnectionType
|
2021-08-31 17:38:46 +00:00
|
|
|
* @return MockObject&LBFactory
|
2018-10-11 19:11:43 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockLBFactory(
|
2021-08-31 17:38:46 +00:00
|
|
|
DBConnRef $mockDb,
|
2018-10-11 19:11:43 +00:00
|
|
|
$expectedConnectionType = null
|
|
|
|
|
) {
|
2021-04-28 18:08:20 +00:00
|
|
|
$mock = $this->createMock( LBFactory::class );
|
2022-07-25 20:34:51 +00:00
|
|
|
$mock->method( 'getLocalDomainID' )
|
|
|
|
|
->willReturn( 'phpunitdb' );
|
2023-05-05 11:53:32 +00:00
|
|
|
$mock->method( 'getPrimaryDatabase' )
|
|
|
|
|
->willReturn( $mockDb );
|
|
|
|
|
$mock->method( 'getReplicaDatabase' )
|
|
|
|
|
->willReturn( $mockDb );
|
2022-04-26 03:54:59 +00:00
|
|
|
$mock->method( 'getLBsForOwner' )
|
|
|
|
|
->willReturn( [] );
|
2018-10-11 19:11:43 +00:00
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-03 00:21:36 +00:00
|
|
|
/**
|
2021-03-21 20:00:41 +00:00
|
|
|
* The job queue is used in three different places - two "push" calls, and a
|
|
|
|
|
* "lazyPush" call - we don't test any of the "push" calls, so the callback
|
|
|
|
|
* can just run the job, but we do test the "lazyPush" call, and so the test
|
|
|
|
|
* that is using this may want to do something other than just run the job, since
|
|
|
|
|
* for ActivityUpdateJob instances this results in using global functions, which we
|
|
|
|
|
* cannot do in this unit test
|
|
|
|
|
*
|
|
|
|
|
* @param bool $mockLazyPush whether to add mock behavior for "lazyPush"
|
2019-10-06 04:54:59 +00:00
|
|
|
* @return MockObject|JobQueueGroup
|
2018-03-03 00:21:36 +00:00
|
|
|
*/
|
2021-03-21 20:00:41 +00:00
|
|
|
private function getMockJobQueueGroup( $mockLazyPush = true ) {
|
2021-04-28 18:08:20 +00:00
|
|
|
$mock = $this->createMock( JobQueueGroup::class );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'push' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( Job $job ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
$job->run();
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2021-03-21 20:00:41 +00:00
|
|
|
if ( $mockLazyPush ) {
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'lazyPush' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( Job $job ) {
|
2021-03-21 20:00:41 +00:00
|
|
|
$job->run();
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2021-03-21 20:00:41 +00:00
|
|
|
}
|
2018-03-03 00:21:36 +00:00
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 11:53:01 +00:00
|
|
|
/**
|
2019-10-06 04:54:59 +00:00
|
|
|
* @return MockObject|HashBagOStuff
|
2016-02-01 11:53:01 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockCache() {
|
2016-03-10 13:07:35 +00:00
|
|
|
$mock = $this->getMockBuilder( HashBagOStuff::class )
|
2016-02-01 11:53:01 +00:00
|
|
|
->disableOriginalConstructor()
|
2021-03-20 15:18:58 +00:00
|
|
|
->onlyMethods( [ 'get', 'set', 'delete', 'makeKey' ] )
|
2016-02-01 11:53:01 +00:00
|
|
|
->getMock();
|
2021-04-22 08:40:46 +00:00
|
|
|
$mock->method( 'makeKey' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( ...$args ) {
|
Get rid of unnecessary func_get_args() and friends
HHVM does not support variadic arguments with type hints. This is
mostly not a big problem, because we can just drop the type hint, but
for some reason PHPUnit adds a type hint of "array" when it creates
mocks, so a class with a variadic method can't be mocked (at least in
some cases). As such, I left alone all the classes that seem like
someone might like to mock them, like Title and User. If anyone wants
to mock them in the future, they'll have to switch back to
func_get_args(). Some of the changes are definitely safe, like
functions and test classes.
In most cases, func_get_args() (and/or func_get_arg(), func_num_args() )
were only present because the code was written before we required PHP
5.6, and writing them as variadic functions is strictly superior. In
some cases I left them alone, aside from HHVM compatibility:
* Forwarding all arguments to another function. It's useful to keep
func_get_args() here where we want to keep the list of expected
arguments and their meanings in the function signature line for
documentation purposes, but don't want to copy-paste a long line of
argument names.
* Handling deprecated calling conventions.
* One or two miscellaneous cases where we're basically using the
arguments individually but want to use them as an array as well for
some reason.
Change-Id: I066ec95a7beb7c0665146195a08e7cce1222c788
2018-10-08 14:10:45 +00:00
|
|
|
return implode( ':', $args );
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2016-02-01 11:53:01 +00:00
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
/**
|
|
|
|
|
* No methods may be called except provided callbacks, if any.
|
|
|
|
|
*
|
|
|
|
|
* @param array $callbacks Keys are method names, values are callbacks
|
|
|
|
|
* @param array $counts Keys are method names, values are expected number of times to be called
|
|
|
|
|
* (default is any number is okay)
|
2021-04-28 18:08:20 +00:00
|
|
|
* @return MockObject|RevisionLookup
|
2019-04-29 17:25:13 +00:00
|
|
|
*/
|
|
|
|
|
private function getMockRevisionLookup(
|
2021-04-28 18:08:20 +00:00
|
|
|
array $callbacks = [],
|
|
|
|
|
array $counts = []
|
2021-07-22 03:11:47 +00:00
|
|
|
): RevisionLookup {
|
2022-07-18 08:39:00 +00:00
|
|
|
$mock = $this->createNoOpMock( RevisionLookup::class, array_keys( $callbacks ) );
|
2019-04-29 17:25:13 +00:00
|
|
|
foreach ( $callbacks as $method => $callback ) {
|
|
|
|
|
$count = isset( $counts[$method] ) ? $this->exactly( $counts[$method] ) : $this->any();
|
|
|
|
|
$mock->expects( $count )
|
|
|
|
|
->method( $method )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( $callbacks[$method] );
|
2019-04-29 17:25:13 +00:00
|
|
|
}
|
|
|
|
|
return $mock;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 21:37:23 +00:00
|
|
|
/**
|
2021-08-31 17:38:46 +00:00
|
|
|
* @param DBConnRef $mockDb
|
2021-04-28 18:08:20 +00:00
|
|
|
* @return LinkBatchFactory
|
2020-08-07 21:37:23 +00:00
|
|
|
*/
|
2021-08-31 17:38:46 +00:00
|
|
|
private function getMockLinkBatchFactory( DBConnRef $mockDb ) {
|
2020-08-07 21:37:23 +00:00
|
|
|
return new LinkBatchFactory(
|
|
|
|
|
$this->createMock( LinkCache::class ),
|
|
|
|
|
$this->createMock( TitleFormatter::class ),
|
|
|
|
|
$this->createMock( Language::class ),
|
|
|
|
|
$this->createMock( GenderCache::class ),
|
2023-05-05 11:53:32 +00:00
|
|
|
$this->getMockLBFactory( $mockDb ),
|
2022-04-21 19:35:30 +00:00
|
|
|
$this->createMock( LinksMigration::class ),
|
2022-01-27 23:32:56 +00:00
|
|
|
LoggerFactory::getInstance( 'LinkBatch' )
|
2020-08-07 21:37:23 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
/**
|
|
|
|
|
* @param array $mocks Associative array providing mocks to use when constructing the
|
|
|
|
|
* WatchedItemStore. Anything not provided will fall back to a default. Valid keys:
|
|
|
|
|
* * lbFactory
|
|
|
|
|
* * db
|
|
|
|
|
* * queueGroup
|
|
|
|
|
* * cache
|
|
|
|
|
* * readOnlyMode
|
|
|
|
|
* * nsInfo
|
|
|
|
|
* * revisionLookup
|
2020-02-24 19:55:37 +00:00
|
|
|
* * expiryEnabled
|
2020-04-20 22:22:27 +00:00
|
|
|
* * maxExpiryDuration
|
2021-03-25 01:15:42 +00:00
|
|
|
* * watchlistPurgeRate
|
2020-02-24 19:55:37 +00:00
|
|
|
* @return WatchedItemStore
|
2019-04-29 17:25:13 +00:00
|
|
|
*/
|
2021-07-22 03:11:47 +00:00
|
|
|
private function newWatchedItemStore( array $mocks = [] ): WatchedItemStore {
|
2020-04-20 22:22:27 +00:00
|
|
|
$options = new ServiceOptions( WatchedItemStore::CONSTRUCTOR_OPTIONS, [
|
|
|
|
|
'UpdateRowsPerQuery' => 1000,
|
|
|
|
|
'WatchlistExpiry' => $mocks['expiryEnabled'] ?? true,
|
|
|
|
|
'WatchlistExpiryMaxDuration' => $mocks['maxExpiryDuration'] ?? null,
|
2021-03-25 01:15:42 +00:00
|
|
|
'WatchlistPurgeRate' => $mocks['watchlistPurgeRate'] ?? 0.1,
|
2020-04-20 22:22:27 +00:00
|
|
|
] );
|
|
|
|
|
|
2020-08-07 21:37:23 +00:00
|
|
|
$db = $mocks['db'] ?? $this->getMockDb();
|
2021-05-03 20:38:48 +00:00
|
|
|
|
|
|
|
|
// If we don't use a manual mock for something specific, get a full
|
|
|
|
|
// NamespaceInfo service from DummyServicesTrait::getDummyNamespaceInfo
|
|
|
|
|
$nsInfo = $mocks['nsInfo'] ?? $this->getDummyNamespaceInfo();
|
|
|
|
|
|
2016-04-01 11:46:15 +00:00
|
|
|
return new WatchedItemStore(
|
2020-04-20 22:22:27 +00:00
|
|
|
$options,
|
2019-04-29 17:25:13 +00:00
|
|
|
$mocks['lbFactory'] ??
|
2020-08-07 21:37:23 +00:00
|
|
|
$this->getMockLBFactory( $db ),
|
2019-04-29 17:25:13 +00:00
|
|
|
$mocks['queueGroup'] ?? $this->getMockJobQueueGroup(),
|
2021-10-05 00:28:01 +00:00
|
|
|
$mocks['stash'] ?? new HashBagOStuff(),
|
2019-04-29 17:25:13 +00:00
|
|
|
$mocks['cache'] ?? $this->getMockCache(),
|
2021-05-26 03:35:05 +00:00
|
|
|
$mocks['readOnlyMode'] ?? $this->getDummyReadOnlyMode( false ),
|
2021-05-03 20:38:48 +00:00
|
|
|
$nsInfo,
|
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
|
|
|
$mocks['revisionLookup'] ?? $this->getMockRevisionLookup(),
|
2021-09-21 23:22:19 +00:00
|
|
|
$this->getMockLinkBatchFactory( $db )
|
2016-04-01 11:46:15 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-15 00:36:02 +00:00
|
|
|
public function testClearWatchedItems() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 7, 'MockUser' );
|
2016-03-15 00:36:02 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'selectField' )
|
|
|
|
|
->with(
|
2020-04-28 00:10:46 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-15 00:36:02 +00:00
|
|
|
'COUNT(*)',
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => $user->getId(),
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 12 );
|
2016-03-15 00:36:02 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_user' => 7 ],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( 'RM-KEY' );
|
|
|
|
|
|
2020-04-08 02:41:06 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
2021-04-28 18:08:20 +00:00
|
|
|
'expiryEnabled' => false,
|
|
|
|
|
] );
|
2020-04-08 02:41:06 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $store )
|
|
|
|
|
->cacheIndex = [ 0 => [ 'F' => [ 7 => 'RM-KEY', 9 => 'KEEP-KEY' ] ] ];
|
|
|
|
|
|
|
|
|
|
$this->assertTrue( $store->clearUserWatchedItems( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testClearWatchedItems_watchlistExpiry() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 7, 'MockUser' );
|
2020-04-08 02:41:06 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
// Select watchlist IDs.
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'selectFieldValues' )
|
|
|
|
|
->willReturn( [ 1, 2 ] );
|
|
|
|
|
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->withConsecutive(
|
|
|
|
|
[
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_id' => [ 1, 2 ] ]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'watchlist_expiry',
|
|
|
|
|
[ 'we_item' => [ 1, 2 ] ]
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( 'RM-KEY' );
|
|
|
|
|
|
|
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
2021-04-28 18:08:20 +00:00
|
|
|
'expiryEnabled' => true,
|
|
|
|
|
] );
|
2016-03-15 00:36:02 +00:00
|
|
|
TestingAccessWrapper::newFromObject( $store )
|
|
|
|
|
->cacheIndex = [ 0 => [ 'F' => [ 7 => 'RM-KEY', 9 => 'KEEP-KEY' ] ] ];
|
|
|
|
|
|
|
|
|
|
$this->assertTrue( $store->clearUserWatchedItems( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testClearWatchedItems_tooManyItemsWatched() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 7, 'MockUser' );
|
2016-03-15 00:36:02 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'selectField' )
|
|
|
|
|
->with(
|
2020-04-28 00:10:46 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-15 00:36:02 +00:00
|
|
|
'COUNT(*)',
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => $user->getId(),
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 99999 );
|
2016-03-15 00:36:02 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2020-04-28 00:10:46 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
2021-04-28 18:08:20 +00:00
|
|
|
'expiryEnabled' => false,
|
|
|
|
|
] );
|
2016-03-15 00:36:02 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse( $store->clearUserWatchedItems( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-14 16:54:28 +00:00
|
|
|
public function testCountWatchedItems() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-14 16:54:28 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-04-28 00:10:46 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2016-03-14 16:54:28 +00:00
|
|
|
->method( 'selectField' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2016-03-14 16:54:28 +00:00
|
|
|
'COUNT(*)',
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => $user->getId(),
|
2020-04-28 00:10:46 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-03-14 16:54:28 +00:00
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( '12' );
|
2016-03-14 16:54:28 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-14 16:54:28 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( 12, $store->countWatchedItems( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
public function provideTestPageFactory() {
|
2021-04-29 16:24:12 +00:00
|
|
|
yield [ static function ( $pageId, $namespace, $dbKey ) {
|
2021-03-25 03:08:22 +00:00
|
|
|
return new TitleValue( $namespace, $dbKey );
|
|
|
|
|
} ];
|
2021-04-29 16:24:12 +00:00
|
|
|
yield [ static function ( $pageId, $namespace, $dbKey ) {
|
2021-03-25 03:08:22 +00:00
|
|
|
return new PageIdentityValue( $pageId, $namespace, $dbKey, PageIdentityValue::LOCAL );
|
|
|
|
|
} ];
|
|
|
|
|
yield [ function ( $pageId, $namespace, $dbKey ) {
|
|
|
|
|
return $this->makeMockTitle( $dbKey, [
|
|
|
|
|
'id' => $pageId,
|
|
|
|
|
'namespace' => $namespace
|
|
|
|
|
] );
|
|
|
|
|
} ];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testCountWatchers( $testPageFactory ) {
|
|
|
|
|
$titleValue = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-05-14 19:35:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2016-01-26 20:12:37 +00:00
|
|
|
->method( 'selectField' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2016-01-26 20:12:37 +00:00
|
|
|
'COUNT(*)',
|
|
|
|
|
[
|
|
|
|
|
'wl_namespace' => $titleValue->getNamespace(),
|
|
|
|
|
'wl_title' => $titleValue->getDBkey(),
|
2020-05-14 19:35:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-01-26 20:12:37 +00:00
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( '7' );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( 7, $store->countWatchers( $titleValue ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testCountWatchersMultiple( $testPageFactory ) {
|
2016-01-26 20:12:37 +00:00
|
|
|
$titleValues = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
|
|
|
|
$testPageFactory( 101, 0, 'OtherDbKey' ),
|
|
|
|
|
$testPageFactory( 102, 1, 'AnotherDbKey' ),
|
2016-01-26 20:12:37 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
|
|
|
|
|
$dbResult = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[ 'wl_title' => 'SomeDbKey', 'wl_namespace' => '0', 'watchers' => '100' ],
|
|
|
|
|
(object)[ 'wl_title' => 'OtherDbKey', 'wl_namespace' => '0', 'watchers' => '300' ],
|
|
|
|
|
(object)[ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => '1', 'watchers' => '500' ],
|
2016-01-26 20:12:37 +00:00
|
|
|
];
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeWhereFrom2d' )
|
|
|
|
|
->with(
|
|
|
|
|
[ [ 'SomeDbKey' => 1, 'OtherDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'string' )
|
2021-04-28 18:08:20 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 'makeWhereFrom2d return value' );
|
2020-05-14 19:35:37 +00:00
|
|
|
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
|
|
|
|
|
2016-01-26 20:12:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2016-01-26 20:12:37 +00:00
|
|
|
[ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
|
2020-05-14 19:35:37 +00:00
|
|
|
[
|
|
|
|
|
'makeWhereFrom2d return value',
|
|
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
|
|
|
|
],
|
2016-01-26 20:12:37 +00:00
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[
|
|
|
|
|
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $dbResult );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => 500 ],
|
|
|
|
|
];
|
|
|
|
|
$this->assertEquals( $expected, $store->countWatchersMultiple( $titleValues ) );
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:36:19 +00:00
|
|
|
public static function provideIntWithDbUnsafeVersion() {
|
2016-01-26 20:12:37 +00:00
|
|
|
return [
|
|
|
|
|
[ 50 ],
|
|
|
|
|
[ "50; DROP TABLE watchlist;\n--" ],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
public function provideTestPageFactoryAndIntWithDbUnsafeVersion() {
|
2022-12-04 19:09:28 +00:00
|
|
|
foreach ( $this->provideIntWithDbUnsafeVersion() as $dbint ) {
|
2021-03-25 03:08:22 +00:00
|
|
|
foreach ( $this->provideTestPageFactory() as $testPageFactory ) {
|
|
|
|
|
yield [ $dbint[0], $testPageFactory[0] ];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-26 20:12:37 +00:00
|
|
|
/**
|
2021-03-25 03:08:22 +00:00
|
|
|
* @dataProvider provideTestPageFactoryAndIntWithDbUnsafeVersion
|
2016-01-26 20:12:37 +00:00
|
|
|
*/
|
2021-03-25 03:08:22 +00:00
|
|
|
public function testCountWatchersMultiple_withMinimumWatchers( $minWatchers, $testPageFactory ) {
|
2016-01-26 20:12:37 +00:00
|
|
|
$titleValues = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
|
|
|
|
$testPageFactory( 101, 0, 'OtherDbKey' ),
|
|
|
|
|
$testPageFactory( 102, 1, 'AnotherDbKey' ),
|
2016-01-26 20:12:37 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
|
|
|
|
|
$dbResult = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[ 'wl_title' => 'SomeDbKey', 'wl_namespace' => '0', 'watchers' => '100' ],
|
|
|
|
|
(object)[ 'wl_title' => 'OtherDbKey', 'wl_namespace' => '0', 'watchers' => '300' ],
|
|
|
|
|
(object)[ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => '1', 'watchers' => '500' ],
|
2016-01-26 20:12:37 +00:00
|
|
|
];
|
2020-05-14 19:35:37 +00:00
|
|
|
|
2016-01-26 20:12:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeWhereFrom2d' )
|
|
|
|
|
->with(
|
|
|
|
|
[ [ 'SomeDbKey' => 1, 'OtherDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 'makeWhereFrom2d return value' );
|
2020-05-14 19:35:37 +00:00
|
|
|
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
|
|
|
|
|
2016-01-26 20:12:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2016-01-26 20:12:37 +00:00
|
|
|
[ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
|
2020-05-14 19:35:37 +00:00
|
|
|
[
|
|
|
|
|
'makeWhereFrom2d return value',
|
|
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
|
|
|
|
],
|
2016-01-26 20:12:37 +00:00
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[
|
|
|
|
|
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
|
2022-08-12 11:20:33 +00:00
|
|
|
'HAVING' => [ 'COUNT(*) >= 50' ],
|
2016-01-26 20:12:37 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $dbResult );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-01-26 20:12:37 +00:00
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => 500 ],
|
|
|
|
|
];
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expected,
|
|
|
|
|
$store->countWatchersMultiple( $titleValues, [ 'minimumWatchers' => $minWatchers ] )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testCountVisitingWatchers( $testPageFactory ) {
|
|
|
|
|
$titleValue = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-02-05 12:04:58 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-05-14 19:35:37 +00:00
|
|
|
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2016-02-05 12:04:58 +00:00
|
|
|
->method( 'selectField' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2016-02-05 12:04:58 +00:00
|
|
|
'COUNT(*)',
|
|
|
|
|
[
|
|
|
|
|
'wl_namespace' => $titleValue->getNamespace(),
|
|
|
|
|
'wl_title' => $titleValue->getDBkey(),
|
|
|
|
|
'wl_notificationtimestamp >= \'TS111TS\' OR wl_notificationtimestamp IS NULL',
|
2020-05-14 19:35:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > \'20200101000000\''
|
2016-02-05 12:04:58 +00:00
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( '7' );
|
2020-05-14 19:35:37 +00:00
|
|
|
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
2016-02-05 12:04:58 +00:00
|
|
|
->method( 'addQuotes' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-02-05 12:04:58 +00:00
|
|
|
return "'$value'";
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2020-05-14 19:35:37 +00:00
|
|
|
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
2016-02-05 12:04:58 +00:00
|
|
|
->method( 'timestamp' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2020-05-14 19:35:37 +00:00
|
|
|
if ( $value === 0 ) {
|
|
|
|
|
return '20200101000000';
|
|
|
|
|
}
|
2016-02-05 12:04:58 +00:00
|
|
|
return 'TS' . $value . 'TS';
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2016-02-05 12:04:58 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-05 12:04:58 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testCountVisitingWatchersMultiple( $testPageFactory ) {
|
2016-03-15 10:39:22 +00:00
|
|
|
$titleValuesWithThresholds = [
|
2021-03-25 03:08:22 +00:00
|
|
|
[ $testPageFactory( 100, 0, 'SomeDbKey' ), '111' ],
|
|
|
|
|
[ $testPageFactory( 101, 0, 'OtherDbKey' ), '111' ],
|
|
|
|
|
[ $testPageFactory( 102, 1, 'AnotherDbKey' ), '123' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$dbResult = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[ 'wl_title' => 'SomeDbKey', 'wl_namespace' => '0', 'watchers' => '100' ],
|
|
|
|
|
(object)[ 'wl_title' => 'OtherDbKey', 'wl_namespace' => '0', 'watchers' => '300' ],
|
|
|
|
|
(object)[ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => '1', 'watchers' => '500' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
];
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-05-14 19:35:37 +00:00
|
|
|
$mockDb->expects( $this->exactly( 2 * 3 + 1 ) )
|
2016-03-15 10:39:22 +00:00
|
|
|
->method( 'addQuotes' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-03-15 10:39:22 +00:00
|
|
|
return "'$value'";
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2020-05-14 19:35:37 +00:00
|
|
|
|
|
|
|
|
$mockDb->expects( $this->exactly( 4 ) )
|
2016-03-15 10:39:22 +00:00
|
|
|
->method( 'timestamp' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2020-05-14 19:35:37 +00:00
|
|
|
if ( $value === 0 ) {
|
|
|
|
|
return '20200101000000';
|
|
|
|
|
}
|
2016-03-15 10:39:22 +00:00
|
|
|
return 'TS' . $value . 'TS';
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2020-05-14 19:35:37 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockDb->method( 'makeList' )
|
2016-03-15 10:39:22 +00:00
|
|
|
->with(
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'int' )
|
|
|
|
|
)
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $a, $conj ) {
|
2016-03-15 10:39:22 +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-03-15 10:39:22 +00:00
|
|
|
return '(' . $s . ')';
|
|
|
|
|
}, $a
|
|
|
|
|
) );
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2016-03-15 10:39:22 +00:00
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'makeWhereFrom2d' );
|
|
|
|
|
|
|
|
|
|
$expectedCond =
|
|
|
|
|
'((wl_namespace = 0) AND (' .
|
|
|
|
|
"(((wl_title = 'SomeDbKey') AND (" .
|
|
|
|
|
"(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
|
|
|
|
|
')) OR (' .
|
|
|
|
|
"(wl_title = 'OtherDbKey') AND (" .
|
|
|
|
|
"(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
|
|
|
|
|
'))))' .
|
|
|
|
|
') OR ((wl_namespace = 1) AND (' .
|
2018-09-07 17:01:32 +00:00
|
|
|
"(((wl_title = 'AnotherDbKey') AND (" .
|
2016-03-15 10:39:22 +00:00
|
|
|
"(wl_notificationtimestamp >= 'TS123TS') OR (wl_notificationtimestamp IS NULL)" .
|
|
|
|
|
')))))';
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
|
2020-05-14 19:35:37 +00:00
|
|
|
[
|
|
|
|
|
$expectedCond,
|
|
|
|
|
'we_expiry IS NULL OR we_expiry > \'20200101000000\''
|
|
|
|
|
],
|
2016-03-15 10:39:22 +00:00
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[
|
|
|
|
|
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $dbResult );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => 500 ],
|
|
|
|
|
];
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expected,
|
|
|
|
|
$store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testCountVisitingWatchersMultiple_withMissingTargets( $testPageFactory ) {
|
2016-03-15 10:39:22 +00:00
|
|
|
$titleValuesWithThresholds = [
|
2021-03-25 03:08:22 +00:00
|
|
|
[ $testPageFactory( 100, 0, 'SomeDbKey' ), '111' ],
|
|
|
|
|
[ $testPageFactory( 101, 0, 'OtherDbKey' ), '111' ],
|
|
|
|
|
[ $testPageFactory( 102, 1, 'AnotherDbKey' ), '123' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
[ new TitleValue( 0, 'SomeNotExisitingDbKey' ), null ],
|
|
|
|
|
[ new TitleValue( 0, 'OtherNotExisitingDbKey' ), null ],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$dbResult = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[ 'wl_title' => 'SomeDbKey', 'wl_namespace' => '0', 'watchers' => '100' ],
|
|
|
|
|
(object)[ 'wl_title' => 'OtherDbKey', 'wl_namespace' => '0', 'watchers' => '300' ],
|
|
|
|
|
(object)[ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => '1', 'watchers' => '500' ],
|
|
|
|
|
(object)[ 'wl_title' => 'SomeNotExisitingDbKey', 'wl_namespace' => '0', 'watchers' => '100' ],
|
|
|
|
|
(object)[ 'wl_title' => 'OtherNotExisitingDbKey', 'wl_namespace' => '0', 'watchers' => '200' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
];
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 * 3 ) )
|
|
|
|
|
->method( 'addQuotes' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-03-15 10:39:22 +00:00
|
|
|
return "'$value'";
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2016-03-15 10:39:22 +00:00
|
|
|
$mockDb->expects( $this->exactly( 3 ) )
|
|
|
|
|
->method( 'timestamp' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-03-15 10:39:22 +00:00
|
|
|
return 'TS' . $value . 'TS';
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockDb->method( 'makeList' )
|
2016-03-15 10:39:22 +00:00
|
|
|
->with(
|
|
|
|
|
$this->isType( 'array' ),
|
|
|
|
|
$this->isType( 'int' )
|
|
|
|
|
)
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $a, $conj ) {
|
2016-03-15 10:39:22 +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-03-15 10:39:22 +00:00
|
|
|
return '(' . $s . ')';
|
|
|
|
|
}, $a
|
|
|
|
|
) );
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2016-03-15 10:39:22 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeWhereFrom2d' )
|
|
|
|
|
->with(
|
|
|
|
|
[ [ 'SomeNotExisitingDbKey' => 1, 'OtherNotExisitingDbKey' => 1 ] ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 'makeWhereFrom2d return value' );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$expectedCond =
|
|
|
|
|
'((wl_namespace = 0) AND (' .
|
|
|
|
|
"(((wl_title = 'SomeDbKey') AND (" .
|
|
|
|
|
"(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
|
|
|
|
|
')) OR (' .
|
|
|
|
|
"(wl_title = 'OtherDbKey') AND (" .
|
|
|
|
|
"(wl_notificationtimestamp >= 'TS111TS') OR (wl_notificationtimestamp IS NULL)" .
|
|
|
|
|
'))))' .
|
|
|
|
|
') OR ((wl_namespace = 1) AND (' .
|
2018-09-07 17:01:32 +00:00
|
|
|
"(((wl_title = 'AnotherDbKey') AND (" .
|
2016-03-15 10:39:22 +00:00
|
|
|
"(wl_notificationtimestamp >= 'TS123TS') OR (wl_notificationtimestamp IS NULL)" .
|
|
|
|
|
'))))' .
|
|
|
|
|
') OR ' .
|
|
|
|
|
'(makeWhereFrom2d return value)';
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2020-05-14 19:35:37 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
|
2020-05-14 19:35:37 +00:00
|
|
|
[ $expectedCond ],
|
2016-03-15 10:39:22 +00:00
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[
|
|
|
|
|
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
|
|
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $dbResult );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2020-05-14 19:35:37 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'expiryEnabled' => false
|
|
|
|
|
] );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
0 => [
|
|
|
|
|
'SomeDbKey' => 100, 'OtherDbKey' => 300,
|
|
|
|
|
'SomeNotExisitingDbKey' => 100, 'OtherNotExisitingDbKey' => 200
|
|
|
|
|
],
|
|
|
|
|
1 => [ 'AnotherDbKey' => 500 ],
|
|
|
|
|
];
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expected,
|
|
|
|
|
$store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-03-25 03:08:22 +00:00
|
|
|
* @dataProvider provideTestPageFactoryAndIntWithDbUnsafeVersion
|
2016-03-15 10:39:22 +00:00
|
|
|
*/
|
2021-03-25 03:08:22 +00:00
|
|
|
public function testCountVisitingWatchersMultiple_withMinimumWatchers( $minWatchers, $testPageFactory ) {
|
2016-03-15 10:39:22 +00:00
|
|
|
$titleValuesWithThresholds = [
|
2021-03-25 03:08:22 +00:00
|
|
|
[ $testPageFactory( 100, 0, 'SomeDbKey' ), '111' ],
|
|
|
|
|
[ $testPageFactory( 101, 0, 'OtherDbKey' ), '111' ],
|
|
|
|
|
[ $testPageFactory( 102, 1, 'AnotherDbKey' ), '123' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-04-22 08:28:11 +00:00
|
|
|
$mockDb->method( 'makeList' )
|
|
|
|
|
->willReturn( 'makeList return value' );
|
2016-03-15 10:39:22 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2020-05-14 19:35:37 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
|
2020-05-14 19:35:37 +00:00
|
|
|
[ 'makeList return value' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[
|
|
|
|
|
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
|
2022-08-12 11:20:33 +00:00
|
|
|
'HAVING' => [ 'COUNT(*) >= 50' ],
|
2016-03-15 10:39:22 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2020-05-14 19:35:37 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
2021-04-28 18:08:20 +00:00
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'expiryEnabled' => false,
|
|
|
|
|
] );
|
2016-03-15 10:39:22 +00:00
|
|
|
|
|
|
|
|
$expected = [
|
|
|
|
|
0 => [ 'SomeDbKey' => 0, 'OtherDbKey' => 0 ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => 0 ],
|
|
|
|
|
];
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expected,
|
|
|
|
|
$store->countVisitingWatchersMultiple( $titleValuesWithThresholds, $minWatchers )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-10 12:04:31 +00:00
|
|
|
public function testCountUnreadNotifications() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2016-03-10 12:04:31 +00:00
|
|
|
->method( 'selectRowCount' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-10 12:04:31 +00:00
|
|
|
'1',
|
|
|
|
|
[
|
|
|
|
|
"wl_notificationtimestamp IS NOT NULL",
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( '9' );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideIntWithDbUnsafeVersion
|
|
|
|
|
*/
|
|
|
|
|
public function testCountUnreadNotifications_withUnreadLimit_overLimit( $limit ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2016-03-10 12:04:31 +00:00
|
|
|
->method( 'selectRowCount' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-10 12:04:31 +00:00
|
|
|
'1',
|
|
|
|
|
[
|
|
|
|
|
"wl_notificationtimestamp IS NOT NULL",
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[ 'LIMIT' => 50 ]
|
|
|
|
|
)
|
2022-08-12 11:20:33 +00:00
|
|
|
->willReturn( 50 );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$this->assertSame(
|
|
|
|
|
true,
|
|
|
|
|
$store->countUnreadNotifications( $user, $limit )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideIntWithDbUnsafeVersion
|
|
|
|
|
*/
|
|
|
|
|
public function testCountUnreadNotifications_withUnreadLimit_underLimit( $limit ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-04-22 07:42:28 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2016-03-10 12:04:31 +00:00
|
|
|
->method( 'selectRowCount' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-10 12:04:31 +00:00
|
|
|
'1',
|
|
|
|
|
[
|
|
|
|
|
"wl_notificationtimestamp IS NOT NULL",
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[ 'LIMIT' => 50 ]
|
|
|
|
|
)
|
2022-08-12 11:20:33 +00:00
|
|
|
->willReturn( 9 );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-10 12:04:31 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
9,
|
|
|
|
|
$store->countUnreadNotifications( $user, $limit )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testDuplicateEntry_nothingToDuplicate( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-07-17 23:15:07 +00:00
|
|
|
[ 'wl_user', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Old_Title',
|
|
|
|
|
],
|
2020-07-17 23:15:07 +00:00
|
|
|
'WatchedItemStore::fetchWatchedItemsForPage',
|
|
|
|
|
[ 'FOR UPDATE' ],
|
|
|
|
|
[ 'watchlist_expiry' => [ 'LEFT JOIN', [ 'wl_id = we_item' ] ] ]
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( new FakeResultWrapper( [] ) );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$store->duplicateEntry(
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'Old_Title' ),
|
|
|
|
|
$testPageFactory( 101, 0, 'New_Title' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testDuplicateEntry_somethingToDuplicate( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$fakeRows = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-07-17 23:15:07 +00:00
|
|
|
'wl_user' => '1',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2020-07-17 23:15:07 +00:00
|
|
|
'wl_user' => '2',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
2016-02-01 11:53:01 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockDb
|
2016-02-01 11:53:01 +00:00
|
|
|
->method( 'select' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[ 'watchlist' ],
|
|
|
|
|
[ 'wl_user', 'wl_notificationtimestamp' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace' => 0,
|
2022-10-06 20:25:11 +00:00
|
|
|
'wl_title' => 'Old_Title',
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'New_Title',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 2,
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'New_Title',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
|
|
|
|
],
|
2016-02-01 11:53:01 +00:00
|
|
|
],
|
2022-10-06 20:25:11 +00:00
|
|
|
$this->isType( 'string' )
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
->willReturnOnConsecutiveCalls(
|
|
|
|
|
new FakeResultWrapper( $fakeRows ),
|
|
|
|
|
$this->createMock( IResultWrapper::class )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2020-07-17 23:15:07 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'expiryEnabled' => false,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$store->duplicateEntry(
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'Old_Title' ),
|
|
|
|
|
$testPageFactory( 101, 0, 'New_Title' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testDuplicateAllAssociatedEntries_nothingToDuplicate( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockDb
|
2016-02-01 11:53:01 +00:00
|
|
|
->method( 'select' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
|
|
|
|
[ 'wl_user', 'wl_notificationtimestamp', 'we_expiry' ],
|
|
|
|
|
[
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Old_Title',
|
|
|
|
|
]
|
|
|
|
|
],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
|
|
|
|
[ 'wl_user', 'wl_notificationtimestamp', 'we_expiry' ],
|
|
|
|
|
[
|
|
|
|
|
'wl_namespace' => 1,
|
|
|
|
|
'wl_title' => 'Old_Title',
|
|
|
|
|
]
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( new FakeResultWrapper( [] ) );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$store->duplicateAllAssociatedEntries(
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'Old_Title' ),
|
|
|
|
|
$testPageFactory( 101, 0, 'New_Title' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-18 11:32:46 +00:00
|
|
|
public function provideLinkTargetPairs() {
|
2021-03-25 03:08:22 +00:00
|
|
|
foreach ( $this->provideTestPageFactory() as $testPageFactoryArray ) {
|
|
|
|
|
$testPageFactory = $testPageFactoryArray[0];
|
|
|
|
|
yield [ $testPageFactory( 100, 0, 'Old_Title' ), $testPageFactory( 101, 0, 'New_Title' ) ];
|
|
|
|
|
}
|
2016-04-18 11:32:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-03-25 03:08:22 +00:00
|
|
|
* @param LinkTarget|PageIdentity $oldTarget
|
|
|
|
|
* @param LinkTarget|PageIdentity $newTarget
|
2016-04-18 11:32:46 +00:00
|
|
|
* @dataProvider provideLinkTargetPairs
|
|
|
|
|
*/
|
|
|
|
|
public function testDuplicateAllAssociatedEntries_somethingToDuplicate(
|
2021-03-25 03:08:22 +00:00
|
|
|
$oldTarget,
|
|
|
|
|
$newTarget
|
2016-04-18 11:32:46 +00:00
|
|
|
) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$fakeRows = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-07-17 23:15:07 +00:00
|
|
|
'wl_user' => '1',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
'we_expiry' => null,
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
2016-02-01 11:53:01 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockDb
|
2016-02-01 11:53:01 +00:00
|
|
|
->method( 'select' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[ 'watchlist' ],
|
|
|
|
|
[ 'wl_user', 'wl_notificationtimestamp' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
'wl_namespace' => $oldTarget->getNamespace(),
|
|
|
|
|
'wl_title' => $oldTarget->getDBkey(),
|
|
|
|
|
]
|
2016-02-01 11:53:01 +00:00
|
|
|
],
|
|
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[ 'watchlist' ],
|
|
|
|
|
[ 'wl_user', 'wl_notificationtimestamp' ],
|
|
|
|
|
[
|
|
|
|
|
'wl_namespace' => $oldTarget->getNamespace() + 1,
|
|
|
|
|
'wl_title' => $oldTarget->getDBkey(),
|
|
|
|
|
]
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( new FakeResultWrapper( $fakeRows ) );
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockDb
|
2016-02-01 11:53:01 +00:00
|
|
|
->method( 'replace' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
'watchlist',
|
|
|
|
|
[ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2022-10-06 20:25:11 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'wl_namespace' => $newTarget->getNamespace(),
|
|
|
|
|
'wl_title' => $newTarget->getDBkey(),
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
],
|
2016-02-01 11:53:01 +00:00
|
|
|
],
|
2022-10-06 20:25:11 +00:00
|
|
|
$this->isType( 'string' )
|
2016-02-01 11:53:01 +00:00
|
|
|
],
|
2022-10-06 20:25:11 +00:00
|
|
|
[
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'wl_namespace' => $newTarget->getNamespace() + 1,
|
|
|
|
|
'wl_title' => $newTarget->getDBkey(),
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
]
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockDb
|
|
|
|
|
->method( 'newSelectQueryBuilder' )
|
|
|
|
|
->willReturn( new SelectQueryBuilder( $mockDb ) );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2020-07-17 23:15:07 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'expiryEnabled' => false,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$store->duplicateAllAssociatedEntries(
|
2016-04-18 11:32:46 +00:00
|
|
|
$oldTarget,
|
|
|
|
|
$newTarget
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testAddWatch_nonAnonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'insert' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Some_Page',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:Some_Page:1' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$store->addWatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'Some_Page' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testAddWatch_anonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'insert' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )
|
|
|
|
|
->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$store->addWatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'Some_Page' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testAddWatchBatchForUser_readOnlyDBReturnsFalse( $testPageFactory ) {
|
2016-04-18 11:25:54 +00:00
|
|
|
$store = $this->newWatchedItemStore(
|
2021-05-26 03:35:05 +00:00
|
|
|
[ 'readOnlyMode' => $this->getDummyReadOnlyMode( true ) ]
|
2021-04-28 18:08:20 +00:00
|
|
|
);
|
2016-04-18 11:25:54 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->addWatchBatchForUser(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
[ $testPageFactory( 100, 0, 'Some_Page' ), $testPageFactory( 101, 1, 'Some_Page' ) ]
|
2016-04-18 11:25:54 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testAddWatchBatchForUser_nonAnonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'insert' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Some_Page',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
|
|
|
|
'wl_namespace' => 1,
|
|
|
|
|
'wl_title' => 'Some_Page',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
2018-10-02 19:55:53 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'affectedRows' )
|
|
|
|
|
->willReturn( 2 );
|
|
|
|
|
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'delete' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
|
|
|
|
[ '0:Some_Page:1' ],
|
|
|
|
|
[ '1:Some_Page:1' ]
|
|
|
|
|
);
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-02-15 18:58:09 +00:00
|
|
|
$mockUser = new UserIdentityValue( 1, 'MockUser' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
2016-03-22 18:07:49 +00:00
|
|
|
$store->addWatchBatchForUser(
|
|
|
|
|
$mockUser,
|
2021-03-25 03:08:22 +00:00
|
|
|
[ $testPageFactory( 100, 0, 'Some_Page' ), $testPageFactory( 101, 1, 'Some_Page' ) ]
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testAddWatchBatchForUser_anonymousUsersAreSkipped( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'insert' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )
|
|
|
|
|
->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
2016-03-22 18:07:49 +00:00
|
|
|
$store->addWatchBatchForUser(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
[ $testPageFactory( 100, 0, 'Other_Page' ) ]
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-22 18:07:49 +00:00
|
|
|
public function testAddWatchBatchReturnsTrue_whenGivenEmptyList() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'insert' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )
|
|
|
|
|
->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2016-03-22 18:07:49 +00:00
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->addWatchBatchForUser( $user, [] )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testLoadWatchedItem_existingItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
2020-02-24 19:55:37 +00:00
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
'we_expiry' => '20300101000000'
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with(
|
|
|
|
|
'0:SomeDbKey:1'
|
|
|
|
|
);
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$watchedItem = $store->loadWatchedItem(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
|
2021-02-25 13:04:26 +00:00
|
|
|
$this->assertSame( 1, $watchedItem->getUserIdentity()->getId() );
|
2021-03-25 03:08:22 +00:00
|
|
|
$this->assertEquals( 'SomeDbKey', $watchedItem->getTarget()->getDBkey() );
|
2020-02-24 19:55:37 +00:00
|
|
|
$this->assertSame( '20300101000000', $watchedItem->getExpiry() );
|
2021-03-25 03:08:22 +00:00
|
|
|
$this->assertSame( 0, $watchedItem->getTarget()->getNamespace() );
|
2016-02-01 11:53:01 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testLoadWatchedItem_noItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->loadWatchedItem(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testLoadWatchedItem_anonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->loadWatchedItem(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testRemoveWatch_existingItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
2020-03-05 17:19:30 +00:00
|
|
|
->method( 'selectFieldValues' )
|
2020-02-24 19:55:37 +00:00
|
|
|
->willReturn( [ 1, 2 ] );
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
2016-02-01 11:53:01 +00:00
|
|
|
->method( 'delete' )
|
2018-10-02 19:55:53 +00:00
|
|
|
->withConsecutive(
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
2018-10-02 19:55:53 +00:00
|
|
|
'watchlist',
|
2020-02-24 19:55:37 +00:00
|
|
|
[ 'wl_id' => [ 1, 2 ] ]
|
2018-10-02 19:55:53 +00:00
|
|
|
],
|
|
|
|
|
[
|
2020-02-24 19:55:37 +00:00
|
|
|
'watchlist_expiry',
|
|
|
|
|
[ 'we_item' => [ 1, 2 ] ]
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
);
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
2016-02-01 11:53:01 +00:00
|
|
|
->method( 'affectedRows' )
|
2018-10-02 19:55:53 +00:00
|
|
|
->willReturn( 2 );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
2018-10-02 19:55:53 +00:00
|
|
|
->withConsecutive(
|
|
|
|
|
[ '0:SomeDbKey:1' ],
|
|
|
|
|
[ '1:SomeDbKey:1' ]
|
|
|
|
|
);
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->removeWatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testRemoveWatch_noItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
2020-03-05 17:19:30 +00:00
|
|
|
->method( 'selectFieldValues' )
|
2021-08-30 00:00:34 +00:00
|
|
|
->willReturn( [] );
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'delete' );
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'affectedRows' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
2018-10-02 19:55:53 +00:00
|
|
|
->withConsecutive(
|
|
|
|
|
[ '0:SomeDbKey:1' ],
|
|
|
|
|
[ '1:SomeDbKey:1' ]
|
|
|
|
|
);
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->removeWatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testRemoveWatch_anonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'delete' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->never() )
|
|
|
|
|
->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->removeWatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItem_existingItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
2020-02-24 19:55:37 +00:00
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
'we_expiry' => '20300101000000'
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with(
|
|
|
|
|
'0:SomeDbKey:1'
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( null );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with(
|
|
|
|
|
'0:SomeDbKey:1'
|
|
|
|
|
);
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$watchedItem = $store->getWatchedItem(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
);
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
|
2021-02-25 13:04:26 +00:00
|
|
|
$this->assertSame( 1, $watchedItem->getUserIdentity()->getId() );
|
2021-03-25 03:08:22 +00:00
|
|
|
$this->assertEquals( 'SomeDbKey', $watchedItem->getTarget()->getDBkey() );
|
2020-02-24 19:55:37 +00:00
|
|
|
$this->assertSame( '20300101000000', $watchedItem->getExpiry() );
|
2021-03-25 03:08:22 +00:00
|
|
|
$this->assertSame( 0, $watchedItem->getTarget()->getNamespace() );
|
2016-02-01 11:53:01 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItem_cachedItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'selectRow' );
|
|
|
|
|
|
2021-02-15 18:58:09 +00:00
|
|
|
$mockUser = new UserIdentityValue( 1, 'MockUser' );
|
2021-03-25 03:08:22 +00:00
|
|
|
$linkTarget = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$cachedItem = new WatchedItem( $mockUser, $linkTarget, '20151212010101' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with(
|
|
|
|
|
'0:SomeDbKey:1'
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $cachedItem );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$cachedItem,
|
|
|
|
|
$store->getWatchedItem(
|
|
|
|
|
$mockUser,
|
|
|
|
|
$linkTarget
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItem_noItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with( '0:SomeDbKey:1' )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( false );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->getWatchedItem(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItem_anonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'selectRow' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->getWatchedItem(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-15 22:19:16 +00:00
|
|
|
public function testGetWatchedItemsForUser() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2016-03-15 22:19:16 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-02-24 19:55:37 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
|
|
|
|
[ 'wl_user' => 1, 'we_expiry IS NULL OR we_expiry > 20200101000000' ]
|
2016-03-15 22:19:16 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2016-03-15 22:19:16 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Foo1',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry' => '20300101000000'
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2016-03-15 22:19:16 +00:00
|
|
|
'wl_namespace' => 1,
|
|
|
|
|
'wl_title' => 'Foo2',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-03-15 22:19:16 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-15 22:19:16 +00:00
|
|
|
|
|
|
|
|
$watchedItems = $store->getWatchedItemsForUser( $user );
|
|
|
|
|
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsArray( $watchedItems );
|
2016-03-15 22:19:16 +00:00
|
|
|
$this->assertCount( 2, $watchedItems );
|
|
|
|
|
foreach ( $watchedItems as $watchedItem ) {
|
2018-01-13 00:02:09 +00:00
|
|
|
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
|
2016-03-15 22:19:16 +00:00
|
|
|
}
|
|
|
|
|
$this->assertEquals(
|
2020-02-24 19:55:37 +00:00
|
|
|
new WatchedItem(
|
|
|
|
|
$user,
|
|
|
|
|
new TitleValue( 0, 'Foo1' ),
|
|
|
|
|
'20151212010101',
|
|
|
|
|
'20300101000000'
|
|
|
|
|
),
|
2016-03-15 22:19:16 +00:00
|
|
|
$watchedItems[0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
|
|
|
|
|
$watchedItems[1]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-23 11:36:19 +00:00
|
|
|
public static function provideDbTypes() {
|
2016-03-15 22:19:16 +00:00
|
|
|
return [
|
2017-08-04 18:53:34 +00:00
|
|
|
[ false, DB_REPLICA ],
|
2021-04-29 02:37:11 +00:00
|
|
|
[ true, DB_PRIMARY ],
|
2016-03-15 22:19:16 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideDbTypes
|
|
|
|
|
*/
|
|
|
|
|
public function testGetWatchedItemsForUser_optionsAndEmptyResult( $forWrite, $dbType ) {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2018-10-11 19:11:43 +00:00
|
|
|
$mockLoadBalancer = $this->getMockLBFactory( $mockDb, $dbType );
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-15 22:19:16 +00:00
|
|
|
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2016-03-15 22:19:16 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-02-24 19:55:37 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
|
|
|
|
[ 'wl_user' => 1, 'we_expiry IS NULL OR we_expiry > 20200101000000' ],
|
2016-03-16 13:40:40 +00:00
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
[ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
|
2016-03-15 22:19:16 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-03-15 22:19:16 +00:00
|
|
|
|
2016-04-01 11:46:15 +00:00
|
|
|
$store = $this->newWatchedItemStore(
|
2019-04-29 17:25:13 +00:00
|
|
|
[ 'lbFactory' => $mockLoadBalancer, 'cache' => $mockCache ] );
|
2016-03-15 22:19:16 +00:00
|
|
|
|
|
|
|
|
$watchedItems = $store->getWatchedItemsForUser(
|
|
|
|
|
$user,
|
2016-03-16 13:40:40 +00:00
|
|
|
[ 'forWrite' => $forWrite, 'sort' => WatchedItemStore::SORT_ASC ]
|
2016-03-15 22:19:16 +00:00
|
|
|
);
|
|
|
|
|
$this->assertEquals( [], $watchedItems );
|
|
|
|
|
}
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2020-07-10 22:09:10 +00:00
|
|
|
public function testGetWatchedItemsForUser_sortByExpiry() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-07-10 22:09:10 +00:00
|
|
|
[
|
|
|
|
|
'wl_namespace',
|
|
|
|
|
'wl_title',
|
|
|
|
|
'wl_notificationtimestamp',
|
|
|
|
|
'we_expiry',
|
|
|
|
|
'wl_has_expiry' => null
|
|
|
|
|
],
|
|
|
|
|
[ 'wl_user' => 1, 'we_expiry IS NULL OR we_expiry > 20200101000000' ]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-07-10 22:09:10 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Foo1',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
'we_expiry' => '20300101000000'
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2020-07-10 22:09:10 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'Foo2',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
|
|
|
|
'we_expiry' => '20300701000000'
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
2020-07-10 22:09:10 +00:00
|
|
|
'wl_namespace' => 1,
|
|
|
|
|
'wl_title' => 'Foo3',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2020-07-10 22:09:10 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
|
|
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2020-07-10 22:09:10 +00:00
|
|
|
|
|
|
|
|
$watchedItems = $store->getWatchedItemsForUser(
|
|
|
|
|
$user,
|
|
|
|
|
[ 'sortByExpiry' => true, 'sort' => WatchedItemStore::SORT_ASC ]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->assertIsArray( $watchedItems );
|
|
|
|
|
$this->assertCount( 3, $watchedItems );
|
|
|
|
|
foreach ( $watchedItems as $watchedItem ) {
|
|
|
|
|
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
|
|
|
|
|
}
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem(
|
|
|
|
|
$user,
|
|
|
|
|
new TitleValue( 0, 'Foo1' ),
|
|
|
|
|
'20151212010101',
|
|
|
|
|
'20300101000000'
|
|
|
|
|
),
|
|
|
|
|
$watchedItems[0]
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
new WatchedItem( $user, new TitleValue( 1, 'Foo3' ), null ),
|
|
|
|
|
$watchedItems[2]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-16 13:40:40 +00:00
|
|
|
public function testGetWatchedItemsForUser_badSortOptionThrowsException() {
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore();
|
2016-03-16 13:40:40 +00:00
|
|
|
|
2019-10-05 15:39:46 +00:00
|
|
|
$this->expectException( InvalidArgumentException::class );
|
2016-03-16 13:40:40 +00:00
|
|
|
$store->getWatchedItemsForUser(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2016-03-16 13:40:40 +00:00
|
|
|
[ 'sort' => 'foo' ]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testIsWatchedItem_existingItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with( '0:SomeDbKey:1' )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( false );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with(
|
|
|
|
|
'0:SomeDbKey:1'
|
|
|
|
|
);
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->isWatched(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testIsWatchedItem_noItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with( '0:SomeDbKey:1' )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( false );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->isWatched(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testIsWatchedItem_anonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'selectRow' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->isWatched(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetNotificationTimestampsBatch( $testPageFactory ) {
|
2016-03-18 13:43:26 +00:00
|
|
|
$targets = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
|
|
|
|
$testPageFactory( 101, 1, 'AnotherDbKey' ),
|
2016-03-18 13:43:26 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$dbResult = [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2017-11-30 16:12:59 +00:00
|
|
|
'wl_namespace' => '0',
|
2016-03-18 13:43:26 +00:00
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
],
|
|
|
|
|
(object)[
|
|
|
|
|
'wl_namespace' => '1',
|
|
|
|
|
'wl_title' => 'AnotherDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => null,
|
|
|
|
|
],
|
2016-03-18 13:43:26 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeWhereFrom2d' )
|
|
|
|
|
->with(
|
|
|
|
|
[ [ 'SomeDbKey' => 1 ], [ 'AnotherDbKey' => 1 ] ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 'makeWhereFrom2d return value' );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-18 13:43:26 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
|
|
|
|
|
[
|
|
|
|
|
'makeWhereFrom2d return value',
|
|
|
|
|
'wl_user' => 1
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( $dbResult );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->withConsecutive(
|
|
|
|
|
[ '0:SomeDbKey:1' ],
|
|
|
|
|
[ '1:AnotherDbKey:1' ]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( null );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
0 => [ 'SomeDbKey' => '20151212010101', ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => null, ],
|
|
|
|
|
],
|
2019-04-28 11:07:18 +00:00
|
|
|
$store->getNotificationTimestampsBatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ), $targets )
|
2016-03-18 13:43:26 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetNotificationTimestampsBatch_notWatchedTarget( $testPageFactory ) {
|
2016-03-18 13:43:26 +00:00
|
|
|
$targets = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'OtherDbKey' ),
|
2016-03-18 13:43:26 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeWhereFrom2d' )
|
|
|
|
|
->with(
|
|
|
|
|
[ [ 'OtherDbKey' => 1 ] ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 'makeWhereFrom2d return value' );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-18 13:43:26 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
|
|
|
|
|
[
|
|
|
|
|
'makeWhereFrom2d return value',
|
|
|
|
|
'wl_user' => 1
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( (object)[] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with( '0:OtherDbKey:1' )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( null );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
0 => [ 'OtherDbKey' => false, ],
|
|
|
|
|
],
|
2019-04-28 11:07:18 +00:00
|
|
|
$store->getNotificationTimestampsBatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ), $targets )
|
2016-03-18 13:43:26 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetNotificationTimestampsBatch_cachedItem( $testPageFactory ) {
|
2016-03-18 13:43:26 +00:00
|
|
|
$targets = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
|
|
|
|
$testPageFactory( 101, 1, 'AnotherDbKey' ),
|
2016-03-18 13:43:26 +00:00
|
|
|
];
|
|
|
|
|
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-18 13:43:26 +00:00
|
|
|
$cachedItem = new WatchedItem( $user, $targets[0], '20151212010101' );
|
|
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeWhereFrom2d' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 1 => [ 'AnotherDbKey' => 1 ] ],
|
|
|
|
|
$this->isType( 'string' ),
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 'makeWhereFrom2d return value' );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'select' )
|
|
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist' ],
|
2016-03-18 13:43:26 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
|
|
|
|
|
[
|
|
|
|
|
'makeWhereFrom2d return value',
|
|
|
|
|
'wl_user' => 1
|
|
|
|
|
],
|
|
|
|
|
$this->isType( 'string' )
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[ 'wl_namespace' => '1', 'wl_title' => 'AnotherDbKey', 'wl_notificationtimestamp' => null, ]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockCache
|
2016-03-18 13:43:26 +00:00
|
|
|
->method( 'get' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
|
|
|
|
[ '0:SomeDbKey:1' ],
|
|
|
|
|
[ '1:AnotherDbKey:1' ]
|
|
|
|
|
)
|
|
|
|
|
->willReturnOnConsecutiveCalls( $cachedItem, null );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
0 => [ 'SomeDbKey' => '20151212010101', ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => null, ],
|
|
|
|
|
],
|
|
|
|
|
$store->getNotificationTimestampsBatch( $user, $targets )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetNotificationTimestampsBatch_allItemsCached( $testPageFactory ) {
|
2016-03-18 13:43:26 +00:00
|
|
|
$targets = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
|
|
|
|
$testPageFactory( 101, 1, 'AnotherDbKey' ),
|
2016-03-18 13:43:26 +00:00
|
|
|
];
|
|
|
|
|
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-03-18 13:43:26 +00:00
|
|
|
$cachedItems = [
|
|
|
|
|
new WatchedItem( $user, $targets[0], '20151212010101' ),
|
|
|
|
|
new WatchedItem( $user, $targets[1], null ),
|
|
|
|
|
];
|
2021-08-31 17:38:46 +00:00
|
|
|
$mockDb = $this->createNoOpMock( DBConnRef::class );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2022-10-06 20:25:11 +00:00
|
|
|
$mockCache
|
2016-03-18 13:43:26 +00:00
|
|
|
->method( 'get' )
|
2022-10-06 20:25:11 +00:00
|
|
|
->withConsecutive(
|
|
|
|
|
[ '0:SomeDbKey:1' ],
|
|
|
|
|
[ '1:AnotherDbKey:1' ]
|
|
|
|
|
)
|
|
|
|
|
->willReturnOnConsecutiveCalls( $cachedItems[0], $cachedItems[1] );
|
2016-03-18 13:43:26 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
0 => [ 'SomeDbKey' => '20151212010101', ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => null, ],
|
|
|
|
|
],
|
|
|
|
|
$store->getNotificationTimestampsBatch( $user, $targets )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testGetNotificationTimestampsBatch_anonymousUser( $testPageFactory ) {
|
2016-03-18 13:43:26 +00:00
|
|
|
$targets = [
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
|
|
|
|
$testPageFactory( 101, 1, 'AnotherDbKey' ),
|
2016-03-18 13:43:26 +00:00
|
|
|
];
|
|
|
|
|
|
2021-08-31 17:38:46 +00:00
|
|
|
$mockDb = $this->createNoOpMock( DBConnRef::class );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
2019-08-28 10:01:39 +00:00
|
|
|
$mockCache = $this->createNoOpMock( HashBagOStuff::class );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-18 13:43:26 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
0 => [ 'SomeDbKey' => false, ],
|
|
|
|
|
1 => [ 'AnotherDbKey' => false, ],
|
|
|
|
|
],
|
2019-04-28 11:07:18 +00:00
|
|
|
$store->getNotificationTimestampsBatch(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ), $targets )
|
2016-03-18 13:43:26 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_anonymousUser( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'selectRow' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' )
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_noItem( $testPageFactory ) {
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2022-01-12 11:39:41 +00:00
|
|
|
$mockCache->expects( $this->once() )->method( 'get' );
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2021-03-08 04:01:32 +00:00
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2021-03-17 13:51:05 +00:00
|
|
|
|
2021-03-08 04:01:32 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
2021-03-08 04:01:32 +00:00
|
|
|
$user,
|
2021-03-17 13:51:05 +00:00
|
|
|
$title
|
2016-02-01 11:53:01 +00:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_item( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2022-01-12 11:39:41 +00:00
|
|
|
$mockCache->expects( $this->once() )->method( 'get' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with(
|
|
|
|
|
'0:SomeDbKey:1',
|
|
|
|
|
$this->isInstanceOf( WatchedItem::class )
|
|
|
|
|
);
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
|
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2018-03-03 00:21:36 +00:00
|
|
|
$mockQueueGroup->expects( $this->once() )
|
|
|
|
|
->method( 'lazyPush' )
|
2021-02-06 19:40:52 +00:00
|
|
|
->willReturnCallback( static function ( ActivityUpdateJob $job ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
// don't run
|
|
|
|
|
} );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
// We don't care if these methods actually do anything here
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup( [
|
2021-02-07 13:10:36 +00:00
|
|
|
'getRevisionByTitle' => static function () {
|
2019-04-29 17:25:13 +00:00
|
|
|
return null;
|
|
|
|
|
},
|
2021-02-07 13:10:36 +00:00
|
|
|
'getTimestampFromId' => static function () {
|
2019-04-29 17:25:13 +00:00
|
|
|
return '00000000000000';
|
|
|
|
|
},
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_noItemForced( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'selectRow' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2018-04-12 20:18:39 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
|
|
|
|
// We don't care if these methods actually do anything here
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup( [
|
2021-02-07 13:10:36 +00:00
|
|
|
'getRevisionByTitle' => static function () {
|
2019-04-29 17:25:13 +00:00
|
|
|
return null;
|
|
|
|
|
},
|
2021-02-07 13:10:36 +00:00
|
|
|
'getTimestampFromId' => static function () {
|
2019-04-29 17:25:13 +00:00
|
|
|
return '00000000000000';
|
|
|
|
|
},
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockQueueGroup->method( 'lazyPush' )
|
2021-02-06 19:40:52 +00:00
|
|
|
->willReturnCallback( static function ( ActivityUpdateJob $job ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
// don't run
|
2021-02-06 19:40:52 +00:00
|
|
|
} );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
'force'
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @param ActivityUpdateJob $job
|
|
|
|
|
* @param LinkTarget|PageIdentity $expectedTitle
|
|
|
|
|
* @param string $expectedUserId
|
|
|
|
|
* @param callable $notificationTimestampCondition
|
|
|
|
|
*/
|
2016-04-20 13:51:26 +00:00
|
|
|
private function verifyCallbackJob(
|
2018-03-03 00:21:36 +00:00
|
|
|
ActivityUpdateJob $job,
|
2021-03-25 03:08:22 +00:00
|
|
|
$expectedTitle,
|
2016-04-20 13:51:26 +00:00
|
|
|
$expectedUserId,
|
|
|
|
|
callable $notificationTimestampCondition
|
|
|
|
|
) {
|
|
|
|
|
$this->assertEquals( $expectedTitle->getDBkey(), $job->getTitle()->getDBkey() );
|
|
|
|
|
$this->assertEquals( $expectedTitle->getNamespace(), $job->getTitle()->getNamespace() );
|
|
|
|
|
|
|
|
|
|
$jobParams = $job->getParams();
|
|
|
|
|
$this->assertArrayHasKey( 'type', $jobParams );
|
|
|
|
|
$this->assertEquals( 'updateWatchlistNotification', $jobParams['type'] );
|
|
|
|
|
$this->assertArrayHasKey( 'userid', $jobParams );
|
|
|
|
|
$this->assertEquals( $expectedUserId, $jobParams['userid'] );
|
|
|
|
|
$this->assertArrayHasKey( 'notifTime', $jobParams );
|
|
|
|
|
$this->assertTrue( $notificationTimestampCondition( $jobParams['notifTime'] ) );
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$oldid = 22;
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeTitle' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'selectRow' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2018-04-12 20:18:39 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeTitle:1' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
2019-08-28 10:01:39 +00:00
|
|
|
$mockRevisionRecord = $this->createNoOpMock( RevisionRecord::class );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup( [
|
2021-02-07 13:10:36 +00:00
|
|
|
'getTimestampFromId' => static function () {
|
2019-04-29 17:25:13 +00:00
|
|
|
return '00000000000000';
|
|
|
|
|
},
|
|
|
|
|
'getRevisionById' => function ( $id, $flags ) use ( $oldid, $mockRevisionRecord ) {
|
|
|
|
|
$this->assertSame( $oldid, $id );
|
|
|
|
|
$this->assertSame( 0, $flags );
|
|
|
|
|
return $mockRevisionRecord;
|
|
|
|
|
},
|
|
|
|
|
'getNextRevision' =>
|
2019-05-18 14:38:48 +00:00
|
|
|
function ( $oldRev ) use ( $mockRevisionRecord ) {
|
2019-04-29 17:25:13 +00:00
|
|
|
$this->assertSame( $mockRevisionRecord, $oldRev );
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
], [
|
|
|
|
|
'getNextRevision' => 1,
|
|
|
|
|
] );
|
|
|
|
|
|
|
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockQueueGroup->method( 'lazyPush' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback(
|
2018-03-03 00:21:36 +00:00
|
|
|
function ( ActivityUpdateJob $job ) use ( $title, $user ) {
|
|
|
|
|
$this->verifyCallbackJob(
|
|
|
|
|
$job,
|
|
|
|
|
$title,
|
|
|
|
|
$user->getId(),
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( $time ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
return $time === null;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-28 18:08:20 +00:00
|
|
|
);
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
'force',
|
|
|
|
|
$oldid
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-02-01 11:53:01 +00:00
|
|
|
$oldid = 22;
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
2019-08-28 10:01:39 +00:00
|
|
|
$mockRevision = $this->createNoOpMock( RevisionRecord::class );
|
|
|
|
|
$mockNextRevision = $this->createNoOpMock( RevisionRecord::class );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-02-01 11:53:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-02-01 11:53:01 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-02-01 11:53:01 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-02-01 11:53:01 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2018-04-12 20:18:39 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup(
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => function ( $oldidParam ) use ( $oldid ) {
|
|
|
|
|
$this->assertSame( $oldid, $oldidParam );
|
|
|
|
|
},
|
|
|
|
|
'getRevisionById' => function ( $id ) use ( $oldid, $mockRevision ) {
|
|
|
|
|
$this->assertSame( $oldid, $id );
|
|
|
|
|
return $mockRevision;
|
|
|
|
|
},
|
|
|
|
|
'getNextRevision' =>
|
|
|
|
|
function ( RevisionRecord $rev ) use ( $mockRevision, $mockNextRevision ) {
|
|
|
|
|
$this->assertSame( $mockRevision, $rev );
|
|
|
|
|
return $mockNextRevision;
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => 2,
|
|
|
|
|
'getRevisionById' => 1,
|
|
|
|
|
'getNextRevision' => 1,
|
|
|
|
|
]
|
|
|
|
|
);
|
2021-03-08 04:01:32 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
2016-02-01 11:53:01 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockQueueGroup->method( 'lazyPush' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback(
|
2018-03-03 00:21:36 +00:00
|
|
|
function ( ActivityUpdateJob $job ) use ( $title, $user ) {
|
|
|
|
|
$this->verifyCallbackJob(
|
|
|
|
|
$job,
|
|
|
|
|
$title,
|
|
|
|
|
$user->getId(),
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( $time ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
return $time !== null && $time > '20151212010101';
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-28 18:08:20 +00:00
|
|
|
);
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
'force',
|
|
|
|
|
$oldid
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_notWatchedPageForced( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-04-20 13:51:26 +00:00
|
|
|
$oldid = 22;
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-04-20 13:51:26 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-04-20 13:51:26 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-04-20 13:51:26 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-04-20 13:51:26 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( false );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2018-04-12 20:18:39 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
2019-08-28 10:01:39 +00:00
|
|
|
$mockRevision = $this->createNoOpMock( RevisionRecord::class );
|
|
|
|
|
$mockNextRevision = $this->createNoOpMock( RevisionRecord::class );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup(
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => function ( $oldidParam ) use ( $oldid ) {
|
|
|
|
|
$this->assertSame( $oldid, $oldidParam );
|
|
|
|
|
},
|
|
|
|
|
'getRevisionById' => function ( $id ) use ( $oldid, $mockRevision ) {
|
|
|
|
|
$this->assertSame( $oldid, $id );
|
|
|
|
|
return $mockRevision;
|
|
|
|
|
},
|
|
|
|
|
'getNextRevision' =>
|
|
|
|
|
function ( RevisionRecord $rev ) use ( $mockRevision, $mockNextRevision ) {
|
|
|
|
|
$this->assertSame( $mockRevision, $rev );
|
|
|
|
|
return $mockNextRevision;
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => 1,
|
|
|
|
|
'getRevisionById' => 1,
|
|
|
|
|
'getNextRevision' => 1,
|
|
|
|
|
]
|
2016-04-20 13:51:26 +00:00
|
|
|
);
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
|
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockQueueGroup->method( 'lazyPush' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback(
|
2018-03-03 00:21:36 +00:00
|
|
|
function ( ActivityUpdateJob $job ) use ( $title, $user ) {
|
|
|
|
|
$this->verifyCallbackJob(
|
|
|
|
|
$job,
|
|
|
|
|
$title,
|
|
|
|
|
$user->getId(),
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( $time ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
return $time === null;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-28 18:08:20 +00:00
|
|
|
);
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
'force',
|
|
|
|
|
$oldid
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_futureNotificationTimestampForced( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-04-20 13:51:26 +00:00
|
|
|
$oldid = 22;
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-04-20 13:51:26 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-04-20 13:51:26 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-04-20 13:51:26 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
2020-02-24 19:55:37 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000'
|
2016-04-20 13:51:26 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '30151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2018-04-12 20:18:39 +00:00
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
2019-08-28 10:01:39 +00:00
|
|
|
$mockRevision = $this->createNoOpMock( RevisionRecord::class );
|
|
|
|
|
$mockNextRevision = $this->createNoOpMock( RevisionRecord::class );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup(
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => function ( $oldidParam ) use ( $oldid ) {
|
|
|
|
|
$this->assertEquals( $oldid, $oldidParam );
|
|
|
|
|
},
|
|
|
|
|
'getRevisionById' => function ( $id ) use ( $oldid, $mockRevision ) {
|
|
|
|
|
$this->assertSame( $oldid, $id );
|
|
|
|
|
return $mockRevision;
|
|
|
|
|
},
|
|
|
|
|
'getNextRevision' =>
|
|
|
|
|
function ( RevisionRecord $rev ) use ( $mockRevision, $mockNextRevision ) {
|
|
|
|
|
$this->assertSame( $mockRevision, $rev );
|
|
|
|
|
return $mockNextRevision;
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => 2,
|
|
|
|
|
'getRevisionById' => 1,
|
|
|
|
|
'getNextRevision' => 1,
|
|
|
|
|
]
|
2016-04-20 13:51:26 +00:00
|
|
|
);
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
|
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockQueueGroup->method( 'lazyPush' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback(
|
2018-03-03 00:21:36 +00:00
|
|
|
function ( ActivityUpdateJob $job ) use ( $title, $user ) {
|
|
|
|
|
$this->verifyCallbackJob(
|
|
|
|
|
$job,
|
|
|
|
|
$title,
|
|
|
|
|
$user->getId(),
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( $time ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
return $time === '30151212010101';
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-28 18:08:20 +00:00
|
|
|
);
|
2016-02-01 11:53:01 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
'force',
|
|
|
|
|
$oldid
|
|
|
|
|
)
|
|
|
|
|
);
|
2016-04-20 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_futureNotificationTimestampNotForced( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-04-20 13:51:26 +00:00
|
|
|
$oldid = 22;
|
2021-03-25 03:08:22 +00:00
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2020-02-24 19:55:37 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql = "wl_namespace = 0 AND wl_title = 'SomeDbKey'";
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnOnConsecutiveCalls( $makeListSql, $makeListSql );
|
2016-04-20 13:51:26 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2016-04-20 13:51:26 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2020-12-14 22:29:18 +00:00
|
|
|
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp', 'we_expiry' ],
|
2016-04-20 13:51:26 +00:00
|
|
|
[
|
|
|
|
|
'wl_user' => 1,
|
2020-12-14 22:29:18 +00:00
|
|
|
$makeListSql,
|
|
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000',
|
2016-04-20 13:51:26 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '30151212010101',
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
2022-01-12 11:39:41 +00:00
|
|
|
$mockCache->expects( $this->once() )->method( 'get' );
|
2018-04-12 20:18:39 +00:00
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
$mockQueueGroup = $this->getMockJobQueueGroup( false );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
2019-08-28 10:01:39 +00:00
|
|
|
$mockRevision = $this->createNoOpMock( RevisionRecord::class );
|
|
|
|
|
$mockNextRevision = $this->createNoOpMock( RevisionRecord::class );
|
2019-04-29 17:25:13 +00:00
|
|
|
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup(
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => function ( $oldidParam ) use ( $oldid ) {
|
|
|
|
|
$this->assertEquals( $oldid, $oldidParam );
|
|
|
|
|
},
|
|
|
|
|
'getRevisionById' => function ( $id ) use ( $oldid, $mockRevision ) {
|
|
|
|
|
$this->assertSame( $oldid, $id );
|
|
|
|
|
return $mockRevision;
|
|
|
|
|
},
|
|
|
|
|
'getNextRevision' =>
|
|
|
|
|
function ( RevisionRecord $rev ) use ( $mockRevision, $mockNextRevision ) {
|
|
|
|
|
$this->assertSame( $mockRevision, $rev );
|
|
|
|
|
return $mockNextRevision;
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => 2,
|
|
|
|
|
'getRevisionById' => 1,
|
|
|
|
|
'getNextRevision' => 1,
|
|
|
|
|
]
|
|
|
|
|
);
|
2021-03-08 04:01:32 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'db' => $mockDb,
|
|
|
|
|
'queueGroup' => $mockQueueGroup,
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
] );
|
2016-04-20 13:51:26 +00:00
|
|
|
|
2021-04-22 08:40:46 +00:00
|
|
|
$mockQueueGroup->method( 'lazyPush' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback(
|
2018-03-03 00:21:36 +00:00
|
|
|
function ( ActivityUpdateJob $job ) use ( $title, $user ) {
|
|
|
|
|
$this->verifyCallbackJob(
|
|
|
|
|
$job,
|
|
|
|
|
$title,
|
|
|
|
|
$user->getId(),
|
2021-02-07 13:10:36 +00:00
|
|
|
static function ( $time ) {
|
2018-03-03 00:21:36 +00:00
|
|
|
return $time === false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-04-28 18:08:20 +00:00
|
|
|
);
|
2016-04-20 13:51:26 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->resetNotificationTimestamp(
|
|
|
|
|
$user,
|
|
|
|
|
$title,
|
|
|
|
|
'',
|
|
|
|
|
$oldid
|
|
|
|
|
)
|
|
|
|
|
);
|
2016-02-01 11:53:01 +00:00
|
|
|
}
|
|
|
|
|
|
2016-05-04 15:49:27 +00:00
|
|
|
public function testSetNotificationTimestampsForUser_anonUser() {
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore();
|
2019-04-28 11:07:18 +00:00
|
|
|
$this->assertFalse( $store->setNotificationTimestampsForUser(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 0, 'AnonUser' ), '' ) );
|
2016-05-04 15:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testSetNotificationTimestampsForUser_allRows() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-05-04 15:49:27 +00:00
|
|
|
$timestamp = '20100101010101';
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore();
|
2016-05-04 15:49:27 +00:00
|
|
|
|
2019-01-16 21:51:54 +00:00
|
|
|
// Note: This does not actually assert the job is correct
|
|
|
|
|
$callableCallCounter = 0;
|
|
|
|
|
$mockCallback = function ( $callable ) use ( &$callableCallCounter ) {
|
|
|
|
|
$callableCallCounter++;
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsCallable( $callable );
|
2019-01-16 21:51:54 +00:00
|
|
|
};
|
|
|
|
|
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
|
|
|
|
|
|
2016-05-04 15:49:27 +00:00
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->setNotificationTimestampsForUser( $user, $timestamp )
|
|
|
|
|
);
|
2020-05-30 10:36:42 +00:00
|
|
|
$this->assertSame( 1, $callableCallCounter );
|
2016-05-04 15:49:27 +00:00
|
|
|
}
|
|
|
|
|
|
2016-12-16 19:31:24 +00:00
|
|
|
public function testSetNotificationTimestampsForUser_nullTimestamp() {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-12-16 19:31:24 +00:00
|
|
|
$timestamp = null;
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore();
|
2016-12-16 19:31:24 +00:00
|
|
|
|
2019-01-16 21:51:54 +00:00
|
|
|
// Note: This does not actually assert the job is correct
|
|
|
|
|
$callableCallCounter = 0;
|
|
|
|
|
$mockCallback = function ( $callable ) use ( &$callableCallCounter ) {
|
|
|
|
|
$callableCallCounter++;
|
2019-12-13 14:29:10 +00:00
|
|
|
$this->assertIsCallable( $callable );
|
2019-01-16 21:51:54 +00:00
|
|
|
};
|
|
|
|
|
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
|
|
|
|
|
|
2016-12-16 19:31:24 +00:00
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->setNotificationTimestampsForUser( $user, $timestamp )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testSetNotificationTimestampsForUser_specificTargets( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2016-05-04 15:49:27 +00:00
|
|
|
$timestamp = '20100101010101';
|
2021-03-25 03:08:22 +00:00
|
|
|
$targets = [ $testPageFactory( 100, 0, 'Foo' ), $testPageFactory( 101, 0, 'Bar' ) ];
|
2016-05-04 15:49:27 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-08-19 21:32:01 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'selectFieldValues' )
|
|
|
|
|
->with(
|
|
|
|
|
[ 'watchlist' ],
|
|
|
|
|
'wl_id',
|
|
|
|
|
[ 'wl_user' => 1, 'wl_namespace' => 0, 'wl_title' => [ 'Foo', 'Bar' ] ]
|
|
|
|
|
)
|
|
|
|
|
->willReturn( [ '2', '3' ] );
|
2016-05-04 15:49:27 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'update' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ],
|
2021-08-19 21:32:01 +00:00
|
|
|
[ 'wl_id' => [ 2, 3 ] ]
|
2016-05-04 15:49:27 +00:00
|
|
|
)
|
2021-04-22 07:42:28 +00:00
|
|
|
->willReturn( true );
|
|
|
|
|
$mockDb->expects( $this->once() )
|
2016-05-04 15:49:27 +00:00
|
|
|
->method( 'timestamp' )
|
2021-04-28 18:08:20 +00:00
|
|
|
->willReturnCallback( static function ( $value ) {
|
2016-05-04 15:49:27 +00:00
|
|
|
return 'TS' . $value . 'TS';
|
2021-04-28 18:08:20 +00:00
|
|
|
} );
|
2016-05-04 15:49:27 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2019-01-16 21:51:54 +00:00
|
|
|
->method( 'affectedRows' )
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( 2 );
|
2016-05-04 15:49:27 +00:00
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb ] );
|
2016-05-04 15:49:27 +00:00
|
|
|
|
|
|
|
|
$this->assertTrue(
|
|
|
|
|
$store->setNotificationTimestampsForUser( $user, $timestamp, $targets )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testUpdateNotificationTimestamp_watchersExist( $testPageFactory ) {
|
2016-02-05 12:14:13 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-06-17 19:08:11 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2016-02-05 12:14:13 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2021-08-19 21:32:01 +00:00
|
|
|
->method( 'select' )
|
2016-02-05 12:14:13 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2021-08-19 21:32:01 +00:00
|
|
|
[ 'wl_id', 'wl_user' ],
|
2016-02-05 12:14:13 +00:00
|
|
|
[
|
|
|
|
|
'wl_user != 1',
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
2021-08-19 21:32:01 +00:00
|
|
|
'wl_notificationtimestamp' => null,
|
2020-06-17 19:08:11 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000',
|
2016-02-05 12:14:13 +00:00
|
|
|
]
|
|
|
|
|
)
|
2021-08-19 21:32:01 +00:00
|
|
|
->willReturn( [
|
|
|
|
|
(object)[
|
|
|
|
|
'wl_id' => '4',
|
|
|
|
|
'wl_user' => '2',
|
|
|
|
|
],
|
|
|
|
|
(object)[
|
|
|
|
|
'wl_id' => '5',
|
|
|
|
|
'wl_user' => '3',
|
|
|
|
|
],
|
|
|
|
|
] );
|
2016-02-05 12:14:13 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'update' )
|
|
|
|
|
->with(
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_notificationtimestamp' => null ],
|
|
|
|
|
[
|
2021-08-19 21:32:01 +00:00
|
|
|
'wl_id' => [ 4, 5 ],
|
2016-02-05 12:14:13 +00:00
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-05 12:14:13 +00:00
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
// updateNotificationTimestamp calls DeferredUpdates::addCallableUpdate
|
|
|
|
|
// in normal operation, but we want to test that update actually running, so
|
|
|
|
|
// override it
|
|
|
|
|
$mockCallback = function ( $callable, $stage, $dbw ) use ( $mockDb ) {
|
|
|
|
|
$this->assertIsCallable( $callable );
|
|
|
|
|
$this->assertSame( DeferredUpdates::POSTSEND, $stage );
|
|
|
|
|
$this->assertSame( $mockDb, $dbw );
|
|
|
|
|
( $callable )();
|
|
|
|
|
};
|
|
|
|
|
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
|
|
|
|
|
|
2016-02-05 12:14:13 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
[ 2, 3 ],
|
|
|
|
|
$store->updateNotificationTimestamp(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
2016-02-05 12:14:13 +00:00
|
|
|
'20151212010101'
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testUpdateNotificationTimestamp_noWatchers( $testPageFactory ) {
|
2016-02-05 12:14:13 +00:00
|
|
|
$mockDb = $this->getMockDb();
|
2020-06-17 19:08:11 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
2016-02-05 12:14:13 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
2021-08-19 21:32:01 +00:00
|
|
|
->method( 'select' )
|
2016-02-05 12:14:13 +00:00
|
|
|
->with(
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist', 'watchlist_expiry' => 'watchlist_expiry' ],
|
2021-08-19 21:32:01 +00:00
|
|
|
[ 'wl_id', 'wl_user' ],
|
2016-02-05 12:14:13 +00:00
|
|
|
[
|
|
|
|
|
'wl_user != 1',
|
|
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
2021-08-19 21:32:01 +00:00
|
|
|
'wl_notificationtimestamp' => null,
|
2020-06-17 19:08:11 +00:00
|
|
|
'we_expiry IS NULL OR we_expiry > 20200101000000',
|
|
|
|
|
],
|
|
|
|
|
'WatchedItemStore::updateNotificationTimestamp',
|
|
|
|
|
[],
|
|
|
|
|
[ 'watchlist_expiry' => [ 'LEFT JOIN', 'wl_id = we_item' ] ]
|
2016-02-05 12:14:13 +00:00
|
|
|
)
|
2021-04-22 08:28:11 +00:00
|
|
|
->willReturn( [] );
|
2016-02-05 12:14:13 +00:00
|
|
|
$mockDb->expects( $this->never() )
|
|
|
|
|
->method( 'update' );
|
|
|
|
|
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'set' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'get' );
|
|
|
|
|
$mockCache->expects( $this->never() )->method( 'delete' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-02-05 12:14:13 +00:00
|
|
|
|
|
|
|
|
$watchers = $store->updateNotificationTimestamp(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2021-03-25 03:08:22 +00:00
|
|
|
$testPageFactory( 100, 0, 'SomeDbKey' ),
|
2016-02-05 12:14:13 +00:00
|
|
|
'20151212010101'
|
|
|
|
|
);
|
2020-02-28 15:45:22 +00:00
|
|
|
$this->assertSame( [], $watchers );
|
2016-02-05 12:14:13 +00:00
|
|
|
}
|
|
|
|
|
|
2021-03-25 03:08:22 +00:00
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testUpdateNotificationTimestamp_clearsCachedItems( $testPageFactory ) {
|
2021-02-15 18:58:09 +00:00
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
2021-03-25 03:08:22 +00:00
|
|
|
$titleValue = $testPageFactory( 100, 0, 'SomeDbKey' );
|
2016-03-10 13:43:36 +00:00
|
|
|
|
|
|
|
|
$mockDb = $this->getMockDb();
|
2021-08-19 21:32:01 +00:00
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
2020-12-14 22:29:18 +00:00
|
|
|
->method( 'select' )
|
2021-08-19 21:32:01 +00:00
|
|
|
->willReturnOnConsecutiveCalls( [
|
2021-01-15 09:15:59 +00:00
|
|
|
(object)[
|
2020-12-14 22:29:18 +00:00
|
|
|
'wl_namespace' => 0,
|
|
|
|
|
'wl_title' => 'SomeDbKey',
|
|
|
|
|
'wl_notificationtimestamp' => '20151212010101'
|
2021-01-15 09:15:59 +00:00
|
|
|
]
|
2021-08-19 21:32:01 +00:00
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
(object)[
|
|
|
|
|
'wl_id' => '4',
|
|
|
|
|
'wl_user' => '2',
|
|
|
|
|
],
|
|
|
|
|
(object)[
|
|
|
|
|
'wl_id' => '5',
|
|
|
|
|
'wl_user' => '3',
|
|
|
|
|
],
|
2021-04-22 08:28:11 +00:00
|
|
|
] );
|
2016-03-10 13:43:36 +00:00
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'update' );
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'set' )
|
|
|
|
|
->with( '0:SomeDbKey:1', $this->isType( 'object' ) );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
|
|
|
|
$mockCache->expects( $this->once() )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->with( '0:SomeDbKey:1' );
|
|
|
|
|
|
2019-04-29 17:25:13 +00:00
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
2016-03-10 13:43:36 +00:00
|
|
|
|
|
|
|
|
// This will add the item to the cache
|
|
|
|
|
$store->getWatchedItem( $user, $titleValue );
|
|
|
|
|
|
2021-03-21 20:00:41 +00:00
|
|
|
// updateNotificationTimestamp calls DeferredUpdates::addCallableUpdate
|
|
|
|
|
// in normal operation, but we want to test that update actually running, so
|
|
|
|
|
// override it
|
|
|
|
|
$mockCallback = function ( $callable, $stage, $dbw ) use ( $mockDb ) {
|
|
|
|
|
$this->assertIsCallable( $callable );
|
|
|
|
|
$this->assertSame( DeferredUpdates::POSTSEND, $stage );
|
|
|
|
|
$this->assertSame( $mockDb, $dbw );
|
|
|
|
|
( $callable )();
|
|
|
|
|
};
|
|
|
|
|
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
|
|
|
|
|
|
2016-03-10 13:43:36 +00:00
|
|
|
$store->updateNotificationTimestamp(
|
2021-02-15 18:58:09 +00:00
|
|
|
new UserIdentityValue( 1, 'MockUser' ),
|
2016-03-10 13:43:36 +00:00
|
|
|
$titleValue,
|
|
|
|
|
'20151212010101'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-14 05:55:07 +00:00
|
|
|
public function testRemoveExpired() {
|
|
|
|
|
$mockDb = $this->getMockDb();
|
|
|
|
|
|
|
|
|
|
// addQuotes is used for the expiry value.
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'addQuotes' )
|
|
|
|
|
->willReturn( '20200101000000' );
|
|
|
|
|
|
|
|
|
|
// Select watchlist IDs.
|
|
|
|
|
$mockDb->expects( $this->exactly( 2 ) )
|
|
|
|
|
->method( 'selectFieldValues' )
|
|
|
|
|
->withConsecutive(
|
|
|
|
|
// Select expired items.
|
|
|
|
|
[
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist_expiry' ],
|
2020-02-14 05:55:07 +00:00
|
|
|
'we_item',
|
|
|
|
|
[ 'we_expiry <= 20200101000000' ],
|
|
|
|
|
'WatchedItemStore::removeExpired',
|
|
|
|
|
[ 'LIMIT' => 2 ]
|
|
|
|
|
],
|
|
|
|
|
// Select orphaned items.
|
|
|
|
|
[
|
2022-08-12 11:20:33 +00:00
|
|
|
[ 'watchlist_expiry', 'watchlist' => 'watchlist' ],
|
2020-02-14 05:55:07 +00:00
|
|
|
'we_item',
|
|
|
|
|
[ 'wl_id' => null, 'we_expiry' => null ],
|
|
|
|
|
'WatchedItemStore::removeExpired',
|
|
|
|
|
[],
|
|
|
|
|
[ 'watchlist' => [ 'LEFT JOIN', 'wl_id = we_item' ] ]
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
->willReturnOnConsecutiveCalls(
|
|
|
|
|
[ 1, 2 ],
|
|
|
|
|
[ 3 ]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Return whatever is passed to makeList, to be tested below.
|
|
|
|
|
$mockDb->expects( $this->once() )
|
|
|
|
|
->method( 'makeList' )
|
|
|
|
|
->willReturnArgument( 0 );
|
|
|
|
|
|
|
|
|
|
// Delete from watchlist and watchlist_expiry.
|
|
|
|
|
$mockDb->expects( $this->exactly( 3 ) )
|
|
|
|
|
->method( 'delete' )
|
|
|
|
|
->withConsecutive(
|
|
|
|
|
// Delete expired items from watchlist
|
|
|
|
|
[
|
|
|
|
|
'watchlist',
|
|
|
|
|
[ 'wl_id' => [ 1, 2 ] ],
|
|
|
|
|
'WatchedItemStore::removeExpired'
|
|
|
|
|
],
|
|
|
|
|
// Delete expired items from watchlist_expiry
|
|
|
|
|
[
|
|
|
|
|
'watchlist_expiry',
|
|
|
|
|
[ 'we_item' => [ 1, 2 ] ],
|
|
|
|
|
'WatchedItemStore::removeExpired'
|
|
|
|
|
],
|
|
|
|
|
// Delete orphaned items
|
|
|
|
|
[
|
|
|
|
|
'watchlist_expiry',
|
|
|
|
|
[ 'we_item' => [ 3 ] ],
|
|
|
|
|
'WatchedItemStore::removeExpired'
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$store = $this->newWatchedItemStore( [ 'db' => $mockDb, 'cache' => $mockCache ] );
|
|
|
|
|
$store->removeExpired( 2, true );
|
|
|
|
|
}
|
2021-10-05 00:28:01 +00:00
|
|
|
|
|
|
|
|
public static function provideGetLatestNotificationTimestamp() {
|
|
|
|
|
$emptyMap = new MapCacheLRU( 300 );
|
|
|
|
|
$oldMap = new MapCacheLRU( 300 );
|
|
|
|
|
$oldMap->set( '0:Title', '20090101000000' );
|
|
|
|
|
$newMap = new MapCacheLRU( 300 );
|
|
|
|
|
$newMap->set( '0:Title', '20110101000000' );
|
2021-09-19 17:27:24 +00:00
|
|
|
$wrongKeyMap = new MapCacheLRU( 300 );
|
|
|
|
|
$wrongKeyMap->set( '0:Wrong', '20110101000000' );
|
2021-10-05 00:28:01 +00:00
|
|
|
// Arrays are used for stash values after T282105. We test forwards and
|
|
|
|
|
// backwards compatibility. The MapCacheLRU cases can be removed after
|
|
|
|
|
// deployment of T282105 has finished.
|
|
|
|
|
return [
|
|
|
|
|
'empty cache' => [
|
|
|
|
|
null,
|
|
|
|
|
true
|
|
|
|
|
],
|
|
|
|
|
'empty MapCacheLRU' => [
|
|
|
|
|
$emptyMap,
|
|
|
|
|
true
|
|
|
|
|
],
|
|
|
|
|
'empty array' => [
|
|
|
|
|
$emptyMap->toArray(),
|
|
|
|
|
true
|
|
|
|
|
],
|
|
|
|
|
'old MapCacheLRU' => [
|
|
|
|
|
$oldMap,
|
|
|
|
|
true,
|
|
|
|
|
],
|
|
|
|
|
'old array' => [
|
|
|
|
|
$oldMap->toArray(),
|
|
|
|
|
true
|
|
|
|
|
],
|
|
|
|
|
'new MapCacheLRU' => [
|
|
|
|
|
$newMap,
|
|
|
|
|
false
|
|
|
|
|
],
|
|
|
|
|
'new array' => [
|
2022-04-08 16:47:27 +00:00
|
|
|
$newMap->toArray(),
|
2021-10-05 00:28:01 +00:00
|
|
|
false
|
2021-09-19 17:27:24 +00:00
|
|
|
],
|
|
|
|
|
'wrong key MapCacheLRU' => [
|
|
|
|
|
$wrongKeyMap,
|
|
|
|
|
true
|
|
|
|
|
],
|
|
|
|
|
'wrong key array' => [
|
|
|
|
|
$wrongKeyMap->toArray(),
|
|
|
|
|
true
|
|
|
|
|
],
|
2021-10-05 00:28:01 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @dataProvider provideGetLatestNotificationTimestamp */
|
|
|
|
|
public function testGetLatestNotificationTimestamp( $cacheValue, $expectNonNull ) {
|
|
|
|
|
$user = new UserIdentityValue( 1, 'User' );
|
|
|
|
|
$title = new TitleValue( 0, 'Title' );
|
|
|
|
|
$stash = new HashBagOStuff;
|
|
|
|
|
$stash->set(
|
|
|
|
|
$stash->makeGlobalKey(
|
|
|
|
|
'watchlist-recent-updates',
|
2022-07-25 20:34:51 +00:00
|
|
|
'phpunitdb',
|
2021-10-05 00:28:01 +00:00
|
|
|
$user->getId()
|
|
|
|
|
),
|
|
|
|
|
$cacheValue
|
|
|
|
|
);
|
|
|
|
|
$store = $this->newWatchedItemStore( [ 'stash' => $stash ] );
|
|
|
|
|
$result = $store->getLatestNotificationTimestamp(
|
|
|
|
|
'20100101000000', $user, $title );
|
|
|
|
|
$this->assertSame( $expectNonNull, $result !== null );
|
|
|
|
|
}
|
2021-09-19 17:27:24 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideTestPageFactory
|
|
|
|
|
*/
|
|
|
|
|
public function testResetNotificationTimestamp_stashItemTypeCheck( $testPageFactory ) {
|
|
|
|
|
$user = new UserIdentityValue( 1, 'MockUser' );
|
|
|
|
|
$oldid = 22;
|
|
|
|
|
$title = $testPageFactory( 100, 0, 'SomeDbKey' );
|
|
|
|
|
$stash = new HashBagOStuff;
|
|
|
|
|
$mockCache = $this->getMockCache();
|
|
|
|
|
$mockRevision = $this->createNoOpMock( RevisionRecord::class );
|
|
|
|
|
$mockNextRevision = $this->createNoOpMock( RevisionRecord::class );
|
|
|
|
|
$mockRevisionLookup = $this->getMockRevisionLookup(
|
|
|
|
|
[
|
|
|
|
|
'getTimestampFromId' => static function () {
|
|
|
|
|
return '00000000000000';
|
|
|
|
|
},
|
|
|
|
|
'getRevisionByTitle' => static function () {
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
'getRevisionById' => static function ( $id ) use ( $mockRevision ) {
|
|
|
|
|
return $mockRevision;
|
|
|
|
|
},
|
|
|
|
|
'getNextRevision' => static function ( RevisionRecord $rev ) use ( $mockNextRevision ) {
|
|
|
|
|
return $mockNextRevision;
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
$store = $this->newWatchedItemStore( [
|
|
|
|
|
'cache' => $mockCache,
|
|
|
|
|
'revisionLookup' => $mockRevisionLookup,
|
|
|
|
|
'stash' => $stash,
|
|
|
|
|
'queueGroup' => $this->getMockJobQueueGroup( false ),
|
|
|
|
|
] );
|
|
|
|
|
$store->resetNotificationTimestamp( $user,
|
|
|
|
|
$title,
|
|
|
|
|
'force',
|
|
|
|
|
$oldid );
|
2022-07-25 20:34:51 +00:00
|
|
|
$this->assertIsArray( $stash->get( 'global:watchlist-recent-updates:phpunitdb:1' ) );
|
2021-09-19 17:27:24 +00:00
|
|
|
}
|
2016-02-01 11:53:01 +00:00
|
|
|
}
|