Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4091145
WatchedItemStoreUnitTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
106 KB
Referenced Files
None
Subscribers
None
WatchedItemStoreUnitTest.php
View Options
<?php
use
MediaWiki\Cache\GenderCache
;
use
MediaWiki\Cache\LinkBatchFactory
;
use
MediaWiki\Cache\LinkCache
;
use
MediaWiki\Config\ServiceOptions
;
use
MediaWiki\Deferred\DeferredUpdates
;
use
MediaWiki\Language\Language
;
use
MediaWiki\Linker\LinksMigration
;
use
MediaWiki\Linker\LinkTarget
;
use
MediaWiki\Logger\LoggerFactory
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\Page\PageIdentity
;
use
MediaWiki\Page\PageIdentityValue
;
use
MediaWiki\Revision\RevisionLookup
;
use
MediaWiki\Revision\RevisionRecord
;
use
MediaWiki\Tests\Unit\DummyServicesTrait
;
use
MediaWiki\Tests\Unit\Libs\Rdbms\AddQuoterMock
;
use
MediaWiki\Title\TitleFormatter
;
use
MediaWiki\Title\TitleValue
;
use
MediaWiki\User\UserIdentityValue
;
use
MediaWiki\Watchlist\ActivityUpdateJob
;
use
MediaWiki\Watchlist\WatchedItem
;
use
MediaWiki\Watchlist\WatchedItemStore
;
use
PHPUnit\Framework\MockObject\MockObject
;
use
Wikimedia\ObjectCache\HashBagOStuff
;
use
Wikimedia\Rdbms\DeleteQueryBuilder
;
use
Wikimedia\Rdbms\Expression
;
use
Wikimedia\Rdbms\FakeResultWrapper
;
use
Wikimedia\Rdbms\IDatabase
;
use
Wikimedia\Rdbms\IExpression
;
use
Wikimedia\Rdbms\InsertQueryBuilder
;
use
Wikimedia\Rdbms\LBFactory
;
use
Wikimedia\Rdbms\ReplaceQueryBuilder
;
use
Wikimedia\Rdbms\SelectQueryBuilder
;
use
Wikimedia\Rdbms\UpdateQueryBuilder
;
use
Wikimedia\Stats\StatsFactory
;
use
Wikimedia\TestingAccessWrapper
;
/**
* @author Addshore
* @author DannyS712
*
* @covers \MediaWiki\Watchlist\WatchedItemStore
*/
class
WatchedItemStoreUnitTest
extends
MediaWikiUnitTestCase
{
use
DummyServicesTrait
;
use
MockTitleTrait
;
/** @var StatsFactory */
private
$statsFactory
;
protected
function
setUp
():
void
{
parent
::
setUp
();
$this
->
statsFactory
=
StatsFactory
::
newNull
();
}
/**
* @return MockObject&IDatabase
*/
private
function
getMockDb
()
{
$mock
=
$this
->
createMock
(
IDatabase
::
class
);
$mock
->
method
(
'newSelectQueryBuilder'
)->
willReturnCallback
(
static
fn
()
=>
new
SelectQueryBuilder
(
$mock
)
);
$mock
->
method
(
'newUpdateQueryBuilder'
)->
willReturnCallback
(
static
fn
()
=>
new
UpdateQueryBuilder
(
$mock
)
);
$mock
->
method
(
'newDeleteQueryBuilder'
)->
willReturnCallback
(
static
fn
()
=>
new
DeleteQueryBuilder
(
$mock
)
);
$mock
->
method
(
'newReplaceQueryBuilder'
)->
willReturnCallback
(
static
fn
()
=>
new
ReplaceQueryBuilder
(
$mock
)
);
$mock
->
method
(
'newInsertQueryBuilder'
)->
willReturnCallback
(
static
fn
()
=>
new
InsertQueryBuilder
(
$mock
)
);
$mock
->
method
(
'expr'
)->
willReturnCallback
(
static
function
(
$field
,
$op
,
$value
)
{
return
new
Expression
(
$field
,
$op
,
$value
);
}
);
return
$mock
;
}
private
function
expandExpr
(
array
$conds
):
array
{
foreach
(
$conds
as
&
$cond
)
{
if
(
$cond
instanceof
IExpression
)
{
$cond
=
$cond
->
toSql
(
new
AddQuoterMock
()
);
}
}
return
$conds
;
}
/**
* @param IDatabase $mockDb
* @param string|null $expectedConnectionType
* @return MockObject&LBFactory
*/
private
function
getMockLBFactory
(
IDatabase
$mockDb
,
$expectedConnectionType
=
null
)
{
$mock
=
$this
->
createMock
(
LBFactory
::
class
);
$mock
->
method
(
'getLocalDomainID'
)
->
willReturn
(
'phpunitdb'
);
$mock
->
method
(
'getPrimaryDatabase'
)
->
willReturn
(
$mockDb
);
$mock
->
method
(
'getReplicaDatabase'
)
->
willReturn
(
$mockDb
);
$mock
->
method
(
'getLBsForOwner'
)
->
willReturn
(
[]
);
return
$mock
;
}
/**
* 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"
* @return MockObject|JobQueueGroup
*/
private
function
getMockJobQueueGroup
(
$mockLazyPush
=
true
)
{
$mock
=
$this
->
createMock
(
JobQueueGroup
::
class
);
$mock
->
method
(
'push'
)
->
willReturnCallback
(
static
function
(
Job
$job
)
{
$job
->
run
();
}
);
if
(
$mockLazyPush
)
{
$mock
->
method
(
'lazyPush'
)
->
willReturnCallback
(
static
function
(
Job
$job
)
{
$job
->
run
();
}
);
}
return
$mock
;
}
/**
* @return MockObject|HashBagOStuff
*/
private
function
getMockCache
()
{
$mock
=
$this
->
getMockBuilder
(
HashBagOStuff
::
class
)
->
disableOriginalConstructor
()
->
onlyMethods
(
[
'get'
,
'set'
,
'delete'
,
'makeKey'
]
)
->
getMock
();
$mock
->
method
(
'makeKey'
)
->
willReturnCallback
(
static
function
(
...
$args
)
{
return
implode
(
':'
,
$args
);
}
);
return
$mock
;
}
/**
* 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)
* @return MockObject|RevisionLookup
*/
private
function
getMockRevisionLookup
(
array
$callbacks
=
[],
array
$counts
=
[]
):
RevisionLookup
{
$mock
=
$this
->
createNoOpMock
(
RevisionLookup
::
class
,
array_keys
(
$callbacks
)
);
foreach
(
$callbacks
as
$method
=>
$callback
)
{
$count
=
isset
(
$counts
[
$method
]
)
?
$this
->
exactly
(
$counts
[
$method
]
)
:
$this
->
any
();
$mock
->
expects
(
$count
)
->
method
(
$method
)
->
willReturnCallback
(
$callbacks
[
$method
]
);
}
return
$mock
;
}
/**
* @param IDatabase $mockDb
* @return LinkBatchFactory
*/
private
function
getMockLinkBatchFactory
(
IDatabase
$mockDb
)
{
return
new
LinkBatchFactory
(
$this
->
createMock
(
LinkCache
::
class
),
$this
->
createMock
(
TitleFormatter
::
class
),
$this
->
createMock
(
Language
::
class
),
$this
->
createMock
(
GenderCache
::
class
),
$this
->
getMockLBFactory
(
$mockDb
),
$this
->
createMock
(
LinksMigration
::
class
),
LoggerFactory
::
getInstance
(
'LinkBatch'
)
);
}
/**
* @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
* * expiryEnabled
* * maxExpiryDuration
* * watchlistPurgeRate
* @return WatchedItemStore
*/
private
function
newWatchedItemStore
(
array
$mocks
=
[]
):
WatchedItemStore
{
$options
=
new
ServiceOptions
(
WatchedItemStore
::
CONSTRUCTOR_OPTIONS
,
[
MainConfigNames
::
UpdateRowsPerQuery
=>
1000
,
MainConfigNames
::
WatchlistExpiry
=>
$mocks
[
'expiryEnabled'
]
??
true
,
MainConfigNames
::
WatchlistExpiryMaxDuration
=>
$mocks
[
'maxExpiryDuration'
]
??
null
,
MainConfigNames
::
WatchlistPurgeRate
=>
$mocks
[
'watchlistPurgeRate'
]
??
0.1
,
]
);
$db
=
$mocks
[
'db'
]
??
$this
->
getMockDb
();
// If we don't use a manual mock for something specific, get a full
// NamespaceInfo service from DummyServicesTrait::getDummyNamespaceInfo
$nsInfo
=
$mocks
[
'nsInfo'
]
??
$this
->
getDummyNamespaceInfo
();
return
new
WatchedItemStore
(
$options
,
$mocks
[
'lbFactory'
]
??
$this
->
getMockLBFactory
(
$db
),
$mocks
[
'queueGroup'
]
??
$this
->
getMockJobQueueGroup
(),
$mocks
[
'stash'
]
??
new
HashBagOStuff
(),
$mocks
[
'cache'
]
??
$this
->
getMockCache
(),
$mocks
[
'readOnlyMode'
]
??
$this
->
getDummyReadOnlyMode
(
false
),
$nsInfo
,
$mocks
[
'revisionLookup'
]
??
$this
->
getMockRevisionLookup
(),
$this
->
getMockLinkBatchFactory
(
$db
),
$this
->
statsFactory
);
}
public
function
testClearWatchedItems
()
{
$user
=
new
UserIdentityValue
(
7
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectField'
)
->
with
(
[
'watchlist'
],
'COUNT(*)'
,
[
'wl_user'
=>
$user
->
getId
(),
],
$this
->
isType
(
'string'
)
)
->
willReturn
(
12
);
$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'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'expiryEnabled'
=>
false
,
]
);
TestingAccessWrapper
::
newFromObject
(
$store
)
->
cacheIndex
=
[
0
=>
[
'F'
=>
[
7
=>
'RM-KEY'
,
9
=>
'KEEP-KEY'
]
]
];
$this
->
assertTrue
(
$store
->
clearUserWatchedItems
(
$user
)
);
}
public
function
testClearWatchedItems_watchlistExpiry
()
{
$user
=
new
UserIdentityValue
(
7
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
// Select watchlist IDs.
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectFieldValues'
)
->
willReturn
(
[
1
,
2
]
);
$deleteArgs
=
[
[
'watchlist'
,
[
'wl_id'
=>
[
1
,
2
]
]
],
[
'watchlist_expiry'
,
[
'we_item'
=>
[
1
,
2
]
]
]
];
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'delete'
)
->
willReturnCallback
(
function
(
$table
,
$conds
)
use
(
&
$deleteArgs
):
void
{
[
$nextTable
,
$nextConds
]
=
array_shift
(
$deleteArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextConds
,
$conds
);
}
);
$mockDb
->
method
(
'timestamp'
)
->
willReturnArgument
(
0
);
$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
,
'expiryEnabled'
=>
true
,
]
);
TestingAccessWrapper
::
newFromObject
(
$store
)
->
cacheIndex
=
[
0
=>
[
'F'
=>
[
7
=>
'RM-KEY'
,
9
=>
'KEEP-KEY'
]
]
];
$this
->
assertTrue
(
$store
->
clearUserWatchedItems
(
$user
)
);
}
public
function
testClearWatchedItems_tooManyItemsWatched
()
{
$user
=
new
UserIdentityValue
(
7
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectField'
)
->
with
(
[
'watchlist'
],
'COUNT(*)'
,
[
'wl_user'
=>
$user
->
getId
(),
],
$this
->
isType
(
'string'
)
)
->
willReturn
(
99999
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'expiryEnabled'
=>
false
,
]
);
$this
->
assertFalse
(
$store
->
clearUserWatchedItems
(
$user
)
);
}
public
function
testCountWatchedItems
()
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
'COUNT(*)'
,
[
'wl_user'
=>
$user
->
getId
(),
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
'12'
,
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectField'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
12
,
$store
->
countWatchedItems
(
$user
)
);
}
public
function
provideTestPageFactory
()
{
yield
[
static
function
(
$pageId
,
$namespace
,
$dbKey
)
{
return
new
TitleValue
(
$namespace
,
$dbKey
);
}
];
yield
[
static
function
(
$pageId
,
$namespace
,
$dbKey
)
{
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'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
'COUNT(*)'
,
[
'wl_namespace'
=>
$titleValue
->
getNamespace
(),
'wl_title'
=>
$titleValue
->
getDBkey
(),
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
'7'
,
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectField'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
7
,
$store
->
countWatchers
(
$titleValue
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testCountWatchersMultiple
(
$testPageFactory
)
{
$titleValues
=
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
$testPageFactory
(
101
,
0
,
'OtherDbKey'
),
$testPageFactory
(
102
,
1
,
'AnotherDbKey'
),
];
$mockDb
=
$this
->
getMockDb
();
$dbResult
=
[
(
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'
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'makeWhereFrom2d'
)
->
with
(
[
[
'SomeDbKey'
=>
1
,
'OtherDbKey'
=>
1
],
[
'AnotherDbKey'
=>
1
]
],
$this
->
isType
(
'string'
),
$this
->
isType
(
'string'
)
)
->
willReturn
(
'makeWhereFrom2d return value'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_title'
,
'wl_namespace'
,
'watchers'
=>
'COUNT(*)'
],
[
'makeWhereFrom2d return value'
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
[
'GROUP BY'
=>
[
'wl_namespace'
,
'wl_title'
],
],
new
FakeResultWrapper
(
$dbResult
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$nextOptions
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
$this
->
assertSame
(
$nextOptions
,
$options
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$expected
=
[
0
=>
[
'SomeDbKey'
=>
100
,
'OtherDbKey'
=>
300
],
1
=>
[
'AnotherDbKey'
=>
500
],
];
$this
->
assertEquals
(
$expected
,
$store
->
countWatchersMultiple
(
$titleValues
)
);
}
public
static
function
provideIntWithDbUnsafeVersion
()
{
return
[
[
50
],
[
"50; DROP TABLE watchlist;
\n
--"
],
];
}
public
function
provideTestPageFactoryAndIntWithDbUnsafeVersion
()
{
foreach
(
$this
->
provideIntWithDbUnsafeVersion
()
as
$dbint
)
{
foreach
(
$this
->
provideTestPageFactory
()
as
$testPageFactory
)
{
yield
[
$dbint
[
0
],
$testPageFactory
[
0
]
];
}
}
}
/**
* @dataProvider provideTestPageFactoryAndIntWithDbUnsafeVersion
*/
public
function
testCountWatchersMultiple_withMinimumWatchers
(
$minWatchers
,
$testPageFactory
)
{
$titleValues
=
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
$testPageFactory
(
101
,
0
,
'OtherDbKey'
),
$testPageFactory
(
102
,
1
,
'AnotherDbKey'
),
];
$mockDb
=
$this
->
getMockDb
();
$dbResult
=
[
(
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'
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'makeWhereFrom2d'
)
->
with
(
[
[
'SomeDbKey'
=>
1
,
'OtherDbKey'
=>
1
],
[
'AnotherDbKey'
=>
1
]
],
$this
->
isType
(
'string'
),
$this
->
isType
(
'string'
)
)
->
willReturn
(
'makeWhereFrom2d return value'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_title'
,
'wl_namespace'
,
'watchers'
=>
'COUNT(*)'
],
[
'makeWhereFrom2d return value'
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
[
'GROUP BY'
=>
[
'wl_namespace'
,
'wl_title'
],
'HAVING'
=>
[
'COUNT(*) >= 50'
],
],
new
FakeResultWrapper
(
$dbResult
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$nextOptions
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
$this
->
assertSame
(
$nextOptions
,
$options
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$expected
=
[
0
=>
[
'SomeDbKey'
=>
100
,
'OtherDbKey'
=>
300
],
1
=>
[
'AnotherDbKey'
=>
500
],
];
$this
->
assertEquals
(
$expected
,
$store
->
countWatchersMultiple
(
$titleValues
,
[
'minimumWatchers'
=>
$minWatchers
]
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testCountVisitingWatchers
(
$testPageFactory
)
{
$titleValue
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
'COUNT(*)'
,
[
'wl_namespace'
=>
$titleValue
->
getNamespace
(),
'wl_title'
=>
$titleValue
->
getDBkey
(),
'(wl_notificationtimestamp >=
\'
TS111TS
\'
OR wl_notificationtimestamp IS NULL)'
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
'7'
,
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectField'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'timestamp'
)
->
willReturnCallback
(
static
function
(
$value
)
{
if
(
$value
===
0
)
{
return
'20200101000000'
;
}
return
'TS'
.
$value
.
'TS'
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
7
,
$store
->
countVisitingWatchers
(
$titleValue
,
'111'
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testCountVisitingWatchersMultiple
(
$testPageFactory
)
{
$titleValuesWithThresholds
=
[
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
'111'
],
[
$testPageFactory
(
101
,
0
,
'OtherDbKey'
),
'111'
],
[
$testPageFactory
(
102
,
1
,
'AnotherDbKey'
),
'123'
],
];
$dbResult
=
[
(
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'
],
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
exactly
(
4
)
)
->
method
(
'timestamp'
)
->
willReturnCallback
(
static
function
(
$value
)
{
if
(
$value
===
0
)
{
return
'20200101000000'
;
}
return
'TS'
.
$value
.
'TS'
;
}
);
$mockDb
->
method
(
'makeList'
)
->
with
(
$this
->
isType
(
'array'
),
$this
->
isType
(
'int'
)
)
->
willReturnCallback
(
static
function
(
$a
,
$conj
)
{
$sqlConj
=
$conj
===
LIST_AND
?
' AND '
:
' OR '
;
return
implode
(
$sqlConj
,
array_map
(
static
function
(
$s
)
{
if
(
$s
instanceof
IExpression
)
{
return
$s
->
toSql
(
new
AddQuoterMock
()
);
}
return
'('
.
$s
.
')'
;
},
$a
)
);
}
);
$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 ('
.
"((wl_title = 'AnotherDbKey' AND ("
.
"wl_notificationtimestamp >= 'TS123TS' OR wl_notificationtimestamp IS NULL"
.
')))))'
;
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'watchers'
=>
'COUNT(*)'
],
[
$expectedCond
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
[
'GROUP BY'
=>
[
'wl_namespace'
,
'wl_title'
],
],
new
FakeResultWrapper
(
$dbResult
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$nextOptions
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
$this
->
assertSame
(
$nextOptions
,
$options
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$expected
=
[
0
=>
[
'SomeDbKey'
=>
100
,
'OtherDbKey'
=>
300
],
1
=>
[
'AnotherDbKey'
=>
500
],
];
$this
->
assertEquals
(
$expected
,
$store
->
countVisitingWatchersMultiple
(
$titleValuesWithThresholds
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testCountVisitingWatchersMultiple_withMissingTargets
(
$testPageFactory
)
{
$titleValuesWithThresholds
=
[
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
'111'
],
[
$testPageFactory
(
101
,
0
,
'OtherDbKey'
),
'111'
],
[
$testPageFactory
(
102
,
1
,
'AnotherDbKey'
),
'123'
],
[
new
TitleValue
(
0
,
'SomeNotExisitingDbKey'
),
null
],
[
new
TitleValue
(
0
,
'OtherNotExisitingDbKey'
),
null
],
];
$dbResult
=
[
(
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'
],
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
exactly
(
3
)
)
->
method
(
'timestamp'
)
->
willReturnCallback
(
static
function
(
$value
)
{
return
'TS'
.
$value
.
'TS'
;
}
);
$mockDb
->
method
(
'makeList'
)
->
with
(
$this
->
isType
(
'array'
),
$this
->
isType
(
'int'
)
)
->
willReturnCallback
(
static
function
(
$a
,
$conj
)
{
$sqlConj
=
$conj
===
LIST_AND
?
' AND '
:
' OR '
;
return
implode
(
$sqlConj
,
array_map
(
static
function
(
$s
)
{
if
(
$s
instanceof
IExpression
)
{
return
$s
->
toSql
(
new
AddQuoterMock
()
);
}
return
'('
.
$s
.
')'
;
},
$a
)
);
}
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'makeWhereFrom2d'
)
->
with
(
[
[
'SomeNotExisitingDbKey'
=>
1
,
'OtherNotExisitingDbKey'
=>
1
]
],
$this
->
isType
(
'string'
),
$this
->
isType
(
'string'
)
)
->
willReturn
(
'makeWhereFrom2d return value'
);
$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 ('
.
"((wl_title = 'AnotherDbKey' AND ("
.
"wl_notificationtimestamp >= 'TS123TS' OR wl_notificationtimestamp IS NULL"
.
'))))'
.
') OR '
.
'(makeWhereFrom2d return value)'
;
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
],
[
'wl_namespace'
,
'wl_title'
,
'watchers'
=>
'COUNT(*)'
],
[
$expectedCond
],
$this
->
isType
(
'string'
),
[
'GROUP BY'
=>
[
'wl_namespace'
,
'wl_title'
],
]
)
->
willReturn
(
new
FakeResultWrapper
(
$dbResult
)
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'expiryEnabled'
=>
false
]
);
$expected
=
[
0
=>
[
'SomeDbKey'
=>
100
,
'OtherDbKey'
=>
300
,
'SomeNotExisitingDbKey'
=>
100
,
'OtherNotExisitingDbKey'
=>
200
],
1
=>
[
'AnotherDbKey'
=>
500
],
];
$this
->
assertEquals
(
$expected
,
$store
->
countVisitingWatchersMultiple
(
$titleValuesWithThresholds
)
);
}
/**
* @dataProvider provideTestPageFactoryAndIntWithDbUnsafeVersion
*/
public
function
testCountVisitingWatchersMultiple_withMinimumWatchers
(
$minWatchers
,
$testPageFactory
)
{
$titleValuesWithThresholds
=
[
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
'111'
],
[
$testPageFactory
(
101
,
0
,
'OtherDbKey'
),
'111'
],
[
$testPageFactory
(
102
,
1
,
'AnotherDbKey'
),
'123'
],
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
method
(
'makeList'
)
->
willReturn
(
'makeList return value'
);
$mockDb
->
method
(
'timestamp'
)
->
willReturnArgument
(
0
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
],
[
'wl_namespace'
,
'wl_title'
,
'watchers'
=>
'COUNT(*)'
],
[
'makeList return value'
],
$this
->
isType
(
'string'
),
[
'GROUP BY'
=>
[
'wl_namespace'
,
'wl_title'
],
'HAVING'
=>
[
'COUNT(*) >= 50'
],
]
)
->
willReturn
(
new
FakeResultWrapper
(
[]
)
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'expiryEnabled'
=>
false
,
]
);
$expected
=
[
0
=>
[
'SomeDbKey'
=>
0
,
'OtherDbKey'
=>
0
],
1
=>
[
'AnotherDbKey'
=>
0
],
];
$this
->
assertEquals
(
$expected
,
$store
->
countVisitingWatchersMultiple
(
$titleValuesWithThresholds
,
$minWatchers
)
);
}
public
function
testCountUnreadNotifications
()
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectRowCount'
)
->
with
(
[
'watchlist'
],
'1'
,
[
"wl_notificationtimestamp IS NOT NULL"
,
'wl_user'
=>
1
,
],
$this
->
isType
(
'string'
)
)
->
willReturn
(
9
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
9
,
$store
->
countUnreadNotifications
(
$user
)
);
}
/**
* @dataProvider provideIntWithDbUnsafeVersion
*/
public
function
testCountUnreadNotifications_withUnreadLimit_overLimit
(
$limit
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectRowCount'
)
->
with
(
[
'watchlist'
],
'1'
,
[
"wl_notificationtimestamp IS NOT NULL"
,
'wl_user'
=>
1
,
],
$this
->
isType
(
'string'
),
[
'LIMIT'
=>
50
]
)
->
willReturn
(
50
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertSame
(
true
,
$store
->
countUnreadNotifications
(
$user
,
$limit
)
);
}
/**
* @dataProvider provideIntWithDbUnsafeVersion
*/
public
function
testCountUnreadNotifications_withUnreadLimit_underLimit
(
$limit
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectRowCount'
)
->
with
(
[
'watchlist'
],
'1'
,
[
"wl_notificationtimestamp IS NOT NULL"
,
'wl_user'
=>
1
,
],
$this
->
isType
(
'string'
),
[
'LIMIT'
=>
50
]
)
->
willReturn
(
9
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
9
,
$store
->
countUnreadNotifications
(
$user
,
$limit
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testDuplicateEntry_nothingToDuplicate
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_user'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_namespace'
=>
0
,
'wl_title'
=>
'Old_Title'
,
],
'MediaWiki
\W
atchlist
\W
atchedItemStore::fetchWatchedItemsForPage'
,
[
'FOR UPDATE'
],
[
'watchlist_expiry'
=>
[
'LEFT JOIN'
,
[
'wl_id = we_item'
]
]
]
)
->
willReturn
(
new
FakeResultWrapper
(
[]
)
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
]
);
$store
->
duplicateEntry
(
$testPageFactory
(
100
,
0
,
'Old_Title'
),
$testPageFactory
(
101
,
0
,
'New_Title'
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testDuplicateEntry_somethingToDuplicate
(
$testPageFactory
)
{
$fakeRows
=
[
(
object
)[
'wl_user'
=>
'1'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
],
(
object
)[
'wl_user'
=>
'2'
,
'wl_notificationtimestamp'
=>
null
,
],
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
],
[
'wl_user'
,
'wl_notificationtimestamp'
],
[
'wl_namespace'
=>
0
,
'wl_title'
=>
'Old_Title'
,
]
)
->
willReturn
(
new
FakeResultWrapper
(
$fakeRows
),
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'expiryEnabled'
=>
false
,
]
);
$store
->
duplicateEntry
(
$testPageFactory
(
100
,
0
,
'Old_Title'
),
$testPageFactory
(
101
,
0
,
'New_Title'
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testDuplicateAllAssociatedEntries_nothingToDuplicate
(
$testPageFactory
)
{
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_user'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_namespace'
=>
0
,
'wl_title'
=>
'Old_Title'
,
]
],
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_user'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_namespace'
=>
1
,
'wl_title'
=>
'Old_Title'
,
]
]
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$conds
);
return
new
FakeResultWrapper
(
[]
);
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$store
->
duplicateAllAssociatedEntries
(
$testPageFactory
(
100
,
0
,
'Old_Title'
),
$testPageFactory
(
101
,
0
,
'New_Title'
)
);
}
public
function
provideLinkTargetPairs
()
{
foreach
(
$this
->
provideTestPageFactory
()
as
$testPageFactoryArray
)
{
$testPageFactory
=
$testPageFactoryArray
[
0
];
yield
[
$testPageFactory
(
100
,
0
,
'Old_Title'
),
$testPageFactory
(
101
,
0
,
'New_Title'
)
];
}
}
/**
* @param LinkTarget|PageIdentity $oldTarget
* @param LinkTarget|PageIdentity $newTarget
* @dataProvider provideLinkTargetPairs
*/
public
function
testDuplicateAllAssociatedEntries_somethingToDuplicate
(
$oldTarget
,
$newTarget
)
{
$fakeRows
=
[
(
object
)[
'wl_user'
=>
'1'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
'we_expiry'
=>
null
,
],
];
$selectArgs
=
[
[
[
'watchlist'
],
[
'wl_user'
,
'wl_notificationtimestamp'
],
[
'wl_namespace'
=>
$oldTarget
->
getNamespace
(),
'wl_title'
=>
$oldTarget
->
getDBkey
(),
]
],
[
[
'watchlist'
],
[
'wl_user'
,
'wl_notificationtimestamp'
],
[
'wl_namespace'
=>
$oldTarget
->
getNamespace
()
+
1
,
'wl_title'
=>
$oldTarget
->
getDBkey
(),
]
]
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
)
use
(
&
$selectArgs
,
$fakeRows
)
{
[
$nextTable
,
$nextVars
,
$nextConds
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$conds
);
return
new
FakeResultWrapper
(
$fakeRows
);
}
);
$replaceArgs
=
[
[
'watchlist'
,
[
[
'wl_user'
,
'wl_namespace'
,
'wl_title'
]
],
[
[
'wl_user'
=>
'1'
,
'wl_namespace'
=>
$newTarget
->
getNamespace
(),
'wl_title'
=>
$newTarget
->
getDBkey
(),
'wl_notificationtimestamp'
=>
'20151212010101'
,
],
],
],
[
'watchlist'
,
[
[
'wl_user'
,
'wl_namespace'
,
'wl_title'
]
],
[
[
'wl_user'
=>
'1'
,
'wl_namespace'
=>
$newTarget
->
getNamespace
()
+
1
,
'wl_title'
=>
$newTarget
->
getDBkey
(),
'wl_notificationtimestamp'
=>
'20151212010101'
,
],
],
],
];
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'replace'
)
->
willReturnCallback
(
function
(
$table
,
$uniqueKeys
,
$rows
)
use
(
&
$replaceArgs
):
void
{
[
$nextTable
,
$nextUniqueKeys
,
$nextRows
]
=
array_shift
(
$replaceArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextUniqueKeys
,
$uniqueKeys
);
$this
->
assertSame
(
$nextRows
,
$rows
);
}
);
$mockDb
->
method
(
'newSelectQueryBuilder'
)
->
willReturn
(
new
SelectQueryBuilder
(
$mockDb
)
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'expiryEnabled'
=>
false
,
]
);
$store
->
duplicateAllAssociatedEntries
(
$oldTarget
,
$newTarget
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testAddWatch_nonAnonymousUser
(
$testPageFactory
)
{
$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'
);
$mockDb
->
method
(
'select'
)->
willReturn
(
new
FakeResultWrapper
(
[]
)
);
$mockDb
->
method
(
'timestamp'
)
->
willReturnArgument
(
0
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$store
->
addWatch
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'Some_Page'
)
);
$this
->
assertSame
(
1
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_cache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testAddWatch_anonymousUser
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'insert'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)
->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$store
->
addWatch
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$testPageFactory
(
100
,
0
,
'Some_Page'
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_cache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testAddWatchBatchForUser_readOnlyDBReturnsFalse
(
$testPageFactory
)
{
$store
=
$this
->
newWatchedItemStore
(
[
'readOnlyMode'
=>
$this
->
getDummyReadOnlyMode
(
true
)
]
);
$this
->
assertFalse
(
$store
->
addWatchBatchForUser
(
new
UserIdentityValue
(
1
,
'MockUser'
),
[
$testPageFactory
(
100
,
0
,
'Some_Page'
),
$testPageFactory
(
101
,
1
,
'Some_Page'
)
]
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testAddWatchBatchForUser_nonAnonymousUser
(
$testPageFactory
)
{
$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
,
]
]
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'affectedRows'
)
->
willReturn
(
2
);
$cacheKeys
=
[
'0:Some_Page:1'
,
'1:Some_Page:1'
];
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'delete'
)
->
with
(
$this
->
callback
(
static
function
(
$key
)
use
(
&
$cacheKeys
)
{
$nextKey
=
array_shift
(
$cacheKeys
);
return
$nextKey
===
$key
;
}
)
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$mockUser
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$this
->
assertTrue
(
$store
->
addWatchBatchForUser
(
$mockUser
,
[
$testPageFactory
(
100
,
0
,
'Some_Page'
),
$testPageFactory
(
101
,
1
,
'Some_Page'
)
]
)
);
$this
->
assertSame
(
2
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testAddWatchBatchForUser_anonymousUsersAreSkipped
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'insert'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)
->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
addWatchBatchForUser
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
[
$testPageFactory
(
100
,
0
,
'Other_Page'
)
]
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
}
public
function
testAddWatchBatchReturnsTrue_whenGivenEmptyList
()
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'insert'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)
->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertTrue
(
$store
->
addWatchBatchForUser
(
$user
,
[]
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testLoadWatchedItem_existingItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
'we_expiry'
=>
'20300101000000'
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'set'
)
->
with
(
'0:SomeDbKey:1'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$watchedItem
=
$store
->
loadWatchedItem
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
);
$this
->
assertInstanceOf
(
WatchedItem
::
class
,
$watchedItem
);
$this
->
assertSame
(
1
,
$watchedItem
->
getUserIdentity
()->
getId
()
);
$this
->
assertEquals
(
'SomeDbKey'
,
$watchedItem
->
getTarget
()->
getDBkey
()
);
$this
->
assertSame
(
'20300101000000'
,
$watchedItem
->
getExpiry
()
);
$this
->
assertSame
(
0
,
$watchedItem
->
getTarget
()->
getNamespace
()
);
$this
->
assertSame
(
1
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_cache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testLoadWatchedItem_noItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturn
(
new
FakeResultWrapper
(
[]
)
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
loadWatchedItem
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_cache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testLoadWatchedItem_anonymousUser
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'select'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
loadWatchedItem
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_cache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testRemoveWatch_existingItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectFieldValues'
)
->
willReturn
(
[
1
,
2
]
);
$deleteArgs
=
[
[
'watchlist'
,
[
'wl_id'
=>
[
1
,
2
]
]
],
[
'watchlist_expiry'
,
[
'we_item'
=>
[
1
,
2
]
]
]
];
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'delete'
)
->
willReturnCallback
(
function
(
$table
,
$conds
)
use
(
&
$deleteArgs
):
void
{
[
$nextTable
,
$nextConds
]
=
array_shift
(
$deleteArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextConds
,
$conds
);
}
);
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'affectedRows'
)
->
willReturn
(
2
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'delete'
)
->
with
(
'0:SomeDbKey:1'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertTrue
(
$store
->
removeWatch
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
1
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testRemoveWatch_noItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectFieldValues'
)
->
willReturn
(
[]
);
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'delete'
);
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'affectedRows'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'delete'
)
->
with
(
'0:SomeDbKey:1'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
removeWatch
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
1
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testRemoveWatch_anonymousUser
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'delete'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)
->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
removeWatch
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_uncache_total'
)->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetWatchedItem_existingItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
'we_expiry'
=>
'20300101000000'
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'get'
)
->
with
(
'0:SomeDbKey:1'
)
->
willReturn
(
null
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'set'
)
->
with
(
'0:SomeDbKey:1'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$watchedItem
=
$store
->
getWatchedItem
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
);
$this
->
assertInstanceOf
(
WatchedItem
::
class
,
$watchedItem
);
$this
->
assertSame
(
1
,
$watchedItem
->
getUserIdentity
()->
getId
()
);
$this
->
assertEquals
(
'SomeDbKey'
,
$watchedItem
->
getTarget
()->
getDBkey
()
);
$this
->
assertSame
(
'20300101000000'
,
$watchedItem
->
getExpiry
()
);
$this
->
assertSame
(
0
,
$watchedItem
->
getTarget
()->
getNamespace
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetWatchedItem_cachedItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'selectRow'
);
$mockUser
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$linkTarget
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$cachedItem
=
new
WatchedItem
(
$mockUser
,
$linkTarget
,
'20151212010101'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'get'
)
->
with
(
'0:SomeDbKey:1'
)
->
willReturn
(
$cachedItem
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
$cachedItem
,
$store
->
getWatchedItem
(
$mockUser
,
$linkTarget
)
);
$this
->
assertSame
(
1
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_getWatchedItem_accesses_total'
)
->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetWatchedItem_noItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'get'
)
->
with
(
'0:SomeDbKey:1'
)
->
willReturn
(
false
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
getWatchedItem
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
1
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_getWatchedItem_accesses_total'
)
->
getSampleCount
()
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetWatchedItem_anonymousUser
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'selectRow'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
getWatchedItem
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
$this
->
assertSame
(
0
,
$this
->
statsFactory
->
getCounter
(
'WatchedItemStore_getWatchedItem_accesses_total'
)
->
getSampleCount
()
);
}
public
function
testGetWatchedItemsForUser
()
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'Foo1'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
'we_expiry'
=>
'20300101000000'
],
(
object
)[
'wl_namespace'
=>
1
,
'wl_title'
=>
'Foo2'
,
'wl_notificationtimestamp'
=>
null
,
],
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$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
]
);
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$watchedItems
=
$store
->
getWatchedItemsForUser
(
$user
);
$this
->
assertIsArray
(
$watchedItems
);
$this
->
assertCount
(
2
,
$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
,
'Foo2'
),
null
),
$watchedItems
[
1
]
);
}
public
static
function
provideDbTypes
()
{
return
[
[
false
,
DB_REPLICA
],
[
true
,
DB_PRIMARY
],
];
}
/**
* @dataProvider provideDbTypes
*/
public
function
testGetWatchedItemsForUser_optionsAndEmptyResult
(
$forWrite
,
$dbType
)
{
$mockDb
=
$this
->
getMockDb
();
$mockCache
=
$this
->
getMockCache
();
$mockLoadBalancer
=
$this
->
getMockLBFactory
(
$mockDb
,
$dbType
);
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
[
'ORDER BY'
=>
[
'wl_namespace ASC'
,
'wl_title ASC'
]
],
new
FakeResultWrapper
(
[]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$nextOptions
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
$this
->
assertSame
(
$nextOptions
,
$options
);
return
$returnValue
;
}
);
$store
=
$this
->
newWatchedItemStore
(
[
'lbFactory'
=>
$mockLoadBalancer
,
'cache'
=>
$mockCache
]
);
$watchedItems
=
$store
->
getWatchedItemsForUser
(
$user
,
[
'forWrite'
=>
$forWrite
,
'sort'
=>
WatchedItemStore
::
SORT_ASC
]
);
$this
->
assertEquals
(
[],
$watchedItems
);
}
public
function
testGetWatchedItemsForUser_sortByExpiry
()
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'wl_has_expiry'
=>
null
,
'we_expiry'
,
],
[
'wl_user'
=>
1
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'Foo1'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
'we_expiry'
=>
'20300101000000'
],
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'Foo2'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
'we_expiry'
=>
'20300701000000'
],
(
object
)[
'wl_namespace'
=>
1
,
'wl_title'
=>
'Foo3'
,
'wl_notificationtimestamp'
=>
null
,
],
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$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
]
);
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$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
]
);
}
public
function
testGetWatchedItemsForUser_badSortOptionThrowsException
()
{
$store
=
$this
->
newWatchedItemStore
();
$this
->
expectException
(
InvalidArgumentException
::
class
);
$store
->
getWatchedItemsForUser
(
new
UserIdentityValue
(
1
,
'MockUser'
),
[
'sort'
=>
'foo'
]
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testIsWatchedItem_existingItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'get'
)
->
with
(
'0:SomeDbKey:1'
)
->
willReturn
(
false
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'set'
)
->
with
(
'0:SomeDbKey:1'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertTrue
(
$store
->
isWatched
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testIsWatchedItem_noItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'get'
)
->
with
(
'0:SomeDbKey:1'
)
->
willReturn
(
false
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
isWatched
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testIsWatchedItem_anonymousUser
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'selectRow'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
isWatched
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetNotificationTimestampsBatch
(
$testPageFactory
)
{
$targets
=
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
$testPageFactory
(
101
,
1
,
'AnotherDbKey'
),
];
$mockDb
=
$this
->
getMockDb
();
$dbResult
=
[
(
object
)[
'wl_namespace'
=>
'0'
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
],
(
object
)[
'wl_namespace'
=>
'1'
,
'wl_title'
=>
'AnotherDbKey'
,
'wl_notificationtimestamp'
=>
null
,
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'makeWhereFrom2d'
)
->
with
(
[
[
'SomeDbKey'
=>
1
],
[
'AnotherDbKey'
=>
1
]
],
$this
->
isType
(
'string'
),
$this
->
isType
(
'string'
)
)
->
willReturn
(
'makeWhereFrom2d return value'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
],
[
'makeWhereFrom2d return value'
,
'wl_user'
=>
1
],
$this
->
isType
(
'string'
)
)
->
willReturn
(
new
FakeResultWrapper
(
$dbResult
)
);
$cacheKeys
=
[
'0:SomeDbKey:1'
,
'1:AnotherDbKey:1'
];
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'get'
)
->
willReturnCallback
(
function
(
$key
)
use
(
&
$cacheKeys
)
{
$nextKey
=
array_shift
(
$cacheKeys
);
$this
->
assertSame
(
$nextKey
,
$key
);
return
null
;
}
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
[
0
=>
[
'SomeDbKey'
=>
'20151212010101'
,
],
1
=>
[
'AnotherDbKey'
=>
null
,
],
],
$store
->
getNotificationTimestampsBatch
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$targets
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetNotificationTimestampsBatch_notWatchedTarget
(
$testPageFactory
)
{
$targets
=
[
$testPageFactory
(
100
,
0
,
'OtherDbKey'
),
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'makeWhereFrom2d'
)
->
with
(
[
[
'OtherDbKey'
=>
1
]
],
$this
->
isType
(
'string'
),
$this
->
isType
(
'string'
)
)
->
willReturn
(
'makeWhereFrom2d return value'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
],
[
'makeWhereFrom2d return value'
,
'wl_user'
=>
1
],
$this
->
isType
(
'string'
)
)
->
willReturn
(
new
FakeResultWrapper
(
[]
)
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'get'
)
->
with
(
'0:OtherDbKey:1'
)
->
willReturn
(
null
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
[
0
=>
[
'OtherDbKey'
=>
false
,
],
],
$store
->
getNotificationTimestampsBatch
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$targets
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetNotificationTimestampsBatch_cachedItem
(
$testPageFactory
)
{
$targets
=
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
$testPageFactory
(
101
,
1
,
'AnotherDbKey'
),
];
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$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'
)
)
->
willReturn
(
'makeWhereFrom2d return value'
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
with
(
[
'watchlist'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
],
[
'makeWhereFrom2d return value'
,
'wl_user'
=>
1
],
$this
->
isType
(
'string'
)
)
->
willReturn
(
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
'1'
,
'wl_title'
=>
'AnotherDbKey'
,
'wl_notificationtimestamp'
=>
null
,
]
]
)
);
$cacheKeys
=
[
[
'0:SomeDbKey:1'
,
$cachedItem
],
[
'1:AnotherDbKey:1'
,
null
]
];
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'get'
)
->
willReturnCallback
(
function
(
$key
)
use
(
&
$cacheKeys
)
{
[
$nextKey
,
$returnValue
]
=
array_shift
(
$cacheKeys
);
$this
->
assertSame
(
$nextKey
,
$key
);
return
$returnValue
;
}
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
[
0
=>
[
'SomeDbKey'
=>
'20151212010101'
,
],
1
=>
[
'AnotherDbKey'
=>
null
,
],
],
$store
->
getNotificationTimestampsBatch
(
$user
,
$targets
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetNotificationTimestampsBatch_allItemsCached
(
$testPageFactory
)
{
$targets
=
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
$testPageFactory
(
101
,
1
,
'AnotherDbKey'
),
];
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$cachedItems
=
[
new
WatchedItem
(
$user
,
$targets
[
0
],
'20151212010101'
),
new
WatchedItem
(
$user
,
$targets
[
1
],
null
),
];
$mockDb
=
$this
->
createNoOpMock
(
IDatabase
::
class
);
$cacheKeys
=
[
[
'0:SomeDbKey:1'
,
$cachedItems
[
0
]
],
[
'1:AnotherDbKey:1'
,
$cachedItems
[
1
]
],
];
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'get'
)
->
willReturnCallback
(
function
(
$key
)
use
(
&
$cacheKeys
)
{
[
$nextKey
,
$returnValue
]
=
array_shift
(
$cacheKeys
);
$this
->
assertSame
(
$nextKey
,
$key
);
return
$returnValue
;
}
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
[
0
=>
[
'SomeDbKey'
=>
'20151212010101'
,
],
1
=>
[
'AnotherDbKey'
=>
null
,
],
],
$store
->
getNotificationTimestampsBatch
(
$user
,
$targets
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testGetNotificationTimestampsBatch_anonymousUser
(
$testPageFactory
)
{
$targets
=
[
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
$testPageFactory
(
101
,
1
,
'AnotherDbKey'
),
];
$mockDb
=
$this
->
createNoOpMock
(
IDatabase
::
class
);
$mockCache
=
$this
->
createNoOpMock
(
HashBagOStuff
::
class
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertEquals
(
[
0
=>
[
'SomeDbKey'
=>
false
,
],
1
=>
[
'AnotherDbKey'
=>
false
,
],
],
$store
->
getNotificationTimestampsBatch
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$targets
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_anonymousUser
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'selectRow'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$this
->
assertFalse
(
$store
->
resetNotificationTimestamp
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
)
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_noItem
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
once
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
]
);
$this
->
assertFalse
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_item
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
once
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'set'
)
->
with
(
'0:SomeDbKey:1'
,
$this
->
isInstanceOf
(
WatchedItem
::
class
)
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'delete'
)
->
with
(
'0:SomeDbKey:1'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
$mockQueueGroup
->
expects
(
$this
->
once
()
)
->
method
(
'lazyPush'
)
->
willReturnCallback
(
static
function
(
ActivityUpdateJob
$job
)
{
// don't run
}
);
// We don't care if these methods actually do anything here
$mockRevisionLookup
=
$this
->
getMockRevisionLookup
(
[
'getRevisionByTitle'
=>
static
function
()
{
return
null
;
},
'getTimestampFromId'
=>
static
function
()
{
return
'00000000000000'
;
},
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_noItemForced
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'selectRow'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'delete'
)
->
with
(
'0:SomeDbKey:1'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
// We don't care if these methods actually do anything here
$mockRevisionLookup
=
$this
->
getMockRevisionLookup
(
[
'getRevisionByTitle'
=>
static
function
()
{
return
null
;
},
'getTimestampFromId'
=>
static
function
()
{
return
'00000000000000'
;
},
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$mockQueueGroup
->
method
(
'lazyPush'
)
->
willReturnCallback
(
static
function
(
ActivityUpdateJob
$job
)
{
// don't run
}
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
'force'
)
);
}
/**
* @param ActivityUpdateJob $job
* @param LinkTarget|PageIdentity $expectedTitle
* @param string $expectedUserId
* @param callable $notificationTimestampCondition
*/
private
function
verifyCallbackJob
(
ActivityUpdateJob
$job
,
$expectedTitle
,
$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'
]
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$oldid
=
22
;
$title
=
$testPageFactory
(
100
,
0
,
'SomeTitle'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'selectRow'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'delete'
)
->
with
(
'0:SomeTitle:1'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
$mockRevisionRecord
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$mockRevisionLookup
=
$this
->
getMockRevisionLookup
(
[
'getTimestampFromId'
=>
static
function
()
{
return
'00000000000000'
;
},
'getRevisionById'
=>
function
(
$id
,
$flags
)
use
(
$oldid
,
$mockRevisionRecord
)
{
$this
->
assertSame
(
$oldid
,
$id
);
$this
->
assertSame
(
0
,
$flags
);
return
$mockRevisionRecord
;
},
'getNextRevision'
=>
function
(
$oldRev
)
use
(
$mockRevisionRecord
)
{
$this
->
assertSame
(
$mockRevisionRecord
,
$oldRev
);
return
false
;
},
],
[
'getNextRevision'
=>
1
,
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$mockQueueGroup
->
method
(
'lazyPush'
)
->
willReturnCallback
(
function
(
ActivityUpdateJob
$job
)
use
(
$title
,
$user
)
{
$this
->
verifyCallbackJob
(
$job
,
$title
,
$user
->
getId
(),
static
function
(
$time
)
{
return
$time
===
null
;
}
);
}
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
'force'
,
$oldid
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$oldid
=
22
;
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$mockNextRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
,
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$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'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
$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
,
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$mockQueueGroup
->
method
(
'lazyPush'
)
->
willReturnCallback
(
function
(
ActivityUpdateJob
$job
)
use
(
$title
,
$user
)
{
$this
->
verifyCallbackJob
(
$job
,
$title
,
$user
->
getId
(),
static
function
(
$time
)
{
return
$time
!==
null
&&
$time
>
'20151212010101'
;
}
);
}
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
'force'
,
$oldid
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_notWatchedPageForced
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$oldid
=
22
;
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
once
()
)
->
method
(
'delete'
)
->
with
(
'0:SomeDbKey:1'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
$mockRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$mockNextRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$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
,
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$mockQueueGroup
->
method
(
'lazyPush'
)
->
willReturnCallback
(
function
(
ActivityUpdateJob
$job
)
use
(
$title
,
$user
)
{
$this
->
verifyCallbackJob
(
$job
,
$title
,
$user
->
getId
(),
static
function
(
$time
)
{
return
$time
===
null
;
}
);
}
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
'force'
,
$oldid
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_futureNotificationTimestampForced
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$oldid
=
22
;
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'30151212010101'
,
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$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'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
$mockRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$mockNextRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$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
,
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$mockQueueGroup
->
method
(
'lazyPush'
)
->
willReturnCallback
(
function
(
ActivityUpdateJob
$job
)
use
(
$title
,
$user
)
{
$this
->
verifyCallbackJob
(
$job
,
$title
,
$user
->
getId
(),
static
function
(
$time
)
{
return
$time
===
'30151212010101'
;
}
);
}
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
'force'
,
$oldid
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testResetNotificationTimestamp_futureNotificationTimestampNotForced
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$oldid
=
22
;
$title
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$makeListSql
=
"wl_namespace = 0 AND wl_title = 'SomeDbKey'"
;
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'makeList'
)
->
willReturnOnConsecutiveCalls
(
$makeListSql
,
$makeListSql
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_namespace'
,
'wl_title'
,
'wl_notificationtimestamp'
,
'we_expiry'
],
[
'wl_user'
=>
1
,
$makeListSql
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
,
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'30151212010101'
,
]
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
once
()
)->
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'
);
$mockQueueGroup
=
$this
->
getMockJobQueueGroup
(
false
);
$mockRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$mockNextRevision
=
$this
->
createNoOpMock
(
RevisionRecord
::
class
);
$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
,
]
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'queueGroup'
=>
$mockQueueGroup
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
]
);
$mockQueueGroup
->
method
(
'lazyPush'
)
->
willReturnCallback
(
function
(
ActivityUpdateJob
$job
)
use
(
$title
,
$user
)
{
$this
->
verifyCallbackJob
(
$job
,
$title
,
$user
->
getId
(),
static
function
(
$time
)
{
return
$time
===
false
;
}
);
}
);
$this
->
assertTrue
(
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
''
,
$oldid
)
);
}
public
function
testSetNotificationTimestampsForUser_anonUser
()
{
$store
=
$this
->
newWatchedItemStore
();
$this
->
assertFalse
(
$store
->
setNotificationTimestampsForUser
(
new
UserIdentityValue
(
0
,
'AnonUser'
),
''
)
);
}
public
function
testSetNotificationTimestampsForUser_allRows
()
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$timestamp
=
'20100101010101'
;
$store
=
$this
->
newWatchedItemStore
();
// Note: This does not actually assert the job is correct
$callableCallCounter
=
0
;
$mockCallback
=
function
(
$callable
)
use
(
&
$callableCallCounter
)
{
$callableCallCounter
++;
$this
->
assertIsCallable
(
$callable
);
};
$scopedOverride
=
$store
->
overrideDeferredUpdatesAddCallableUpdateCallback
(
$mockCallback
);
$this
->
assertTrue
(
$store
->
setNotificationTimestampsForUser
(
$user
,
$timestamp
)
);
$this
->
assertSame
(
1
,
$callableCallCounter
);
}
public
function
testSetNotificationTimestampsForUser_nullTimestamp
()
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$timestamp
=
null
;
$store
=
$this
->
newWatchedItemStore
();
// Note: This does not actually assert the job is correct
$callableCallCounter
=
0
;
$mockCallback
=
function
(
$callable
)
use
(
&
$callableCallCounter
)
{
$callableCallCounter
++;
$this
->
assertIsCallable
(
$callable
);
};
$scopedOverride
=
$store
->
overrideDeferredUpdatesAddCallableUpdateCallback
(
$mockCallback
);
$this
->
assertTrue
(
$store
->
setNotificationTimestampsForUser
(
$user
,
$timestamp
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testSetNotificationTimestampsForUser_specificTargets
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$timestamp
=
'20100101010101'
;
$targets
=
[
$testPageFactory
(
100
,
0
,
'Foo'
),
$testPageFactory
(
101
,
0
,
'Bar'
)
];
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'selectFieldValues'
)
->
with
(
[
'watchlist'
],
'wl_id'
,
[
'wl_user'
=>
1
,
'wl_namespace'
=>
0
,
'wl_title'
=>
[
'Foo'
,
'Bar'
]
]
)
->
willReturn
(
[
'2'
,
'3'
]
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'update'
)
->
with
(
'watchlist'
,
[
'wl_notificationtimestamp'
=>
'TS'
.
$timestamp
.
'TS'
],
[
'wl_id'
=>
[
2
,
3
]
]
)
->
willReturn
(
true
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturnCallback
(
static
function
(
$value
)
{
return
'TS'
.
$value
.
'TS'
;
}
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'affectedRows'
)
->
willReturn
(
2
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
]
);
$this
->
assertTrue
(
$store
->
setNotificationTimestampsForUser
(
$user
,
$timestamp
,
$targets
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testUpdateNotificationTimestamp_watchersExist
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_id'
,
'wl_user'
],
[
'wl_user != 1'
,
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
null
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
,
],
new
FakeResultWrapper
(
[
(
object
)[
'wl_id'
=>
'4'
,
'wl_user'
=>
'2'
,
],
(
object
)[
'wl_id'
=>
'5'
,
'wl_user'
=>
'3'
,
],
]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
return
$returnValue
;
}
);
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'update'
)
->
with
(
'watchlist'
,
[
'wl_notificationtimestamp'
=>
'20200101000000'
],
[
'wl_id'
=>
[
4
,
5
],
]
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
// 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
);
$this
->
assertEquals
(
[
2
,
3
],
$store
->
updateNotificationTimestamp
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
'20151212010101'
)
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testUpdateNotificationTimestamp_noWatchers
(
$testPageFactory
)
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectArgs
=
[
[
[
'watchlist'
,
'watchlist_expiry'
=>
'watchlist_expiry'
],
[
'wl_id'
,
'wl_user'
],
[
'wl_user != 1'
,
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
null
,
'(we_expiry IS NULL OR we_expiry >
\'
20200101000000
\'
)'
,
],
[],
[
'watchlist_expiry'
=>
[
'LEFT JOIN'
,
'wl_id = we_item'
]
],
new
FakeResultWrapper
(
[]
),
],
];
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'select'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$nextOptions
,
$nextJoinConds
,
$returnValue
]
=
array_shift
(
$selectArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
$this
->
assertSame
(
$nextOptions
,
$options
);
$this
->
assertSame
(
$nextJoinConds
,
$join_conds
);
return
$returnValue
;
}
);
$mockDb
->
expects
(
$this
->
never
()
)
->
method
(
'update'
);
$mockCache
=
$this
->
getMockCache
();
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'set'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'get'
);
$mockCache
->
expects
(
$this
->
never
()
)->
method
(
'delete'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$watchers
=
$store
->
updateNotificationTimestamp
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$testPageFactory
(
100
,
0
,
'SomeDbKey'
),
'20151212010101'
);
$this
->
assertSame
(
[],
$watchers
);
}
/**
* @dataProvider provideTestPageFactory
*/
public
function
testUpdateNotificationTimestamp_clearsCachedItems
(
$testPageFactory
)
{
$user
=
new
UserIdentityValue
(
1
,
'MockUser'
);
$titleValue
=
$testPageFactory
(
100
,
0
,
'SomeDbKey'
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
method
(
'timestamp'
)
->
willReturnArgument
(
0
);
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'select'
)
->
willReturnOnConsecutiveCalls
(
new
FakeResultWrapper
(
[
(
object
)[
'wl_namespace'
=>
0
,
'wl_title'
=>
'SomeDbKey'
,
'wl_notificationtimestamp'
=>
'20151212010101'
]
]
),
new
FakeResultWrapper
(
[
(
object
)[
'wl_id'
=>
'4'
,
'wl_user'
=>
'2'
,
],
(
object
)[
'wl_id'
=>
'5'
,
'wl_user'
=>
'3'
,
],
]
)
);
$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'
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
// This will add the item to the cache
$store
->
getWatchedItem
(
$user
,
$titleValue
);
// 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
);
$store
->
updateNotificationTimestamp
(
new
UserIdentityValue
(
1
,
'MockUser'
),
$titleValue
,
'20151212010101'
);
}
public
function
testRemoveExpired
()
{
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'timestamp'
)
->
willReturn
(
'20200101000000'
);
$selectFieldValuesArgs
=
[
// Select expired items.
[
[
'watchlist_expiry'
],
'we_item'
,
[
'we_expiry <=
\'
20200101000000
\'
'
],
[
'LIMIT'
=>
2
],
[],
[
1
,
2
],
],
// Select orphaned items.
[
[
'watchlist_expiry'
,
'watchlist'
=>
'watchlist'
],
'we_item'
,
[
'wl_id'
=>
null
,
'we_expiry'
=>
null
],
[],
[
'watchlist'
=>
[
'LEFT JOIN'
,
'wl_id = we_item'
]
],
[
3
]
]
];
// Select watchlist IDs.
$mockDb
->
expects
(
$this
->
exactly
(
2
)
)
->
method
(
'selectFieldValues'
)
->
willReturnCallback
(
function
(
$table
,
$vars
,
$conds
,
$fname
,
$options
,
$join_conds
)
use
(
&
$selectFieldValuesArgs
)
{
[
$nextTable
,
$nextVars
,
$nextConds
,
$nextOptions
,
$nextJoinConds
,
$returnValue
]
=
array_shift
(
$selectFieldValuesArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextVars
,
$vars
);
$this
->
assertSame
(
$nextConds
,
$this
->
expandExpr
(
$conds
)
);
$this
->
assertSame
(
$nextOptions
,
$options
);
$this
->
assertSame
(
$nextJoinConds
,
$join_conds
);
return
$returnValue
;
}
);
// Return whatever is passed to makeList, to be tested below.
$mockDb
->
expects
(
$this
->
once
()
)
->
method
(
'makeList'
)
->
willReturnArgument
(
0
);
// Delete from watchlist and watchlist_expiry.
$deleteArgs
=
[
// Delete expired items from watchlist
[
'watchlist'
,
[
'wl_id'
=>
[
1
,
2
]
],
],
// Delete expired items from watchlist_expiry
[
'watchlist_expiry'
,
[
'we_item'
=>
[
1
,
2
]
],
],
// Delete orphaned items
[
'watchlist_expiry'
,
[
'we_item'
=>
[
3
]
],
]
];
$mockDb
->
expects
(
$this
->
exactly
(
3
)
)
->
method
(
'delete'
)
->
willReturnCallback
(
function
(
$table
,
$conds
)
use
(
&
$deleteArgs
):
void
{
[
$nextTable
,
$nextConds
]
=
array_shift
(
$deleteArgs
);
$this
->
assertSame
(
$nextTable
,
$table
);
$this
->
assertSame
(
$nextConds
,
$conds
);
}
);
$mockCache
=
$this
->
getMockCache
();
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
]
);
$store
->
removeExpired
(
2
,
true
);
}
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'
);
$wrongKeyMap
=
new
MapCacheLRU
(
300
);
$wrongKeyMap
->
set
(
'0:Wrong'
,
'20110101000000'
);
// 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'
=>
[
$newMap
->
toArray
(),
false
],
'wrong key MapCacheLRU'
=>
[
$wrongKeyMap
,
true
],
'wrong key array'
=>
[
$wrongKeyMap
->
toArray
(),
true
],
];
}
/** @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'
,
'phpunitdb'
,
$user
->
getId
()
),
$cacheValue
);
$store
=
$this
->
newWatchedItemStore
(
[
'stash'
=>
$stash
]
);
$result
=
$store
->
getLatestNotificationTimestamp
(
'20100101000000'
,
$user
,
$title
);
$this
->
assertSame
(
$expectNonNull
,
$result
!==
null
);
}
/**
* @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
;
},
]
);
$mockDb
=
$this
->
getMockDb
();
$mockDb
->
method
(
'timestamp'
)
->
willReturnArgument
(
0
);
$mockDb
->
method
(
'select'
)->
willReturn
(
new
FakeResultWrapper
(
[]
)
);
$store
=
$this
->
newWatchedItemStore
(
[
'db'
=>
$mockDb
,
'cache'
=>
$mockCache
,
'revisionLookup'
=>
$mockRevisionLookup
,
'stash'
=>
$stash
,
'queueGroup'
=>
$this
->
getMockJobQueueGroup
(
false
),
]
);
$store
->
resetNotificationTimestamp
(
$user
,
$title
,
'force'
,
$oldid
);
$this
->
assertIsArray
(
$stash
->
get
(
'global:watchlist-recent-updates:phpunitdb:1'
)
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 18 2026, 11:34 (5 w, 1 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
98/18/3fb117213bd8954a7f0d5f134528
Default Alt Text
WatchedItemStoreUnitTest.php (106 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment