Файловый менеджер - Редактировать - /var/www/html/recentchanges.zip
Ðазад
PK ! Q�S S CategoryMembershipChangeTest.phpnu �Iw�� <?php use MediaWiki\Revision\RevisionRecord; use MediaWiki\Title\Title; use MediaWiki\User\UserIdentity; /** * @covers \CategoryMembershipChange * * @group Database * * @author Addshore */ class CategoryMembershipChangeTest extends MediaWikiLangTestCase { /** * @var array */ private static $lastNotifyArgs; /** * @var int */ private static $notifyCallCounter = 0; /** * @var RecentChange */ private static $mockRecentChange; /** * @var RevisionRecord */ private static $pageRev = null; /** * @var UserIdentity */ private static $revUser = null; /** * @var string */ private static $pageName = 'CategoryMembershipChangeTestPage'; public static function newForCategorizationCallback( ...$args ) { self::$lastNotifyArgs = $args; self::$notifyCallCounter += 1; return self::$mockRecentChange; } protected function setUp(): void { parent::setUp(); self::$notifyCallCounter = 0; self::$mockRecentChange = $this->createMock( RecentChange::class ); $this->setContentLang( 'qqx' ); } public function addDBDataOnce() { $info = $this->insertPage( self::$pageName ); $title = $info['title']; $page = $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $title ); self::$pageRev = $page->getRevisionRecord(); self::$revUser = self::$pageRev->getUser( RevisionRecord::RAW ); } private function newChange( ?RevisionRecord $revision = null ) { $title = Title::makeTitle( NS_MAIN, self::$pageName ); $blcFactory = $this->getServiceContainer()->getBacklinkCacheFactory(); $change = new CategoryMembershipChange( $title, $blcFactory->getBacklinkCache( $title ), $revision ); $change->overrideNewForCategorizationCallback( 'CategoryMembershipChangeTest::newForCategorizationCallback' ); return $change; } public function testChangeAddedNoRev() { $change = $this->newChange(); $change->triggerCategoryAddedNotification( Title::makeTitle( NS_CATEGORY, 'CategoryName' ) ); $this->assertSame( 1, self::$notifyCallCounter ); $this->assertSame( 14, strlen( self::$lastNotifyArgs[0] ) ); $this->assertEquals( 'Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText() ); $this->assertEquals( '(autochange-username)', self::$lastNotifyArgs[2]->getName() ); $this->assertEquals( '(recentchanges-page-added-to-category: ' . self::$pageName . ')', self::$lastNotifyArgs[3] ); $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() ); $this->assertSame( 0, self::$lastNotifyArgs[5] ); $this->assertSame( 0, self::$lastNotifyArgs[6] ); $this->assertNull( self::$lastNotifyArgs[7] ); $this->assertSame( 1, self::$lastNotifyArgs[8] ); $this->assertSame( '', self::$lastNotifyArgs[9] ); $this->assertSame( 0, self::$lastNotifyArgs[10] ); } public function testChangeRemovedNoRev() { $change = $this->newChange(); $change->triggerCategoryRemovedNotification( Title::makeTitle( NS_CATEGORY, 'CategoryName' ) ); $this->assertSame( 1, self::$notifyCallCounter ); $this->assertSame( 14, strlen( self::$lastNotifyArgs[0] ) ); $this->assertEquals( 'Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText() ); $this->assertEquals( '(autochange-username)', self::$lastNotifyArgs[2]->getName() ); $this->assertEquals( '(recentchanges-page-removed-from-category: ' . self::$pageName . ')', self::$lastNotifyArgs[3] ); $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() ); $this->assertSame( 0, self::$lastNotifyArgs[5] ); $this->assertSame( 0, self::$lastNotifyArgs[6] ); $this->assertNull( self::$lastNotifyArgs[7] ); $this->assertSame( 1, self::$lastNotifyArgs[8] ); $this->assertSame( '', self::$lastNotifyArgs[9] ); $this->assertSame( 0, self::$lastNotifyArgs[10] ); } public function testChangeAddedWithRev() { $revision = $this->getServiceContainer() ->getRevisionLookup() ->getRevisionByTitle( Title::makeTitle( NS_MAIN, self::$pageName ) ); $change = $this->newChange( $revision ); $change->triggerCategoryAddedNotification( Title::makeTitle( NS_CATEGORY, 'CategoryName' ) ); $this->assertSame( 1, self::$notifyCallCounter ); $this->assertSame( 14, strlen( self::$lastNotifyArgs[0] ) ); $this->assertEquals( 'Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText() ); $this->assertEquals( self::$revUser->getName(), self::$lastNotifyArgs[2]->getName() ); $this->assertEquals( '(recentchanges-page-added-to-category: ' . self::$pageName . ')', self::$lastNotifyArgs[3] ); $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() ); $this->assertEquals( self::$pageRev->getParentId(), self::$lastNotifyArgs[5] ); $this->assertEquals( $revision->getId(), self::$lastNotifyArgs[6] ); $this->assertNull( self::$lastNotifyArgs[7] ); $this->assertSame( 0, self::$lastNotifyArgs[8] ); $this->assertEquals( '127.0.0.1', self::$lastNotifyArgs[9] ); $this->assertSame( 0, self::$lastNotifyArgs[10] ); } public function testChangeRemovedWithRev() { $revision = $this->getServiceContainer() ->getRevisionLookup() ->getRevisionByTitle( Title::makeTitle( NS_MAIN, self::$pageName ) ); $change = $this->newChange( $revision ); $change->triggerCategoryRemovedNotification( Title::makeTitle( NS_CATEGORY, 'CategoryName' ) ); $this->assertSame( 1, self::$notifyCallCounter ); $this->assertSame( 14, strlen( self::$lastNotifyArgs[0] ) ); $this->assertEquals( 'Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText() ); $this->assertEquals( self::$revUser->getName(), self::$lastNotifyArgs[2]->getName() ); $this->assertEquals( '(recentchanges-page-removed-from-category: ' . self::$pageName . ')', self::$lastNotifyArgs[3] ); $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() ); $this->assertEquals( self::$pageRev->getParentId(), self::$lastNotifyArgs[5] ); $this->assertEquals( $revision->getId(), self::$lastNotifyArgs[6] ); $this->assertNull( self::$lastNotifyArgs[7] ); $this->assertSame( 0, self::$lastNotifyArgs[8] ); $this->assertEquals( '127.0.0.1', self::$lastNotifyArgs[9] ); $this->assertSame( 0, self::$lastNotifyArgs[10] ); } } PK ! '$��� � TestRecentChangesHelper.phpnu �Iw�� <?php use MediaWiki\Context\RequestContext; use MediaWiki\MediaWikiServices; use MediaWiki\Title\Title; use MediaWiki\User\User; /** * Helper for generating test recent changes entries. * * @author Katie Filbert < aude.wiki@gmail.com > */ class TestRecentChangesHelper { public function makeEditRecentChange( User $user, $titleText, $curid, $thisid, $lastid, $timestamp, $counter, $watchingUsers ) { $attribs = array_merge( $this->getDefaultAttributes( $titleText, $timestamp ), [ 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName(), 'rc_this_oldid' => $thisid, 'rc_last_oldid' => $lastid, 'rc_cur_id' => $curid ] ); return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); } public function makeLogRecentChange( $logType, $logAction, User $user, $titleText, $timestamp, $counter, $watchingUsers ) { $attribs = array_merge( $this->getDefaultAttributes( $titleText, $timestamp ), [ 'rc_cur_id' => 0, 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName(), 'rc_this_oldid' => 0, 'rc_last_oldid' => 0, 'rc_old_len' => null, 'rc_new_len' => null, 'rc_type' => 3, 'rc_logid' => 25, 'rc_log_type' => $logType, 'rc_log_action' => $logAction, 'rc_source' => 'mw.log' ] ); return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); } public function makeDeletedEditRecentChange( User $user, $titleText, $timestamp, $curid, $thisid, $lastid, $counter, $watchingUsers ) { $attribs = array_merge( $this->getDefaultAttributes( $titleText, $timestamp ), [ 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName(), 'rc_deleted' => 5, 'rc_cur_id' => $curid, 'rc_this_oldid' => $thisid, 'rc_last_oldid' => $lastid ] ); return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); } public function makeNewBotEditRecentChange( User $user, $titleText, $curid, $thisid, $lastid, $timestamp, $counter, $watchingUsers ) { $attribs = array_merge( $this->getDefaultAttributes( $titleText, $timestamp ), [ 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName(), 'rc_this_oldid' => $thisid, 'rc_last_oldid' => $lastid, 'rc_cur_id' => $curid, 'rc_type' => 1, 'rc_bot' => 1, 'rc_source' => 'mw.new' ] ); return $this->makeRecentChange( $attribs, $counter, $watchingUsers ); } private function makeRecentChange( $attribs, $counter, $watchingUsers ) { $change = new RecentChange(); $change->setAttribs( $attribs ); $change->counter = $counter; $change->numberofWatchingusers = $watchingUsers; return $change; } public function getCacheEntry( $recentChange ) { $rcCacheFactory = new RCCacheEntryFactory( new RequestContext(), [ 'diff' => 'diff', 'cur' => 'cur', 'last' => 'last' ], MediaWikiServices::getInstance()->getLinkRenderer() ); return $rcCacheFactory->newFromRecentChange( $recentChange, false ); } public function makeCategorizationRecentChange( User $user, $titleText, $curid, $thisid, $lastid, $timestamp ) { $attribs = array_merge( $this->getDefaultAttributes( $titleText, $timestamp ), [ 'rc_type' => RC_CATEGORIZE, 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName(), 'rc_this_oldid' => $thisid, 'rc_last_oldid' => $lastid, 'rc_cur_id' => $curid, 'rc_comment' => '[[:Testpage]] added to category', 'rc_comment_text' => '[[:Testpage]] added to category', 'rc_comment_data' => null, 'rc_old_len' => 0, 'rc_new_len' => 0, ] ); return $this->makeRecentChange( $attribs, 0, 0 ); } private function getDefaultAttributes( $titleText, $timestamp ) { return [ 'rc_id' => 545, 'rc_user' => 0, 'rc_user_text' => '127.0.0.1', 'rc_ip' => '127.0.0.1', 'rc_title' => $titleText, 'rc_namespace' => 0, 'rc_timestamp' => $timestamp, 'rc_old_len' => 212, 'rc_new_len' => 188, 'rc_comment' => '', 'rc_comment_text' => '', 'rc_comment_data' => null, 'rc_minor' => 0, 'rc_bot' => 0, 'rc_type' => 0, 'rc_patrolled' => 1, 'rc_deleted' => 0, 'rc_logid' => 0, 'rc_log_type' => null, 'rc_log_action' => '', 'rc_params' => '', 'rc_source' => 'mw.edit' ]; } public function getTestContext( User $user ) { $context = new RequestContext(); $context->setLanguage( 'en' ); $context->setUser( $user ); $title = Title::makeTitle( NS_SPECIAL, 'RecentChanges' ); $context->setTitle( $title ); return $context; } } PK ! ��=� � RCCacheEntryFactoryTest.phpnu �Iw�� <?php use MediaWiki\Linker\LinkRenderer; use MediaWiki\Title\Title; /** * @covers \RCCacheEntryFactory * @group Database * @author Katie Filbert <aude.wiki@gmail.com> */ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase { /** * @var TestRecentChangesHelper */ private $testRecentChangesHelper; /** * @var LinkRenderer */ private $linkRenderer; protected function setUp(): void { parent::setUp(); $this->linkRenderer = $this->getServiceContainer()->getLinkRenderer(); $this->testRecentChangesHelper = new TestRecentChangesHelper(); } public function testNewFromRecentChange() { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeEditRecentChange( $user, 'Xyz', 5, // curid 191, // thisid 190, // lastid '20131103212153', 0, // counter 0 // number of watching users ); $cacheEntryFactory = new RCCacheEntryFactory( $this->getContext(), $this->getMessages(), $this->linkRenderer ); $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, false ); $this->assertInstanceOf( RCCacheEntry::class, $cacheEntry ); $this->assertFalse( $cacheEntry->watched, 'watched' ); $this->assertEquals( '21:21', $cacheEntry->timestamp, 'timestamp' ); $this->assertSame( 0, $cacheEntry->numberofWatchingusers, 'watching users' ); $this->assertFalse( $cacheEntry->unpatrolled, 'unpatrolled' ); $this->assertUserLinks( $user->getName(), $cacheEntry ); $this->assertTitleLink( 'Xyz', $cacheEntry ); $diff = [ 'curid' => 5, 'diff' => 191, 'oldid' => 190 ]; $cur = [ 'curid' => 5, 'diff' => 0, 'oldid' => 191 ]; $this->assertQueryLink( 'cur', $cur, $cacheEntry->curlink ); $this->assertQueryLink( 'prev', $diff, $cacheEntry->lastlink ); $this->assertQueryLink( 'diff', $diff, $cacheEntry->difflink ); } public function testNewForDeleteChange() { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeLogRecentChange( 'delete', 'delete', $user, 'Abc', '20131103212153', 0, // counter 0 // number of watching users ); $cacheEntryFactory = new RCCacheEntryFactory( $this->getContext(), $this->getMessages(), $this->linkRenderer ); $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, false ); $this->assertInstanceOf( RCCacheEntry::class, $cacheEntry ); $this->assertFalse( $cacheEntry->watched, 'watched' ); $this->assertEquals( '21:21', $cacheEntry->timestamp, 'timestamp' ); $this->assertSame( 0, $cacheEntry->numberofWatchingusers, 'watching users' ); $this->assertFalse( $cacheEntry->unpatrolled, 'unpatrolled' ); $this->assertDeleteLogLink( $cacheEntry ); $this->assertUserLinks( $user->getName(), $cacheEntry ); $this->assertEquals( 'cur', $cacheEntry->curlink, 'cur link for delete log or rev' ); $this->assertEquals( 'diff', $cacheEntry->difflink, 'diff link for delete log or rev' ); $this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' ); } public function testNewForRevUserDeleteChange() { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeDeletedEditRecentChange( $user, 'Zzz', '20131103212153', 191, // thisid 190, // lastid '20131103212153', 0, // counter 0 // number of watching users ); $cacheEntryFactory = new RCCacheEntryFactory( $this->getContext(), $this->getMessages(), $this->linkRenderer ); $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, false ); $this->assertInstanceOf( RCCacheEntry::class, $cacheEntry ); $this->assertFalse( $cacheEntry->watched, 'watched' ); $this->assertEquals( '21:21', $cacheEntry->timestamp, 'timestamp' ); $this->assertSame( 0, $cacheEntry->numberofWatchingusers, 'watching users' ); $this->assertFalse( $cacheEntry->unpatrolled, 'unpatrolled' ); $this->assertRevDel( $cacheEntry ); $this->assertTitleLink( 'Zzz', $cacheEntry ); $this->assertEquals( 'cur', $cacheEntry->curlink, 'cur link for delete log or rev' ); $this->assertEquals( 'diff', $cacheEntry->difflink, 'diff link for delete log or rev' ); $this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' ); } private function assertValidHTML( $actual ) { $this->assertNotSame( '', $actual ); $document = new DOMDocument; $oldUseInternalErrors = libxml_use_internal_errors( true ); try { $loaded = $document->loadHTML( $actual ); $message = ''; foreach ( libxml_get_errors() as $error ) { $message .= "\n" . $error->message; } $this->assertNotFalse( $loaded, $message ?: 'Invalid for unknown reason' ); } finally { libxml_use_internal_errors( $oldUseInternalErrors ); } } private function assertUserLinks( $user, $cacheEntry ) { $this->assertValidHTML( $cacheEntry->userlink ); $this->assertMatchesRegularExpression( '#^<a .*class="new mw-userlink".*><bdi>' . $user . '</bdi></a>#', $cacheEntry->userlink, 'verify user link' ); $this->assertValidHTML( $cacheEntry->usertalklink ); $this->assertMatchesRegularExpression( '#^ <span class="mw-usertoollinks mw-changeslist-links">.*<span><a .+>talk</a></span>.*</span>#', $cacheEntry->usertalklink, 'verify user talk link' ); $this->assertValidHTML( $cacheEntry->usertalklink ); $this->assertMatchesRegularExpression( '#^ <span class="mw-usertoollinks mw-changeslist-links">.*<span><a .+>' . 'contribs</a></span>.*</span>$#', $cacheEntry->usertalklink, 'verify user tool links' ); } private function assertDeleteLogLink( $cacheEntry ) { $this->assertEquals( '(<a href="/wiki/Special:Log/delete" title="Special:Log/delete">Deletion log</a>)', $cacheEntry->link, 'verify deletion log link' ); $this->assertValidHTML( $cacheEntry->link ); } private function assertRevDel( $cacheEntry ) { $this->assertEquals( ' <span class="history-deleted">(username removed)</span>', $cacheEntry->userlink, 'verify user link for change with deleted revision and user' ); $this->assertValidHTML( $cacheEntry->userlink ); } private function assertTitleLink( $title, $cacheEntry ) { $this->assertEquals( '<a href="/wiki/' . $title . '" title="' . $title . '">' . $title . '</a>', $cacheEntry->link, 'verify title link' ); $this->assertValidHTML( $cacheEntry->link ); } private function assertQueryLink( $content, $params, $link ) { $this->assertMatchesRegularExpression( "#^<a .+>$content</a>$#", $link, 'verify query link element' ); $this->assertValidHTML( $link ); foreach ( $params as $key => $value ) { $this->assertMatchesRegularExpression( '/' . $key . '=' . $value . '/', $link, "verify $key link params" ); } } private function getMessages() { return [ 'cur' => 'cur', 'diff' => 'diff', 'hist' => 'hist', 'enhancedrc-history' => 'history', 'last' => 'prev', 'blocklink' => 'block', 'history' => 'Page history', 'semicolon-separator' => '; ', 'pipe-separator' => ' | ' ]; } private function getContext() { $user = $this->getMutableTestUser()->getUser(); $context = $this->testRecentChangesHelper->getTestContext( $user ); $title = Title::makeTitle( NS_SPECIAL, 'RecentChanges' ); $context->setTitle( $title ); return $context; } } PK ! R��� rcfeed/RCFeedIntegrationTest.phpnu �Iw�� <?php use MediaWiki\MainConfigNames; use MediaWiki\RCFeed\FormattedRCFeed; use MediaWiki\RCFeed\JSONRCFeedFormatter; use MediaWiki\SpecialPage\SpecialPage; use MediaWiki\Title\Title; /** * @group medium * @group Database * @covers \MediaWiki\RCFeed\FormattedRCFeed * @covers \RecentChange * @covers \MediaWiki\RCFeed\JSONRCFeedFormatter * @covers \MediaWiki\RCFeed\MachineReadableRCFeedFormatter * @covers \MediaWiki\RCFeed\RCFeed */ class RCFeedIntegrationTest extends MediaWikiIntegrationTestCase { protected function setUp(): void { parent::setUp(); $this->overrideConfigValues( [ MainConfigNames::CanonicalServer => 'https://example.org', MainConfigNames::ServerName => 'example.org', MainConfigNames::ScriptPath => '/w', MainConfigNames::Script => '/w/index.php', MainConfigNames::DBname => 'example', MainConfigNames::DBprefix => self::dbPrefix(), MainConfigNames::RCFeeds => [], MainConfigNames::RCEngines => [], ] ); } public function testNotify() { $feed = $this->getMockBuilder( FormattedRCFeed::class ) ->setConstructorArgs( [ [ 'formatter' => JSONRCFeedFormatter::class ] ] ) ->onlyMethods( [ 'send' ] ) ->getMock(); $feed->expects( $this->once() ) ->method( 'send' ) ->willReturn( true ) ->with( $this->anything(), $this->callback( function ( $line ) { $this->assertJsonStringEqualsJsonString( json_encode( [ 'id' => null, 'type' => 'log', 'namespace' => 0, 'title' => 'Example', 'title_url' => 'https://example.org/wiki/Example', 'comment' => '', 'timestamp' => 1301644800, 'user' => 'UTSysop', 'bot' => false, 'notify_url' => null, 'log_id' => 0, 'log_type' => 'move', 'log_action' => 'move', 'log_params' => [ 'color' => 'green', 'nr' => 42, 'pet' => 'cat', ], 'log_action_comment' => '', 'server_url' => 'https://example.org', 'server_name' => 'example.org', 'server_script_path' => '/w', 'wiki' => 'example-' . self::dbPrefix(), ] ), $line ); return true; } ) ); $this->overrideConfigValue( MainConfigNames::RCFeeds, [ 'myfeed' => [ 'class' => $feed, 'uri' => 'test://localhost:1234', 'formatter' => JSONRCFeedFormatter::class, ], ] ); $logpage = SpecialPage::getTitleFor( 'Log', 'move' ); $user = $this->getTestSysop()->getUser(); $rc = RecentChange::newLogEntry( '20110401080000', $logpage, // &$title $user, // &$user '', // $actionComment '127.0.0.1', // $ip 'move', // $type 'move', // $action Title::makeTitle( 0, 'Example' ), // $target '', // $logComment LogEntryBase::makeParamBlob( [ '4::color' => 'green', '5:number:nr' => 42, 'pet' => 'cat', ] ) ); $rc->notifyRCFeeds(); } } PK ! �ˣ�< < RecentChangesUpdateJobTest.phpnu �Iw�� <?php use MediaWiki\MainConfigNames; use Wikimedia\Timestamp\ConvertibleTimestamp; /** * @group Database * @covers RecentChangesUpdateJob * @author Dreamy Jazz */ class RecentChangesUpdateJobTest extends MediaWikiIntegrationTestCase { private function addTestingExpiredRows() { // Make three testing edits, which will trigger a recentchanges insert. Two of the edits will be made // over wgRCMaxAge seconds ago while the other will be made a day ago $testPage = $this->getExistingTestPage(); $testUser = $this->getTestUser()->getAuthority(); // So that only our two testing edits are present, and nothing from creating the test page or test user $this->truncateTable( 'recentchanges' ); // Fix wgRCMaxAge at a high value to ensure that the recentchanges entries we are creating are not purged // by later testing edits. $this->overrideConfigValue( MainConfigNames::RCMaxAge, 24 * 3600 * 1000 ); ConvertibleTimestamp::setFakeTime( '20230405060708' ); $this->editPage( $testPage, 'testing1234', '', NS_MAIN, $testUser ); ConvertibleTimestamp::setFakeTime( '20230705060708' ); $this->editPage( $testPage, 'testing12345', '', NS_MAIN, $testUser ); ConvertibleTimestamp::setFakeTime( '20240405060708' ); $this->editPage( $testPage, 'testing123456', '', NS_MAIN, $testUser ); // Verify that the recentchanges table row count is as expected for the test $this->newSelectQueryBuilder() ->field( 'COUNT(*)' ) ->table( 'recentchanges' ) ->assertFieldValue( 3 ); } public function testNewPurgeJob() { $this->addTestingExpiredRows(); // Set the time as one day beyond the last test edit ConvertibleTimestamp::setFakeTime( '20240406060708' ); // Fix wgRCMaxAge for the test, in case the default value changes. $this->overrideConfigValue( MainConfigNames::RCMaxAge, 90 * 24 * 3600 ); $hookRunAtLeastOnce = false; $this->setTemporaryHook( 'RecentChangesPurgeRows', function ( $rows ) use ( &$hookRunAtLeastOnce ) { // Check that the first row has the expected columns. Checking just the first row should be fine // as the value of $rows should come from ::fetchResultSet which returns the same columns for each // returned row. $rowAsArray = (array)$rows[0]; // To get the expected fields, use the value of the items in the 'fields' array. The exception to this // is where the key is a string, when it should be used instead (as this is an alias). $recentChangeQueryFields = RecentChange::getQueryInfo()['fields']; $expectedFields = []; foreach ( $recentChangeQueryFields as $key => $value ) { if ( is_string( $key ) ) { $expectedFields[] = $key; } else { $expectedFields[] = $value; } } $this->assertArrayEquals( $expectedFields, array_keys( $rowAsArray ), false, true, 'Columns in the provided $row are not as expected' ); $hookRunAtLeastOnce = true; } ); // Call the code we are testing $objectUnderTest = RecentChangesUpdateJob::newPurgeJob(); $this->assertInstanceOf( RecentChangesUpdateJob::class, $objectUnderTest ); $objectUnderTest->run(); // Verify that only the edit made a day ago is now in the recentchanges table $this->newSelectQueryBuilder() ->field( 'rc_timestamp' ) ->table( 'recentchanges' ) ->assertFieldValue( $this->getDb()->timestamp( '20240405060708' ) ); // Verify that the lock placed to do the purge is no longer active. $this->assertTrue( $this->getDb()->lockIsFree( $this->getDb()->getDomainID() . ':recentchanges-prune', __METHOD__ ) ); // Check that the RecentChangesPurgeRows hook was run at least once $this->assertTrue( $hookRunAtLeastOnce, 'RecentChangesPurgeRows hook was not run' ); } /** @dataProvider provideInvalidTypes */ public function testWhenTypeForInvalidType( $type ) { $this->expectException( InvalidArgumentException::class ); $objectUnderTest = new RecentChangesUpdateJob( $this->getExistingTestPage()->getTitle(), [ 'type' => $type ] ); $objectUnderTest->run(); } public static function provideInvalidTypes() { return [ 'Type is null' => [ null ], 'Type is a unrecognised string' => [ 'unknown-type' ], ]; } } PK ! ?��� � OldChangesListTest.phpnu �Iw�� <?php use MediaWiki\Context\RequestContext; use MediaWiki\Title\Title; /** * @todo add tests to cover article link, timestamp, character difference, * log entry, user tool links, direction marks, tags, rollback, * watching users, and date header. * * @covers \OldChangesList * @group Database * @author Katie Filbert <aude.wiki@gmail.com> */ class OldChangesListTest extends MediaWikiLangTestCase { /** * @var TestRecentChangesHelper */ private $testRecentChangesHelper; protected function setUp(): void { parent::setUp(); $this->setUserLang( 'qqx' ); $this->testRecentChangesHelper = new TestRecentChangesHelper(); } /** * @dataProvider recentChangesLine_CssForLineNumberProvider */ public function testRecentChangesLine_CssForLineNumber( $expected, $linenumber, $message ) { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, false, $linenumber ); $this->assertMatchesRegularExpression( $expected, $line, $message ); } public static function recentChangesLine_CssForLineNumberProvider() { return [ [ '/mw-line-odd/', 1, 'odd line number' ], [ '/mw-line-even/', 2, 'even line number' ] ]; } public function testRecentChangesLine_NotWatchedCssClass() { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( '/mw-changeslist-line-not-watched/', $line ); } public function testRecentChangesLine_WatchedCssClass() { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, true, 1 ); $this->assertMatchesRegularExpression( '/mw-changeslist-line-watched/', $line ); } public function testRecentChangesLine_LogTitle() { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getLogChange( 'delete', 'delete' ); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( '/href="\/wiki\/Special:Log\/delete/', $line, 'link has href attribute' ); $this->assertMatchesRegularExpression( '/title="Special:Log\/delete/', $line, 'link has title attribute' ); $this->assertMatchesRegularExpression( "/dellogpage/", $line, 'link text' ); } public function testRecentChangesLine_DiffHistLinks() { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( '/title=Cat&curid=20131103212153&diff=5&oldid=191/', $line, 'assert diff link' ); $this->assertMatchesRegularExpression( '/title=Cat&curid=20131103212153&action=history"/', $line, 'assert history link' ); } public function testRecentChangesLine_Flags() { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getNewBotEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertStringContainsString( '<abbr class="newpage" title="(recentchanges-label-newpage)">(newpageletter)</abbr>', $line, 'new page flag' ); $this->assertStringContainsString( '<abbr class="botedit" title="(recentchanges-label-bot)">(boteditletter)</abbr>', $line, 'bot flag' ); } public function testRecentChangesLine_Attribs() { $recentChange = $this->getEditChange(); $recentChange->mAttribs['ts_tags'] = 'vandalism,newbie'; $this->setTemporaryHook( 'OldChangesListRecentChangesLine', static function ( $oldChangesList, &$html, $rc, $classes, $attribs ) { $html = $html . '/<div>Additional change line </div>/'; } ); $oldChangesList = $this->getOldChangesList(); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertStringContainsString( '/<div>Additional change line </div>/', $line ); $this->assertMatchesRegularExpression( '/<li data-mw-revid="\d+" data-mw-ts="\d+" class="[\w\s-]*mw-tag-vandalism[\w\s-]*">/', $line ); $this->assertMatchesRegularExpression( '/<li data-mw-revid="\d+" data-mw-ts="\d+" class="[\w\s-]*mw-tag-newbie[\w\s-]*">/', $line ); } public function testRecentChangesLine_numberOfWatchingUsers() { $oldChangesList = $this->getOldChangesList(); $recentChange = $this->getEditChange(); $recentChange->numberofWatchingusers = 100; $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( "/(number-of-watching-users-for-recent-changes: 100)/", $line ); } public function testRecentChangesLine_watchlistCssClass() { $oldChangesList = $this->getOldChangesList(); $oldChangesList->setWatchlistDivs( true ); $recentChange = $this->getEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( "/watchlist-0-Cat/", $line ); } public function testRecentChangesLine_dataAttribute() { $oldChangesList = $this->getOldChangesList(); $oldChangesList->setWatchlistDivs( true ); $recentChange = $this->getEditChange(); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( '/data-target-page=\"Cat\"/', $line ); $recentChange = $this->getLogChange( 'delete', 'delete' ); $line = $oldChangesList->recentChangesLine( $recentChange, false, 1 ); $this->assertMatchesRegularExpression( '/data-target-page="Abc"/', $line ); } public function testRecentChangesLine_prefix() { $mockContext = $this->getMockBuilder( RequestContext::class ) ->onlyMethods( [ 'getTitle' ] ) ->getMock(); $mockContext->method( 'getTitle' ) ->willReturn( Title::makeTitle( NS_MAIN, 'Expected Context Title' ) ); $oldChangesList = $this->getOldChangesList(); $oldChangesList->setContext( $mockContext ); $recentChange = $this->getEditChange(); $oldChangesList->setChangeLinePrefixer( function ( $rc, $changesList ) { // Make sure RecentChange and ChangesList objects are the same $this->assertEquals( 'Expected Context Title', $changesList->getContext()->getTitle() ); $this->assertEquals( 'Cat', $rc->getTitle() ); return 'I am a prefix'; } ); $line = $oldChangesList->recentChangesLine( $recentChange ); $this->assertMatchesRegularExpression( "/I am a prefix/", $line ); } private function getNewBotEditChange() { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeNewBotEditRecentChange( $user, 'Abc', '20131103212153', 5, 191, 190, 0, 0 ); return $recentChange; } private function getLogChange( $logType, $logAction ) { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeLogRecentChange( $logType, $logAction, $user, 'Abc', '20131103212153', 0, 0 ); return $recentChange; } private function getEditChange() { $user = $this->getMutableTestUser()->getUser(); $recentChange = $this->testRecentChangesHelper->makeEditRecentChange( $user, 'Cat', '20131103212153', 5, 191, 190, 0, 0 ); return $recentChange; } private function getOldChangesList() { $context = $this->getContext(); return new OldChangesList( $context ); } private function getContext() { $user = $this->getMutableTestUser()->getUser(); $context = $this->testRecentChangesHelper->getTestContext( $user ); $context->setLanguage( 'qqx' ); return $context; } } PK ! �#OO�@ �@ RecentChangeTest.phpnu �Iw�� <?php use MediaWiki\MainConfigNames; use MediaWiki\Page\PageIdentity; use MediaWiki\Page\PageIdentityValue; use MediaWiki\Page\PageProps; use MediaWiki\Page\PageReference; use MediaWiki\Page\PageReferenceValue; use MediaWiki\Permissions\PermissionStatus; use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait; use MediaWiki\Tests\User\TempUser\TempUserTestTrait; use MediaWiki\Title\Title; use MediaWiki\User\UserIdentity; use MediaWiki\User\UserIdentityValue; use MediaWiki\Utils\MWTimestamp; /** * @group Database */ class RecentChangeTest extends MediaWikiIntegrationTestCase { use MockAuthorityTrait; use MockTitleTrait; use TempUserTestTrait; /** @var PageIdentity */ protected $title; /** @var PageIdentity */ protected $target; /** @var UserIdentity */ protected $user; private const USER_COMMENT = '<User comment about action>'; protected function setUp(): void { parent::setUp(); $this->title = new PageIdentityValue( 17, NS_MAIN, 'SomeTitle', PageIdentity::LOCAL ); $this->target = new PageIdentityValue( 78, NS_MAIN, 'TestTarget', PageIdentity::LOCAL ); $user = $this->getTestUser()->getUser(); $this->user = new UserIdentityValue( $user->getId(), $user->getName() ); $this->overrideConfigValues( [ MainConfigNames::CanonicalServer => 'https://example.org', MainConfigNames::ServerName => 'example.org', MainConfigNames::ScriptPath => '/w', MainConfigNames::Script => '/w/index.php', MainConfigNames::UseRCPatrol => false, MainConfigNames::UseNPPatrol => false, MainConfigNames::RCFeeds => [], MainConfigNames::RCEngines => [], ] ); } public static function provideAttribs() { $attribs = [ 'rc_timestamp' => wfTimestamp( TS_MW ), 'rc_namespace' => NS_USER, 'rc_title' => 'Tony', 'rc_type' => RC_EDIT, 'rc_source' => RecentChange::SRC_EDIT, 'rc_minor' => 0, 'rc_cur_id' => 77, 'rc_user' => 858173476, 'rc_user_text' => 'Tony', 'rc_comment' => '', 'rc_comment_text' => '', 'rc_comment_data' => null, 'rc_this_oldid' => 70, 'rc_last_oldid' => 71, 'rc_bot' => 0, 'rc_ip' => '', 'rc_patrolled' => 0, 'rc_new' => 0, 'rc_old_len' => 80, 'rc_new_len' => 88, 'rc_deleted' => 0, 'rc_logid' => 0, 'rc_log_type' => null, 'rc_log_action' => '', 'rc_params' => '', ]; yield 'external user' => [ [ 'rc_type' => RC_EXTERNAL, 'rc_source' => 'foo', 'rc_user' => 0, 'rc_user_text' => 'm>External User', ] + $attribs ]; yield 'anon user' => [ [ 'rc_type' => RC_EXTERNAL, 'rc_source' => 'foo', 'rc_user' => 0, 'rc_user_text' => '192.168.0.1', ] + $attribs ]; yield 'special title' => [ [ 'rc_namespace' => NS_SPECIAL, 'rc_title' => 'Log', 'rc_type' => RC_LOG, 'rc_source' => RecentChange::SRC_LOG, 'rc_log_type' => 'delete', 'rc_log_action' => 'delete', ] + $attribs ]; yield 'no title' => [ [ 'rc_namespace' => NS_MAIN, 'rc_title' => '', 'rc_type' => RC_LOG, 'rc_source' => RecentChange::SRC_LOG, 'rc_log_type' => 'delete', 'rc_log_action' => 'delete', ] + $attribs ]; } /** * @covers \RecentChange::save * @covers \RecentChange::newFromId * @covers \RecentChange::getTitle * @covers \RecentChange::getPerformerIdentity * @dataProvider provideAttribs */ public function testDatabaseRoundTrip( $attribs ) { $rc_user = $attribs['rc_user'] ?? 0; if ( !$rc_user ) { $this->disableAutoCreateTempUser(); } $rc = new RecentChange; $rc->mAttribs = $attribs; $rc->mExtra = [ 'pageStatus' => 'changed' ]; $rc->save(); $id = $rc->getAttribute( 'rc_id' ); $rc = RecentChange::newFromId( $id ); $actualAttribs = array_intersect_key( $rc->mAttribs, $attribs ); $this->assertArrayEquals( $attribs, $actualAttribs, false, true ); $user = new UserIdentityValue( $rc_user, $attribs['rc_user_text'] ); $this->assertTrue( $user->equals( $rc->getPerformerIdentity() ) ); if ( empty( $attribs['rc_title'] ) ) { $this->assertNull( $rc->getPage() ); } else { $title = Title::makeTitle( $attribs['rc_namespace'], $attribs['rc_title'] ); $this->assertTrue( $title->isSamePageAs( $rc->getTitle() ) ); $this->assertTrue( $title->isSamePageAs( $rc->getPage() ) ); } } /** * @covers \RecentChange::newFromRow * @covers \RecentChange::loadFromRow * @covers \RecentChange::getAttributes * @covers \RecentChange::getPerformerIdentity */ public function testNewFromRow() { $user = $this->getTestUser()->getUser(); $row = (object)[ 'rc_foo' => 'AAA', 'rc_timestamp' => '20150921134808', 'rc_deleted' => 'bar', 'rc_comment_text' => 'comment', 'rc_comment_data' => null, 'rc_user' => $user->getId(), // lookup by id ]; $rc = RecentChange::newFromRow( $row ); $expected = [ 'rc_foo' => 'AAA', 'rc_timestamp' => '20150921134808', 'rc_deleted' => 'bar', 'rc_comment' => 'comment', 'rc_comment_text' => 'comment', 'rc_comment_data' => null, 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName() ]; $this->assertEquals( $expected, $rc->getAttributes() ); $this->assertTrue( $user->equals( $rc->getPerformerIdentity() ) ); $row = (object)[ 'rc_foo' => 'AAA', 'rc_timestamp' => '20150921134808', 'rc_deleted' => 'bar', 'rc_comment' => 'comment', 'rc_user_text' => $user->getName(), // lookup by name ]; $rc = @RecentChange::newFromRow( $row ); $expected = [ 'rc_foo' => 'AAA', 'rc_timestamp' => '20150921134808', 'rc_deleted' => 'bar', 'rc_comment' => 'comment', 'rc_comment_text' => 'comment', 'rc_comment_data' => null, 'rc_user' => $user->getId(), 'rc_user_text' => $user->getName() ]; $this->assertEquals( $expected, $rc->getAttributes() ); $this->assertEquals( $expected, $rc->getAttributes() ); $this->assertTrue( $user->equals( $rc->getPerformerIdentity() ) ); } /** * @covers \RecentChange::notifyNew * @covers \RecentChange::newFromId * @covers \RecentChange::getAttributes * @covers \RecentChange::getPerformerIdentity */ public function testNotifyNew() { $now = MWTimestamp::now(); $rc = RecentChange::notifyNew( $now, $this->title, false, $this->user, self::USER_COMMENT, false ); $expected = [ 'rc_timestamp' => $now, 'rc_deleted' => 0, 'rc_comment_text' => self::USER_COMMENT, 'rc_user' => $this->user->getId(), 'rc_user_text' => $this->user->getName() ]; $actual = array_intersect_key( $rc->getAttributes(), $expected ); $this->assertEquals( $expected, $actual ); $this->assertTrue( $this->user->equals( $rc->getPerformerIdentity() ) ); $rc = RecentChange::newFromId( $rc->getAttribute( 'rc_id' ) ); $actual = array_intersect_key( $rc->getAttributes(), $expected ); $this->assertEquals( $expected, $actual ); $this->assertTrue( $this->user->equals( $rc->getPerformerIdentity() ) ); } /** * @covers \RecentChange::notifyNew * @covers \RecentChange::newFromId * @covers \RecentChange::getAttributes * @covers \RecentChange::getPerformerIdentity */ public function testNotifyEdit() { $now = MWTimestamp::now(); $rc = RecentChange::notifyEdit( $now, $this->title, false, $this->user, self::USER_COMMENT, 0, $now, false ); $expected = [ 'rc_timestamp' => $now, 'rc_deleted' => 0, 'rc_comment_text' => self::USER_COMMENT, 'rc_user' => $this->user->getId(), 'rc_user_text' => $this->user->getName() ]; $actual = array_intersect_key( $rc->getAttributes(), $expected ); $this->assertEquals( $expected, $actual ); $this->assertTrue( $this->user->equals( $rc->getPerformerIdentity() ) ); $rc = RecentChange::newFromId( $rc->getAttribute( 'rc_id' ) ); $actual = array_intersect_key( $rc->getAttributes(), $expected ); $this->assertEquals( $expected, $actual ); $this->assertTrue( $this->user->equals( $rc->getPerformerIdentity() ) ); } /** * @covers \RecentChange::notifyNew * @covers \RecentChange::newFromId * @covers \RecentChange::getAttributes * @covers \RecentChange::getPerformerIdentity */ public function testNewLogEntry() { $now = MWTimestamp::now(); $logPage = new PageReferenceValue( NS_SPECIAL, 'Log/test', PageReference::LOCAL ); $rc = RecentChange::newLogEntry( $now, $logPage, $this->user, 'action comment', '192.168.0.2', 'test', 'testing', $this->title, self::USER_COMMENT, 'a|b|c', 7, '', 42, false, true ); $expected = [ 'rc_timestamp' => $now, 'rc_comment_text' => self::USER_COMMENT, 'rc_user' => $this->user->getId(), 'rc_user_text' => $this->user->getName(), 'rc_title' => $this->title->getDBkey(), 'rc_logid' => 7, 'rc_log_type' => 'test', 'rc_log_action' => 'testing', 'rc_this_oldid' => 42, 'rc_patrolled' => RecentChange::PRC_AUTOPATROLLED, 'rc_bot' => 1, ]; $actual = array_intersect_key( $rc->getAttributes(), $expected ); $this->assertEquals( $expected, $actual ); $this->assertTrue( $this->user->equals( $rc->getPerformerIdentity() ) ); $this->assertTrue( $this->title->isSamePageAs( $rc->getPage() ) ); $this->assertTrue( $this->title->isSamePageAs( $rc->getTitle() ) ); } public static function provideParseParams() { // $expected, $raw yield 'extracting an array' => [ [ 'root' => [ 'A' => 1, 'B' => 'two' ] ], 'a:1:{s:4:"root";a:2:{s:1:"A";i:1;s:1:"B";s:3:"two";}}' ]; yield 'null' => [ null, null ]; yield 'false' => [ null, serialize( false ) ]; yield 'non-array' => [ null, 'not-an-array' ]; } /** * @covers \RecentChange::parseParams * @dataProvider provideParseParams * @param array $expectedParseParams * @param string|null $rawRcParams */ public function testParseParams( $expectedParseParams, $rawRcParams ) { $rc = new RecentChange; $rc->setAttribs( [ 'rc_params' => $rawRcParams ] ); $actualParseParams = $rc->parseParams(); $this->assertEquals( $expectedParseParams, $actualParseParams ); } /** * @covers \RecentChange::getNotifyUrl */ public function testGetNotifyUrlForEdit() { $rc = new RecentChange; $rc->mAttribs = [ 'rc_id' => 60, 'rc_timestamp' => '20110401090000', 'rc_namespace' => NS_MAIN, 'rc_title' => 'Example', 'rc_type' => RC_EDIT, 'rc_cur_id' => 42, 'rc_this_oldid' => 50, 'rc_last_oldid' => 30, 'rc_patrolled' => 0, ]; $this->assertSame( 'https://example.org/w/index.php?diff=50&oldid=30', $rc->getNotifyUrl(), 'Notify url' ); $this->overrideConfigValue( MainConfigNames::UseRCPatrol, true ); $this->assertSame( 'https://example.org/w/index.php?diff=50&oldid=30&rcid=60', $rc->getNotifyUrl(), 'Notify url (RC Patrol)' ); } /** * @covers \RecentChange::getNotifyUrl */ public function testGetNotifyUrlForCreate() { $rc = new RecentChange; $rc->mAttribs = [ 'rc_id' => 60, 'rc_timestamp' => '20110401090000', 'rc_namespace' => NS_MAIN, 'rc_title' => 'Example', 'rc_type' => RC_NEW, 'rc_cur_id' => 42, 'rc_this_oldid' => 50, 'rc_last_oldid' => 0, 'rc_patrolled' => 0, ]; $this->assertSame( 'https://example.org/w/index.php?oldid=50', $rc->getNotifyUrl(), 'Notify url' ); $this->overrideConfigValue( MainConfigNames::UseNPPatrol, true ); $this->assertSame( 'https://example.org/w/index.php?oldid=50&rcid=60', $rc->getNotifyUrl(), 'Notify url (NP Patrol)' ); } /** * @covers \RecentChange::getNotifyUrl */ public function testGetNotifyUrlForLog() { $rc = new RecentChange; $rc->mAttribs = [ 'rc_id' => 60, 'rc_timestamp' => '20110401090000', 'rc_namespace' => NS_MAIN, 'rc_title' => 'Example', 'rc_type' => RC_LOG, 'rc_cur_id' => 42, 'rc_this_oldid' => 50, 'rc_last_oldid' => 0, 'rc_patrolled' => 2, 'rc_logid' => 160, 'rc_log_type' => 'delete', 'rc_log_action' => 'delete', ]; $this->assertSame( null, $rc->getNotifyUrl(), 'Notify url' ); } /** * @return array */ public static function provideIsInRCLifespan() { return [ [ 6000, -3000, 0, true ], [ 3000, -6000, 0, false ], [ 6000, -3000, 6000, true ], [ 3000, -6000, 6000, true ], ]; } /** * @covers \RecentChange::isInRCLifespan * @dataProvider provideIsInRCLifespan */ public function testIsInRCLifespan( $maxAge, $offset, $tolerance, $expected ) { $this->overrideConfigValue( MainConfigNames::RCMaxAge, $maxAge ); // Calculate this here instead of the data provider because the provider // is expanded early on and the full test suite may take longer than 100 minutes // when coverage is enabled. $timestamp = time() + $offset; $this->assertEquals( $expected, RecentChange::isInRCLifespan( $timestamp, $tolerance ) ); } public static function provideRCTypes() { return [ [ RC_EDIT, 'edit' ], [ RC_NEW, 'new' ], [ RC_LOG, 'log' ], [ RC_EXTERNAL, 'external' ], [ RC_CATEGORIZE, 'categorize' ], ]; } /** * @dataProvider provideRCTypes * @covers \RecentChange::parseFromRCType */ public function testParseFromRCType( $rcType, $type ) { $this->assertEquals( $type, RecentChange::parseFromRCType( $rcType ) ); } /** * @dataProvider provideRCTypes * @covers \RecentChange::parseToRCType */ public function testParseToRCType( $rcType, $type ) { $this->assertEquals( $rcType, RecentChange::parseToRCType( $type ) ); } public static function provideCategoryContent() { return [ [ true ], [ false ], ]; } /** * @dataProvider provideCategoryContent * @covers \RecentChange::newForCategorization */ public function testHiddenCategoryChange( $isHidden ) { $categoryTitle = Title::makeTitle( NS_CATEGORY, 'CategoryPage' ); $pageProps = $this->createMock( PageProps::class ); $pageProps->expects( $this->once() ) ->method( 'getProperties' ) ->with( $categoryTitle, 'hiddencat' ) ->willReturn( $isHidden ? [ $categoryTitle->getArticleID() => '' ] : [] ); $this->setService( 'PageProps', $pageProps ); $rc = RecentChange::newForCategorization( '0', $categoryTitle, $this->user, self::USER_COMMENT, $this->title, $categoryTitle->getLatestRevID(), $categoryTitle->getLatestRevID(), '0', false ); $this->assertEquals( $isHidden, $rc->getParam( 'hidden-cat' ) ); } private function getDummyEditRecentChange(): RecentChange { return RecentChange::notifyEdit( MWTimestamp::now(), $this->title, false, $this->user, self::USER_COMMENT, 0, MWTimestamp::now(), false ); } /** * @covers \RecentChange::markPatrolled */ public function testMarkPatrolledPermissions() { $rc = $this->getDummyEditRecentChange(); $performer = $this->mockRegisteredAuthority( static function ( string $permission, PageIdentity $page, PermissionStatus $status ) { if ( $permission === 'patrol' ) { $status->fatal( 'missing-patrol' ); return false; } return true; } ); $status = $rc->markPatrolled( $performer ); $this->assertStatusError( 'missing-patrol', $status ); } /** * @covers \RecentChange::markPatrolled */ public function testMarkPatrolledPermissions_Hook() { $rc = $this->getDummyEditRecentChange(); $this->setTemporaryHook( 'MarkPatrolled', static function () { return false; } ); $status = $rc->markPatrolled( $this->mockRegisteredUltimateAuthority() ); $this->assertStatusError( 'hookaborted', $status ); } /** * @covers \RecentChange::markPatrolled */ public function testMarkPatrolledPermissions_Self() { $rc = $this->getDummyEditRecentChange(); $status = $rc->markPatrolled( $this->mockUserAuthorityWithoutPermissions( $this->user, [ 'autopatrol' ] ) ); $this->assertStatusError( 'markedaspatrollederror-noautopatrol', $status ); } /** * @covers \RecentChange::markPatrolled */ public function testMarkPatrolledPermissions_NoRcPatrol() { $rc = $this->getDummyEditRecentChange(); $status = $rc->markPatrolled( $this->mockRegisteredUltimateAuthority() ); $this->assertStatusError( 'rcpatroldisabled', $status ); } /** * @covers \RecentChange::markPatrolled */ public function testMarkPatrolled() { $this->overrideConfigValue( MainConfigNames::UseRCPatrol, true ); $rc = $this->getDummyEditRecentChange(); $status = $rc->markPatrolled( $this->mockUserAuthorityWithPermissions( $this->user, [ 'patrol', 'autopatrol' ] ) ); $this->assertStatusGood( $status ); $reloadedRC = RecentChange::newFromId( $rc->getAttribute( 'rc_id' ) ); $this->assertSame( '1', $reloadedRC->getAttribute( 'rc_patrolled' ) ); } } PK ! ��E�'