Merge "selenium: invoke jobs to enforce eventual consistency"
[lhc/web/wiklou.git] / tests / phpunit / includes / Storage / DerivedPageDataUpdaterTest.php
index 0e0d609..8b472d4 100644 (file)
@@ -4,6 +4,7 @@ namespace MediaWiki\Tests\Storage;
 
 use CommentStoreComment;
 use Content;
+use ContentHandler;
 use LinksUpdate;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Storage\DerivedPageDataUpdater;
@@ -13,6 +14,10 @@ use MediaWiki\Storage\RevisionRecord;
 use MediaWiki\Storage\RevisionSlotsUpdate;
 use MediaWiki\Storage\SlotRecord;
 use MediaWikiTestCase;
+use MWCallableUpdate;
+use PHPUnit\Framework\MockObject\MockObject;
+use TextContent;
+use TextContentHandler;
 use Title;
 use User;
 use Wikimedia\TestingAccessWrapper;
@@ -22,7 +27,7 @@ use WikitextContent;
 /**
  * @group Database
  *
- * @covers MediaWiki\Storage\DerivedPageDataUpdater
+ * @covers \MediaWiki\Storage\DerivedPageDataUpdater
  */
 class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
 
@@ -246,11 +251,13 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
                $sysop = $this->getTestUser( [ 'sysop' ] )->getUser();
                $page = $this->getPage( __METHOD__ );
 
-               $mainContent1 = new WikitextContent( 'first [[main]] ({{REVISIONUSER}}) ~~~' );
-               $mainContent2 = new WikitextContent( 'second' );
+               $mainContent1 = new WikitextContent( 'first [[main]] ({{REVISIONUSER}}) #~~~#' );
+               $mainContent2 = new WikitextContent( 'second ({{subst:REVISIONUSER}}) #~~~#' );
 
                $rev = $this->createRevision( $page, 'first', $mainContent1 );
                $mainContent1 = $rev->getContent( 'main' ); // get post-pst content
+               $userName = $rev->getUser()->getName();
+               $sysopName = $sysop->getName();
 
                $update = new RevisionSlotsUpdate();
                $update->modifyContent( 'main', $mainContent1 );
@@ -268,9 +275,11 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
 
                // parser-output for null-edit uses the original author's name
                $html = $updater1->getRenderedRevision()->getRevisionParserOutput()->getText();
-               $this->assertNotContains( $sysop->getName(), $html, '{{REVISIONUSER}}' );
+               $this->assertNotContains( $sysopName, $html, '{{REVISIONUSER}}' );
                $this->assertNotContains( '{{REVISIONUSER}}', $html, '{{REVISIONUSER}}' );
-               $this->assertContains( '(' . $rev->getUser()->getName() . ')', $html, '{{REVISIONUSER}}' );
+               $this->assertNotContains( '~~~', $html, 'signature ~~~' );
+               $this->assertContains( '(' . $userName . ')', $html, '{{REVISIONUSER}}' );
+               $this->assertContains( '>' . $userName . '<', $html, 'signature ~~~' );
 
                // TODO: MCR: test inheritance from parent
                $update = new RevisionSlotsUpdate();
@@ -278,6 +287,13 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
                $updater2 = $this->getDerivedPageDataUpdater( $page );
                $updater2->prepareContent( $sysop, $update, false );
 
+               // non-null edit use the new user name in PST
+               $pstText = $updater2->getSlots()->getContent( 'main' )->serialize();
+               $this->assertNotContains( '{{subst:REVISIONUSER}}', $pstText, '{{subst:REVISIONUSER}}' );
+               $this->assertNotContains( '~~~', $pstText, 'signature ~~~' );
+               $this->assertContains( '(' . $sysopName . ')', $pstText, '{{subst:REVISIONUSER}}' );
+               $this->assertContains( ':' . $sysopName . '|', $pstText, 'signature ~~~' );
+
                $this->assertFalse( $updater2->isCreation() );
                $this->assertTrue( $updater2->isChange() );
        }
@@ -489,7 +505,6 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
 
                $dataUpdates = $updater->getSecondaryDataUpdates();
 
-               // TODO: MCR: assert updates from all slots!
                $this->assertNotEmpty( $dataUpdates );
 
                $linksUpdates = array_filter( $dataUpdates, function ( $du ) {
@@ -498,6 +513,109 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
                $this->assertCount( 1, $linksUpdates );
        }
 
+       /**
+        * @param string $name
+        *
+        * @return ContentHandler
+        */
+       private function defineMockContentModelForUpdateTesting( $name ) {
+               /** @var ContentHandler|MockObject $handler */
+               $handler = $this->getMockBuilder( TextContentHandler::class )
+                       ->setConstructorArgs( [ $name ] )
+                       ->setMethods(
+                               [ 'getSecondaryDataUpdates', 'getDeletionUpdates', 'unserializeContent' ]
+                       )
+                       ->getMock();
+
+               $dataUpdate = new MWCallableUpdate( 'time' );
+               $dataUpdate->_name = "$name data update";
+
+               $deletionUpdate = new MWCallableUpdate( 'time' );
+               $deletionUpdate->_name = "$name deletion update";
+
+               $handler->method( 'getSecondaryDataUpdates' )->willReturn( [ $dataUpdate ] );
+               $handler->method( 'getDeletionUpdates' )->willReturn( [ $deletionUpdate ] );
+               $handler->method( 'unserializeContent' )->willReturnCallback(
+                       function ( $text ) use ( $handler ) {
+                               return $this->createMockContent( $handler, $text );
+                       }
+               );
+
+               $this->mergeMwGlobalArrayValue(
+                       'wgContentHandlers', [
+                               $name => function () use ( $handler ){
+                                       return $handler;
+                               }
+                       ]
+               );
+
+               return $handler;
+       }
+
+       /**
+        * @param ContentHandler $handler
+        * @param string $text
+        *
+        * @return Content
+        */
+       private function createMockContent( ContentHandler $handler, $text ) {
+               /** @var Content|MockObject $content */
+               $content = $this->getMockBuilder( TextContent::class )
+                       ->setConstructorArgs( [ $text ] )
+                       ->setMethods( [ 'getModel', 'getContentHandler' ] )
+                       ->getMock();
+
+               $content->method( 'getModel' )->willReturn( $handler->getModelID() );
+               $content->method( 'getContentHandler' )->willReturn( $handler );
+
+               return $content;
+       }
+
+       public function testGetSecondaryDataUpdatesWithSlotRemoval() {
+               global $wgMultiContentRevisionSchemaMigrationStage;
+
+               if ( ! ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_READ_NEW ) ) {
+                       $this->markTestSkipped( 'Slot removal cannot happen with MCR being enabled' );
+               }
+
+               $m1 = $this->defineMockContentModelForUpdateTesting( 'M1' );
+               $a1 = $this->defineMockContentModelForUpdateTesting( 'A1' );
+               $m2 = $this->defineMockContentModelForUpdateTesting( 'M2' );
+
+               $mainContent1 = $this->createMockContent( $m1, 'main 1' );
+               $auxContent1 = $this->createMockContent( $a1, 'aux 1' );
+               $mainContent2 = $this->createMockContent( $m2, 'main 2' );
+
+               $user = $this->getTestUser()->getUser();
+               $page = $this->getPage( __METHOD__ );
+               $this->createRevision(
+                       $page,
+                       __METHOD__,
+                       [ 'main' => $mainContent1, 'aux' => $auxContent1 ]
+               );
+
+               $update = new RevisionSlotsUpdate();
+               $update->modifyContent( 'main', $mainContent2 );
+               $update->removeSlot( 'aux' );
+
+               $page = $this->getPage( __METHOD__ );
+               $updater = $this->getDerivedPageDataUpdater( $page );
+               $updater->prepareContent( $user, $update, false );
+
+               $dataUpdates = $updater->getSecondaryDataUpdates();
+
+               $this->assertNotEmpty( $dataUpdates );
+
+               $updateNames = array_map( function ( $du ) {
+                       return isset( $du->_name ) ? $du->_name : get_class( $du );
+               }, $dataUpdates );
+
+               $this->assertContains( LinksUpdate::class, $updateNames );
+               $this->assertContains( 'A1 deletion update', $updateNames );
+               $this->assertContains( 'M2 data update', $updateNames );
+               $this->assertNotContains( 'M1 data update', $updateNames );
+       }
+
        /**
         * Creates a dummy revision object without touching the database.
         *
@@ -530,8 +648,26 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
                return $rev;
        }
 
+       /**
+        * @param int $id
+        * @return Title
+        */
+       private function getMockTitle( $id = 23 ) {
+               $mock = $this->getMockBuilder( Title::class )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               $mock->expects( $this->any() )
+                       ->method( 'getDBkey' )
+                       ->will( $this->returnValue( __CLASS__ ) );
+               $mock->expects( $this->any() )
+                       ->method( 'getArticleID' )
+                       ->will( $this->returnValue( $id ) );
+
+               return $mock;
+       }
+
        public function provideIsReusableFor() {
-               $title = Title::makeTitleSafe( NS_MAIN, __METHOD__ );
+               $title = $this->getMockTitle();
 
                $user1 = User::newFromName( 'Alice' );
                $user2 = User::newFromName( 'Bob' );
@@ -718,6 +854,8 @@ class DerivedPageDataUpdaterTest extends MediaWikiTestCase {
 
        /**
         * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doUpdates()
+        * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doSecondaryDataUpdates()
+        * @covers \MediaWiki\Storage\DerivedPageDataUpdater::doParserCacheUpdate()
         */
        public function testDoUpdates() {
                $page = $this->getPage( __METHOD__ );