diff options
author | Máté Szabó <mszabo@fandom.com> | 2023-02-01 01:12:31 +0100 |
---|---|---|
committer | Máté Szabó <mszabo@fandom.com> | 2023-02-01 10:24:57 +0100 |
commit | 43afc7fb2daa19af28a4b7d5430d20c51d3ef58c (patch) | |
tree | 13b13e3f75f44d8c256be36cca72e306ae3a0927 /tests/phpunit/includes/ExportTest.php | |
parent | b708c981a658c63bde062ba9cdc889e0714d558b (diff) | |
download | mediawikicore-43afc7fb2daa19af28a4b7d5430d20c51d3ef58c.tar.gz mediawikicore-43afc7fb2daa19af28a4b7d5430d20c51d3ef58c.zip |
Fix XML dumps for content types with non-string getNativeData()
In fdc3e9f9524d91a492bdc212486d4518991c0fe2, the code generating XML
dumps was updated to support multi-content revisions. This refactor
included a workaround for content types that are subclasses of
TextContent to use getNativeData() rather than serialize(), apparently
to satisfy the Flow extension.
However, this assumes that getNativeData() always returns a string. As
demonstrated in T155582, this is not the case, which is one of the
reasons why the method was deprecated. Notably, if a wiki has a custom
content type defined whose getNativeData() returns a non-string value,
and has pages using that content type, this breaks XML dump generation
(dumpBackup.php) for that wiki and also makes those pages unexportable
via Special:Export.
Fix it by using getText() instead of getNativeData(), which is the
recommended migration path anyways per T155582. I am somewhat perplexed
by the reference to Flow in the original code comment, because Flow's
BoardContent does not seem to extend TextContent at all.
Bug: T155582
Bug: T328503
Change-Id: I670fb53f193ec20d3d4c258e54c89e7f64cf2d1b
Diffstat (limited to 'tests/phpunit/includes/ExportTest.php')
-rw-r--r-- | tests/phpunit/includes/ExportTest.php | 133 |
1 files changed, 116 insertions, 17 deletions
diff --git a/tests/phpunit/includes/ExportTest.php b/tests/phpunit/includes/ExportTest.php index fd13c5baaba2..526306635211 100644 --- a/tests/phpunit/includes/ExportTest.php +++ b/tests/phpunit/includes/ExportTest.php @@ -1,6 +1,8 @@ <?php +use MediaWiki\Content\Renderer\ContentParseParams; use MediaWiki\MainConfigNames; +use MediaWiki\Page\PageIdentity; /** * Test class for Export methods. @@ -23,26 +25,10 @@ class ExportTest extends MediaWikiLangTestCase { $pageTitle = 'UTPage'; $services = $this->getServiceContainer(); - $exporter = $services - ->getWikiExporterFactory() - ->getWikiExporter( $this->db, WikiExporter::FULL ); $title = Title::newFromText( $pageTitle ); - $sink = new DumpStringOutput; - $exporter->setOutputSink( $sink ); - $exporter->openStream(); - $exporter->pageByTitle( $title ); - $exporter->closeStream(); - - // phpcs:ignore Generic.PHP.NoSilencedErrors -- suppress deprecation per T268847 - $oldDisable = @libxml_disable_entity_loader( true ); - - // This throws error if invalid xml output - $xmlObject = simplexml_load_string( $sink ); - - // phpcs:ignore Generic.PHP.NoSilencedErrors - @libxml_disable_entity_loader( $oldDisable ); + $xmlObject = $this->getXmlDumpForPage( $title ); /** * Check namespaces match xml @@ -66,4 +52,117 @@ class ExportTest extends MediaWikiLangTestCase { $this->assertNotEquals( '', $text[0] ); } + /** + * Regression test for T328503 to verify that custom content types + * with a getNativeData() override that returns a non-string value export correctly. + * + * @covers XmlDumpWriter::writeText + */ + public function testShouldExportContentWithNonStringNativeData(): void { + // Make a mock ContentHandler for a Content that has a getNativeData() method + // with a non-string return value. + $contentModelId = 'non-string-test-content-model'; + $contentHandler = new class( $contentModelId ) extends ContentHandler { + + public function __construct( $contentModelId ) { + parent::__construct( + $contentModelId, + [ CONTENT_FORMAT_TEXT ] + ); + } + + public function serializeContent( Content $content, $format = null ) { + return json_encode( $content->getNativeData() ); + } + + public function unserializeContent( $blob, $format = null ) { + return $this->getTestContent( $blob ); + } + + public function makeEmptyContent() { + return $this->getTestContent( '{}' ); + } + + protected function fillParserOutput( + Content $content, + ContentParseParams $cpoParams, + ParserOutput &$output + ) { + $output->setText( json_encode( $content->getNativeData() ) ); + } + + private function getTestContent( string $blob ): Content { + return new class( $blob, $this->getModelID() ) extends TextContent { + /** @var array */ + private $data; + + public function __construct( $text, $contentModelId ) { + parent::__construct( + $text, + $contentModelId + ); + + $this->data = json_decode( $text, true ); + } + + public function getNativeData() { + return $this->data; + } + }; + } + }; + + $this->setTemporaryHook( + 'ContentHandlerForModelID', + static function ( + string $modelId, + ?ContentHandler &$handlerRef + ) use ( $contentModelId, $contentHandler ): void { + if ( $modelId === $contentModelId ) { + $handlerRef = $contentHandler; + } + } + ); + + $wikiPage = $this->getNonexistingTestPage( 'NonStringNativeDataExportTest' ); + + $testText = json_encode( [ 'test' => 'data' ] ); + $content = $contentHandler->unserializeContent( $testText ); + + $this->editPage( $wikiPage, $content ); + + $xmlObject = $this->getXmlDumpForPage( $wikiPage ); + + $this->assertSame( $contentModelId, (string)$xmlObject->page->revision->model ); + $this->assertSame( $testText, (string)$xmlObject->page->revision->text ); + } + + /** + * Convenience function to export the content of the given page in MediaWiki's XML dump format. + * @param PageIdentity $page page to export + * @return SimpleXMLElement root element of the generated XML + */ + private function getXmlDumpForPage( PageIdentity $page ): SimpleXMLElement { + $exporter = $this->getServiceContainer() + ->getWikiExporterFactory() + ->getWikiExporter( $this->db, WikiExporter::FULL ); + + $sink = new DumpStringOutput(); + $exporter->setOutputSink( $sink ); + $exporter->openStream(); + $exporter->pageByTitle( $page ); + $exporter->closeStream(); + + // phpcs:ignore Generic.PHP.NoSilencedErrors -- suppress deprecation per T268847 + $oldDisable = @libxml_disable_entity_loader( true ); + + // This throws error if invalid xml output + $xmlObject = simplexml_load_string( $sink ); + + // phpcs:ignore Generic.PHP.NoSilencedErrors + @libxml_disable_entity_loader( $oldDisable ); + + return $xmlObject; + } + } |