Add doc-typehints to class properties found by the PropertyDocumentation sniff to improve the documentation. Once the sniff is enabled it avoids that new code is missing type declarations. This is focused on documentation and does not change code. Change-Id: I82b4f543ed004fc960b6b1f8ac718db2a63a40fe
625 lines
16 KiB
PHP
625 lines
16 KiB
PHP
<?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' ) );
|
|
}
|
|
}
|