Update rows in batches, using the same logic as is used by removeWatchBatchForUser(). Also remove the functionality for updating all rows, and move that to resetAllNotificationTimestampsForUser() instead. To that end, add a timestamp parameter to that method and to the job it uses, and make setNotificationTimestampsForUser() behave like a backwards-compatibility wrapper around resetAllNotificationTimestampsForUser() when no list of titles is specified. Bug: T207941 Change-Id: I58342257395de6fcfb4c392b3945b12883ca1680 Follows-Up: I2008ff89c95fe6f66a3fd789d2cef0e8fe52bd93
248 lines
8.8 KiB
PHP
248 lines
8.8 KiB
PHP
<?php
|
|
|
|
use MediaWiki\MediaWikiServices;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @author Addshore
|
|
*
|
|
* @group Database
|
|
*
|
|
* @covers WatchedItemStore
|
|
*/
|
|
class WatchedItemStoreIntegrationTest extends MediaWikiTestCase {
|
|
|
|
public function setUp() {
|
|
parent::setUp();
|
|
self::$users['WatchedItemStoreIntegrationTestUser']
|
|
= new TestUser( 'WatchedItemStoreIntegrationTestUser' );
|
|
}
|
|
|
|
private function getUser() {
|
|
return self::$users['WatchedItemStoreIntegrationTestUser']->getUser();
|
|
}
|
|
|
|
public function testWatchAndUnWatchItem() {
|
|
$user = $this->getUser();
|
|
$title = Title::newFromText( 'WatchedItemStoreIntegrationTestPage' );
|
|
$store = MediaWikiServices::getInstance()->getWatchedItemStore();
|
|
// Cleanup after previous tests
|
|
$store->removeWatch( $user, $title );
|
|
$initialWatchers = $store->countWatchers( $title );
|
|
$initialUserWatchedItems = $store->countWatchedItems( $user );
|
|
|
|
$this->assertFalse(
|
|
$store->isWatched( $user, $title ),
|
|
'Page should not initially be watched'
|
|
);
|
|
|
|
$store->addWatch( $user, $title );
|
|
$this->assertTrue(
|
|
$store->isWatched( $user, $title ),
|
|
'Page should be watched'
|
|
);
|
|
$this->assertEquals( $initialUserWatchedItems + 1, $store->countWatchedItems( $user ) );
|
|
$watchedItemsForUser = $store->getWatchedItemsForUser( $user );
|
|
$this->assertCount( $initialUserWatchedItems + 1, $watchedItemsForUser );
|
|
$watchedItemsForUserHasExpectedItem = false;
|
|
foreach ( $watchedItemsForUser as $watchedItem ) {
|
|
if (
|
|
$watchedItem->getUser()->equals( $user ) &&
|
|
$watchedItem->getLinkTarget() == $title->getTitleValue()
|
|
) {
|
|
$watchedItemsForUserHasExpectedItem = true;
|
|
}
|
|
}
|
|
$this->assertTrue(
|
|
$watchedItemsForUserHasExpectedItem,
|
|
'getWatchedItemsForUser should contain the page'
|
|
);
|
|
$this->assertEquals( $initialWatchers + 1, $store->countWatchers( $title ) );
|
|
$this->assertEquals(
|
|
$initialWatchers + 1,
|
|
$store->countWatchersMultiple( [ $title ] )[$title->getNamespace()][$title->getDBkey()]
|
|
);
|
|
$this->assertEquals(
|
|
[ 0 => [ 'WatchedItemStoreIntegrationTestPage' => $initialWatchers + 1 ] ],
|
|
$store->countWatchersMultiple( [ $title ], [ 'minimumWatchers' => $initialWatchers + 1 ] )
|
|
);
|
|
$this->assertEquals(
|
|
[ 0 => [ 'WatchedItemStoreIntegrationTestPage' => 0 ] ],
|
|
$store->countWatchersMultiple( [ $title ], [ 'minimumWatchers' => $initialWatchers + 2 ] )
|
|
);
|
|
$this->assertEquals(
|
|
[ $title->getNamespace() => [ $title->getDBkey() => null ] ],
|
|
$store->getNotificationTimestampsBatch( $user, [ $title ] )
|
|
);
|
|
|
|
$store->removeWatch( $user, $title );
|
|
$this->assertFalse(
|
|
$store->isWatched( $user, $title ),
|
|
'Page should be unwatched'
|
|
);
|
|
$this->assertEquals( $initialUserWatchedItems, $store->countWatchedItems( $user ) );
|
|
$watchedItemsForUser = $store->getWatchedItemsForUser( $user );
|
|
$this->assertCount( $initialUserWatchedItems, $watchedItemsForUser );
|
|
$watchedItemsForUserHasExpectedItem = false;
|
|
foreach ( $watchedItemsForUser as $watchedItem ) {
|
|
if (
|
|
$watchedItem->getUser()->equals( $user ) &&
|
|
$watchedItem->getLinkTarget() == $title->getTitleValue()
|
|
) {
|
|
$watchedItemsForUserHasExpectedItem = true;
|
|
}
|
|
}
|
|
$this->assertFalse(
|
|
$watchedItemsForUserHasExpectedItem,
|
|
'getWatchedItemsForUser should not contain the page'
|
|
);
|
|
$this->assertEquals( $initialWatchers, $store->countWatchers( $title ) );
|
|
$this->assertEquals(
|
|
$initialWatchers,
|
|
$store->countWatchersMultiple( [ $title ] )[$title->getNamespace()][$title->getDBkey()]
|
|
);
|
|
$this->assertEquals(
|
|
[ $title->getNamespace() => [ $title->getDBkey() => false ] ],
|
|
$store->getNotificationTimestampsBatch( $user, [ $title ] )
|
|
);
|
|
}
|
|
|
|
public function testWatchBatchAndClearItems() {
|
|
$user = $this->getUser();
|
|
$title1 = Title::newFromText( 'WatchedItemStoreIntegrationTestPage1' );
|
|
$title2 = Title::newFromText( 'WatchedItemStoreIntegrationTestPage2' );
|
|
$store = MediaWikiServices::getInstance()->getWatchedItemStore();
|
|
|
|
$store->addWatchBatchForUser( $user, [ $title1, $title2 ] );
|
|
|
|
$this->assertTrue( $store->isWatched( $user, $title1 ) );
|
|
$this->assertTrue( $store->isWatched( $user, $title2 ) );
|
|
|
|
$store->clearUserWatchedItems( $user );
|
|
|
|
$this->assertFalse( $store->isWatched( $user, $title1 ) );
|
|
$this->assertFalse( $store->isWatched( $user, $title2 ) );
|
|
}
|
|
|
|
public function testUpdateResetAndSetNotificationTimestamp() {
|
|
$user = $this->getUser();
|
|
$otherUser = ( new TestUser( 'WatchedItemStoreIntegrationTestUser_otherUser' ) )->getUser();
|
|
$title = Title::newFromText( 'WatchedItemStoreIntegrationTestPage' );
|
|
$store = MediaWikiServices::getInstance()->getWatchedItemStore();
|
|
$store->addWatch( $user, $title );
|
|
$this->assertNull( $store->loadWatchedItem( $user, $title )->getNotificationTimestamp() );
|
|
$initialVisitingWatchers = $store->countVisitingWatchers( $title, '20150202020202' );
|
|
$initialUnreadNotifications = $store->countUnreadNotifications( $user );
|
|
|
|
$store->updateNotificationTimestamp( $otherUser, $title, '20150202010101' );
|
|
$this->assertEquals(
|
|
'20150202010101',
|
|
$store->loadWatchedItem( $user, $title )->getNotificationTimestamp()
|
|
);
|
|
$this->assertEquals(
|
|
[ $title->getNamespace() => [ $title->getDBkey() => '20150202010101' ] ],
|
|
$store->getNotificationTimestampsBatch( $user, [ $title ] )
|
|
);
|
|
$this->assertEquals(
|
|
$initialVisitingWatchers - 1,
|
|
$store->countVisitingWatchers( $title, '20150202020202' )
|
|
);
|
|
$this->assertEquals(
|
|
$initialVisitingWatchers - 1,
|
|
$store->countVisitingWatchersMultiple(
|
|
[ [ $title, '20150202020202' ] ]
|
|
)[$title->getNamespace()][$title->getDBkey()]
|
|
);
|
|
$this->assertEquals(
|
|
$initialUnreadNotifications + 1,
|
|
$store->countUnreadNotifications( $user )
|
|
);
|
|
$this->assertSame(
|
|
true,
|
|
$store->countUnreadNotifications( $user, $initialUnreadNotifications + 1 )
|
|
);
|
|
|
|
$this->assertTrue( $store->resetNotificationTimestamp( $user, $title ) );
|
|
$this->assertNull( $store->getWatchedItem( $user, $title )->getNotificationTimestamp() );
|
|
$this->assertEquals(
|
|
[ $title->getNamespace() => [ $title->getDBkey() => null ] ],
|
|
$store->getNotificationTimestampsBatch( $user, [ $title ] )
|
|
);
|
|
|
|
// Run the job queue
|
|
JobQueueGroup::destroySingletons();
|
|
$jobs = new RunJobs;
|
|
$jobs->loadParamsAndArgs( null, [ 'quiet' => true ], null );
|
|
$jobs->execute();
|
|
|
|
$this->assertEquals(
|
|
$initialVisitingWatchers,
|
|
$store->countVisitingWatchers( $title, '20150202020202' )
|
|
);
|
|
$this->assertEquals(
|
|
$initialVisitingWatchers,
|
|
$store->countVisitingWatchersMultiple(
|
|
[ [ $title, '20150202020202' ] ]
|
|
)[$title->getNamespace()][$title->getDBkey()]
|
|
);
|
|
$this->assertEquals(
|
|
[ 0 => [ 'WatchedItemStoreIntegrationTestPage' => $initialVisitingWatchers ] ],
|
|
$store->countVisitingWatchersMultiple(
|
|
[ [ $title, '20150202020202' ] ], $initialVisitingWatchers
|
|
)
|
|
);
|
|
$this->assertEquals(
|
|
[ 0 => [ 'WatchedItemStoreIntegrationTestPage' => 0 ] ],
|
|
$store->countVisitingWatchersMultiple(
|
|
[ [ $title, '20150202020202' ] ], $initialVisitingWatchers + 1
|
|
)
|
|
);
|
|
|
|
// setNotificationTimestampsForUser specifying a title
|
|
$this->assertTrue(
|
|
$store->setNotificationTimestampsForUser( $user, '20100202020202', [ $title ] )
|
|
);
|
|
$this->assertEquals(
|
|
'20100202020202',
|
|
$store->getWatchedItem( $user, $title )->getNotificationTimestamp()
|
|
);
|
|
|
|
// setNotificationTimestampsForUser not specifying a title
|
|
// This will try to use a DeferredUpdate; disable that
|
|
$mockCallback = function ( $callback ) {
|
|
$callback();
|
|
};
|
|
$scopedOverride = $store->overrideDeferredUpdatesAddCallableUpdateCallback( $mockCallback );
|
|
$this->assertTrue(
|
|
$store->setNotificationTimestampsForUser( $user, '20110202020202' )
|
|
);
|
|
// Because the operation above is normally deferred, it doesn't clear the cache
|
|
// Clear the cache manually
|
|
$wrappedStore = TestingAccessWrapper::newFromObject( $store );
|
|
$wrappedStore->uncacheUser( $user );
|
|
$this->assertEquals(
|
|
'20110202020202',
|
|
$store->getWatchedItem( $user, $title )->getNotificationTimestamp()
|
|
);
|
|
}
|
|
|
|
public function testDuplicateAllAssociatedEntries() {
|
|
$user = $this->getUser();
|
|
$titleOld = Title::newFromText( 'WatchedItemStoreIntegrationTestPageOld' );
|
|
$titleNew = Title::newFromText( 'WatchedItemStoreIntegrationTestPageNew' );
|
|
$store = MediaWikiServices::getInstance()->getWatchedItemStore();
|
|
$store->addWatch( $user, $titleOld->getSubjectPage() );
|
|
$store->addWatch( $user, $titleOld->getTalkPage() );
|
|
// Cleanup after previous tests
|
|
$store->removeWatch( $user, $titleNew->getSubjectPage() );
|
|
$store->removeWatch( $user, $titleNew->getTalkPage() );
|
|
|
|
$store->duplicateAllAssociatedEntries( $titleOld, $titleNew );
|
|
|
|
$this->assertTrue( $store->isWatched( $user, $titleOld->getSubjectPage() ) );
|
|
$this->assertTrue( $store->isWatched( $user, $titleOld->getTalkPage() ) );
|
|
$this->assertTrue( $store->isWatched( $user, $titleNew->getSubjectPage() ) );
|
|
$this->assertTrue( $store->isWatched( $user, $titleNew->getTalkPage() ) );
|
|
}
|
|
|
|
}
|