Merge "mediawiki.content.json: Remove file and author annotations"
[mediawiki.git] / tests / phpunit / includes / user / TalkPageNotificationManagerTest.php
blob502ff813d4ccc0809726d90106907f5056bc939e
1 <?php
3 use MediaWiki\Config\HashConfig;
4 use MediaWiki\Config\ServiceOptions;
5 use MediaWiki\Deferred\DeferredUpdates;
6 use MediaWiki\MainConfigNames;
7 use MediaWiki\Revision\MutableRevisionRecord;
8 use MediaWiki\Revision\RevisionLookup;
9 use MediaWiki\Revision\RevisionRecord;
10 use MediaWiki\Tests\Unit\DummyServicesTrait;
11 use MediaWiki\Title\Title;
12 use MediaWiki\User\TalkPageNotificationManager;
13 use MediaWiki\User\UserIdentity;
14 use MediaWiki\User\UserIdentityValue;
15 use MediaWiki\Utils\MWTimestamp;
16 use PHPUnit\Framework\AssertionFailedError;
18 /**
19 * @covers \MediaWiki\User\TalkPageNotificationManager
20 * @group Database
22 class TalkPageNotificationManagerTest extends MediaWikiIntegrationTestCase {
23 use DummyServicesTrait;
25 private function editUserTalk( UserIdentity $user, string $text ): RevisionRecord {
26 // UserIdentity doesn't have getUserPage/getTalkPage, but we can easily recreate
27 // it, and its easier than needing to depend on a full user object
28 $userTalk = Title::makeTitle( NS_USER_TALK, $user->getName() );
29 $status = $this->editPage(
30 $userTalk,
31 $text,
32 '',
33 NS_MAIN,
34 $this->getTestSysop()->getUser()
36 $this->assertStatusGood( $status, 'create revision of user talk' );
37 return $status->getNewRevision();
40 private function getManager(
41 bool $disableAnonTalk = false,
42 bool $isReadOnly = false,
43 ?RevisionLookup $revisionLookup = null
44 ) {
45 $services = $this->getServiceContainer();
46 return new TalkPageNotificationManager(
47 new ServiceOptions(
48 TalkPageNotificationManager::CONSTRUCTOR_OPTIONS,
49 new HashConfig( [
50 MainConfigNames::DisableAnonTalk => $disableAnonTalk
51 ] )
53 $services->getConnectionProvider(),
54 $this->getDummyReadOnlyMode( $isReadOnly ),
55 $revisionLookup ?? $services->getRevisionLookup(),
56 $this->createHookContainer(),
57 $services->getUserFactory()
61 public static function provideUserHasNewMessages() {
62 yield 'Registered user' => [ UserIdentityValue::newRegistered( 123, 'MyName' ) ];
63 yield 'Anonymous user' => [ UserIdentityValue::newAnonymous( '1.2.3.4' ) ];
66 /**
67 * @dataProvider provideUserHasNewMessages
68 * @covers \MediaWiki\User\TalkPageNotificationManager::userHasNewMessages
69 * @covers \MediaWiki\User\TalkPageNotificationManager::setUserHasNewMessages
70 * @covers \MediaWiki\User\TalkPageNotificationManager::clearInstanceCache
71 * @covers \MediaWiki\User\TalkPageNotificationManager::removeUserHasNewMessages
73 public function testUserHasNewMessages( UserIdentity $user ) {
74 $manager = $this->getManager();
75 $this->assertFalse( $manager->userHasNewMessages( $user ),
76 'Should be false before updated' );
77 $revRecord = $this->editUserTalk( $user, __METHOD__ );
78 $manager->setUserHasNewMessages( $user, $revRecord );
79 $this->assertTrue( $manager->userHasNewMessages( $user ),
80 'Should be true after updated' );
81 $manager->clearInstanceCache( $user );
82 $this->assertTrue( $manager->userHasNewMessages( $user ),
83 'Should be true after cache cleared' );
84 $manager->removeUserHasNewMessages( $user );
85 $this->assertFalse( $manager->userHasNewMessages( $user ),
86 'Should be false after updated' );
87 $manager->clearInstanceCache( $user );
88 $this->assertFalse( $manager->userHasNewMessages( $user ),
89 'Should be false after cache cleared' );
90 $manager->setUserHasNewMessages( $user, null );
91 $this->assertTrue( $manager->userHasNewMessages( $user ),
92 'Should be true after updated' );
93 $manager->removeUserHasNewMessages( $user );
94 $this->assertFalse( $manager->userHasNewMessages( $user ),
95 'Should be false after updated' );
98 /**
99 * @covers \MediaWiki\User\TalkPageNotificationManager::userHasNewMessages
100 * @covers \MediaWiki\User\TalkPageNotificationManager::setUserHasNewMessages
102 public function testUserHasNewMessagesDisabledAnon() {
103 $user = new UserIdentityValue( 0, '1.2.3.4' );
104 $revRecord = $this->editUserTalk( $user, __METHOD__ );
105 $manager = $this->getManager( true );
106 $this->assertFalse( $manager->userHasNewMessages( $user ),
107 'New anon should have no new messages' );
108 $manager->setUserHasNewMessages( $user, $revRecord );
109 $this->assertFalse( $manager->userHasNewMessages( $user ),
110 'Must not set new messages for anon if disabled' );
111 $manager->clearInstanceCache( $user );
112 $this->assertFalse( $manager->userHasNewMessages( $user ),
113 'Must not set to database if anon messages disabled' );
117 * @covers \MediaWiki\User\TalkPageNotificationManager::getLatestSeenMessageTimestamp
119 public function testGetLatestSeenMessageTimestamp() {
120 $user = $this->getTestUser()->getUser();
121 $firstRev = $this->editUserTalk( $user, __METHOD__ . ' 1' );
122 $secondRev = $this->editUserTalk( $user, __METHOD__ . ' 2' );
123 $manager = $this->getManager();
124 $manager->setUserHasNewMessages( $user, $secondRev );
125 $this->assertSame( $firstRev->getTimestamp(), $manager->getLatestSeenMessageTimestamp( $user ) );
129 * @covers \MediaWiki\User\TalkPageNotificationManager::getLatestSeenMessageTimestamp
131 public function testGetLatestSeenMessageTimestampOutOfOrderRevision() {
132 $user = $this->getTestUser()->getUser();
133 $firstRev = $this->editUserTalk( $user, __METHOD__ . ' 1' );
134 $secondRev = $this->editUserTalk( $user, __METHOD__ . ' 2' );
135 $thirdRev = $this->editUserTalk( $user, __METHOD__ . ' 3' );
136 $veryOldTimestamp = MWTimestamp::convert( TS_MW, 1 );
137 $mockOldRev = $this->createMock( RevisionRecord::class );
138 $mockOldRev->method( 'getTimestamp' )
139 ->willReturn( $veryOldTimestamp );
140 $mockRevLookup = $this->getMockForAbstractClass( RevisionLookup::class );
141 $mockRevLookup->method( 'getPreviousRevision' )
142 ->willReturnCallback( static function ( RevisionRecord $rev )
143 use ( $firstRev, $secondRev, $thirdRev, $mockOldRev )
145 if ( $rev === $secondRev ) {
146 return $firstRev;
148 if ( $rev === $thirdRev ) {
149 return $mockOldRev;
151 throw new AssertionFailedError(
152 'RevisionLookup::getPreviousRevision called with wrong rev ' . $rev->getId()
154 } );
155 $manager = $this->getManager( false, false, $mockRevLookup );
156 $manager->setUserHasNewMessages( $user, $thirdRev );
157 $this->assertSame( $veryOldTimestamp, $manager->getLatestSeenMessageTimestamp( $user ) );
158 $manager->setUserHasNewMessages( $user, $secondRev );
159 $this->assertSame( $veryOldTimestamp, $manager->getLatestSeenMessageTimestamp( $user ) );
163 * @covers \MediaWiki\User\TalkPageNotificationManager::getLatestSeenMessageTimestamp
165 public function testGetLatestSeenMessageTimestampNoNewMessages() {
166 $user = $this->getTestUser()->getUser();
167 $manager = $this->getManager();
168 $this->assertNull( $manager->getLatestSeenMessageTimestamp( $user ),
169 'Must be null if no new messages' );
173 * @covers \MediaWiki\User\TalkPageNotificationManager::userHasNewMessages
174 * @covers \MediaWiki\User\TalkPageNotificationManager::setUserHasNewMessages
175 * @covers \MediaWiki\User\TalkPageNotificationManager::removeUserHasNewMessages
177 public function testDoesNotCrashOnReadOnly() {
178 $user = $this->getTestUser()->getUser();
179 $this->editUserTalk( $user, __METHOD__ );
181 $manager = $this->getManager( false, true );
182 $this->assertTrue( $manager->userHasNewMessages( $user ) );
183 $manager->removeUserHasNewMessages( $user );
184 $this->assertFalse( $manager->userHasNewMessages( $user ) );
188 * @covers \MediaWiki\User\TalkPageNotificationManager::clearForPageView
190 public function testClearForPageView() {
191 $user = $this->getTestUser()->getUser();
192 $title = $user->getTalkPage();
193 $revision = new MutableRevisionRecord( $title );
194 $revision->setPageId( 100 );
195 $revision->setId( 101 );
196 $manager = $this->getManager();
197 $manager->setUserHasNewMessages( $user );
198 $this->assertTrue( $manager->userHasNewMessages( $user ) );
200 // DB should have the notification
201 $this->newSelectQueryBuilder()
202 ->select( 'user_id' )
203 ->from( 'user_newtalk' )
204 ->where( [ 'user_id' => $user->getId() ] )
205 ->assertFieldValue( $user->getId() );
207 $this->getDb()->startAtomic( __METHOD__ ); // let deferred updates queue up
209 $updateCountBefore = DeferredUpdates::pendingUpdatesCount();
210 $manager->clearForPageView( $user, $revision );
211 // Cache should already be updated
212 $this->assertFalse( $manager->userHasNewMessages( $user ) );
214 $updateCountAfter = DeferredUpdates::pendingUpdatesCount();
215 $this->assertGreaterThan( $updateCountBefore, $updateCountAfter, 'An update should have been queued' );
217 $this->getDb()->endAtomic( __METHOD__ ); // run deferred updates
218 $this->runDeferredUpdates();
220 $this->assertSame( 0, DeferredUpdates::pendingUpdatesCount(), 'No pending updates' );
222 // Notification should have been deleted from the DB
223 $this->newSelectQueryBuilder()
224 ->select( 'user_id' )
225 ->from( 'user_newtalk' )
226 ->where( [ 'user_id' => $user->getId() ] )
227 ->assertEmptyResult();