wiki.techinc.nl/tests/phpunit/includes/WatchedItemStoreUnitTest.php
Timo Tijhof 447ce7e39a phpunit: Avoid use of deprecated getMock for PHPUnit 5 compat
The default will remain PHPUnit 4.x due to PHP 5.5 support.

But, we should allow developers to run tests with newer PHPUnit
versions which are noticably faster (especially for code coverage
reports).

* <https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-for-PHPUnit-5.4.0>
  PHPUnit 5 deprecates the getMock() shortcut for getMockBuilder()->getMock().
  It instead introduces the shortcut createMock() which has better defaults
  than getMockBuilder(). For example, it sets 'disableArgumentCloning' and
  other things by default.

  Going forward, code should either use getMockBuilder directly and configure
  it using the setter methods (instead of the confusing variadic arguments
  of getMock) or simply use the new minimalistic createMock method. This patch
  backports the createMock method to MediaWikiTestCase so that we can start
  using it.

Change-Id: I091c0289b21d2b1c876adba89529dc3e72b99af2
2017-04-06 00:44:32 +00:00

2604 lines
67 KiB
PHP

<?php
use MediaWiki\Linker\LinkTarget;
use Wikimedia\ScopedCallback;
/**
* @author Addshore
*
* @covers WatchedItemStore
*/
class WatchedItemStoreUnitTest extends MediaWikiTestCase {
/**
* @return PHPUnit_Framework_MockObject_MockObject|IDatabase
*/
private function getMockDb() {
return $this->createMock( IDatabase::class );
}
/**
* @return PHPUnit_Framework_MockObject_MockObject|LoadBalancer
*/
private function getMockLoadBalancer(
$mockDb,
$expectedConnectionType = null,
$readOnlyReason = false
) {
$mock = $this->getMockBuilder( LoadBalancer::class )
->disableOriginalConstructor()
->getMock();
if ( $expectedConnectionType !== null ) {
$mock->expects( $this->any() )
->method( 'getConnectionRef' )
->with( $expectedConnectionType )
->will( $this->returnValue( $mockDb ) );
} else {
$mock->expects( $this->any() )
->method( 'getConnectionRef' )
->will( $this->returnValue( $mockDb ) );
}
$mock->expects( $this->any() )
->method( 'getReadOnlyReason' )
->will( $this->returnValue( $readOnlyReason ) );
return $mock;
}
/**
* @return PHPUnit_Framework_MockObject_MockObject|HashBagOStuff
*/
private function getMockCache() {
$mock = $this->getMockBuilder( HashBagOStuff::class )
->disableOriginalConstructor()
->getMock();
$mock->expects( $this->any() )
->method( 'makeKey' )
->will( $this->returnCallback( function() {
return implode( ':', func_get_args() );
} ) );
return $mock;
}
/**
* @param int $id
* @return PHPUnit_Framework_MockObject_MockObject|User
*/
private function getMockNonAnonUserWithId( $id ) {
$mock = $this->createMock( User::class );
$mock->expects( $this->any() )
->method( 'isAnon' )
->will( $this->returnValue( false ) );
$mock->expects( $this->any() )
->method( 'getId' )
->will( $this->returnValue( $id ) );
return $mock;
}
/**
* @return User
*/
private function getAnonUser() {
return User::newFromName( 'Anon_User' );
}
private function getFakeRow( array $rowValues ) {
$fakeRow = new stdClass();
foreach ( $rowValues as $valueName => $value ) {
$fakeRow->$valueName = $value;
}
return $fakeRow;
}
private function newWatchedItemStore( LoadBalancer $loadBalancer, HashBagOStuff $cache ) {
return new WatchedItemStore(
$loadBalancer,
$cache
);
}
public function testCountWatchedItems() {
$user = $this->getMockNonAnonUserWithId( 1 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
->method( 'selectField' )
->with(
'watchlist',
'COUNT(*)',
[
'wl_user' => $user->getId(),
],
$this->isType( 'string' )
)
->will( $this->returnValue( 12 ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals( 12, $store->countWatchedItems( $user ) );
}
public function testCountWatchers() {
$titleValue = new TitleValue( 0, 'SomeDbKey' );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
->method( 'selectField' )
->with(
'watchlist',
'COUNT(*)',
[
'wl_namespace' => $titleValue->getNamespace(),
'wl_title' => $titleValue->getDBkey(),
],
$this->isType( 'string' )
)
->will( $this->returnValue( 7 ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals( 7, $store->countWatchers( $titleValue ) );
}
public function testCountWatchersMultiple() {
$titleValues = [
new TitleValue( 0, 'SomeDbKey' ),
new TitleValue( 0, 'OtherDbKey' ),
new TitleValue( 1, 'AnotherDbKey' ),
];
$mockDb = $this->getMockDb();
$dbResult = [
$this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
$this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
$this->getFakeRow( [ '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' )
)
->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
$mockDb->expects( $this->once() )
->method( 'select' )
->with(
'watchlist',
[ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
[ 'makeWhereFrom2d return value' ],
$this->isType( 'string' ),
[
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
]
)
->will(
$this->returnValue( $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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$expected = [
0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
1 => [ 'AnotherDbKey' => 500 ],
];
$this->assertEquals( $expected, $store->countWatchersMultiple( $titleValues ) );
}
public function provideIntWithDbUnsafeVersion() {
return [
[ 50 ],
[ "50; DROP TABLE watchlist;\n--" ],
];
}
/**
* @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountWatchersMultiple_withMinimumWatchers( $minWatchers ) {
$titleValues = [
new TitleValue( 0, 'SomeDbKey' ),
new TitleValue( 0, 'OtherDbKey' ),
new TitleValue( 1, 'AnotherDbKey' ),
];
$mockDb = $this->getMockDb();
$dbResult = [
$this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
$this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
$this->getFakeRow( [ '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' )
)
->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
$mockDb->expects( $this->once() )
->method( 'select' )
->with(
'watchlist',
[ 'wl_title', 'wl_namespace', 'watchers' => 'COUNT(*)' ],
[ 'makeWhereFrom2d return value' ],
$this->isType( 'string' ),
[
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
'HAVING' => 'COUNT(*) >= 50',
]
)
->will(
$this->returnValue( $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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$expected = [
0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
1 => [ 'AnotherDbKey' => 500 ],
];
$this->assertEquals(
$expected,
$store->countWatchersMultiple( $titleValues, [ 'minimumWatchers' => $minWatchers ] )
);
}
public function testCountVisitingWatchers() {
$titleValue = new TitleValue( 0, 'SomeDbKey' );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
->method( 'selectField' )
->with(
'watchlist',
'COUNT(*)',
[
'wl_namespace' => $titleValue->getNamespace(),
'wl_title' => $titleValue->getDBkey(),
'wl_notificationtimestamp >= \'TS111TS\' OR wl_notificationtimestamp IS NULL',
],
$this->isType( 'string' )
)
->will( $this->returnValue( 7 ) );
$mockDb->expects( $this->exactly( 1 ) )
->method( 'addQuotes' )
->will( $this->returnCallback( function( $value ) {
return "'$value'";
} ) );
$mockDb->expects( $this->exactly( 1 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
}
public function testCountVisitingWatchersMultiple() {
$titleValuesWithThresholds = [
[ new TitleValue( 0, 'SomeDbKey' ), '111' ],
[ new TitleValue( 0, 'OtherDbKey' ), '111' ],
[ new TitleValue( 1, 'AnotherDbKey' ), '123' ],
];
$dbResult = [
$this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
$this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
$this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ] ),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 2 * 3 ) )
->method( 'addQuotes' )
->will( $this->returnCallback( function( $value ) {
return "'$value'";
} ) );
$mockDb->expects( $this->exactly( 3 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$mockDb->expects( $this->any() )
->method( 'makeList' )
->with(
$this->isType( 'array' ),
$this->isType( 'int' )
)
->will( $this->returnCallback( function( $a, $conj ) {
$sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
return join( $sqlConj, array_map( function( $s ) {
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)" .
')))))';
$mockDb->expects( $this->once() )
->method( 'select' )
->with(
'watchlist',
[ 'wl_namespace', 'wl_title', 'watchers' => 'COUNT(*)' ],
$expectedCond,
$this->isType( 'string' ),
[
'GROUP BY' => [ 'wl_namespace', 'wl_title' ],
]
)
->will(
$this->returnValue( $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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$expected = [
0 => [ 'SomeDbKey' => 100, 'OtherDbKey' => 300 ],
1 => [ 'AnotherDbKey' => 500 ],
];
$this->assertEquals(
$expected,
$store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
);
}
public function testCountVisitingWatchersMultiple_withMissingTargets() {
$titleValuesWithThresholds = [
[ new TitleValue( 0, 'SomeDbKey' ), '111' ],
[ new TitleValue( 0, 'OtherDbKey' ), '111' ],
[ new TitleValue( 1, 'AnotherDbKey' ), '123' ],
[ new TitleValue( 0, 'SomeNotExisitingDbKey' ), null ],
[ new TitleValue( 0, 'OtherNotExisitingDbKey' ), null ],
];
$dbResult = [
$this->getFakeRow( [ 'wl_title' => 'SomeDbKey', 'wl_namespace' => 0, 'watchers' => 100 ] ),
$this->getFakeRow( [ 'wl_title' => 'OtherDbKey', 'wl_namespace' => 0, 'watchers' => 300 ] ),
$this->getFakeRow( [ 'wl_title' => 'AnotherDbKey', 'wl_namespace' => 1, 'watchers' => 500 ] ),
$this->getFakeRow(
[ 'wl_title' => 'SomeNotExisitingDbKey', 'wl_namespace' => 0, 'watchers' => 100 ]
),
$this->getFakeRow(
[ 'wl_title' => 'OtherNotExisitingDbKey', 'wl_namespace' => 0, 'watchers' => 200 ]
),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 2 * 3 ) )
->method( 'addQuotes' )
->will( $this->returnCallback( function( $value ) {
return "'$value'";
} ) );
$mockDb->expects( $this->exactly( 3 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$mockDb->expects( $this->any() )
->method( 'makeList' )
->with(
$this->isType( 'array' ),
$this->isType( 'int' )
)
->will( $this->returnCallback( function( $a, $conj ) {
$sqlConj = $conj === LIST_AND ? ' AND ' : ' OR ';
return join( $sqlConj, array_map( function( $s ) {
return '(' . $s . ')';
}, $a
) );
} ) );
$mockDb->expects( $this->once() )
->method( 'makeWhereFrom2d' )
->with(
[ [ 'SomeNotExisitingDbKey' => 1, 'OtherNotExisitingDbKey' => 1 ] ],
$this->isType( 'string' ),
$this->isType( 'string' )
)
->will( $this->returnValue( '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' ],
]
)
->will(
$this->returnValue( $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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$expected = [
0 => [
'SomeDbKey' => 100, 'OtherDbKey' => 300,
'SomeNotExisitingDbKey' => 100, 'OtherNotExisitingDbKey' => 200
],
1 => [ 'AnotherDbKey' => 500 ],
];
$this->assertEquals(
$expected,
$store->countVisitingWatchersMultiple( $titleValuesWithThresholds )
);
}
/**
* @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountVisitingWatchersMultiple_withMinimumWatchers( $minWatchers ) {
$titleValuesWithThresholds = [
[ new TitleValue( 0, 'SomeDbKey' ), '111' ],
[ new TitleValue( 0, 'OtherDbKey' ), '111' ],
[ new TitleValue( 1, 'AnotherDbKey' ), '123' ],
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->any() )
->method( 'makeList' )
->will( $this->returnValue( 'makeList return value' ) );
$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',
]
)
->will(
$this->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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$expected = [
0 => [ 'SomeDbKey' => 0, 'OtherDbKey' => 0 ],
1 => [ 'AnotherDbKey' => 0 ],
];
$this->assertEquals(
$expected,
$store->countVisitingWatchersMultiple( $titleValuesWithThresholds, $minWatchers )
);
}
public function testCountUnreadNotifications() {
$user = $this->getMockNonAnonUserWithId( 1 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
->method( 'selectRowCount' )
->with(
'watchlist',
'1',
[
"wl_notificationtimestamp IS NOT NULL",
'wl_user' => 1,
],
$this->isType( 'string' )
)
->will( $this->returnValue( 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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
}
/**
* @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountUnreadNotifications_withUnreadLimit_overLimit( $limit ) {
$user = $this->getMockNonAnonUserWithId( 1 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
->method( 'selectRowCount' )
->with(
'watchlist',
'1',
[
"wl_notificationtimestamp IS NOT NULL",
'wl_user' => 1,
],
$this->isType( 'string' ),
[ 'LIMIT' => 50 ]
)
->will( $this->returnValue( 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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertSame(
true,
$store->countUnreadNotifications( $user, $limit )
);
}
/**
* @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountUnreadNotifications_withUnreadLimit_underLimit( $limit ) {
$user = $this->getMockNonAnonUserWithId( 1 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
->method( 'selectRowCount' )
->with(
'watchlist',
'1',
[
"wl_notificationtimestamp IS NOT NULL",
'wl_user' => 1,
],
$this->isType( 'string' ),
[ 'LIMIT' => 50 ]
)
->will( $this->returnValue( 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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
9,
$store->countUnreadNotifications( $user, $limit )
);
}
public function testDuplicateEntry_nothingToDuplicate() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'select' )
->with(
'watchlist',
[
'wl_user',
'wl_notificationtimestamp',
],
[
'wl_namespace' => 0,
'wl_title' => 'Old_Title',
],
'WatchedItemStore::duplicateEntry',
[ 'FOR UPDATE' ]
)
->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
);
$store->duplicateEntry(
Title::newFromText( 'Old_Title' ),
Title::newFromText( 'New_Title' )
);
}
public function testDuplicateEntry_somethingToDuplicate() {
$fakeRows = [
$this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
$this->getFakeRow( [ 'wl_user' => 2, 'wl_notificationtimestamp' => null ] ),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->at( 0 ) )
->method( 'select' )
->with(
'watchlist',
[
'wl_user',
'wl_notificationtimestamp',
],
[
'wl_namespace' => 0,
'wl_title' => 'Old_Title',
]
)
->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
$mockDb->expects( $this->at( 1 ) )
->method( 'replace' )
->with(
'watchlist',
[ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
[
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'New_Title',
'wl_notificationtimestamp' => '20151212010101',
],
[
'wl_user' => 2,
'wl_namespace' => 0,
'wl_title' => 'New_Title',
'wl_notificationtimestamp' => null,
],
],
$this->isType( 'string' )
);
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$store->duplicateEntry(
Title::newFromText( 'Old_Title' ),
Title::newFromText( 'New_Title' )
);
}
public function testDuplicateAllAssociatedEntries_nothingToDuplicate() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->at( 0 ) )
->method( 'select' )
->with(
'watchlist',
[
'wl_user',
'wl_notificationtimestamp',
],
[
'wl_namespace' => 0,
'wl_title' => 'Old_Title',
]
)
->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
$mockDb->expects( $this->at( 1 ) )
->method( 'select' )
->with(
'watchlist',
[
'wl_user',
'wl_notificationtimestamp',
],
[
'wl_namespace' => 1,
'wl_title' => 'Old_Title',
]
)
->will( $this->returnValue( new FakeResultWrapper( [] ) ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$store->duplicateAllAssociatedEntries(
Title::newFromText( 'Old_Title' ),
Title::newFromText( 'New_Title' )
);
}
public function provideLinkTargetPairs() {
return [
[ Title::newFromText( 'Old_Title' ), Title::newFromText( 'New_Title' ) ],
[ new TitleValue( 0, 'Old_Title' ), new TitleValue( 0, 'New_Title' ) ],
];
}
/**
* @dataProvider provideLinkTargetPairs
*/
public function testDuplicateAllAssociatedEntries_somethingToDuplicate(
LinkTarget $oldTarget,
LinkTarget $newTarget
) {
$fakeRows = [
$this->getFakeRow( [ 'wl_user' => 1, 'wl_notificationtimestamp' => '20151212010101' ] ),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->at( 0 ) )
->method( 'select' )
->with(
'watchlist',
[
'wl_user',
'wl_notificationtimestamp',
],
[
'wl_namespace' => $oldTarget->getNamespace(),
'wl_title' => $oldTarget->getDBkey(),
]
)
->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
$mockDb->expects( $this->at( 1 ) )
->method( 'replace' )
->with(
'watchlist',
[ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
[
[
'wl_user' => 1,
'wl_namespace' => $newTarget->getNamespace(),
'wl_title' => $newTarget->getDBkey(),
'wl_notificationtimestamp' => '20151212010101',
],
],
$this->isType( 'string' )
);
$mockDb->expects( $this->at( 2 ) )
->method( 'select' )
->with(
'watchlist',
[
'wl_user',
'wl_notificationtimestamp',
],
[
'wl_namespace' => $oldTarget->getNamespace() + 1,
'wl_title' => $oldTarget->getDBkey(),
]
)
->will( $this->returnValue( new FakeResultWrapper( $fakeRows ) ) );
$mockDb->expects( $this->at( 3 ) )
->method( 'replace' )
->with(
'watchlist',
[ [ 'wl_user', 'wl_namespace', 'wl_title' ] ],
[
[
'wl_user' => 1,
'wl_namespace' => $newTarget->getNamespace() + 1,
'wl_title' => $newTarget->getDBkey(),
'wl_notificationtimestamp' => '20151212010101',
],
],
$this->isType( 'string' )
);
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$store->duplicateAllAssociatedEntries(
$oldTarget,
$newTarget
);
}
public function testAddWatch_nonAnonymousUser() {
$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' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$store->addWatch(
$this->getMockNonAnonUserWithId( 1 ),
Title::newFromText( 'Some_Page' )
);
}
public function testAddWatch_anonymousUser() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'insert' );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$store->addWatch(
$this->getAnonUser(),
Title::newFromText( 'Some_Page' )
);
}
public function testAddWatchBatchForUser_readOnlyDBReturnsFalse() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb(), null, 'Some Reason' ),
$this->getMockCache()
);
$this->assertFalse(
$store->addWatchBatchForUser(
$this->getMockNonAnonUserWithId( 1 ),
[ new TitleValue( 0, 'Some_Page' ), new TitleValue( 1, 'Some_Page' ) ]
)
);
}
public function testAddWatchBatchForUser_nonAnonymousUser() {
$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,
]
]
);
$mockCache = $this->getMockCache();
$mockCache->expects( $this->exactly( 2 ) )
->method( 'delete' );
$mockCache->expects( $this->at( 1 ) )
->method( 'delete' )
->with( '0:Some_Page:1' );
$mockCache->expects( $this->at( 3 ) )
->method( 'delete' )
->with( '1:Some_Page:1' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$mockUser = $this->getMockNonAnonUserWithId( 1 );
$this->assertTrue(
$store->addWatchBatchForUser(
$mockUser,
[ new TitleValue( 0, 'Some_Page' ), new TitleValue( 1, 'Some_Page' ) ]
)
);
}
public function testAddWatchBatchForUser_anonymousUsersAreSkipped() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'insert' );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->addWatchBatchForUser(
$this->getAnonUser(),
[ new TitleValue( 0, 'Other_Page' ) ]
)
);
}
public function testAddWatchBatchReturnsTrue_whenGivenEmptyList() {
$user = $this->getMockNonAnonUserWithId( 1 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'insert' );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertTrue(
$store->addWatchBatchForUser( $user, [] )
);
}
public function testLoadWatchedItem_existingItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->once() )
->method( 'set' )
->with(
'0:SomeDbKey:1'
);
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$watchedItem = $store->loadWatchedItem(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
);
$this->assertInstanceOf( 'WatchedItem', $watchedItem );
$this->assertEquals( 1, $watchedItem->getUser()->getId() );
$this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
$this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
}
public function testLoadWatchedItem_noItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue( [] ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->loadWatchedItem(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testLoadWatchedItem_anonymousUser() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'selectRow' );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->loadWatchedItem(
$this->getAnonUser(),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testRemoveWatch_existingItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'delete' )
->with(
'watchlist',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
);
$mockDb->expects( $this->once() )
->method( 'affectedRows' )
->will( $this->returnValue( 1 ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->once() )
->method( 'delete' )
->with( '0:SomeDbKey:1' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertTrue(
$store->removeWatch(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testRemoveWatch_noItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'delete' )
->with(
'watchlist',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
);
$mockDb->expects( $this->once() )
->method( 'affectedRows' )
->will( $this->returnValue( 0 ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->once() )
->method( 'delete' )
->with( '0:SomeDbKey:1' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->removeWatch(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testRemoveWatch_anonymousUser() {
$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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->removeWatch(
$this->getAnonUser(),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testGetWatchedItem_existingItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'delete' );
$mockCache->expects( $this->once() )
->method( 'get' )
->with(
'0:SomeDbKey:1'
)
->will( $this->returnValue( null ) );
$mockCache->expects( $this->once() )
->method( 'set' )
->with(
'0:SomeDbKey:1'
);
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$watchedItem = $store->getWatchedItem(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
);
$this->assertInstanceOf( 'WatchedItem', $watchedItem );
$this->assertEquals( 1, $watchedItem->getUser()->getId() );
$this->assertEquals( 'SomeDbKey', $watchedItem->getLinkTarget()->getDBkey() );
$this->assertEquals( 0, $watchedItem->getLinkTarget()->getNamespace() );
}
public function testGetWatchedItem_cachedItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'selectRow' );
$mockUser = $this->getMockNonAnonUserWithId( 1 );
$linkTarget = new TitleValue( 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'
)
->will( $this->returnValue( $cachedItem ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
$cachedItem,
$store->getWatchedItem(
$mockUser,
$linkTarget
)
);
}
public function testGetWatchedItem_noItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->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' )
->will( $this->returnValue( false ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->getWatchedItem(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testGetWatchedItem_anonymousUser() {
$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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->getWatchedItem(
$this->getAnonUser(),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testGetWatchedItemsForUser() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'select' )
->with(
'watchlist',
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
[ 'wl_user' => 1 ]
)
->will( $this->returnValue( [
$this->getFakeRow( [
'wl_namespace' => 0,
'wl_title' => 'Foo1',
'wl_notificationtimestamp' => '20151212010101',
] ),
$this->getFakeRow( [
'wl_namespace' => 1,
'wl_title' => 'Foo2',
'wl_notificationtimestamp' => null,
] ),
] ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'delete' );
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'set' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$user = $this->getMockNonAnonUserWithId( 1 );
$watchedItems = $store->getWatchedItemsForUser( $user );
$this->assertInternalType( 'array', $watchedItems );
$this->assertCount( 2, $watchedItems );
foreach ( $watchedItems as $watchedItem ) {
$this->assertInstanceOf( 'WatchedItem', $watchedItem );
}
$this->assertEquals(
new WatchedItem( $user, new TitleValue( 0, 'Foo1' ), '20151212010101' ),
$watchedItems[0]
);
$this->assertEquals(
new WatchedItem( $user, new TitleValue( 1, 'Foo2' ), null ),
$watchedItems[1]
);
}
public function provideDbTypes() {
return [
[ false, DB_SLAVE ],
[ true, DB_MASTER ],
];
}
/**
* @dataProvider provideDbTypes
*/
public function testGetWatchedItemsForUser_optionsAndEmptyResult( $forWrite, $dbType ) {
$mockDb = $this->getMockDb();
$mockCache = $this->getMockCache();
$mockLoadBalancer = $this->getMockLoadBalancer( $mockDb, $dbType );
$user = $this->getMockNonAnonUserWithId( 1 );
$mockDb->expects( $this->once() )
->method( 'select' )
->with(
'watchlist',
[ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ],
[ 'wl_user' => 1 ],
$this->isType( 'string' ),
[ 'ORDER BY' => [ 'wl_namespace ASC', 'wl_title ASC' ] ]
)
->will( $this->returnValue( [] ) );
$store = $this->newWatchedItemStore(
$mockLoadBalancer,
$mockCache
);
$watchedItems = $store->getWatchedItemsForUser(
$user,
[ 'forWrite' => $forWrite, 'sort' => WatchedItemStore::SORT_ASC ]
);
$this->assertEquals( [], $watchedItems );
}
public function testGetWatchedItemsForUser_badSortOptionThrowsException() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
$this->getMockCache()
);
$this->setExpectedException( 'InvalidArgumentException' );
$store->getWatchedItemsForUser(
$this->getMockNonAnonUserWithId( 1 ),
[ 'sort' => 'foo' ]
);
}
public function testIsWatchedItem_existingItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'delete' );
$mockCache->expects( $this->once() )
->method( 'get' )
->with( '0:SomeDbKey:1' )
->will( $this->returnValue( false ) );
$mockCache->expects( $this->once() )
->method( 'set' )
->with(
'0:SomeDbKey:1'
);
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertTrue(
$store->isWatched(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testIsWatchedItem_noItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->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' )
->will( $this->returnValue( false ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->isWatched(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testIsWatchedItem_anonymousUser() {
$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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->isWatched(
$this->getAnonUser(),
new TitleValue( 0, 'SomeDbKey' )
)
);
}
public function testGetNotificationTimestampsBatch() {
$targets = [
new TitleValue( 0, 'SomeDbKey' ),
new TitleValue( 1, 'AnotherDbKey' ),
];
$mockDb = $this->getMockDb();
$dbResult = [
$this->getFakeRow( [
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
'wl_notificationtimestamp' => '20151212010101',
] ),
$this->getFakeRow(
[
'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' )
)
->will( $this->returnValue( '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' )
)
->will( $this->returnValue( $dbResult ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->exactly( 2 ) )
->method( 'get' )
->withConsecutive(
[ '0:SomeDbKey:1' ],
[ '1:AnotherDbKey:1' ]
)
->will( $this->returnValue( null ) );
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
[
0 => [ 'SomeDbKey' => '20151212010101', ],
1 => [ 'AnotherDbKey' => null, ],
],
$store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
);
}
public function testGetNotificationTimestampsBatch_notWatchedTarget() {
$targets = [
new TitleValue( 0, 'OtherDbKey' ),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'makeWhereFrom2d' )
->with(
[ [ 'OtherDbKey' => 1 ] ],
$this->isType( 'string' ),
$this->isType( 'string' )
)
->will( $this->returnValue( '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' )
)
->will( $this->returnValue( $this->getFakeRow( [] ) ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->once() )
->method( 'get' )
->with( '0:OtherDbKey:1' )
->will( $this->returnValue( null ) );
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
[
0 => [ 'OtherDbKey' => false, ],
],
$store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
);
}
public function testGetNotificationTimestampsBatch_cachedItem() {
$targets = [
new TitleValue( 0, 'SomeDbKey' ),
new TitleValue( 1, 'AnotherDbKey' ),
];
$user = $this->getMockNonAnonUserWithId( 1 );
$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' )
)
->will( $this->returnValue( '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' )
)
->will( $this->returnValue( [
$this->getFakeRow(
[ 'wl_namespace' => 1, 'wl_title' => 'AnotherDbKey', 'wl_notificationtimestamp' => null, ]
)
] ) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->at( 1 ) )
->method( 'get' )
->with( '0:SomeDbKey:1' )
->will( $this->returnValue( $cachedItem ) );
$mockCache->expects( $this->at( 3 ) )
->method( 'get' )
->with( '1:AnotherDbKey:1' )
->will( $this->returnValue( null ) );
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
[
0 => [ 'SomeDbKey' => '20151212010101', ],
1 => [ 'AnotherDbKey' => null, ],
],
$store->getNotificationTimestampsBatch( $user, $targets )
);
}
public function testGetNotificationTimestampsBatch_allItemsCached() {
$targets = [
new TitleValue( 0, 'SomeDbKey' ),
new TitleValue( 1, 'AnotherDbKey' ),
];
$user = $this->getMockNonAnonUserWithId( 1 );
$cachedItems = [
new WatchedItem( $user, $targets[0], '20151212010101' ),
new WatchedItem( $user, $targets[1], null ),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )->method( $this->anything() );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->at( 1 ) )
->method( 'get' )
->with( '0:SomeDbKey:1' )
->will( $this->returnValue( $cachedItems[0] ) );
$mockCache->expects( $this->at( 3 ) )
->method( 'get' )
->with( '1:AnotherDbKey:1' )
->will( $this->returnValue( $cachedItems[1] ) );
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
[
0 => [ 'SomeDbKey' => '20151212010101', ],
1 => [ 'AnotherDbKey' => null, ],
],
$store->getNotificationTimestampsBatch( $user, $targets )
);
}
public function testGetNotificationTimestampsBatch_anonymousUser() {
$targets = [
new TitleValue( 0, 'SomeDbKey' ),
new TitleValue( 1, 'AnotherDbKey' ),
];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )->method( $this->anything() );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( $this->anything() );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
[
0 => [ 'SomeDbKey' => false, ],
1 => [ 'AnotherDbKey' => false, ],
],
$store->getNotificationTimestampsBatch( $this->getAnonUser(), $targets )
);
}
public function testResetNotificationTimestamp_anonymousUser() {
$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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->resetNotificationTimestamp(
$this->getAnonUser(),
Title::newFromText( 'SomeDbKey' )
)
);
}
public function testResetNotificationTimestamp_noItem() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertFalse(
$store->resetNotificationTimestamp(
$this->getMockNonAnonUserWithId( 1 ),
Title::newFromText( 'SomeDbKey' )
)
);
}
public function testResetNotificationTimestamp_item() {
$user = $this->getMockNonAnonUserWithId( 1 );
$title = Title::newFromText( 'SomeDbKey' );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->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' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
// Note: This does not actually assert the job is correct
$callableCallCounter = 0;
$mockCallback = function( $callable ) use ( &$callableCallCounter ) {
$callableCallCounter++;
$this->assertInternalType( 'callable', $callable );
};
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title
)
);
$this->assertEquals( 1, $callableCallCounter );
ScopedCallback::consume( $scopedOverride );
}
public function testResetNotificationTimestamp_noItemForced() {
$user = $this->getMockNonAnonUserWithId( 1 );
$title = Title::newFromText( 'SomeDbKey' );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'selectRow' );
$mockCache = $this->getMockCache();
$mockDb->expects( $this->never() )
->method( 'get' );
$mockDb->expects( $this->never() )
->method( 'set' );
$mockDb->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
// Note: This does not actually assert the job is correct
$callableCallCounter = 0;
$mockCallback = function( $callable ) use ( &$callableCallCounter ) {
$callableCallCounter++;
$this->assertInternalType( 'callable', $callable );
};
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title,
'force'
)
);
$this->assertEquals( 1, $callableCallCounter );
ScopedCallback::consume( $scopedOverride );
}
/**
* @param $text
* @param int $ns
*
* @return PHPUnit_Framework_MockObject_MockObject|Title
*/
private function getMockTitle( $text, $ns = 0 ) {
$title = $this->createMock( Title::class );
$title->expects( $this->any() )
->method( 'getText' )
->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
$title->expects( $this->any() )
->method( 'getDbKey' )
->will( $this->returnValue( str_replace( '_', ' ', $text ) ) );
$title->expects( $this->any() )
->method( 'getNamespace' )
->will( $this->returnValue( $ns ) );
return $title;
}
private function verifyCallbackJob(
$callback,
LinkTarget $expectedTitle,
$expectedUserId,
callable $notificationTimestampCondition
) {
$this->assertInternalType( 'callable', $callback );
$callbackReflector = new ReflectionFunction( $callback );
$vars = $callbackReflector->getStaticVariables();
$this->assertArrayHasKey( 'job', $vars );
$this->assertInstanceOf( ActivityUpdateJob::class, $vars['job'] );
/** @var ActivityUpdateJob $job */
$job = $vars['job'];
$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'] ) );
}
public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced() {
$user = $this->getMockNonAnonUserWithId( 1 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeTitle' );
$title->expects( $this->once() )
->method( 'getNextRevisionID' )
->with( $oldid )
->will( $this->returnValue( false ) );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'selectRow' );
$mockCache = $this->getMockCache();
$mockDb->expects( $this->never() )
->method( 'get' );
$mockDb->expects( $this->never() )
->method( 'set' );
$mockDb->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$callableCallCounter = 0;
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
function( $callable ) use ( &$callableCallCounter, $title, $user ) {
$callableCallCounter++;
$this->verifyCallbackJob(
$callable,
$title,
$user->getId(),
function( $time ) {
return $time === null;
}
);
}
);
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title,
'force',
$oldid
)
);
$this->assertEquals( 1, $callableCallCounter );
ScopedCallback::consume( $scopedOverride );
}
public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced() {
$user = $this->getMockNonAnonUserWithId( 1 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
->method( 'getNextRevisionID' )
->with( $oldid )
->will( $this->returnValue( 33 ) );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockDb->expects( $this->never() )
->method( 'get' );
$mockDb->expects( $this->never() )
->method( 'set' );
$mockDb->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$addUpdateCallCounter = 0;
$scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
function( $callable ) use ( &$addUpdateCallCounter, $title, $user ) {
$addUpdateCallCounter++;
$this->verifyCallbackJob(
$callable,
$title,
$user->getId(),
function( $time ) {
return $time !== null && $time > '20151212010101';
}
);
}
);
$getTimestampCallCounter = 0;
$scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
function( $titleParam, $oldidParam ) use ( &$getTimestampCallCounter, $title, $oldid ) {
$getTimestampCallCounter++;
$this->assertEquals( $title, $titleParam );
$this->assertEquals( $oldid, $oldidParam );
}
);
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title,
'force',
$oldid
)
);
$this->assertEquals( 1, $addUpdateCallCounter );
$this->assertEquals( 1, $getTimestampCallCounter );
ScopedCallback::consume( $scopedOverrideDeferred );
ScopedCallback::consume( $scopedOverrideRevision );
}
public function testResetNotificationTimestamp_notWatchedPageForced() {
$user = $this->getMockNonAnonUserWithId( 1 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
->method( 'getNextRevisionID' )
->with( $oldid )
->will( $this->returnValue( 33 ) );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue( false ) );
$mockCache = $this->getMockCache();
$mockDb->expects( $this->never() )
->method( 'get' );
$mockDb->expects( $this->never() )
->method( 'set' );
$mockDb->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$callableCallCounter = 0;
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
function( $callable ) use ( &$callableCallCounter, $title, $user ) {
$callableCallCounter++;
$this->verifyCallbackJob(
$callable,
$title,
$user->getId(),
function( $time ) {
return $time === null;
}
);
}
);
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title,
'force',
$oldid
)
);
$this->assertEquals( 1, $callableCallCounter );
ScopedCallback::consume( $scopedOverride );
}
public function testResetNotificationTimestamp_futureNotificationTimestampForced() {
$user = $this->getMockNonAnonUserWithId( 1 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
->method( 'getNextRevisionID' )
->with( $oldid )
->will( $this->returnValue( 33 ) );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '30151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockDb->expects( $this->never() )
->method( 'get' );
$mockDb->expects( $this->never() )
->method( 'set' );
$mockDb->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$addUpdateCallCounter = 0;
$scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
function( $callable ) use ( &$addUpdateCallCounter, $title, $user ) {
$addUpdateCallCounter++;
$this->verifyCallbackJob(
$callable,
$title,
$user->getId(),
function( $time ) {
return $time === '30151212010101';
}
);
}
);
$getTimestampCallCounter = 0;
$scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
function( $titleParam, $oldidParam ) use ( &$getTimestampCallCounter, $title, $oldid ) {
$getTimestampCallCounter++;
$this->assertEquals( $title, $titleParam );
$this->assertEquals( $oldid, $oldidParam );
}
);
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title,
'force',
$oldid
)
);
$this->assertEquals( 1, $addUpdateCallCounter );
$this->assertEquals( 1, $getTimestampCallCounter );
ScopedCallback::consume( $scopedOverrideDeferred );
ScopedCallback::consume( $scopedOverrideRevision );
}
public function testResetNotificationTimestamp_futureNotificationTimestampNotForced() {
$user = $this->getMockNonAnonUserWithId( 1 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
->method( 'getNextRevisionID' )
->with( $oldid )
->will( $this->returnValue( 33 ) );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->with(
'watchlist',
'wl_notificationtimestamp',
[
'wl_user' => 1,
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
)
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '30151212010101' ] )
) );
$mockCache = $this->getMockCache();
$mockDb->expects( $this->never() )
->method( 'get' );
$mockDb->expects( $this->never() )
->method( 'set' );
$mockDb->expects( $this->never() )
->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$addUpdateCallCounter = 0;
$scopedOverrideDeferred = $store->overrideDeferredUpdatesAddCallableUpdateCallback(
function( $callable ) use ( &$addUpdateCallCounter, $title, $user ) {
$addUpdateCallCounter++;
$this->verifyCallbackJob(
$callable,
$title,
$user->getId(),
function( $time ) {
return $time === false;
}
);
}
);
$getTimestampCallCounter = 0;
$scopedOverrideRevision = $store->overrideRevisionGetTimestampFromIdCallback(
function( $titleParam, $oldidParam ) use ( &$getTimestampCallCounter, $title, $oldid ) {
$getTimestampCallCounter++;
$this->assertEquals( $title, $titleParam );
$this->assertEquals( $oldid, $oldidParam );
}
);
$this->assertTrue(
$store->resetNotificationTimestamp(
$user,
$title,
'',
$oldid
)
);
$this->assertEquals( 1, $addUpdateCallCounter );
$this->assertEquals( 1, $getTimestampCallCounter );
ScopedCallback::consume( $scopedOverrideDeferred );
ScopedCallback::consume( $scopedOverrideRevision );
}
public function testSetNotificationTimestampsForUser_anonUser() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
$this->getMockCache()
);
$this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) );
}
public function testSetNotificationTimestampsForUser_allRows() {
$user = $this->getMockNonAnonUserWithId( 1 );
$timestamp = '20100101010101';
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'update' )
->with(
'watchlist',
[ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ],
[ 'wl_user' => 1 ]
)
->will( $this->returnValue( true ) );
$mockDb->expects( $this->exactly( 1 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
);
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, $timestamp )
);
}
public function testSetNotificationTimestampsForUser_nullTimestamp() {
$user = $this->getMockNonAnonUserWithId( 1 );
$timestamp = null;
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'update' )
->with(
'watchlist',
[ 'wl_notificationtimestamp' => null ],
[ 'wl_user' => 1 ]
)
->will( $this->returnValue( true ) );
$mockDb->expects( $this->exactly( 0 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
);
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, $timestamp )
);
}
public function testSetNotificationTimestampsForUser_specificTargets() {
$user = $this->getMockNonAnonUserWithId( 1 );
$timestamp = '20100101010101';
$targets = [ new TitleValue( 0, 'Foo' ), new TitleValue( 0, 'Bar' ) ];
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'update' )
->with(
'watchlist',
[ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ],
[ 'wl_user' => 1, 0 => 'makeWhereFrom2d return value' ]
)
->will( $this->returnValue( true ) );
$mockDb->expects( $this->exactly( 1 ) )
->method( 'timestamp' )
->will( $this->returnCallback( function( $value ) {
return 'TS' . $value . 'TS';
} ) );
$mockDb->expects( $this->once() )
->method( 'makeWhereFrom2d' )
->with(
[ [ 'Foo' => 1, 'Bar' => 1 ] ],
$this->isType( 'string' ),
$this->isType( 'string' )
)
->will( $this->returnValue( 'makeWhereFrom2d return value' ) );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
);
$this->assertTrue(
$store->setNotificationTimestampsForUser( $user, $timestamp, $targets )
);
}
public function testUpdateNotificationTimestamp_watchersExist() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectFieldValues' )
->with(
'watchlist',
'wl_user',
[
'wl_user != 1',
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
'wl_notificationtimestamp IS NULL'
]
)
->will( $this->returnValue( [ '2', '3' ] ) );
$mockDb->expects( $this->once() )
->method( 'update' )
->with(
'watchlist',
[ 'wl_notificationtimestamp' => null ],
[
'wl_user' => [ 2, 3 ],
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
]
);
$mockCache = $this->getMockCache();
$mockCache->expects( $this->never() )->method( 'set' );
$mockCache->expects( $this->never() )->method( 'get' );
$mockCache->expects( $this->never() )->method( 'delete' );
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$this->assertEquals(
[ 2, 3 ],
$store->updateNotificationTimestamp(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' ),
'20151212010101'
)
);
}
public function testUpdateNotificationTimestamp_noWatchers() {
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectFieldValues' )
->with(
'watchlist',
'wl_user',
[
'wl_user != 1',
'wl_namespace' => 0,
'wl_title' => 'SomeDbKey',
'wl_notificationtimestamp IS NULL'
]
)
->will(
$this->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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
$watchers = $store->updateNotificationTimestamp(
$this->getMockNonAnonUserWithId( 1 ),
new TitleValue( 0, 'SomeDbKey' ),
'20151212010101'
);
$this->assertInternalType( 'array', $watchers );
$this->assertEmpty( $watchers );
}
public function testUpdateNotificationTimestamp_clearsCachedItems() {
$user = $this->getMockNonAnonUserWithId( 1 );
$titleValue = new TitleValue( 0, 'SomeDbKey' );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
->method( 'selectRow' )
->will( $this->returnValue(
$this->getFakeRow( [ 'wl_notificationtimestamp' => '20151212010101' ] )
) );
$mockDb->expects( $this->once() )
->method( 'selectFieldValues' )
->will(
$this->returnValue( [ '2', '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(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
);
// This will add the item to the cache
$store->getWatchedItem( $user, $titleValue );
$store->updateNotificationTimestamp(
$this->getMockNonAnonUserWithId( 1 ),
$titleValue,
'20151212010101'
);
}
}