Merge "Add config for serving main Page from the domain root"
[lhc/web/wiklou.git] / tests / phpunit / includes / Revision / RevisionStoreDbTestBase.php
index 3467153..4040ffc 100644 (file)
@@ -4,8 +4,10 @@ namespace MediaWiki\Tests\Revision;
 
 use CommentStoreComment;
 use Content;
+use ContentHandler;
 use Exception;
 use HashBagOStuff;
+use IDBAccessObject;
 use InvalidArgumentException;
 use Language;
 use MediaWiki\Linker\LinkTarget;
@@ -81,10 +83,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->setMwGlobals( [
                        'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
                        'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
-                       'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
                ] );
-
-               $this->overrideMwServices();
        }
 
        protected function addCoreDBData() {
@@ -104,23 +103,24 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
        }
 
        /**
+        * @param string|null $pageTitle whether to force-create a new page
         * @return WikiPage
         */
-       protected function getTestPage() {
-               if ( $this->testPage ) {
+       protected function getTestPage( $pageTitle = null ) {
+               if ( is_null( $pageTitle ) && $this->testPage ) {
                        return $this->testPage;
                }
 
-               $title = $this->getTestPageTitle();
-               $this->testPage = WikiPage::factory( $title );
+               $title = is_null( $pageTitle ) ? $this->getTestPageTitle() : Title::newFromText( $pageTitle );
+               $page = WikiPage::factory( $title );
 
-               if ( !$this->testPage->exists() ) {
+               if ( !$page->exists() ) {
                        // Make sure we don't write to the live db.
                        $this->ensureMockDatabaseConnection( wfGetDB( DB_MASTER ) );
 
                        $user = static::getTestSysop()->getUser();
 
-                       $this->testPage->doEditContent(
+                       $page->doEditContent(
                                new WikitextContent( 'UTContent-' . __CLASS__ ),
                                'UTPageSummary-' . __CLASS__,
                                EDIT_NEW | EDIT_SUPPRESS_RC,
@@ -129,7 +129,10 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                        );
                }
 
-               return $this->testPage;
+               if ( is_null( $pageTitle ) ) {
+                       $this->testPage = $page;
+               }
+               return $page;
        }
 
        /**
@@ -185,7 +188,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideDomainCheck
-        * @covers \MediaWiki\Revision\RevisionStore::checkDatabaseWikiId
+        * @covers \MediaWiki\Revision\RevisionStore::checkDatabaseDomain
         */
        public function testDomainCheck( $wikiId, $dbName, $dbPrefix ) {
                $this->setMwGlobals(
@@ -403,7 +406,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $title = $this->getTestPageTitle();
                $rev = $this->getRevisionRecordFromDetailsArray( $revDetails );
 
-               $this->overrideMwServices();
                $store = MediaWikiServices::getInstance()->getRevisionStore();
                $return = $store->insertRevisionOn( $rev, wfGetDB( DB_MASTER ) );
 
@@ -480,7 +482,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                        'user' => true,
                ];
 
-               $this->overrideMwServices();
                $store = MediaWikiServices::getInstance()->getRevisionStore();
 
                // Insert the first revision
@@ -594,8 +595,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         * @covers \MediaWiki\Revision\RevisionStore::findSlotContentId
         */
        public function testNewNullRevision( Title $title, $revDetails, $comment, $minor = false ) {
-               $this->overrideMwServices();
-
                $user = TestUserRegistry::getMutableTestUser( __METHOD__ )->getUser();
                $page = WikiPage::factory( $title );
 
@@ -897,12 +896,71 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                }
 
                if ( $revMain->hasContentId() ) {
-                       $this->assertSame( $revMain->getContentId(), $recMain->getContentId(), 'getContentId' );
+                       // XXX: the content ID value is ill-defined when SCHEMA_COMPAT_WRITE_BOTH and
+                       //      SCHEMA_COMPAT_READ_OLD is set, since revision insertion will report the
+                       //      content ID used with the new schema, while loading the revision from the
+                       //      old schema will report an emulated ID.
+                       if ( $this->getMcrMigrationStage() & SCHEMA_COMPAT_READ_NEW ) {
+                               $this->assertSame( $revMain->getContentId(), $recMain->getContentId(), 'getContentId' );
+                       }
                }
        }
 
+       /**
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRowAndSlots
+        * @covers \MediaWiki\Revision\RevisionStore::getQueryInfo
+        */
+       public function testNewRevisionFromRowAndSlot_getQueryInfo() {
+               $page = $this->getTestPage();
+               $text = __METHOD__ . 'o-ö';
+               /** @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']
+               );
+
+               $info = $store->getSlotsQueryInfo( [ 'content' ] );
+               $slotRows = $this->db->select(
+                       $info['tables'],
+                       $info['fields'],
+                       $this->getSlotRevisionConditions( $rev->getId() ),
+                       __METHOD__,
+                       [],
+                       $info['joins']
+               );
+
+               $record = $store->newRevisionFromRowAndSlots(
+                       $row,
+                       iterator_to_array( $slotRows ),
+                       [],
+                       $page->getTitle()
+               );
+               $this->assertRevisionRecordMatchesRevision( $rev, $record );
+               $this->assertSame( $text, $rev->getContent()->serialize() );
+       }
+
+       /**
+        * Conditions to use together with getSlotsQueryInfo() when selecting slot rows for a given
+        * revision.
+        *
+        * @return array
+        */
+       abstract protected function getSlotRevisionConditions( $revId );
+
        /**
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRowAndSlots
         * @covers \MediaWiki\Revision\RevisionStore::getQueryInfo
         */
        public function testNewRevisionFromRow_getQueryInfo() {
@@ -935,6 +993,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
 
        /**
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRowAndSlots
         */
        public function testNewRevisionFromRow_anonEdit() {
                $page = $this->getTestPage();
@@ -957,10 +1016,10 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
 
        /**
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRowAndSlots
         */
        public function testNewRevisionFromRow_anonEdit_legacyEncoding() {
                $this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
-               $this->overrideMwServices();
                $page = $this->getTestPage();
                $text = __METHOD__ . 'a-ä';
                /** @var Revision $rev */
@@ -981,6 +1040,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
 
        /**
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRowAndSlots
         */
        public function testNewRevisionFromRow_userEdit() {
                $page = $this->getTestPage();
@@ -1039,7 +1099,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         */
        public function testNewRevisionFromArchiveRow_legacyEncoding() {
                $this->setMwGlobals( 'wgLegacyEncoding', 'windows-1252' );
-               $this->overrideMwServices();
                $store = MediaWikiServices::getInstance()->getRevisionStore();
                $title = Title::newFromText( __METHOD__ );
                $text = __METHOD__ . '-bä';
@@ -1105,6 +1164,7 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
 
        /**
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRowAndSlots
         */
        public function testNewRevisionFromRow_no_user() {
                $store = MediaWikiServices::getInstance()->getRevisionStore();
@@ -1690,8 +1750,10 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         */
        public function testNewMutableRevisionFromArray_legacyEncoding( array $array ) {
                $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
-               $lb = MediaWikiServices::getInstance()->getDBLoadBalancer();
-               $blobStore = new SqlBlobStore( $lb, $cache );
+               $services = MediaWikiServices::getInstance();
+               $lb = $services->getDBLoadBalancer();
+               $access = $services->getExternalStoreAccess();
+               $blobStore = new SqlBlobStore( $lb, $access, $cache );
                $blobStore->setLegacyEncoding( 'windows-1252', Language::factory( 'en' ) );
 
                $factory = $this->getMockBuilder( BlobStoreFactory::class )
@@ -1730,9 +1792,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         * @covers \MediaWiki\Revision\RevisionStore::getKnownCurrentRevision
         */
        public function testGetKnownCurrentRevision_userNameChange() {
-               global $wgActorTableSchemaMigrationStage;
-
-               $this->overrideMwServices();
                $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
                $this->setService( 'MainWANObjectCache', $cache );
 
@@ -1751,11 +1810,9 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->db->update( 'user',
                        [ 'user_name' => $newUserName ],
                        [ 'user_id' => $rev->getUser()->getId() ] );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $this->db->update( 'actor',
-                               [ 'actor_name' => $newUserName ],
-                               [ 'actor_user' => $rev->getUser()->getId() ] );
-               }
+               $this->db->update( 'actor',
+                       [ 'actor_name' => $newUserName ],
+                       [ 'actor_user' => $rev->getUser()->getId() ] );
 
                // Reload the revision and regrab the user name.
                $revAfter = $store->getKnownCurrentRevision( $page->getTitle(), $rev->getId() );
@@ -1773,7 +1830,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         * @covers \MediaWiki\Revision\RevisionStore::getKnownCurrentRevision
         */
        public function testGetKnownCurrentRevision_revDelete() {
-               $this->overrideMwServices();
                $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
                $this->setService( 'MainWANObjectCache', $cache );
 
@@ -1805,8 +1861,6 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
         * @covers \MediaWiki\Revision\RevisionStore::newRevisionFromRow
         */
        public function testNewRevisionFromRow_userNameChange() {
-               global $wgActorTableSchemaMigrationStage;
-
                $page = $this->getTestPage();
                $text = __METHOD__;
                /** @var Revision $rev */
@@ -1836,11 +1890,9 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->db->update( 'user',
                        [ 'user_name' => $newUserName ],
                        [ 'user_id' => $record->getUser()->getId() ] );
-               if ( $wgActorTableSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) {
-                       $this->db->update( 'actor',
-                               [ 'actor_name' => $newUserName ],
-                               [ 'actor_user' => $record->getUser()->getId() ] );
-               }
+               $this->db->update( 'actor',
+                       [ 'actor_name' => $newUserName ],
+                       [ 'actor_user' => $record->getUser()->getId() ] );
 
                // Reload the record, passing $fromCache as true to force fresh info from the db,
                // and regrab the user name
@@ -1904,4 +1956,128 @@ abstract class RevisionStoreDbTestBase extends MediaWikiTestCase {
                $this->assertSame( RevisionRecord::DELETED_TEXT, $deletedAfter );
        }
 
+       public function provideNewRevisionsFromBatchOptions() {
+               yield 'No preload slots or content, single page' => [
+                       null,
+                       []
+               ];
+               yield 'Preload slots and content, single page' => [
+                       null,
+                       [
+                               'slots' => [ SlotRecord::MAIN ],
+                               'content' => true
+                       ]
+               ];
+               yield 'No preload slots or content, multiple pages' => [
+                       'Other_Page',
+                       []
+               ];
+               yield 'Preload slots and content, multiple pages' => [
+                       'Other_Page',
+                       [
+                               'slots' => [ SlotRecord::MAIN ],
+                               'content' => true
+                       ]
+               ];
+       }
+
+       /**
+        * @dataProvider provideNewRevisionsFromBatchOptions
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionsFromBatch
+        * @param string|null $otherPageTitle
+        * @param array|null $options
+        * @throws \MWException
+        */
+       public function testNewRevisionsFromBatch_preloadContent(
+               $otherPageTitle = null,
+               array $options = []
+       ) {
+               $page1 = $this->getTestPage();
+               $text = __METHOD__ . 'b-ä';
+               $editStatus = $this->editPage( $page1->getTitle()->getPrefixedDBkey(), $text . '1' );
+               $this->assertTrue( $editStatus->isGood(), 'Sanity: must create revision 1' );
+               /** @var Revision $rev1 */
+               $rev1 = $editStatus->getValue()['revision'];
+
+               $page2 = $this->getTestPage( $otherPageTitle );
+               $editStatus = $this->editPage( $page2->getTitle()->getPrefixedDBkey(), $text . '2' );
+               $this->assertTrue( $editStatus->isGood(), 'Sanity: must create revision 2' );
+               /** @var Revision $rev2 */
+               $rev2 = $editStatus->getValue()['revision'];
+
+               $store = MediaWikiServices::getInstance()->getRevisionStore();
+               $result = $store->newRevisionsFromBatch(
+                       [ $this->revisionToRow( $rev1 ), $this->revisionToRow( $rev2 ) ],
+                       $options
+               );
+               $this->assertTrue( $result->isGood() );
+               $this->assertEmpty( $result->getErrors() );
+               /** @var RevisionRecord[] $records */
+               $records = $result->getValue();
+               $this->assertRevisionRecordMatchesRevision( $rev1, $records[$rev1->getId()] );
+               $this->assertRevisionRecordMatchesRevision( $rev2, $records[$rev2->getId()] );
+
+               $this->assertSame( $text . '1',
+                       ContentHandler::getContentText( $records[$rev1->getId()]->getContent( SlotRecord::MAIN ) ) );
+               $this->assertSame( $text . '2',
+                       ContentHandler::getContentText( $records[$rev2->getId()]->getContent( SlotRecord::MAIN ) ) );
+               $this->assertEquals( $page1->getTitle()->getDBkey(),
+                       $records[$rev1->getId()]->getPageAsLinkTarget()->getDBkey() );
+               $this->assertEquals( $page2->getTitle()->getDBkey(),
+                       $records[$rev2->getId()]->getPageAsLinkTarget()->getDBkey() );
+       }
+
+       /**
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionsFromBatch
+        */
+       public function testNewRevisionsFromBatch_emptyBatch() {
+               $result = MediaWikiServices::getInstance()->getRevisionStore()
+                       ->newRevisionsFromBatch(
+                               [],
+                               [
+                                       'slots' => [ SlotRecord::MAIN ],
+                                       'content' => true
+                               ]
+                       );
+               $this->assertTrue( $result->isGood() );
+               $this->assertEmpty( $result->getValue() );
+               $this->assertEmpty( $result->getErrors() );
+       }
+
+       /**
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionsFromBatch
+        */
+       public function testNewRevisionsFromBatch_wrongTitle() {
+               $page1 = $this->getTestPage();
+               $text = __METHOD__ . 'b-ä';
+               $editStatus = $this->editPage( $page1->getTitle()->getPrefixedDBkey(), $text . '1' );
+               $this->assertTrue( $editStatus->isGood(), 'Sanity: must create revision 1' );
+               /** @var Revision $rev1 */
+               $rev1 = $editStatus->getValue()['revision'];
+
+               $this->setExpectedException( InvalidArgumentException::class );
+               MediaWikiServices::getInstance()->getRevisionStore()
+                       ->newRevisionsFromBatch(
+                               [ $this->revisionToRow( $rev1 ) ],
+                               [],
+                               IDBAccessObject::READ_NORMAL,
+                               $this->getTestPage( 'Title_Other_Then_The_One_Revision_Belongs_To' )->getTitle()
+                       );
+       }
+
+       /**
+        * @covers \MediaWiki\Revision\RevisionStore::newRevisionsFromBatch
+        */
+       public function testNewRevisionsFromBatch_DuplicateRows() {
+               $page1 = $this->getTestPage();
+               $text = __METHOD__ . 'b-ä';
+               $editStatus = $this->editPage( $page1->getTitle()->getPrefixedDBkey(), $text . '1' );
+               $this->assertTrue( $editStatus->isGood(), 'Sanity: must create revision 1' );
+               /** @var Revision $rev1 */
+               $rev1 = $editStatus->getValue()['revision'];
+
+               $this->setExpectedException( InvalidArgumentException::class );
+               MediaWikiServices::getInstance()->getRevisionStore()
+                       ->newRevisionsFromBatch( [ $this->revisionToRow( $rev1 ), $this->revisionToRow( $rev1 ) ] );
+       }
 }