2013-05-24 22:51:47 +00:00
|
|
|
<?php
|
2018-08-13 07:29:32 +00:00
|
|
|
|
2016-12-08 20:50:03 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
2013-05-24 22:51:47 +00:00
|
|
|
|
2014-07-25 20:19:15 +00:00
|
|
|
/**
|
2018-01-24 00:31:53 +00:00
|
|
|
* @covers MWEchoNotifUser
|
2014-07-25 20:19:15 +00:00
|
|
|
* @group Echo
|
|
|
|
*/
|
2013-05-24 22:51:47 +00:00
|
|
|
class MWEchoNotifUserTest extends MediaWikiTestCase {
|
|
|
|
|
2015-11-01 09:59:16 +00:00
|
|
|
/**
|
2016-12-08 20:50:03 +00:00
|
|
|
* @var WANObjectCache
|
2015-11-01 09:59:16 +00:00
|
|
|
*/
|
|
|
|
private $cache;
|
|
|
|
|
2013-05-24 22:51:47 +00:00
|
|
|
protected function setUp() {
|
|
|
|
parent::setUp();
|
2016-12-08 20:50:03 +00:00
|
|
|
$this->cache = new WANObjectCache( [
|
|
|
|
'cache' => MediaWikiServices::getInstance()->getMainObjectStash(),
|
|
|
|
] );
|
2013-05-24 22:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
2013-06-24 00:22:08 +00:00
|
|
|
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 );
|
2016-05-05 13:05:03 +00:00
|
|
|
$this->assertEquals( $notifUser->getEmailFormat(), EchoEmailFormat::PLAIN_TEXT );
|
2013-06-24 00:22:08 +00:00
|
|
|
}
|
|
|
|
|
2014-08-13 22:00:25 +00:00
|
|
|
public function testMarkRead() {
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
2015-11-01 09:59:16 +00:00
|
|
|
$this->cache,
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => true ] ),
|
2014-08-07 00:07:34 +00:00
|
|
|
$this->mockEchoNotificationMapper(),
|
2016-09-13 20:00:35 +00:00
|
|
|
$this->mockEchoTargetPageMapper()
|
2014-08-13 22:00:25 +00:00
|
|
|
);
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->assertFalse( $notifUser->markRead( [] ) );
|
|
|
|
$this->assertTrue( $notifUser->markRead( [ 1 ] ) );
|
2014-08-13 22:00:25 +00:00
|
|
|
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
2015-11-01 09:59:16 +00:00
|
|
|
$this->cache,
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => false ] ),
|
2014-08-07 00:07:34 +00:00
|
|
|
$this->mockEchoNotificationMapper(),
|
2016-09-13 20:00:35 +00:00
|
|
|
$this->mockEchoTargetPageMapper()
|
2014-08-13 22:00:25 +00:00
|
|
|
);
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->assertFalse( $notifUser->markRead( [] ) );
|
|
|
|
$this->assertFalse( $notifUser->markRead( [ 1 ] ) );
|
2014-08-13 22:00:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testMarkAllRead() {
|
|
|
|
// Successful mark as read & non empty fetch
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
2015-11-01 09:59:16 +00:00
|
|
|
$this->cache,
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => true ] ),
|
|
|
|
$this->mockEchoNotificationMapper( [ $this->mockEchoNotification() ] ),
|
2016-09-13 20:00:35 +00:00
|
|
|
$this->mockEchoTargetPageMapper()
|
2014-08-13 22:00:25 +00:00
|
|
|
);
|
|
|
|
$this->assertTrue( $notifUser->markAllRead() );
|
|
|
|
|
|
|
|
// Unsuccessful mark as read & non empty fetch
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
2015-11-01 09:59:16 +00:00
|
|
|
$this->cache,
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => false ] ),
|
|
|
|
$this->mockEchoNotificationMapper( [ $this->mockEchoNotification() ] ),
|
2016-09-13 20:00:35 +00:00
|
|
|
$this->mockEchoTargetPageMapper()
|
2014-08-13 22:00:25 +00:00
|
|
|
);
|
|
|
|
$this->assertFalse( $notifUser->markAllRead() );
|
|
|
|
|
|
|
|
// Successful mark as read & empty fetch
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
2015-11-01 09:59:16 +00:00
|
|
|
$this->cache,
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => true ] ),
|
2014-08-07 00:07:34 +00:00
|
|
|
$this->mockEchoNotificationMapper(),
|
2016-09-13 20:00:35 +00:00
|
|
|
$this->mockEchoTargetPageMapper()
|
2014-08-13 22:00:25 +00:00
|
|
|
);
|
|
|
|
$this->assertFalse( $notifUser->markAllRead() );
|
|
|
|
|
|
|
|
// Unsuccessful mark as read & empty fetch
|
|
|
|
$notifUser = new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
2015-11-01 09:59:16 +00:00
|
|
|
$this->cache,
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->mockEchoUserNotificationGateway( [ 'markRead' => false ] ),
|
2014-08-07 00:07:34 +00:00
|
|
|
$this->mockEchoNotificationMapper(),
|
2016-09-13 20:00:35 +00:00
|
|
|
$this->mockEchoTargetPageMapper()
|
2014-08-13 22:00:25 +00:00
|
|
|
);
|
|
|
|
$this->assertFalse( $notifUser->markAllRead() );
|
|
|
|
}
|
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
public function mockEchoUserNotificationGateway( array $dbResult = [] ) {
|
|
|
|
$dbResult += [
|
2014-08-13 22:00:25 +00:00
|
|
|
'markRead' => true
|
2016-12-05 18:51:07 +00:00
|
|
|
];
|
2014-08-13 22:00:25 +00:00
|
|
|
$gateway = $this->getMockBuilder( 'EchoUserNotificationGateway' )
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$gateway->expects( $this->any() )
|
|
|
|
->method( 'markRead' )
|
|
|
|
->will( $this->returnValue( $dbResult['markRead'] ) );
|
NotifUser: Redo caching strategy for multi-DC compatibility
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
2018-05-25 17:49:07 +00:00
|
|
|
$gateway->expects( $this->any() )
|
|
|
|
->method( 'getDB' )
|
|
|
|
->will( $this->returnValue(
|
|
|
|
$this->getMockBuilder( Database::class )
|
|
|
|
->disableOriginalConstructor()->getMock()
|
|
|
|
) );
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-08-13 22:00:25 +00:00
|
|
|
return $gateway;
|
|
|
|
}
|
|
|
|
|
2016-12-05 18:51:07 +00:00
|
|
|
public function mockEchoNotificationMapper( array $result = [] ) {
|
2014-08-13 22:00:25 +00:00
|
|
|
$mapper = $this->getMockBuilder( 'EchoNotificationMapper' )
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$mapper->expects( $this->any() )
|
|
|
|
->method( 'fetchUnreadByUser' )
|
|
|
|
->will( $this->returnValue( $result ) );
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-08-13 22:00:25 +00:00
|
|
|
return $mapper;
|
|
|
|
}
|
|
|
|
|
2018-04-12 23:21:09 +00:00
|
|
|
public function mockEchoTargetPageMapper() {
|
|
|
|
return $this->getMockBuilder( EchoTargetPageMapper::class )
|
2014-08-07 00:07:34 +00:00
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
}
|
|
|
|
|
2014-08-13 22:00:25 +00:00
|
|
|
protected function mockEchoNotification() {
|
|
|
|
$notification = $this->getMockBuilder( 'EchoNotification' )
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$notification->expects( $this->any() )
|
|
|
|
->method( 'getEvent' )
|
|
|
|
->will( $this->returnValue( $this->mockEchoEvent() ) );
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-08-13 22:00:25 +00:00
|
|
|
return $notification;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function mockEchoEvent() {
|
|
|
|
$event = $this->getMockBuilder( 'EchoEvent' )
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
$event->expects( $this->any() )
|
|
|
|
->method( 'getId' )
|
|
|
|
->will( $this->returnValue( 1 ) );
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-08-13 22:00:25 +00:00
|
|
|
return $event;
|
|
|
|
}
|
2016-12-08 20:50:03 +00:00
|
|
|
|
|
|
|
protected function newNotifUser() {
|
|
|
|
return new MWEchoNotifUser(
|
|
|
|
User::newFromId( 2 ),
|
|
|
|
$this->cache,
|
|
|
|
$this->mockEchoUserNotificationGateway(),
|
|
|
|
$this->mockEchoNotificationMapper(),
|
|
|
|
$this->mockEchoTargetPageMapper()
|
|
|
|
);
|
|
|
|
}
|
2013-05-24 22:51:47 +00:00
|
|
|
}
|