aboutsummaryrefslogtreecommitdiffstats
path: root/tests/phpunit/includes/ExportTest.php
diff options
context:
space:
mode:
authorMáté Szabó <mszabo@fandom.com>2023-02-01 01:12:31 +0100
committerMáté Szabó <mszabo@fandom.com>2023-02-01 10:24:57 +0100
commit43afc7fb2daa19af28a4b7d5430d20c51d3ef58c (patch)
tree13b13e3f75f44d8c256be36cca72e306ae3a0927 /tests/phpunit/includes/ExportTest.php
parentb708c981a658c63bde062ba9cdc889e0714d558b (diff)
downloadmediawikicore-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.php133
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;
+ }
+
}