mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
synced 2024-11-15 03:35:01 +00:00
d90e2d1066
To use WANObjectCache correctly in a multi-DC-safe way, we need to use getWithSetCallback() to read data, and call delete() when it changes. NotifUser's caching of notification counts and timestamps relied heavily on set() calls, and so wasn't multi-DC-safe. Changes in this commit: * Rather than caching counts/timestamps in separate cache keys, and using separate cache keys for each section (alert/message/all), put all this data in an array and store that in a single cache key. This reduces the number of cache keys per user per wiki from 6 to 1. * Similarly, use a single global cache key per user. The global check key for the last updated timestamp is retained, so we now have 2 global cache keys per user (down from 7) * Remove preloading using getMulti(), no longer needed * Move computation of counts and timestamps into separate compute functions (one for local, one for global), and wrap them with a getter that uses getWithSetCallback(). * Use TS_MW strings instead of MWTimestamp objects internally, to simplify comparisons and max() operations. * Make existing getters wrap around this new getter. They now ignore their $cached and $dbSource parameters, and we should deprecate/change these function signatures. * In resetNotificationCounts(), just delete the cache keys. In global mode, also recompute the notification counts and put them in the echo_unread_wikis table. We could also set() the data into the cache at this point, but don't, because you're not supposed to mix set() and getWithSetCallback() calls and I don't want to find out what happens if you do. Bug: T164860 Change-Id: I4f86aab11d50d20280a33e0504ba8ad0c6c01842
219 lines
6.3 KiB
PHP
219 lines
6.3 KiB
PHP
<?php
|
|
use MediaWiki\MediaWikiServices;
|
|
|
|
/**
|
|
* @covers MWEchoNotifUser
|
|
* @group Echo
|
|
*/
|
|
class MWEchoNotifUserTest extends MediaWikiTestCase {
|
|
|
|
/**
|
|
* @var WANObjectCache
|
|
*/
|
|
private $cache;
|
|
|
|
protected function setUp() {
|
|
parent::setUp();
|
|
$this->cache = new WANObjectCache( [
|
|
'cache' => MediaWikiServices::getInstance()->getMainObjectStash(),
|
|
] );
|
|
}
|
|
|
|
public function testNewFromUser() {
|
|
$exception = false;
|
|
try {
|
|
MWEchoNotifUser::newFromUser( User::newFromId( 0 ) );
|
|
} catch ( Exception $e ) {
|
|
$exception = true;
|
|
$this->assertEquals( "User must be logged in to view notification!",
|
|
$e->getMessage() );
|
|
}
|
|
$this->assertTrue( $exception, "Got exception" );
|
|
|
|
$notifUser = MWEchoNotifUser::newFromUser( User::newFromId( 2 ) );
|
|
$this->assertInstanceOf( 'MWEchoNotifUser', $notifUser );
|
|
}
|
|
|
|
public function testFlagCacheWithNewTalkNotification() {
|
|
$notifUser = $this->newNotifUser();
|
|
|
|
$notifUser->flagCacheWithNewTalkNotification();
|
|
$this->assertEquals( '1', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) );
|
|
}
|
|
|
|
public function testFlagCacheWithNoTalkNotification() {
|
|
$notifUser = $this->newNotifUser();
|
|
|
|
$notifUser->flagCacheWithNoTalkNotification();
|
|
$this->assertEquals( '0', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) );
|
|
}
|
|
|
|
public function testNotifCountHasReachedMax() {
|
|
$notifUser = $this->newNotifUser();
|
|
|
|
if ( $notifUser->getLocalNotificationCount() > MWEchoNotifUser::MAX_BADGE_COUNT ) {
|
|
$this->assertTrue( $notifUser->notifCountHasReachedMax() );
|
|
} else {
|
|
$this->assertFalse( $notifUser->notifCountHasReachedMax() );
|
|
}
|
|
}
|
|
|
|
public function testClearTalkNotification() {
|
|
$notifUser = $this->newNotifUser();
|
|
|
|
$notifUser->flagCacheWithNewTalkNotification();
|
|
|
|
$hasMax = $notifUser->notifCountHasReachedMax();
|
|
|
|
$notifUser->clearTalkNotification();
|
|
if ( $hasMax ) {
|
|
$this->assertEquals( '1', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) );
|
|
} else {
|
|
$this->assertEquals( '0', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) );
|
|
}
|
|
}
|
|
|
|
public function testGetEmailFormat() {
|
|
$user = User::newFromId( 2 );
|
|
$notifUser = MWEchoNotifUser::newFromUser( $user );
|
|
|
|
$this->setMwGlobals( 'wgAllowHTMLEmail', true );
|
|
$this->assertEquals( $notifUser->getEmailFormat(), $user->getOption( 'echo-email-format' ) );
|
|
$this->setMwGlobals( 'wgAllowHTMLEmail', false );
|
|
$this->assertEquals( $notifUser->getEmailFormat(), EchoEmailFormat::PLAIN_TEXT );
|
|
}
|
|
|
|
public function testMarkRead() {
|
|
$notifUser = new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => true ] ),
|
|
$this->mockEchoNotificationMapper(),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
$this->assertFalse( $notifUser->markRead( [] ) );
|
|
$this->assertTrue( $notifUser->markRead( [ 1 ] ) );
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => false ] ),
|
|
$this->mockEchoNotificationMapper(),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
$this->assertFalse( $notifUser->markRead( [] ) );
|
|
$this->assertFalse( $notifUser->markRead( [ 1 ] ) );
|
|
}
|
|
|
|
public function testMarkAllRead() {
|
|
// Successful mark as read & non empty fetch
|
|
$notifUser = new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => true ] ),
|
|
$this->mockEchoNotificationMapper( [ $this->mockEchoNotification() ] ),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
$this->assertTrue( $notifUser->markAllRead() );
|
|
|
|
// Unsuccessful mark as read & non empty fetch
|
|
$notifUser = new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => false ] ),
|
|
$this->mockEchoNotificationMapper( [ $this->mockEchoNotification() ] ),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
$this->assertFalse( $notifUser->markAllRead() );
|
|
|
|
// Successful mark as read & empty fetch
|
|
$notifUser = new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => true ] ),
|
|
$this->mockEchoNotificationMapper(),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
$this->assertFalse( $notifUser->markAllRead() );
|
|
|
|
// Unsuccessful mark as read & empty fetch
|
|
$notifUser = new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => false ] ),
|
|
$this->mockEchoNotificationMapper(),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
$this->assertFalse( $notifUser->markAllRead() );
|
|
}
|
|
|
|
public function mockEchoUserNotificationGateway( array $dbResult = [] ) {
|
|
$dbResult += [
|
|
'markRead' => true
|
|
];
|
|
$gateway = $this->getMockBuilder( 'EchoUserNotificationGateway' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$gateway->expects( $this->any() )
|
|
->method( 'markRead' )
|
|
->will( $this->returnValue( $dbResult['markRead'] ) );
|
|
$gateway->expects( $this->any() )
|
|
->method( 'getDB' )
|
|
->will( $this->returnValue(
|
|
$this->getMockBuilder( Database::class )
|
|
->disableOriginalConstructor()->getMock()
|
|
) );
|
|
|
|
return $gateway;
|
|
}
|
|
|
|
public function mockEchoNotificationMapper( array $result = [] ) {
|
|
$mapper = $this->getMockBuilder( 'EchoNotificationMapper' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$mapper->expects( $this->any() )
|
|
->method( 'fetchUnreadByUser' )
|
|
->will( $this->returnValue( $result ) );
|
|
|
|
return $mapper;
|
|
}
|
|
|
|
public function mockEchoTargetPageMapper() {
|
|
return $this->getMockBuilder( EchoTargetPageMapper::class )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
}
|
|
|
|
protected function mockEchoNotification() {
|
|
$notification = $this->getMockBuilder( 'EchoNotification' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$notification->expects( $this->any() )
|
|
->method( 'getEvent' )
|
|
->will( $this->returnValue( $this->mockEchoEvent() ) );
|
|
|
|
return $notification;
|
|
}
|
|
|
|
protected function mockEchoEvent() {
|
|
$event = $this->getMockBuilder( 'EchoEvent' )
|
|
->disableOriginalConstructor()
|
|
->getMock();
|
|
$event->expects( $this->any() )
|
|
->method( 'getId' )
|
|
->will( $this->returnValue( 1 ) );
|
|
|
|
return $event;
|
|
}
|
|
|
|
protected function newNotifUser() {
|
|
return new MWEchoNotifUser(
|
|
User::newFromId( 2 ),
|
|
$this->cache,
|
|
$this->mockEchoUserNotificationGateway(),
|
|
$this->mockEchoNotificationMapper(),
|
|
$this->mockEchoTargetPageMapper()
|
|
);
|
|
}
|
|
}
|