namespace MediaWiki\Tests\Storage;
use CommentStoreComment;
+use Content;
use Exception;
use HashBagOStuff;
use InvalidArgumentException;
use MediaWiki\Storage\RevisionStore;
use MediaWiki\Storage\SlotRecord;
use MediaWiki\Storage\SqlBlobStore;
+use MediaWiki\User\UserIdentityValue;
use MediaWikiTestCase;
use PHPUnit_Framework_MockObject_MockObject;
use Revision;
*/
abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
+ /**
+ * @var Title
+ */
+ private $testPageTitle;
+
+ /**
+ * @var WikiPage
+ */
+ private $testPage;
+
/**
* @return int
*/
$this->tablesUsed += $this->getMcrTablesToReset();
- $this->setMwGlobals(
- 'wgMultiContentRevisionSchemaMigrationStage',
- $this->getMcrMigrationStage()
- );
-
- $this->setMwGlobals(
- 'wgContentHandlerUseDB',
- $this->getContentHandlerUseDB()
- );
+ $this->setMwGlobals( [
+ 'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
+ 'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
+ 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
+ 'wgActorTableSchemaMigrationStage' => MIGRATION_OLD,
+ ] );
$this->overrideMwServices();
}
// Blank out. This would fail with a modified schema, and we don't need it.
}
+ /**
+ * @return Title
+ */
+ protected function getTestPageTitle() {
+ if ( $this->testPageTitle ) {
+ return $this->testPageTitle;
+ }
+
+ $this->testPageTitle = Title::newFromText( 'UTPage-' . __CLASS__ );
+ return $this->testPageTitle;
+ }
+ /**
+ * @return WikiPage
+ */
+ protected function getTestPage() {
+ if ( $this->testPage ) {
+ return $this->testPage;
+ }
+
+ $title = $this->getTestPageTitle();
+ $this->testPage = WikiPage::factory( $title );
+
+ if ( !$this->testPage->exists() ) {
+ // Make sure we don't write to the live db.
+ $this->ensureMockDatabaseConnection( wfGetDB( DB_MASTER ) );
+
+ $user = static::getTestSysop()->getUser();
+
+ $this->testPage->doEditContent(
+ new WikitextContent( 'UTContent-' . __CLASS__ ),
+ 'UTPageSummary-' . __CLASS__,
+ EDIT_NEW | EDIT_SUPPRESS_RC,
+ false,
+ $user
+ );
+ }
+
+ return $this->testPage;
+ }
+
/**
* @return LoadBalancer|PHPUnit_Framework_MockObject_MockObject
*/
$this->assertSame( 0, $count );
}
- private function assertLinkTargetsEqual( LinkTarget $l1, LinkTarget $l2 ) {
+ protected function assertLinkTargetsEqual( LinkTarget $l1, LinkTarget $l2 ) {
$this->assertEquals( $l1->getDBkey(), $l2->getDBkey() );
$this->assertEquals( $l1->getNamespace(), $l2->getNamespace() );
$this->assertEquals( $l1->getFragment(), $l2->getFragment() );
$this->assertEquals( $l1->getInterwiki(), $l2->getInterwiki() );
}
- private function assertRevisionRecordsEqual( RevisionRecord $r1, RevisionRecord $r2 ) {
+ protected function assertRevisionRecordsEqual( RevisionRecord $r1, RevisionRecord $r2 ) {
+ $this->assertEquals(
+ $r1->getPageAsLinkTarget()->getNamespace(),
+ $r2->getPageAsLinkTarget()->getNamespace()
+ );
+
+ $this->assertEquals(
+ $r1->getPageAsLinkTarget()->getText(),
+ $r2->getPageAsLinkTarget()->getText()
+ );
+
+ if ( $r1->getParentId() ) {
+ $this->assertEquals( $r1->getParentId(), $r2->getParentId() );
+ }
+
$this->assertEquals( $r1->getUser()->getName(), $r2->getUser()->getName() );
$this->assertEquals( $r1->getUser()->getId(), $r2->getUser()->getId() );
$this->assertEquals( $r1->getComment(), $r2->getComment() );
- $this->assertEquals( $r1->getPageAsLinkTarget(), $r2->getPageAsLinkTarget() );
$this->assertEquals( $r1->getTimestamp(), $r2->getTimestamp() );
$this->assertEquals( $r1->getVisibility(), $r2->getVisibility() );
$this->assertEquals( $r1->getSha1(), $r2->getSha1() );
- $this->assertEquals( $r1->getParentId(), $r2->getParentId() );
$this->assertEquals( $r1->getSize(), $r2->getSize() );
$this->assertEquals( $r1->getPageId(), $r2->getPageId() );
- $this->assertEquals( $r1->getSlotRoles(), $r2->getSlotRoles() );
+ $this->assertArrayEquals( $r1->getSlotRoles(), $r2->getSlotRoles() );
$this->assertEquals( $r1->getWikiId(), $r2->getWikiId() );
$this->assertEquals( $r1->isMinor(), $r2->isMinor() );
foreach ( $r1->getSlotRoles() as $role ) {
}
}
- private function assertSlotRecordsEqual( SlotRecord $s1, SlotRecord $s2 ) {
+ protected function assertSlotRecordsEqual( SlotRecord $s1, SlotRecord $s2 ) {
$this->assertSame( $s1->getRole(), $s2->getRole() );
$this->assertSame( $s1->getModel(), $s2->getModel() );
$this->assertSame( $s1->getFormat(), $s2->getFormat() );
$s1->hasAddress() ? $this->assertSame( $s1->hasAddress(), $s2->hasAddress() ) : null;
}
- private function assertRevisionCompleteness( RevisionRecord $r ) {
+ protected function assertRevisionCompleteness( RevisionRecord $r ) {
+ $this->assertTrue( $r->hasSlot( 'main' ) );
+ $this->assertInstanceOf( SlotRecord::class, $r->getSlot( 'main' ) );
+ $this->assertInstanceOf( Content::class, $r->getContent( 'main' ) );
+
foreach ( $r->getSlotRoles() as $role ) {
$this->assertSlotCompleteness( $r, $r->getSlot( $role ) );
}
}
- private function assertSlotCompleteness( RevisionRecord $r, SlotRecord $slot ) {
+ protected function assertSlotCompleteness( RevisionRecord $r, SlotRecord $slot ) {
$this->assertTrue( $slot->hasAddress() );
$this->assertSame( $r->getId(), $slot->getRevision() );
+
+ $this->assertInstanceOf( Content::class, $slot->getContent() );
}
/**
*
* @return RevisionRecord
*/
- private function getRevisionRecordFromDetailsArray( $title, $details = [] ) {
+ private function getRevisionRecordFromDetailsArray( $details = [] ) {
// Convert some values that can't be provided by dataProviders
- $page = WikiPage::factory( $title );
if ( isset( $details['user'] ) && $details['user'] === true ) {
$details['user'] = $this->getTestUser()->getUser();
}
if ( isset( $details['page'] ) && $details['page'] === true ) {
- $details['page'] = $page->getId();
+ $details['page'] = $this->getTestPage()->getId();
}
if ( isset( $details['parent'] ) && $details['parent'] === true ) {
- $details['parent'] = $page->getLatest();
+ $details['parent'] = $this->getTestPage()->getLatest();
}
// Create the RevisionRecord with any available data
- $rev = new MutableRevisionRecord( $title );
+ $rev = new MutableRevisionRecord( $this->getTestPageTitle() );
isset( $details['slot'] ) ? $rev->setSlot( $details['slot'] ) : null;
isset( $details['parent'] ) ? $rev->setParentId( $details['parent'] ) : null;
isset( $details['page'] ) ? $rev->setPageId( $details['page'] ) : null;
isset( $details['visibility'] ) ? $rev->setVisibility( $details['visibility'] ) : null;
isset( $details['id'] ) ? $rev->setId( $details['id'] ) : null;
+ if ( isset( $details['content'] ) ) {
+ foreach ( $details['content'] as $role => $content ) {
+ $rev->setContent( $role, $content );
+ }
+ }
+
return $rev;
}
public function provideInsertRevisionOn_successes() {
yield 'Bare minimum revision insertion' => [
- Title::newFromText( 'UTPage' ),
[
'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
- 'parent' => true,
+ 'page' => true,
'comment' => $this->getRandomCommentStoreComment(),
'timestamp' => '20171117010101',
'user' => true,
],
];
yield 'Detailed revision insertion' => [
- Title::newFromText( 'UTPage' ),
[
'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
'parent' => true,
];
}
- private function getRandomCommentStoreComment() {
+ protected function getRandomCommentStoreComment() {
return CommentStoreComment::newUnsavedComment( __METHOD__ . '.' . rand( 0, 1000 ) );
}
* @covers \MediaWiki\Storage\RevisionStore::insertContentRowOn
*/
public function testInsertRevisionOn_successes(
- Title $title,
array $revDetails = []
) {
- $this->getExistingTestPage( $title );
- $rev = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
+ $title = $this->getTestPageTitle();
+ $rev = $this->getRevisionRecordFromDetailsArray( $revDetails );
$this->overrideMwServices();
$store = MediaWikiServices::getInstance()->getRevisionStore();
$return = $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) );
+ // is the new revision correct?
+ $this->assertRevisionCompleteness( $return );
$this->assertLinkTargetsEqual( $title, $return->getPageAsLinkTarget() );
$this->assertRevisionRecordsEqual( $rev, $return );
- $this->assertRevisionCompleteness( $return );
+
+ // can we load it from the store?
+ $loaded = $store->getRevisionById( $return->getId() );
+ $this->assertRevisionCompleteness( $loaded );
+ $this->assertRevisionRecordsEqual( $return, $loaded );
+
+ // can we find it directly in the database?
$this->assertRevisionExistsInDatabase( $return );
}
protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
+ $row = $this->revisionToRow( new Revision( $rev ), [] );
+
+ // unset nulled fields
+ unset( $row->rev_content_model );
+ unset( $row->rev_content_format );
+
+ // unset fake fields
+ unset( $row->rev_comment_text );
+ unset( $row->rev_comment_data );
+ unset( $row->rev_comment_cid );
+ unset( $row->rev_comment_id );
+
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+ $queryInfo = $store->getQueryInfo( [ 'user' ] );
+
+ $row = get_object_vars( $row );
$this->assertSelect(
- 'revision', [ 'count(*)' ], [ 'rev_id' => $rev->getId() ], [ [ '1' ] ]
+ $queryInfo['tables'],
+ array_keys( $row ),
+ [ 'rev_id' => $rev->getId() ],
+ [ array_values( $row ) ],
+ [],
+ $queryInfo['joins']
);
}
* @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn
*/
public function testInsertRevisionOn_blobAddressExists() {
- $page = $this->getExistingTestPage();
- $title = $page->getTitle();
+ $title = $this->getTestPageTitle();
$revDetails = [
'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
'parent' => true,
$store = MediaWikiServices::getInstance()->getRevisionStore();
// Insert the first revision
- $revOne = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
+ $revOne = $this->getRevisionRecordFromDetailsArray( $revDetails );
$firstReturn = $store->insertRevisionOn( $revOne, wfGetDB( DB_MASTER ) );
$this->assertLinkTargetsEqual( $title, $firstReturn->getPageAsLinkTarget() );
$this->assertRevisionRecordsEqual( $revOne, $firstReturn );
// Insert a second revision inheriting the same blob address
$revDetails['slot'] = SlotRecord::newInherited( $firstReturn->getSlot( 'main' ) );
- $revTwo = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
+ $revTwo = $this->getRevisionRecordFromDetailsArray( $revDetails );
$secondReturn = $store->insertRevisionOn( $revTwo, wfGetDB( DB_MASTER ) );
$this->assertLinkTargetsEqual( $title, $secondReturn->getPageAsLinkTarget() );
$this->assertRevisionRecordsEqual( $revTwo, $secondReturn );
public function provideInsertRevisionOn_failures() {
yield 'no slot' => [
- Title::newFromText( 'UTPage' ),
[
'comment' => $this->getRandomCommentStoreComment(),
'timestamp' => '20171117010101',
'user' => true,
],
- new InvalidArgumentException( 'At least one slot needs to be defined!' )
+ new InvalidArgumentException( 'main slot must be provided' )
];
- yield 'slot that is not main slot' => [
- Title::newFromText( 'UTPage' ),
+ yield 'no main slot' => [
[
- 'slot' => SlotRecord::newUnsaved( 'lalala', new WikitextContent( 'Chicken' ) ),
+ 'slot' => SlotRecord::newUnsaved( 'aux', new WikitextContent( 'Turkey' ) ),
'comment' => $this->getRandomCommentStoreComment(),
'timestamp' => '20171117010101',
'user' => true,
],
- new InvalidArgumentException( 'Only the main slot is supported for now!' )
+ new InvalidArgumentException( 'main slot must be provided' )
];
yield 'no timestamp' => [
- Title::newFromText( 'UTPage' ),
[
'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
'comment' => $this->getRandomCommentStoreComment(),
new IncompleteRevisionException( 'timestamp field must not be NULL!' )
];
yield 'no comment' => [
- Title::newFromText( 'UTPage' ),
[
'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
'timestamp' => '20171117010101',
new IncompleteRevisionException( 'comment must not be NULL!' )
];
yield 'no user' => [
- Title::newFromText( 'UTPage' ),
[
'slot' => SlotRecord::newUnsaved( 'main', new WikitextContent( 'Chicken' ) ),
'comment' => $this->getRandomCommentStoreComment(),
* @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn
*/
public function testInsertRevisionOn_failures(
- Title $title,
array $revDetails = [],
Exception $exception
) {
- $page = $this->getExistingTestPage( $title );
-
- $rev = $this->getRevisionRecordFromDetailsArray( $title, $revDetails );
+ $rev = $this->getRevisionRecordFromDetailsArray( $revDetails );
$store = MediaWikiServices::getInstance()->getRevisionStore();
public function provideNewNullRevision() {
yield [
- Title::newFromText( __METHOD__ ),
+ Title::newFromText( 'UTPage_notAutoCreated' ),
+ [ 'content' => [ 'main' => new WikitextContent( 'Flubber1' ) ] ],
CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment1' ),
true,
];
yield [
- Title::newFromText( __METHOD__ ),
+ Title::newFromText( 'UTPage_notAutoCreated' ),
+ [ 'content' => [ 'main' => new WikitextContent( 'Flubber2' ) ] ],
CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment2', [ 'a' => 1 ] ),
false,
];
* @covers \MediaWiki\Storage\RevisionStore::newNullRevision
* @covers \MediaWiki\Storage\RevisionStore::findSlotContentId
*/
- public function testNewNullRevision( Title $title, $comment, $minor ) {
+ public function testNewNullRevision( Title $title, $revDetails, $comment, $minor = false ) {
$this->overrideMwServices();
- $page = $this->getExistingTestPage( $title );
- $rev = $page->getRevision();
+ $user = TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser();
+ $page = WikiPage::factory( $title );
+
+ if ( !$page->exists() ) {
+ $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__, EDIT_NEW );
+ }
+
+ $revDetails['page'] = $page->getId();
+ $revDetails['timestamp'] = wfTimestampNow();
+ $revDetails['comment'] = CommentStoreComment::newUnsavedComment( 'Base' );
+ $revDetails['user'] = $user;
+ $baseRev = $this->getRevisionRecordFromDetailsArray( $revDetails );
$store = MediaWikiServices::getInstance()->getRevisionStore();
- $user = TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser();
- $parent = $store->getRevisionById( $rev->getId() );
+ $dbw = wfGetDB( DB_MASTER );
+ $baseRev = $store->insertRevisionOn( $baseRev, $dbw );
+ $page->updateRevisionOn( $dbw, new Revision( $baseRev ), $page->getLatest() );
+
$record = $store->newNullRevision(
wfGetDB( DB_MASTER ),
$title,
$this->assertEquals( $comment, $record->getComment() );
$this->assertEquals( $minor, $record->isMinor() );
$this->assertEquals( $user->getName(), $record->getUser()->getName() );
- $this->assertEquals( $parent->getId(), $record->getParentId() );
+ $this->assertEquals( $baseRev->getId(), $record->getParentId() );
- $parentSlot = $parent->getSlot( 'main' );
- $slot = $record->getSlot( 'main' );
+ $this->assertArrayEquals(
+ $baseRev->getSlotRoles(),
+ $record->getSlotRoles()
+ );
+
+ foreach ( $baseRev->getSlotRoles() as $role ) {
+ $parentSlot = $baseRev->getSlot( $role );
+ $slot = $record->getSlot( $role );
- $this->assertTrue( $slot->isInherited(), 'isInherited' );
- $this->assertSame( $parentSlot->getOrigin(), $slot->getOrigin(), 'getOrigin' );
- $this->assertSameSlotContent( $parentSlot, $slot );
+ $this->assertTrue( $slot->isInherited(), 'isInherited' );
+ $this->assertSame( $parentSlot->getOrigin(), $slot->getOrigin(), 'getOrigin' );
+ $this->assertSameSlotContent( $parentSlot, $slot );
+ }
}
/**
* @covers \MediaWiki\Storage\RevisionStore::getRcIdIfUnpatrolled
*/
public function testGetRcIdIfUnpatrolled_returnsRecentChangesId() {
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$status = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
/** @var Revision $rev */
$rev = $status->value['revision'];
$this->assertGreaterThan( 0, $result );
$this->assertSame(
- $page->getRevision()->getRecentChange()->getAttribute( 'rc_id' ),
+ $store->getRecentChange( $revisionRecord )->getAttribute( 'rc_id' ),
$result
);
}
public function testGetRcIdIfUnpatrolled_returnsZeroIfPatrolled() {
// This assumes that sysops are auto patrolled
$sysop = $this->getTestSysop()->getUser();
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$status = $page->doEditContent(
new WikitextContent( __METHOD__ ),
__METHOD__,
* @covers \MediaWiki\Storage\RevisionStore::getRecentChange
*/
public function testGetRecentChange() {
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$content = new WikitextContent( __METHOD__ );
$status = $page->doEditContent( $content, __METHOD__ );
/** @var Revision $rev */
* @covers \MediaWiki\Storage\RevisionStore::getRevisionById
*/
public function testGetRevisionById() {
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$content = new WikitextContent( __METHOD__ );
$status = $page->doEditContent( $content, __METHOD__ );
/** @var Revision $rev */
* @covers \MediaWiki\Storage\RevisionStore::getRevisionByTitle
*/
public function testGetRevisionByTitle() {
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$content = new WikitextContent( __METHOD__ );
$status = $page->doEditContent( $content, __METHOD__ );
/** @var Revision $rev */
* @covers \MediaWiki\Storage\RevisionStore::getRevisionByPageId
*/
public function testGetRevisionByPageId() {
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$content = new WikitextContent( __METHOD__ );
$status = $page->doEditContent( $content, __METHOD__ );
/** @var Revision $rev */
// Make sure there is 1 second between the last revision and the rev we create...
// Otherwise we might not get the correct revision and the test may fail...
// :(
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
sleep( 1 );
$content = new WikitextContent( __METHOD__ );
$status = $page->doEditContent( $content, __METHOD__ );
$this->assertSame( __METHOD__, $revRecord->getComment()->text );
}
- protected function revisionToRow( Revision $rev ) {
+ protected function revisionToRow( Revision $rev, $options = [ 'page', 'user', 'comment' ] ) {
+ // XXX: the WikiPage object loads another RevisionRecord from the database. Not great.
$page = WikiPage::factory( $rev->getTitle() );
- return (object)[
+ $fields = [
'rev_id' => (string)$rev->getId(),
'rev_page' => (string)$rev->getPage(),
- 'rev_text_id' => (string)$rev->getTextId(),
'rev_timestamp' => $this->db->timestamp( $rev->getTimestamp() ),
'rev_user_text' => (string)$rev->getUserText(),
'rev_user' => (string)$rev->getUser(),
'rev_len' => (string)$rev->getSize(),
'rev_parent_id' => (string)$rev->getParentId(),
'rev_sha1' => (string)$rev->getSha1(),
- 'rev_comment_text' => $rev->getComment(),
- 'rev_comment_data' => null,
- 'rev_comment_cid' => null,
- 'rev_content_format' => $rev->getContentFormat(),
- 'rev_content_model' => $rev->getContentModel(),
- 'page_namespace' => (string)$page->getTitle()->getNamespace(),
- 'page_title' => $page->getTitle()->getDBkey(),
- 'page_id' => (string)$page->getId(),
- 'page_latest' => (string)$page->getLatest(),
- 'page_is_redirect' => $page->isRedirect() ? '1' : '0',
- 'page_len' => (string)$page->getContent()->getSize(),
- 'user_name' => (string)$rev->getUserText(),
];
+
+ if ( in_array( 'page', $options ) ) {
+ $fields += [
+ 'page_namespace' => (string)$page->getTitle()->getNamespace(),
+ 'page_title' => $page->getTitle()->getDBkey(),
+ 'page_id' => (string)$page->getId(),
+ 'page_latest' => (string)$page->getLatest(),
+ 'page_is_redirect' => $page->isRedirect() ? '1' : '0',
+ 'page_len' => (string)$page->getContent()->getSize(),
+ ];
+ }
+
+ if ( in_array( 'user', $options ) ) {
+ $fields += [
+ 'user_name' => (string)$rev->getUserText(),
+ ];
+ }
+
+ if ( in_array( 'comment', $options ) ) {
+ $fields += [
+ 'rev_comment_text' => $rev->getComment(),
+ 'rev_comment_data' => null,
+ 'rev_comment_cid' => null,
+ ];
+ }
+
+ if ( $rev->getId() ) {
+ $fields += [
+ 'rev_id' => (string)$rev->getId(),
+ ];
+ }
+
+ return (object)$fields;
}
- private function assertRevisionRecordMatchesRevision(
+ protected function assertRevisionRecordMatchesRevision(
Revision $rev,
RevisionRecord $record
) {
$this->assertSame( $rev->getContentFormat(), $record->getContent( 'main' )->getDefaultFormat() );
$this->assertSame( $rev->getContentModel(), $record->getContent( 'main' )->getModel() );
$this->assertLinkTargetsEqual( $rev->getTitle(), $record->getPageAsLinkTarget() );
+
+ $revRec = $rev->getRevisionRecord();
+ $revMain = $revRec->getSlot( 'main' );
+ $recMain = $record->getSlot( 'main' );
+
+ $this->assertSame( $revMain->hasOrigin(), $recMain->hasOrigin(), 'hasOrigin' );
+ $this->assertSame( $revMain->hasAddress(), $recMain->hasAddress(), 'hasAddress' );
+ $this->assertSame( $revMain->hasContentId(), $recMain->hasContentId(), 'hasContentId' );
+
+ if ( $revMain->hasOrigin() ) {
+ $this->assertSame( $revMain->getOrigin(), $recMain->getOrigin(), 'getOrigin' );
+ }
+
+ if ( $revMain->hasAddress() ) {
+ $this->assertSame( $revMain->getAddress(), $recMain->getAddress(), 'getAddress' );
+ }
+
+ if ( $revMain->hasContentId() ) {
+ $this->assertSame( $revMain->getContentId(), $recMain->getContentId(), 'getContentId' );
+ }
}
/**
* @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
- * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
+ * @covers \MediaWiki\Storage\RevisionStore::getQueryInfo
*/
- public function testNewRevisionFromRow_anonEdit() {
- $page = $this->getExistingTestPage();
+ public function testNewRevisionFromRow_getQueryInfo() {
+ $page = $this->getTestPage();
+ $text = __METHOD__ . 'a-ä';
+ /** @var Revision $rev */
+ $rev = $page->doEditContent(
+ new WikitextContent( $text ),
+ __METHOD__ . 'a'
+ )->value['revision'];
+
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+ $info = $store->getQueryInfo();
+ $row = $this->db->selectRow(
+ $info['tables'],
+ $info['fields'],
+ [ 'rev_id' => $rev->getId() ],
+ __METHOD__,
+ [],
+ $info['joins']
+ );
+ $record = $store->newRevisionFromRow(
+ $row,
+ [],
+ $page->getTitle()
+ );
+ $this->assertRevisionRecordMatchesRevision( $rev, $record );
+ $this->assertSame( $text, $rev->getContent()->serialize() );
+ }
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
+ */
+ public function testNewRevisionFromRow_anonEdit() {
+ $page = $this->getTestPage();
$text = __METHOD__ . 'a-ä';
/** @var Revision $rev */
$rev = $page->doEditContent(
/**
* @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
- * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
*/
public function testNewRevisionFromRow_anonEdit_legacyEncoding() {
$this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
$this->overrideMwServices();
- $page = $this->getExistingTestPage();
-
+ $page = $this->getTestPage();
$text = __METHOD__ . 'a-ä';
/** @var Revision $rev */
$rev = $page->doEditContent(
new WikitextContent( $text ),
- __METHOD__. 'a'
+ __METHOD__ . 'a'
)->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
/**
* @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
- * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow_1_29
*/
public function testNewRevisionFromRow_userEdit() {
- $page = $this->getExistingTestPage();
-
+ $page = $this->getTestPage();
$text = __METHOD__ . 'b-ä';
/** @var Revision $rev */
$rev = $page->doEditContent(
/**
* @covers \MediaWiki\Storage\RevisionStore::newRevisionFromArchiveRow
+ * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo
*/
- public function testNewRevisionFromArchiveRow() {
+ public function testNewRevisionFromArchiveRow_getArchiveQueryInfo() {
$store = MediaWikiServices::getInstance()->getRevisionStore();
$title = Title::newFromText( __METHOD__ );
$text = __METHOD__ . '-bä';
- $page = $this->getExistingTestPage( $title );
+ $page = WikiPage::factory( $title );
/** @var Revision $orig */
$orig = $page->doEditContent( new WikitextContent( $text ), __METHOD__ )
->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$title = Title::newFromText( __METHOD__ );
$text = __METHOD__ . '-bä';
- $page = $this->getExistingTestPage( $title );
+ $page = WikiPage::factory( $title );
/** @var Revision $orig */
$orig = $page->doEditContent( new WikitextContent( $text ), __METHOD__ )
->value['revision'];
$this->assertSame( $text, $record->getContent( 'main' )->serialize() );
}
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromArchiveRow
+ */
+ public function testNewRevisionFromArchiveRow_no_user() {
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+
+ $row = (object)[
+ 'ar_id' => '1',
+ 'ar_page_id' => '2',
+ 'ar_namespace' => '0',
+ 'ar_title' => 'Something',
+ 'ar_rev_id' => '2',
+ 'ar_text_id' => '47',
+ 'ar_timestamp' => '20180528192356',
+ 'ar_minor_edit' => '0',
+ 'ar_deleted' => '0',
+ 'ar_len' => '78',
+ 'ar_parent_id' => '0',
+ 'ar_sha1' => 'deadbeef',
+ 'ar_comment_text' => 'whatever',
+ 'ar_comment_data' => null,
+ 'ar_comment_cid' => null,
+ 'ar_user' => '0',
+ 'ar_user_text' => '', // this is the important bit
+ 'ar_actor' => null,
+ 'ar_content_format' => null,
+ 'ar_content_model' => null,
+ ];
+
+ \Wikimedia\suppressWarnings();
+ $record = $store->newRevisionFromArchiveRow( $row );
+ \Wikimedia\suppressWarnings( true );
+
+ $this->assertInstanceOf( RevisionRecord::class, $record );
+ $this->assertInstanceOf( UserIdentityValue::class, $record->getUser() );
+ $this->assertSame( 'Unknown user', $record->getUser()->getName() );
+ }
+
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::newRevisionFromRow
+ */
+ public function testNewRevisionFromRow_no_user() {
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+ $title = Title::newFromText( __METHOD__ );
+
+ $row = (object)[
+ 'rev_id' => '2',
+ 'rev_page' => '2',
+ 'page_namespace' => '0',
+ 'page_title' => $title->getText(),
+ 'rev_text_id' => '47',
+ 'rev_timestamp' => '20180528192356',
+ 'rev_minor_edit' => '0',
+ 'rev_deleted' => '0',
+ 'rev_len' => '78',
+ 'rev_parent_id' => '0',
+ 'rev_sha1' => 'deadbeef',
+ 'rev_comment_text' => 'whatever',
+ 'rev_comment_data' => null,
+ 'rev_comment_cid' => null,
+ 'rev_user' => '0',
+ 'rev_user_text' => '', // this is the important bit
+ 'rev_actor' => null,
+ 'rev_content_format' => null,
+ 'rev_content_model' => null,
+ ];
+
+ \Wikimedia\suppressWarnings();
+ $record = $store->newRevisionFromRow( $row, 0, $title );
+ \Wikimedia\suppressWarnings( true );
+
+ $this->assertNotNull( $record );
+ $this->assertNotNull( $record->getUser() );
+ $this->assertNotEmpty( $record->getUser()->getName() );
+ }
+
+ /**
+ * @covers \MediaWiki\Storage\RevisionStore::insertRevisionOn
+ */
+ public function testInsertRevisionOn_archive() {
+ // This is a round trip test for deletion and undeletion of a
+ // revision row via the archive table.
+
+ $store = MediaWikiServices::getInstance()->getRevisionStore();
+ $title = Title::newFromText( __METHOD__ );
+
+ $page = WikiPage::factory( $title );
+ /** @var Revision $origRev */
+ $page->doEditContent( new WikitextContent( "First" ), __METHOD__ . '-first' );
+ $origRev = $page->doEditContent( new WikitextContent( "Foo" ), __METHOD__ )
+ ->value['revision'];
+ $orig = $origRev->getRevisionRecord();
+ $page->doDeleteArticle( __METHOD__ );
+
+ // re-create page, so we can later load revisions for it
+ $page->doEditContent( new WikitextContent( 'Two' ), __METHOD__ );
+
+ $db = wfGetDB( DB_MASTER );
+ $arQuery = $store->getArchiveQueryInfo();
+ $row = $db->selectRow(
+ $arQuery['tables'], $arQuery['fields'], [ 'ar_rev_id' => $orig->getId() ],
+ __METHOD__, [], $arQuery['joins']
+ );
+
+ $this->assertNotFalse( $row, 'query failed' );
+
+ $record = $store->newRevisionFromArchiveRow(
+ $row,
+ 0,
+ $title,
+ [ 'page_id' => $title->getArticleID() ]
+ );
+
+ $restored = $store->insertRevisionOn( $record, $db );
+
+ // is the new revision correct?
+ $this->assertRevisionCompleteness( $restored );
+ $this->assertRevisionRecordsEqual( $record, $restored );
+
+ // does the new revision use the original slot?
+ $recMain = $record->getSlot( 'main' );
+ $restMain = $restored->getSlot( 'main' );
+ $this->assertSame( $recMain->getAddress(), $restMain->getAddress() );
+ $this->assertSame( $recMain->getContentId(), $restMain->getContentId() );
+ $this->assertSame( $recMain->getOrigin(), $restMain->getOrigin() );
+ $this->assertSame( 'Foo', $restMain->getContent()->serialize() );
+
+ // can we load it from the store?
+ $loaded = $store->getRevisionById( $restored->getId() );
+ $this->assertNotNull( $loaded );
+ $this->assertRevisionCompleteness( $loaded );
+ $this->assertRevisionRecordsEqual( $restored, $loaded );
+
+ // can we find it directly in the database?
+ $this->assertRevisionExistsInDatabase( $restored );
+ }
+
/**
* @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromId
*/
public function testLoadRevisionFromId() {
$title = Title::newFromText( __METHOD__ );
- $page = $this->getExistingTestPage( $title );
- $rev = $page->getRevision();
+ $page = WikiPage::factory( $title );
+ /** @var Revision $rev */
+ $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
+ ->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$result = $store->loadRevisionFromId( wfGetDB( DB_MASTER ), $rev->getId() );
*/
public function testLoadRevisionFromPageId() {
$title = Title::newFromText( __METHOD__ );
- $page = $this->getExistingTestPage( $title );
- $rev = $page->getRevision();
+ $page = WikiPage::factory( $title );
+ /** @var Revision $rev */
+ $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
+ ->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$result = $store->loadRevisionFromPageId( wfGetDB( DB_MASTER ), $page->getId() );
*/
public function testLoadRevisionFromTitle() {
$title = Title::newFromText( __METHOD__ );
- $page = $this->getExistingTestPage( $title );
- $rev = $page->getRevision();
+ $page = WikiPage::factory( $title );
+ /** @var Revision $rev */
+ $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
+ ->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$result = $store->loadRevisionFromTitle( wfGetDB( DB_MASTER ), $title );
* @covers \MediaWiki\Storage\RevisionStore::loadRevisionFromTimestamp
*/
public function testLoadRevisionFromTimestamp() {
- $page = $this->getNonexistingTestPage( __METHOD__ );
- $title = $page->getTitle();
+ $title = Title::newFromText( __METHOD__ );
+ $page = WikiPage::factory( $title );
/** @var Revision $revOne */
$revOne = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
->value['revision'];
* @covers \MediaWiki\Storage\RevisionStore::listRevisionSizes
*/
public function testGetParentLengths() {
- $page = $this->getNonexistingTestPage( __METHOD__ );
+ $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
/** @var Revision $revOne */
$revOne = $page->doEditContent(
new WikitextContent( __METHOD__ ), __METHOD__
* @covers \MediaWiki\Storage\RevisionStore::getPreviousRevision
*/
public function testGetPreviousRevision() {
- $page = $this->getNonexistingTestPage( __METHOD__ );
+ $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
/** @var Revision $revOne */
$revOne = $page->doEditContent(
new WikitextContent( __METHOD__ ), __METHOD__
* @covers \MediaWiki\Storage\RevisionStore::getNextRevision
*/
public function testGetNextRevision() {
- $page = $this->getNonexistingTestPage( __METHOD__ );
+ $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
/** @var Revision $revOne */
$revOne = $page->doEditContent(
new WikitextContent( __METHOD__ ), __METHOD__
* @covers \MediaWiki\Storage\RevisionStore::getTimestampFromId
*/
public function testGetTimestampFromId_found() {
- $page = $this->getExistingTestPage();
- $rev = $page->getRevision();
+ $page = $this->getTestPage();
+ /** @var Revision $rev */
+ $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
+ ->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$result = $store->getTimestampFromId(
* @covers \MediaWiki\Storage\RevisionStore::getTimestampFromId
*/
public function testGetTimestampFromId_notFound() {
- $page = $this->getExistingTestPage();
- $rev = $page->getRevision();
+ $page = $this->getTestPage();
+ /** @var Revision $rev */
+ $rev = $page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ )
+ ->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$result = $store->getTimestampFromId(
*/
public function testCountRevisionsByPageId() {
$store = MediaWikiServices::getInstance()->getRevisionStore();
- $page = $this->getNonexistingTestPage( __METHOD__ );
+ $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
$this->assertSame(
0,
*/
public function testCountRevisionsByTitle() {
$store = MediaWikiServices::getInstance()->getRevisionStore();
- $page = $this->getNonexistingTestPage( __METHOD__ );
+ $page = WikiPage::factory( Title::newFromText( __METHOD__ ) );
$this->assertSame(
0,
*/
public function testUserWasLastToEdit_false() {
$sysop = $this->getTestSysop()->getUser();
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$page->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
$store = MediaWikiServices::getInstance()->getRevisionStore();
public function testUserWasLastToEdit_true() {
$startTime = wfTimestampNow();
$sysop = $this->getTestSysop()->getUser();
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
$page->doEditContent(
new WikitextContent( __METHOD__ ),
__METHOD__,
* @covers \MediaWiki\Storage\RevisionStore::getKnownCurrentRevision
*/
public function testGetKnownCurrentRevision() {
- $page = $this->getExistingTestPage();
+ $page = $this->getTestPage();
/** @var Revision $rev */
$rev = $page->doEditContent(
new WikitextContent( __METHOD__ . 'b' ),
}
public function provideNewMutableRevisionFromArray() {
- yield 'Basic array, with page & id' => [
- [
- 'id' => 2,
- 'page' => 1,
- 'text_id' => 2,
- 'timestamp' => '20171017114835',
- 'user_text' => '111.0.1.2',
- 'user' => 0,
- 'minor_edit' => false,
- 'deleted' => 0,
- 'len' => 46,
- 'parent_id' => 1,
- 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
- 'comment' => 'Goat Comment!',
- 'content_format' => 'text/x-wiki',
- 'content_model' => 'wikitext',
- ]
- ];
yield 'Basic array, content object' => [
[
'id' => 2,
yield 'Basic array, with title' => [
[
'title' => Title::newFromText( 'SomeText' ),
- 'text_id' => 2,
'timestamp' => '20171017114835',
'user_text' => '111.0.1.2',
'user' => 0,
'parent_id' => 1,
'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
'comment' => 'Goat Comment!',
- 'content_format' => 'text/x-wiki',
- 'content_model' => 'wikitext',
+ 'content' => new WikitextContent( 'Some Content' ),
]
];
yield 'Basic array, no user field' => [
[
'id' => 2,
'page' => 1,
- 'text_id' => 2,
'timestamp' => '20171017114835',
'user_text' => '111.0.1.3',
'minor_edit' => false,
'parent_id' => 1,
'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
'comment' => 'Goat Comment!',
- 'content_format' => 'text/x-wiki',
- 'content_model' => 'wikitext',
+ 'content' => new WikitextContent( 'Some Content' ),
]
];
}
$this->assertSame( $array['sha1'], $result->getSha1() );
$this->assertSame( $array['comment'], $result->getComment()->text );
if ( isset( $array['content'] ) ) {
- $this->assertTrue(
- $result->getSlot( 'main' )->getContent()->equals( $array['content'] )
- );
+ foreach ( $array['content'] as $role => $content ) {
+ $this->assertTrue(
+ $result->getContent( $role )->equals( $content )
+ );
+ }
} elseif ( isset( $array['text'] ) ) {
$this->assertSame( $array['text'], $result->getSlot( 'main' )->getContent()->serialize() );
- } else {
+ } elseif ( isset( $array['content_format'] ) ) {
$this->assertSame(
$array['content_format'],
$result->getSlot( 'main' )->getContent()->getDefaultFormat()
$this->testNewMutableRevisionFromArray( $array );
}
- protected function getDefaultQueryFields( $returnTextIdField = true ) {
- $fields = [
- 'rev_id',
- 'rev_page',
- 'rev_timestamp',
- 'rev_minor_edit',
- 'rev_deleted',
- 'rev_len',
- 'rev_parent_id',
- 'rev_sha1',
- ];
- if ( $returnTextIdField ) {
- $fields[] = 'rev_text_id';
- }
- return $fields;
- }
-
- protected function getCommentQueryFields() {
- return [
- 'rev_comment_text' => 'rev_comment',
- 'rev_comment_data' => 'NULL',
- 'rev_comment_cid' => 'NULL',
- ];
- }
-
- protected function getActorQueryFields() {
- return [
- 'rev_user' => 'rev_user',
- 'rev_user_text' => 'rev_user_text',
- 'rev_actor' => 'NULL',
- ];
- }
-
- protected function getContentHandlerQueryFields() {
- return [
- 'rev_content_format',
- 'rev_content_model',
- ];
- }
-
- abstract public function provideGetQueryInfo();
-
- /**
- * @dataProvider provideGetQueryInfo
- * @covers \MediaWiki\Storage\RevisionStore::getQueryInfo
- */
- public function testGetQueryInfo( $options, $expected ) {
- $store = MediaWikiServices::getInstance()->getRevisionStore();
-
- $queryInfo = $store->getQueryInfo( $options );
-
- $this->assertArrayEqualsIgnoringIntKeyOrder(
- $expected['tables'],
- $queryInfo['tables']
- );
- $this->assertArrayEqualsIgnoringIntKeyOrder(
- $expected['fields'],
- $queryInfo['fields']
- );
- $this->assertArrayEqualsIgnoringIntKeyOrder(
- $expected['joins'],
- $queryInfo['joins']
- );
- }
-
- protected function getDefaultArchiveFields( $returnTextFields = true ) {
- $fields = [
- 'ar_id',
- 'ar_page_id',
- 'ar_namespace',
- 'ar_title',
- 'ar_rev_id',
- 'ar_timestamp',
- 'ar_minor_edit',
- 'ar_deleted',
- 'ar_len',
- 'ar_parent_id',
- 'ar_sha1',
- ];
- if ( $returnTextFields ) {
- $fields[] = 'ar_text_id';
- }
- return $fields;
- }
-
- abstract public function provideGetArchiveQueryInfo();
-
- /**
- * @dataProvider provideGetArchiveQueryInfo
- * @covers \MediaWiki\Storage\RevisionStore::getArchiveQueryInfo
- */
- public function testGetArchiveQueryInfo( $expected ) {
- $store = MediaWikiServices::getInstance()->getRevisionStore();
-
- $archiveQueryInfo = $store->getArchiveQueryInfo();
-
- $this->assertArrayEqualsIgnoringIntKeyOrder(
- $expected['tables'],
- $archiveQueryInfo['tables']
- );
-
- $this->assertArrayEqualsIgnoringIntKeyOrder(
- $expected['fields'],
- $archiveQueryInfo['fields']
- );
-
- $this->assertArrayEqualsIgnoringIntKeyOrder(
- $expected['joins'],
- $archiveQueryInfo['joins']
- );
- }
-
- /**
- * Assert that the two arrays passed are equal, ignoring the order of the values that integer
- * keys.
- *
- * Note: Failures of this assertion can be slightly confusing as the arrays are actually
- * split into a string key array and an int key array before assertions occur.
- *
- * @param array $expected
- * @param array $actual
- */
- private function assertArrayEqualsIgnoringIntKeyOrder( array $expected, array $actual ) {
- $this->objectAssociativeSort( $expected );
- $this->objectAssociativeSort( $actual );
-
- // Separate the int key values from the string key values so that assertion failures are
- // easier to understand.
- $expectedIntKeyValues = [];
- $actualIntKeyValues = [];
-
- // Remove all int keys and re add them at the end after sorting by value
- // This will result in all int keys being in the same order with same ints at the end of
- // the array
- foreach ( $expected as $key => $value ) {
- if ( is_int( $key ) ) {
- unset( $expected[$key] );
- $expectedIntKeyValues[] = $value;
- }
- }
- foreach ( $actual as $key => $value ) {
- if ( is_int( $key ) ) {
- unset( $actual[$key] );
- $actualIntKeyValues[] = $value;
- }
- }
-
- $this->assertArrayEquals( $expected, $actual, false, true );
- $this->assertArrayEquals( $expectedIntKeyValues, $actualIntKeyValues, false, true );
- }
-
}