X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=tests%2Fphpunit%2Fincludes%2FRevisionTest.php;h=0db76ff1932a1a2146d4241d223d4323b1eae024;hb=a2c8c2969420a0f150c03f76e3a0bf9028fcda43;hp=361984be459c42e25449f4d96e825bd5c630b6c8;hpb=d4d9ff0d1152cba349024cc2ee2bf203760fd832;p=lhc%2Fweb%2Fwiklou.git diff --git a/tests/phpunit/includes/RevisionTest.php b/tests/phpunit/includes/RevisionTest.php index 361984be45..8b644c57cb 100644 --- a/tests/phpunit/includes/RevisionTest.php +++ b/tests/phpunit/includes/RevisionTest.php @@ -1,6 +1,15 @@ new JavaScriptContent( 'hellow world.' ) ], ]; + // FIXME: test with and without user ID, and with a user object. + // We can't prepare that here though, since we don't yet have a dummy DB + } + + /** + * @param string $model + * @return Title + */ + public function getMockTitle( $model = CONTENT_MODEL_WIKITEXT ) { + $mock = $this->getMockBuilder( Title::class ) + ->disableOriginalConstructor() + ->getMock(); + $mock->expects( $this->any() ) + ->method( 'getNamespace' ) + ->will( $this->returnValue( $this->getDefaultWikitextNS() ) ); + $mock->expects( $this->any() ) + ->method( 'getPrefixedText' ) + ->will( $this->returnValue( 'RevisionTest' ) ); + $mock->expects( $this->any() ) + ->method( 'getDBkey' ) + ->will( $this->returnValue( 'RevisionTest' ) ); + $mock->expects( $this->any() ) + ->method( 'getArticleID' ) + ->will( $this->returnValue( 23 ) ); + $mock->expects( $this->any() ) + ->method( 'getModel' ) + ->will( $this->returnValue( $model ) ); + + return $mock; } /** * @dataProvider provideConstructFromArray * @covers Revision::__construct - * @covers Revision::constructFromRowArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray */ - public function testConstructFromArray( array $rowArray ) { - $rev = new Revision( $rowArray ); + public function testConstructFromArray( $rowArray ) { + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertNotNull( $rev->getContent(), 'no content object available' ); $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() ); $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() ); } + /** + * @covers Revision::__construct + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testConstructFromEmptyArray() { + $rev = new Revision( [], 0, $this->getMockTitle() ); + $this->assertNull( $rev->getContent(), 'no content object should be available' ); + } + + /** + * @covers Revision::__construct + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testConstructFromArrayWithBadPageId() { + Wikimedia\suppressWarnings(); + $rev = new Revision( [ 'page' => 77777777 ] ); + $this->assertSame( 77777777, $rev->getPage() ); + Wikimedia\restoreWarnings(); + } + public function provideConstructFromArray_userSetAsExpected() { yield 'no user defaults to wgUser' => [ [ @@ -52,30 +110,20 @@ class RevisionTest extends MediaWikiTestCase { 99, 'SomeTextUserName', ]; - // Note: the below XXX test cases are odd and probably result in unexpected behaviour if used - // in production code. - yield 'XXX: user text only' => [ + yield 'user text only' => [ [ 'content' => new JavaScriptContent( 'hello world.' ), 'user_text' => '111.111.111.111', ], - null, + 0, '111.111.111.111', ]; - yield 'XXX: user id only' => [ - [ - 'content' => new JavaScriptContent( 'hello world.' ), - 'user' => 9989, - ], - 9989, - null, - ]; } /** * @dataProvider provideConstructFromArray_userSetAsExpected * @covers Revision::__construct - * @covers Revision::constructFromRowArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray * * @param array $rowArray * @param mixed $expectedUserId null to expect the current wgUser ID @@ -95,7 +143,7 @@ class RevisionTest extends MediaWikiTestCase { $expectedUserName = $testUser->getName(); } - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertEquals( $expectedUserId, $rev->getUser() ); $this->assertEquals( $expectedUserName, $rev->getUserText() ); } @@ -105,28 +153,30 @@ class RevisionTest extends MediaWikiTestCase { [ 'content' => new WikitextContent( 'GOAT' ), 'text_id' => 'someid', - ], + ], new MWException( "Text already stored in external store (id someid), " . "can't serialize content object" ) ]; yield 'with bad content object (class)' => [ [ 'content' => new stdClass() ], - new MWException( '`content` field must contain a Content object.' ) + new MWException( 'content field must contain a Content object.' ) ]; yield 'with bad content object (string)' => [ [ 'content' => 'ImAGoat' ], - new MWException( '`content` field must contain a Content object.' ) + new MWException( 'content field must contain a Content object.' ) ]; yield 'bad row format' => [ 'imastring, not a row', - new MWException( 'Revision constructor passed invalid row format.' ) + new InvalidArgumentException( + '$row must be a row object, an associative array, or a RevisionRecord' + ) ]; } /** * @dataProvider provideConstructFromArrayThrowsExceptions * @covers Revision::__construct - * @covers Revision::constructFromRowArray + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray */ public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) { $this->setExpectedException( @@ -134,14 +184,25 @@ class RevisionTest extends MediaWikiTestCase { $expectedException->getMessage(), $expectedException->getCode() ); - new Revision( $rowArray ); + new Revision( $rowArray, 0, $this->getMockTitle() ); + } + + /** + * @covers Revision::__construct + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testConstructFromNothing() { + $this->setExpectedException( + InvalidArgumentException::class + ); + new Revision( [] ); } public function provideConstructFromRow() { yield 'Full construction' => [ [ - 'rev_id' => '2', - 'rev_page' => '1', + 'rev_id' => '42', + 'rev_page' => '23', 'rev_text_id' => '2', 'rev_timestamp' => '20171017114835', 'rev_user_text' => '127.0.0.1', @@ -158,8 +219,8 @@ class RevisionTest extends MediaWikiTestCase { 'rev_content_model' => 'GOATMODEL', ], function ( RevisionTest $testCase, Revision $rev ) { - $testCase->assertSame( 2, $rev->getId() ); - $testCase->assertSame( 1, $rev->getPage() ); + $testCase->assertSame( 42, $rev->getId() ); + $testCase->assertSame( 23, $rev->getPage() ); $testCase->assertSame( 2, $rev->getTextId() ); $testCase->assertSame( '20171017114835', $rev->getTimestamp() ); $testCase->assertSame( '127.0.0.1', $rev->getUserText() ); @@ -174,10 +235,10 @@ class RevisionTest extends MediaWikiTestCase { $testCase->assertSame( 'GOATMODEL', $rev->getContentModel() ); } ]; - yield 'null fields' => [ + yield 'default field values' => [ [ - 'rev_id' => '2', - 'rev_page' => '1', + 'rev_id' => '42', + 'rev_page' => '23', 'rev_text_id' => '2', 'rev_timestamp' => '20171017114835', 'rev_user_text' => '127.0.0.1', @@ -189,11 +250,24 @@ class RevisionTest extends MediaWikiTestCase { 'rev_comment_cid' => null, ], function ( RevisionTest $testCase, Revision $rev ) { - $testCase->assertNull( $rev->getSize() ); - $testCase->assertNull( $rev->getParentId() ); - $testCase->assertNull( $rev->getSha1() ); - $testCase->assertSame( 'text/x-wiki', $rev->getContentFormat() ); - $testCase->assertSame( 'wikitext', $rev->getContentModel() ); + // parent ID may be null + $testCase->assertSame( null, $rev->getParentId(), 'revision id' ); + + // given fields + $testCase->assertSame( $rev->getTimestamp(), '20171017114835', 'timestamp' ); + $testCase->assertSame( $rev->getUserText(), '127.0.0.1', 'user name' ); + $testCase->assertSame( $rev->getUser(), 0, 'user id' ); + $testCase->assertSame( $rev->getComment(), 'Goat Comment!' ); + $testCase->assertSame( false, $rev->isMinor(), 'minor edit' ); + $testCase->assertSame( 0, $rev->getVisibility(), 'visibility flags' ); + + // computed fields + $testCase->assertNotNull( $rev->getSize(), 'size' ); + $testCase->assertNotNull( $rev->getSha1(), 'hash' ); + + // NOTE: model and format will be detected based on the namespace of the (mock) title + $testCase->assertSame( 'text/x-wiki', $rev->getContentFormat(), 'format' ); + $testCase->assertSame( 'wikitext', $rev->getContentModel(), 'model' ); } ]; } @@ -201,14 +275,50 @@ class RevisionTest extends MediaWikiTestCase { /** * @dataProvider provideConstructFromRow * @covers Revision::__construct - * @covers Revision::constructFromDbRowObject + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray */ public function testConstructFromRow( array $arrayData, $assertions ) { + $data = 'Hello goat.'; // needs to match model and format + + $blobStore = $this->getMockBuilder( SqlBlobStore::class ) + ->disableOriginalConstructor() + ->getMock(); + + $blobStore->method( 'getBlob' ) + ->will( $this->returnValue( $data ) ); + + $blobStore->method( 'getTextIdFromAddress' ) + ->will( $this->returnCallback( + function ( $address ) { + // Turn "tt:1234" into 12345. + // Note that this must be functional so we can test getTextId(). + // Ideally, we'd un-mock getTextIdFromAddress and use its actual implementation. + $parts = explode( ':', $address ); + return (int)array_pop( $parts ); + } + ) ); + + // Note override internal service, so RevisionStore uses it as well. + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); + $row = (object)$arrayData; - $rev = new Revision( $row ); + $rev = new Revision( $row, 0, $this->getMockTitle() ); $assertions( $this, $rev ); } + /** + * @covers Revision::__construct + * @covers \MediaWiki\Storage\RevisionStore::newMutableRevisionFromArray + */ + public function testConstructFromRowWithBadPageId() { + $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); + $this->overrideMwServices(); + Wikimedia\suppressWarnings(); + $rev = new Revision( (object)[ 'rev_page' => 77777777 ] ); + $this->assertSame( 77777777, $rev->getPage() ); + Wikimedia\restoreWarnings(); + } + public function provideGetRevisionText() { yield 'Generic test' => [ 'This is a goat of revision text.', @@ -235,7 +345,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getId */ public function testGetId( $rowArray, $expectedId ) { - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertEquals( $expectedId, $rev->getId() ); } @@ -249,7 +359,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::setId */ public function testSetId( $input, $expected ) { - $rev = new Revision( [] ); + $rev = new Revision( [], 0, $this->getMockTitle() ); $rev->setId( $input ); $this->assertSame( $expected, $rev->getId() ); } @@ -264,7 +374,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::setUserIdAndName */ public function testSetUserIdAndName( $inputId, $expectedId, $name ) { - $rev = new Revision( [] ); + $rev = new Revision( [], 0, $this->getMockTitle() ); $rev->setUserIdAndName( $inputId, $name ); $this->assertSame( $expectedId, $rev->getUser( Revision::RAW ) ); $this->assertEquals( $name, $rev->getUserText( Revision::RAW ) ); @@ -281,7 +391,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getTextId() */ public function testGetTextId( $rowArray, $expected ) { - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertSame( $expected, $rev->getTextId() ); } @@ -296,7 +406,7 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getParentId() */ public function testGetParentId( $rowArray, $expected ) { - $rev = new Revision( $rowArray ); + $rev = new Revision( $rowArray, 0, $this->getMockTitle() ); $this->assertSame( $expected, $rev->getParentId() ); } @@ -329,9 +439,64 @@ class RevisionTest extends MediaWikiTestCase { $this->testGetRevisionText( $expected, $rowData ); } + private function getWANObjectCache() { + return new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ); + } + + /** + * @return SqlBlobStore + */ + private function getBlobStore() { + /** @var LoadBalancer $lb */ + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + $cache = $this->getWANObjectCache(); + + $blobStore = new SqlBlobStore( $lb, $cache ); + return $blobStore; + } + + private function mockBlobStoreFactory( $blobStore ) { + /** @var LoadBalancer $lb */ + $factory = $this->getMockBuilder( BlobStoreFactory::class ) + ->disableOriginalConstructor() + ->getMock(); + $factory->expects( $this->any() ) + ->method( 'newBlobStore' ) + ->willReturn( $blobStore ); + $factory->expects( $this->any() ) + ->method( 'newSqlBlobStore' ) + ->willReturn( $blobStore ); + return $factory; + } + + /** + * @return RevisionStore + */ + private function getRevisionStore() { + /** @var LoadBalancer $lb */ + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + $cache = $this->getWANObjectCache(); + + $blobStore = new RevisionStore( + $lb, + $this->getBlobStore(), + $cache, + MediaWikiServices::getInstance()->getCommentStore(), + MediaWikiServices::getInstance()->getActorMigration() + ); + return $blobStore; + } + public function provideGetRevisionTextWithLegacyEncoding() { yield 'Utf8Native' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => 'utf-8', @@ -340,6 +505,7 @@ class RevisionTest extends MediaWikiTestCase { ]; yield 'Utf8Legacy' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => '', @@ -352,8 +518,11 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getRevisionText * @dataProvider provideGetRevisionTextWithLegacyEncoding */ - public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) { - $this->setMwGlobals( 'wgLegacyEncoding', $encoding ); + public function testGetRevisionWithLegacyEncoding( $expected, $lang, $encoding, $rowData ) { + $blobStore = $this->getBlobStore(); + $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); + $this->testGetRevisionText( $expected, $rowData ); } @@ -365,6 +534,7 @@ class RevisionTest extends MediaWikiTestCase { */ yield 'Utf8NativeGzip' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => 'gzip,utf-8', @@ -373,6 +543,7 @@ class RevisionTest extends MediaWikiTestCase { ]; yield 'Utf8LegacyGzip' => [ "Wiki est l'\xc3\xa9cole superieur !", + 'fr', 'iso-8859-1', [ 'old_flags' => 'gzip', @@ -385,9 +556,13 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getRevisionText * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding */ - public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) { + public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $lang, $encoding, $rowData ) { $this->checkPHPExtension( 'zlib' ); - $this->setMwGlobals( 'wgLegacyEncoding', $encoding ); + + $blobStore = $this->getBlobStore(); + $blobStore->setLegacyEncoding( $encoding, Language::factory( $lang ) ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); + $this->testGetRevisionText( $expected, $rowData ); } @@ -413,7 +588,10 @@ class RevisionTest extends MediaWikiTestCase { */ public function testCompressRevisionTextUtf8Gzip() { $this->checkPHPExtension( 'zlib' ); - $this->setMwGlobals( 'wgCompressRevisions', true ); + + $blobStore = $this->getBlobStore(); + $blobStore->setCompressBlobs( true ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); $row = new stdClass; $row->old_text = "Wiki est l'\xc3\xa9cole superieur !"; @@ -428,20 +606,44 @@ class RevisionTest extends MediaWikiTestCase { Revision::getRevisionText( $row ), "getRevisionText" ); } - public function provideFetchFromConds() { - yield [ 0, [] ]; - yield [ Revision::READ_LOCKING, [ 'FOR UPDATE' ] ]; - } - /** - * @dataProvider provideFetchFromConds - * @covers Revision::fetchFromConds + * @covers Revision::loadFromTitle */ - public function testFetchFromConds( $flags, array $options ) { + public function testLoadFromTitle() { $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); - $conditions = [ 'conditionsArray' ]; + $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD ); + $this->overrideMwServices(); + $title = $this->getMockTitle(); + + $conditions = [ + 'rev_id=page_latest', + 'page_namespace' => $title->getNamespace(), + 'page_title' => $title->getDBkey() + ]; + + $row = (object)[ + 'rev_id' => '42', + 'rev_page' => $title->getArticleID(), + 'rev_text_id' => '2', + 'rev_timestamp' => '20171017114835', + 'rev_user_text' => '127.0.0.1', + 'rev_user' => '0', + 'rev_minor_edit' => '0', + 'rev_deleted' => '0', + 'rev_len' => '46', + 'rev_parent_id' => '1', + 'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', + 'rev_comment_text' => 'Goat Comment!', + 'rev_comment_data' => null, + 'rev_comment_cid' => null, + 'rev_content_format' => 'GOATFORMAT', + 'rev_content_model' => 'GOATMODEL', + ]; $db = $this->getMock( IDatabase::class ); + $db->expects( $this->any() ) + ->method( 'getDomainId' ) + ->will( $this->returnValue( wfWikiID() ) ); $db->expects( $this->once() ) ->method( 'selectRow' ) ->with( @@ -450,17 +652,24 @@ class RevisionTest extends MediaWikiTestCase { $this->isType( 'array' ), $this->equalTo( $conditions ), // Method name - $this->equalTo( 'Revision::fetchFromConds' ), - $this->equalTo( $options ), + $this->stringContains( 'fetchRevisionRowFromConds' ), + // We don't really care about the options here + $this->isType( 'array' ), // We don't really care about the join conds are they come from the joinCond methods $this->isType( 'array' ) ) - ->willReturn( 'RETURNVALUE' ); + ->willReturn( $row ); - $wrapper = TestingAccessWrapper::newFromClass( Revision::class ); - $result = $wrapper->fetchFromConds( $db, $conditions, $flags ); + $revision = Revision::loadFromTitle( $db, $title ); - $this->assertEquals( 'RETURNVALUE', $result ); + $this->assertEquals( $title->getArticleID(), $revision->getTitle()->getArticleID() ); + $this->assertEquals( $row->rev_id, $revision->getId() ); + $this->assertEquals( $row->rev_len, $revision->getSize() ); + $this->assertEquals( $row->rev_sha1, $revision->getSha1() ); + $this->assertEquals( $row->rev_parent_id, $revision->getParentId() ); + $this->assertEquals( $row->rev_timestamp, $revision->getTimestamp() ); + $this->assertEquals( $row->rev_comment_text, $revision->getComment() ); + $this->assertEquals( $row->rev_user_text, $revision->getUserText() ); } public function provideDecompressRevisionText() { @@ -525,8 +734,12 @@ class RevisionTest extends MediaWikiTestCase { * @param mixed $expected */ public function testDecompressRevisionText( $legacyEncoding, $text, $flags, $expected ) { - $this->setMwGlobals( 'wgLegacyEncoding', $legacyEncoding ); - $this->setMwGlobals( 'wgLanguageCode', 'en' ); + $blobStore = $this->getBlobStore(); + if ( $legacyEncoding ) { + $blobStore->setLegacyEncoding( $legacyEncoding, Language::factory( 'en' ) ); + } + + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); $this->assertSame( $expected, Revision::decompressRevisionText( $text, $flags ) @@ -622,14 +835,20 @@ class RevisionTest extends MediaWikiTestCase { * @covers Revision::getRevisionText */ public function testGetRevisionText_external_oldId() { - $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] ); + $cache = $this->getWANObjectCache(); $this->setService( 'MainWANObjectCache', $cache ); + $this->setService( 'ExternalStoreFactory', new ExternalStoreFactory( [ 'ForTesting' ] ) ); - $cacheKey = $cache->makeKey( 'revisiontext', 'textid', '7777' ); + $lb = $this->getMockBuilder( LoadBalancer::class ) + ->disableOriginalConstructor() + ->getMock(); + + $blobStore = new SqlBlobStore( $lb, $cache ); + $this->setService( 'BlobStoreFactory', $this->mockBlobStoreFactory( $blobStore ) ); $this->assertSame( 'AAAABBAAA', @@ -641,6 +860,8 @@ class RevisionTest extends MediaWikiTestCase { ] ) ); + + $cacheKey = $cache->makeKey( 'revisiontext', 'textid', 'tt:7777' ); $this->assertSame( 'AAAABBAAA', $cache->get( $cacheKey ) ); } @@ -649,6 +870,8 @@ class RevisionTest extends MediaWikiTestCase { */ public function testUserJoinCond() { $this->hideDeprecated( 'Revision::userJoinCond' ); + $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD ); + $this->overrideMwServices(); $this->assertEquals( [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ], Revision::userJoinCond() @@ -666,6 +889,42 @@ class RevisionTest extends MediaWikiTestCase { ); } + private function overrideCommentStoreAndActorMigration() { + $mockStore = $this->getMockBuilder( CommentStore::class ) + ->disableOriginalConstructor() + ->getMock(); + $mockStore->expects( $this->any() ) + ->method( 'getFields' ) + ->willReturn( [ 'commentstore' => 'fields' ] ); + $mockStore->expects( $this->any() ) + ->method( 'getJoin' ) + ->willReturn( [ + 'tables' => [ 'commentstore' => 'table' ], + 'fields' => [ 'commentstore' => 'field' ], + 'joins' => [ 'commentstore' => 'join' ], + ] ); + $this->setService( 'CommentStore', $mockStore ); + + $mockStore = $this->getMockBuilder( ActorMigration::class ) + ->disableOriginalConstructor() + ->getMock(); + $mockStore->expects( $this->any() ) + ->method( 'getJoin' ) + ->willReturnCallback( function ( $key ) { + $p = strtok( $key, '_' ); + return [ + 'tables' => [ 'actormigration' => 'table' ], + 'fields' => [ + $p . '_user' => 'actormigration_user', + $p . '_user_text' => 'actormigration_user_text', + $p . '_actor' => 'actormigration_actor', + ], + 'joins' => [ 'actormigration' => 'join' ], + ]; + } ); + $this->setService( 'ActorMigration', $mockStore ); + } + public function provideSelectFields() { yield [ true, @@ -676,14 +935,13 @@ class RevisionTest extends MediaWikiTestCase { 'rev_timestamp', 'rev_user_text', 'rev_user', + 'rev_actor' => 'NULL', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'fields', 'rev_content_format', 'rev_content_model', ] @@ -697,14 +955,13 @@ class RevisionTest extends MediaWikiTestCase { 'rev_timestamp', 'rev_user_text', 'rev_user', + 'rev_actor' => 'NULL', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'fields', ] ]; } @@ -712,12 +969,12 @@ class RevisionTest extends MediaWikiTestCase { /** * @dataProvider provideSelectFields * @covers Revision::selectFields - * @todo a true unit test would mock CommentStore */ public function testSelectFields( $contentHandlerUseDB, $expected ) { $this->hideDeprecated( 'Revision::selectFields' ); $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB ); - $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); + $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD ); + $this->overrideCommentStoreAndActorMigration(); $this->assertEquals( $expected, Revision::selectFields() ); } @@ -733,14 +990,13 @@ class RevisionTest extends MediaWikiTestCase { 'ar_timestamp', 'ar_user_text', 'ar_user', + 'ar_actor' => 'NULL', 'ar_minor_edit', 'ar_deleted', 'ar_len', 'ar_parent_id', 'ar_sha1', - 'ar_comment_text' => 'ar_comment', - 'ar_comment_data' => 'NULL', - 'ar_comment_cid' => 'NULL', + 'commentstore' => 'fields', 'ar_content_format', 'ar_content_model', ] @@ -756,14 +1012,13 @@ class RevisionTest extends MediaWikiTestCase { 'ar_timestamp', 'ar_user_text', 'ar_user', + 'ar_actor' => 'NULL', 'ar_minor_edit', 'ar_deleted', 'ar_len', 'ar_parent_id', 'ar_sha1', - 'ar_comment_text' => 'ar_comment', - 'ar_comment_data' => 'NULL', - 'ar_comment_cid' => 'NULL', + 'commentstore' => 'fields', ] ]; } @@ -771,12 +1026,12 @@ class RevisionTest extends MediaWikiTestCase { /** * @dataProvider provideSelectArchiveFields * @covers Revision::selectArchiveFields - * @todo a true unit test would mock CommentStore */ public function testSelectArchiveFields( $contentHandlerUseDB, $expected ) { $this->hideDeprecated( 'Revision::selectArchiveFields' ); $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB ); - $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD ); + $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD ); + $this->overrideCommentStoreAndActorMigration(); $this->assertEquals( $expected, Revision::selectArchiveFields() ); } @@ -826,170 +1081,70 @@ class RevisionTest extends MediaWikiTestCase { } public function provideGetArchiveQueryInfo() { - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD' => [ - [ - 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, - ], - [ - 'tables' => [ 'archive' ], - 'fields' => [ - 'ar_id', - 'ar_page_id', - 'ar_rev_id', - 'ar_text', - 'ar_text_id', - 'ar_timestamp', - 'ar_user_text', - 'ar_user', - 'ar_minor_edit', - 'ar_deleted', - 'ar_len', - 'ar_parent_id', - 'ar_sha1', - 'ar_comment_text' => 'ar_comment', - 'ar_comment_data' => 'NULL', - 'ar_comment_cid' => 'NULL', - ], - 'joins' => [], - ] - ]; - yield 'wgContentHandlerUseDB true, wgCommentTableSchemaMigrationStage OLD' => [ - [ - 'wgContentHandlerUseDB' => true, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, - ], - [ - 'tables' => [ 'archive' ], - 'fields' => [ - 'ar_id', - 'ar_page_id', - 'ar_rev_id', - 'ar_text', - 'ar_text_id', - 'ar_timestamp', - 'ar_user_text', - 'ar_user', - 'ar_minor_edit', - 'ar_deleted', - 'ar_len', - 'ar_parent_id', - 'ar_sha1', - 'ar_comment_text' => 'ar_comment', - 'ar_comment_data' => 'NULL', - 'ar_comment_cid' => 'NULL', - 'ar_content_format', - 'ar_content_model', - ], - 'joins' => [], - ] - ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_BOTH' => [ - [ - 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH, - ], - [ - 'tables' => [ - 'archive', - 'comment_ar_comment' => 'comment', - ], - 'fields' => [ - 'ar_id', - 'ar_page_id', - 'ar_rev_id', - 'ar_text', - 'ar_text_id', - 'ar_timestamp', - 'ar_user_text', - 'ar_user', - 'ar_minor_edit', - 'ar_deleted', - 'ar_len', - 'ar_parent_id', - 'ar_sha1', - 'ar_comment_text' => 'COALESCE( comment_ar_comment.comment_text, ar_comment )', - 'ar_comment_data' => 'comment_ar_comment.comment_data', - 'ar_comment_cid' => 'comment_ar_comment.comment_id', - ], - 'joins' => [ - 'comment_ar_comment' => [ - 'LEFT JOIN', - 'comment_ar_comment.comment_id = ar_comment_id', - ], - ], - ] - ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_NEW' => [ + yield 'wgContentHandlerUseDB false' => [ [ 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_NEW, ], [ 'tables' => [ 'archive', - 'comment_ar_comment' => 'comment', + 'commentstore' => 'table', + 'actormigration' => 'table', ], 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', 'ar_timestamp', - 'ar_user_text', - 'ar_user', 'ar_minor_edit', 'ar_deleted', 'ar_len', 'ar_parent_id', 'ar_sha1', - 'ar_comment_text' => 'COALESCE( comment_ar_comment.comment_text, ar_comment )', - 'ar_comment_data' => 'comment_ar_comment.comment_data', - 'ar_comment_cid' => 'comment_ar_comment.comment_id', - ], - 'joins' => [ - 'comment_ar_comment' => [ - 'LEFT JOIN', - 'comment_ar_comment.comment_id = ar_comment_id', - ], + 'commentstore' => 'field', + 'ar_user' => 'actormigration_user', + 'ar_user_text' => 'actormigration_user_text', + 'ar_actor' => 'actormigration_actor', ], + 'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ], ] ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage NEW' => [ + yield 'wgContentHandlerUseDB true' => [ [ - 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_NEW, + 'wgContentHandlerUseDB' => true, ], [ 'tables' => [ 'archive', - 'comment_ar_comment' => 'comment', + 'commentstore' => 'table', + 'actormigration' => 'table', ], 'fields' => [ 'ar_id', 'ar_page_id', + 'ar_namespace', + 'ar_title', 'ar_rev_id', 'ar_text', 'ar_text_id', 'ar_timestamp', - 'ar_user_text', - 'ar_user', 'ar_minor_edit', 'ar_deleted', 'ar_len', 'ar_parent_id', 'ar_sha1', - 'ar_comment_text' => 'comment_ar_comment.comment_text', - 'ar_comment_data' => 'comment_ar_comment.comment_data', - 'ar_comment_cid' => 'comment_ar_comment.comment_id', - ], - 'joins' => [ - 'comment_ar_comment' => [ - 'JOIN', - 'comment_ar_comment.comment_id = ar_comment_id', - ], + 'commentstore' => 'field', + 'ar_user' => 'actormigration_user', + 'ar_user_text' => 'actormigration_user_text', + 'ar_actor' => 'actormigration_actor', + 'ar_content_format', + 'ar_content_model', ], + 'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ], ] ]; } @@ -1000,6 +1155,11 @@ class RevisionTest extends MediaWikiTestCase { */ public function testGetArchiveQueryInfo( $globals, $expected ) { $this->setMwGlobals( $globals ); + $this->overrideCommentStoreAndActorMigration(); + + $revisionStore = $this->getRevisionStore(); + $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] ); + $this->setService( 'RevisionStore', $revisionStore ); $this->assertEquals( $expected, Revision::getArchiveQueryInfo() @@ -1007,56 +1167,52 @@ class RevisionTest extends MediaWikiTestCase { } public function provideGetQueryInfo() { - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts none' => [ + yield 'wgContentHandlerUseDB false, opts none' => [ [ 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, ], [], [ - 'tables' => [ 'revision' ], + 'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table' ], 'fields' => [ 'rev_id', 'rev_page', 'rev_text_id', 'rev_timestamp', - 'rev_user_text', - 'rev_user', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'field', + 'rev_user' => 'actormigration_user', + 'rev_user_text' => 'actormigration_user_text', + 'rev_actor' => 'actormigration_actor', ], - 'joins' => [], + 'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ], ], ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts page' => [ + yield 'wgContentHandlerUseDB false, opts page' => [ [ 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, ], [ 'page' ], [ - 'tables' => [ 'revision', 'page' ], + 'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'page' ], 'fields' => [ 'rev_id', 'rev_page', 'rev_text_id', 'rev_timestamp', - 'rev_user_text', - 'rev_user', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'field', + 'rev_user' => 'actormigration_user', + 'rev_user_text' => 'actormigration_user_text', + 'rev_actor' => 'actormigration_actor', 'page_namespace', 'page_title', 'page_id', @@ -1069,68 +1225,68 @@ class RevisionTest extends MediaWikiTestCase { 'INNER JOIN', [ 'page_id = rev_page' ], ], + 'commentstore' => 'join', + 'actormigration' => 'join', ], ], ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts user' => [ + yield 'wgContentHandlerUseDB false, opts user' => [ [ 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, ], [ 'user' ], [ - 'tables' => [ 'revision', 'user' ], + 'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'user' ], 'fields' => [ 'rev_id', 'rev_page', 'rev_text_id', 'rev_timestamp', - 'rev_user_text', - 'rev_user', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'field', + 'rev_user' => 'actormigration_user', + 'rev_user_text' => 'actormigration_user_text', + 'rev_actor' => 'actormigration_actor', 'user_name', ], 'joins' => [ 'user' => [ 'LEFT JOIN', [ - 'rev_user != 0', - 'user_id = rev_user', + 'actormigration_user != 0', + 'user_id = actormigration_user', ], ], + 'commentstore' => 'join', + 'actormigration' => 'join', ], ], ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts text' => [ + yield 'wgContentHandlerUseDB false, opts text' => [ [ 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, ], [ 'text' ], [ - 'tables' => [ 'revision', 'text' ], + 'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'text' ], 'fields' => [ 'rev_id', 'rev_page', 'rev_text_id', 'rev_timestamp', - 'rev_user_text', - 'rev_user', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'field', + 'rev_user' => 'actormigration_user', + 'rev_user_text' => 'actormigration_user_text', + 'rev_actor' => 'actormigration_actor', 'old_text', 'old_flags', ], @@ -1139,32 +1295,34 @@ class RevisionTest extends MediaWikiTestCase { 'INNER JOIN', [ 'rev_text_id=old_id' ], ], + 'commentstore' => 'join', + 'actormigration' => 'join', ], ], ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts 3' => [ + yield 'wgContentHandlerUseDB false, opts 3' => [ [ 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, ], [ 'text', 'page', 'user' ], [ - 'tables' => [ 'revision', 'page', 'user', 'text' ], + 'tables' => [ + 'revision', 'commentstore' => 'table', 'actormigration' => 'table', 'page', 'user', 'text' + ], 'fields' => [ 'rev_id', 'rev_page', 'rev_text_id', 'rev_timestamp', - 'rev_user_text', - 'rev_user', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'field', + 'rev_user' => 'actormigration_user', + 'rev_user_text' => 'actormigration_user_text', + 'rev_actor' => 'actormigration_actor', 'page_namespace', 'page_title', 'page_id', @@ -1183,164 +1341,44 @@ class RevisionTest extends MediaWikiTestCase { 'user' => [ 'LEFT JOIN', [ - 'rev_user != 0', - 'user_id = rev_user', + 'actormigration_user != 0', + 'user_id = actormigration_user', ], ], 'text' => [ 'INNER JOIN', [ 'rev_text_id=old_id' ], ], + 'commentstore' => 'join', + 'actormigration' => 'join', ], ], ]; - yield 'wgContentHandlerUseDB true, wgCommentTableSchemaMigrationStage OLD, opts none' => [ + yield 'wgContentHandlerUseDB true, opts none' => [ [ 'wgContentHandlerUseDB' => true, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD, ], [], [ - 'tables' => [ 'revision' ], + 'tables' => [ 'revision', 'commentstore' => 'table', 'actormigration' => 'table' ], 'fields' => [ 'rev_id', 'rev_page', 'rev_text_id', 'rev_timestamp', - 'rev_user_text', - 'rev_user', 'rev_minor_edit', 'rev_deleted', 'rev_len', 'rev_parent_id', 'rev_sha1', - 'rev_comment_text' => 'rev_comment', - 'rev_comment_data' => 'NULL', - 'rev_comment_cid' => 'NULL', + 'commentstore' => 'field', + 'rev_user' => 'actormigration_user', + 'rev_user_text' => 'actormigration_user_text', + 'rev_actor' => 'actormigration_actor', 'rev_content_format', 'rev_content_model', ], - 'joins' => [], - ], - ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_BOTH, opts none' => [ - [ - 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH, - ], - [], - [ - 'tables' => [ - 'revision', - 'temp_rev_comment' => 'revision_comment_temp', - 'comment_rev_comment' => 'comment', - ], - 'fields' => [ - 'rev_id', - 'rev_page', - 'rev_text_id', - 'rev_timestamp', - 'rev_user_text', - 'rev_user', - 'rev_minor_edit', - 'rev_deleted', - 'rev_len', - 'rev_parent_id', - 'rev_sha1', - 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )', - 'rev_comment_data' => 'comment_rev_comment.comment_data', - 'rev_comment_cid' => 'comment_rev_comment.comment_id', - ], - 'joins' => [ - 'temp_rev_comment' => [ - 'LEFT JOIN', - 'temp_rev_comment.revcomment_rev = rev_id', - ], - 'comment_rev_comment' => [ - 'LEFT JOIN', - 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id', - ], - ], - ], - ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_NEW, opts none' => [ - [ - 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_NEW, - ], - [], - [ - 'tables' => [ - 'revision', - 'temp_rev_comment' => 'revision_comment_temp', - 'comment_rev_comment' => 'comment', - ], - 'fields' => [ - 'rev_id', - 'rev_page', - 'rev_text_id', - 'rev_timestamp', - 'rev_user_text', - 'rev_user', - 'rev_minor_edit', - 'rev_deleted', - 'rev_len', - 'rev_parent_id', - 'rev_sha1', - 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )', - 'rev_comment_data' => 'comment_rev_comment.comment_data', - 'rev_comment_cid' => 'comment_rev_comment.comment_id', - ], - 'joins' => [ - 'temp_rev_comment' => [ - 'LEFT JOIN', - 'temp_rev_comment.revcomment_rev = rev_id', - ], - 'comment_rev_comment' => [ - 'LEFT JOIN', - 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id', - ], - ], - ], - ]; - yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage NEW, opts none' => [ - [ - 'wgContentHandlerUseDB' => false, - 'wgCommentTableSchemaMigrationStage' => MIGRATION_NEW, - ], - [], - [ - 'tables' => [ - 'revision', - 'temp_rev_comment' => 'revision_comment_temp', - 'comment_rev_comment' => 'comment', - ], - 'fields' => [ - 'rev_id', - 'rev_page', - 'rev_text_id', - 'rev_timestamp', - 'rev_user_text', - 'rev_user', - 'rev_minor_edit', - 'rev_deleted', - 'rev_len', - 'rev_parent_id', - 'rev_sha1', - 'rev_comment_text' => 'comment_rev_comment.comment_text', - 'rev_comment_data' => 'comment_rev_comment.comment_data', - 'rev_comment_cid' => 'comment_rev_comment.comment_id', - ], - 'joins' => [ - 'temp_rev_comment' => [ - 'JOIN', - 'temp_rev_comment.revcomment_rev = rev_id', - ], - 'comment_rev_comment' => [ - 'JOIN', - 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id', - ], - ], + 'joins' => [ 'commentstore' => 'join', 'actormigration' => 'join' ], ], ]; } @@ -1351,10 +1389,114 @@ class RevisionTest extends MediaWikiTestCase { */ public function testGetQueryInfo( $globals, $options, $expected ) { $this->setMwGlobals( $globals ); + $this->overrideCommentStoreAndActorMigration(); + + $revisionStore = $this->getRevisionStore(); + $revisionStore->setContentHandlerUseDB( $globals['wgContentHandlerUseDB'] ); + $this->setService( 'RevisionStore', $revisionStore ); + $this->assertEquals( $expected, Revision::getQueryInfo( $options ) ); } + /** + * @covers Revision::getSize + */ + public function testGetSize() { + $title = $this->getMockTitle(); + + $rec = new MutableRevisionRecord( $title ); + $rev = new Revision( $rec, 0, $title ); + + $this->assertSame( 0, $rev->getSize(), 'Size of no slots is 0' ); + + $rec->setSize( 13 ); + $this->assertSame( 13, $rev->getSize() ); + } + + /** + * @covers Revision::getSize + */ + public function testGetSize_failure() { + $title = $this->getMockTitle(); + + $rec = $this->getMockBuilder( RevisionRecord::class ) + ->disableOriginalConstructor() + ->getMock(); + + $rec->method( 'getSize' ) + ->willThrowException( new RevisionAccessException( 'Oops!' ) ); + + $rev = new Revision( $rec, 0, $title ); + $this->assertNull( $rev->getSize() ); + } + + /** + * @covers Revision::getSha1 + */ + public function testGetSha1() { + $title = $this->getMockTitle(); + + $rec = new MutableRevisionRecord( $title ); + $rev = new Revision( $rec, 0, $title ); + + $emptyHash = SlotRecord::base36Sha1( '' ); + $this->assertSame( $emptyHash, $rev->getSha1(), 'Sha1 of no slots is hash of empty string' ); + + $rec->setSha1( 'deadbeef' ); + $this->assertSame( 'deadbeef', $rev->getSha1() ); + } + + /** + * @covers Revision::getSha1 + */ + public function testGetSha1_failure() { + $title = $this->getMockTitle(); + + $rec = $this->getMockBuilder( RevisionRecord::class ) + ->disableOriginalConstructor() + ->getMock(); + + $rec->method( 'getSha1' ) + ->willThrowException( new RevisionAccessException( 'Oops!' ) ); + + $rev = new Revision( $rec, 0, $title ); + $this->assertNull( $rev->getSha1() ); + } + + /** + * @covers Revision::getContent + */ + public function testGetContent() { + $title = $this->getMockTitle(); + + $rec = new MutableRevisionRecord( $title ); + $rev = new Revision( $rec, 0, $title ); + + $this->assertNull( $rev->getContent(), 'Content of no slots is null' ); + + $content = new TextContent( 'Hello Kittens!' ); + $rec->setContent( 'main', $content ); + $this->assertSame( $content, $rev->getContent() ); + } + + /** + * @covers Revision::getContent + */ + public function testGetContent_failure() { + $title = $this->getMockTitle(); + + $rec = $this->getMockBuilder( RevisionRecord::class ) + ->disableOriginalConstructor() + ->getMock(); + + $rec->method( 'getContent' ) + ->willThrowException( new RevisionAccessException( 'Oops!' ) ); + + $rev = new Revision( $rec, 0, $title ); + $this->assertNull( $rev->getContent() ); + } + }