As we convert the RevisionRecord to using Authority, we no longer need Title instances, so we can convert that to PageIdentity. Ideally, we'd part away from using Title at all, but: 1. For foreign wikis PageIdentity has stronger validation, so calling PageIdentity getId() on Title will break things. There's still a lot of code depending on lax Title guarantees, so we keep it. 2. A lot of code still depends on Title, so we try to pass it through even if we don't nesessarily need to, to save cost on recreating it later on. Bug: T271458 Depends-On: I287400b967b467ea18bebbb579e881a785a19158 Change-Id: I63d9807264d7e2295afef51fc9d982447f92fcbd
346 lines
8.5 KiB
PHP
346 lines
8.5 KiB
PHP
<?php
|
|
|
|
namespace MediaWiki\Tests\Revision;
|
|
|
|
use CommentStoreComment;
|
|
use InvalidArgumentException;
|
|
use MediaWiki\Page\PageIdentity;
|
|
use MediaWiki\Page\PageIdentityValue;
|
|
use MediaWiki\Revision\RevisionArchiveRecord;
|
|
use MediaWiki\Revision\RevisionRecord;
|
|
use MediaWiki\Revision\RevisionSlots;
|
|
use MediaWiki\Revision\SlotRecord;
|
|
use MediaWiki\User\UserIdentity;
|
|
use MediaWiki\User\UserIdentityValue;
|
|
use MediaWikiIntegrationTestCase;
|
|
use stdClass;
|
|
use TextContent;
|
|
use Title;
|
|
use TitleValue;
|
|
use Wikimedia\Assert\PreconditionException;
|
|
|
|
/**
|
|
* @covers \MediaWiki\Revision\RevisionArchiveRecord
|
|
* @covers \MediaWiki\Revision\RevisionRecord
|
|
*/
|
|
class RevisionArchiveRecordTest extends MediaWikiIntegrationTestCase {
|
|
|
|
use RevisionRecordTests;
|
|
|
|
/**
|
|
* @param array $rowOverrides
|
|
*
|
|
* @return RevisionArchiveRecord
|
|
*/
|
|
protected function newRevision( array $rowOverrides = [] ) {
|
|
$title = Title::newFromText( 'Dummy' );
|
|
$title->resetArticleID( 17 );
|
|
|
|
$user = new UserIdentityValue( 11, 'Tester', 0 );
|
|
$comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
|
|
|
|
$main = SlotRecord::newUnsaved( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
|
|
$aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) );
|
|
$slots = new RevisionSlots( [ $main, $aux ] );
|
|
|
|
$row = [
|
|
'ar_id' => '5',
|
|
'ar_rev_id' => '7',
|
|
'ar_page_id' => strval( $title->getArticleID() ),
|
|
'ar_timestamp' => '20200101000000',
|
|
'ar_deleted' => 0,
|
|
'ar_minor_edit' => 0,
|
|
'ar_parent_id' => '5',
|
|
'ar_len' => $slots->computeSize(),
|
|
'ar_sha1' => $slots->computeSha1(),
|
|
];
|
|
|
|
foreach ( $rowOverrides as $field => $value ) {
|
|
$field = preg_replace( '/^rev_/', 'ar_', $field );
|
|
$row[$field] = $value;
|
|
}
|
|
|
|
return new RevisionArchiveRecord( $title, $user, $comment, (object)$row, $slots );
|
|
}
|
|
|
|
public function provideConstructor() {
|
|
$user = new UserIdentityValue( 11, 'Tester', 0 );
|
|
$comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
|
|
|
|
$main = SlotRecord::newUnsaved( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
|
|
$aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) );
|
|
$slots = new RevisionSlots( [ $main, $aux ] );
|
|
|
|
$protoRow = [
|
|
'ar_id' => '5',
|
|
'ar_rev_id' => '7',
|
|
'ar_page_id' => '17',
|
|
'ar_timestamp' => '20200101000000',
|
|
'ar_deleted' => 0,
|
|
'ar_minor_edit' => 0,
|
|
'ar_parent_id' => '5',
|
|
'ar_len' => $slots->computeSize(),
|
|
'ar_sha1' => $slots->computeSha1(),
|
|
];
|
|
|
|
$row = $protoRow;
|
|
yield 'all info' => [
|
|
new PageIdentityValue( 17, NS_MAIN, 'Dummy', 'acmewiki' ),
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots,
|
|
'acmewiki',
|
|
PreconditionException::class
|
|
];
|
|
|
|
yield 'all info, local' => [
|
|
new PageIdentityValue( 17, NS_MAIN, 'Dummy', PageIdentity::LOCAL ),
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots,
|
|
];
|
|
|
|
$title = Title::newFromText( 'Dummy' );
|
|
$title->resetArticleID( 17 );
|
|
|
|
yield 'all info, local, with Title' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots,
|
|
];
|
|
|
|
// this case exists for b/c and should be deprecated
|
|
yield 'all info, foreign, with Title' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots,
|
|
'acmewiki',
|
|
];
|
|
|
|
$row = $protoRow;
|
|
$row['ar_minor_edit'] = '1';
|
|
$row['ar_deleted'] = strval( RevisionRecord::DELETED_USER );
|
|
|
|
yield 'minor deleted' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots
|
|
];
|
|
|
|
$row = $protoRow;
|
|
unset( $row['ar_parent'] );
|
|
|
|
yield 'no parent' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots
|
|
];
|
|
|
|
$row = $protoRow;
|
|
$row['ar_len'] = null;
|
|
$row['ar_sha1'] = '';
|
|
|
|
yield 'ar_len is null, ar_sha1 is ""' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots
|
|
];
|
|
|
|
$row = $protoRow;
|
|
yield 'no length, no hash' => [
|
|
Title::newFromText( 'DummyDoesNotExist' ),
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConstructor
|
|
*
|
|
* @param Title $page
|
|
* @param UserIdentity $user
|
|
* @param CommentStoreComment $comment
|
|
* @param stdClass $row
|
|
* @param RevisionSlots $slots
|
|
* @param bool $wikiId
|
|
* @param string|null $expectedException
|
|
*/
|
|
public function testConstructorAndGetters(
|
|
PageIdentity $page,
|
|
UserIdentity $user,
|
|
CommentStoreComment $comment,
|
|
$row,
|
|
RevisionSlots $slots,
|
|
$wikiId = PageIdentity::LOCAL,
|
|
string $expectedException = null
|
|
) {
|
|
$rec = new RevisionArchiveRecord( $page, $user, $comment, $row, $slots, $wikiId );
|
|
|
|
$this->assertTrue( $page->isSamePageAs( $rec->getPage() ), 'getPage' );
|
|
$this->assertSame( $user, $rec->getUser( RevisionRecord::RAW ), 'getUser' );
|
|
$this->assertSame( $comment, $rec->getComment(), 'getComment' );
|
|
|
|
$this->assertSame( $slots->getSlotRoles(), $rec->getSlotRoles(), 'getSlotRoles' );
|
|
$this->assertSame( $wikiId, $rec->getWikiId(), 'getWikiId' );
|
|
|
|
$this->assertSame( (int)$row->ar_id, $rec->getArchiveId(), 'getArchiveId' );
|
|
$this->assertSame( (int)$row->ar_rev_id, $rec->getId(), 'getId' );
|
|
$this->assertSame( (int)$row->ar_page_id, $rec->getPageId(), 'getId' );
|
|
$this->assertSame( $row->ar_timestamp, $rec->getTimestamp(), 'getTimestamp' );
|
|
$this->assertSame( (int)$row->ar_deleted, $rec->getVisibility(), 'getVisibility' );
|
|
$this->assertSame( (bool)$row->ar_minor_edit, $rec->isMinor(), 'getIsMinor' );
|
|
|
|
if ( isset( $row->ar_parent_id ) ) {
|
|
$this->assertSame( (int)$row->ar_parent_id, $rec->getParentId(), 'getParentId' );
|
|
} else {
|
|
$this->assertSame( 0, $rec->getParentId(), 'getParentId' );
|
|
}
|
|
|
|
if ( isset( $row->ar_len ) ) {
|
|
$this->assertSame( (int)$row->ar_len, $rec->getSize(), 'getSize' );
|
|
} else {
|
|
$this->assertSame( $slots->computeSize(), $rec->getSize(), 'getSize' );
|
|
}
|
|
|
|
if ( !empty( $row->ar_sha1 ) ) {
|
|
$this->assertSame( $row->ar_sha1, $rec->getSha1(), 'getSha1' );
|
|
} else {
|
|
$this->assertSame( $slots->computeSha1(), $rec->getSha1(), 'getSha1' );
|
|
}
|
|
|
|
if ( $expectedException ) {
|
|
$this->expectException( $expectedException );
|
|
$rec->getPageAsLinkTarget();
|
|
} else {
|
|
$this->assertTrue(
|
|
TitleValue::newFromPage( $page )->isSameLinkAs( $rec->getPageAsLinkTarget() ),
|
|
'getPageAsLinkTarget'
|
|
);
|
|
}
|
|
}
|
|
|
|
public function provideConstructorFailure() {
|
|
$title = Title::newFromText( 'Dummy' );
|
|
$title->resetArticleID( 17 );
|
|
|
|
$user = new UserIdentityValue( 11, 'Tester', 0 );
|
|
|
|
$comment = CommentStoreComment::newUnsavedComment( 'Hello World' );
|
|
|
|
$main = SlotRecord::newUnsaved( SlotRecord::MAIN, new TextContent( 'Lorem Ipsum' ) );
|
|
$aux = SlotRecord::newUnsaved( 'aux', new TextContent( 'Frumious Bandersnatch' ) );
|
|
$slots = new RevisionSlots( [ $main, $aux ] );
|
|
|
|
$protoRow = [
|
|
'ar_id' => '5',
|
|
'ar_rev_id' => '7',
|
|
'ar_page_id' => strval( $title->getArticleID() ),
|
|
'ar_timestamp' => '20200101000000',
|
|
'ar_deleted' => 0,
|
|
'ar_minor_edit' => 0,
|
|
'ar_parent_id' => '5',
|
|
'ar_len' => $slots->computeSize(),
|
|
'ar_sha1' => $slots->computeSha1(),
|
|
];
|
|
|
|
yield 'mismatching wiki ID' => [
|
|
new PageIdentityValue(
|
|
$title->getArticleID(),
|
|
$title->getNamespace(),
|
|
$title->getDBkey(),
|
|
PageIdentity::LOCAL
|
|
),
|
|
$user,
|
|
$comment,
|
|
'not a row',
|
|
$slots,
|
|
'acmewiki',
|
|
PreconditionException::class
|
|
];
|
|
|
|
yield 'not a row' => [
|
|
new PageIdentityValue(
|
|
$title->getArticleID(),
|
|
$title->getNamespace(),
|
|
$title->getDBkey(),
|
|
'acmewiki'
|
|
),
|
|
$user,
|
|
$comment,
|
|
'not a row',
|
|
$slots,
|
|
'acmewiki'
|
|
];
|
|
|
|
$row = $protoRow;
|
|
$row['ar_timestamp'] = 'kittens';
|
|
|
|
yield 'bad timestamp' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots
|
|
];
|
|
|
|
$row = $protoRow;
|
|
|
|
yield 'bad wiki' => [
|
|
$title,
|
|
$user,
|
|
$comment,
|
|
(object)$row,
|
|
$slots,
|
|
12345
|
|
];
|
|
|
|
// NOTE: $title->getArticleID does *not* have to match ar_page_id in all cases!
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConstructorFailure
|
|
*
|
|
* @param PageIdentity $page
|
|
* @param UserIdentity $user
|
|
* @param CommentStoreComment $comment
|
|
* @param stdClass $row
|
|
* @param RevisionSlots $slots
|
|
* @param bool $wikiId,
|
|
* @param string|null $expectedException
|
|
*/
|
|
public function testConstructorFailure(
|
|
PageIdentity $page,
|
|
UserIdentity $user,
|
|
CommentStoreComment $comment,
|
|
$row,
|
|
RevisionSlots $slots,
|
|
$wikiId = false,
|
|
string $expectedException = InvalidArgumentException::class
|
|
) {
|
|
$this->expectException( $expectedException );
|
|
new RevisionArchiveRecord( $page, $user, $comment, $row, $slots, $wikiId );
|
|
}
|
|
|
|
/**
|
|
* @covers \MediaWiki\Revision\RevisionRecord::isCurrent
|
|
*/
|
|
public function testIsCurrent() {
|
|
$rev = $this->newRevision();
|
|
$this->assertFalse( $rev->isCurrent(),
|
|
RevisionArchiveRecord::class . ' cannot be stored current revision' );
|
|
}
|
|
}
|