'FixDefaultJsonContentPages' => __DIR__ . '/maintenance/fixDefaultJsonContentPages.php',
'FixDoubleRedirects' => __DIR__ . '/maintenance/fixDoubleRedirects.php',
'FixExtLinksProtocolRelative' => __DIR__ . '/maintenance/fixExtLinksProtocolRelative.php',
- 'FixT22757' => __DIR__ . '/maintenance/storage/fixT22757.php',
'FixTimestamps' => __DIR__ . '/maintenance/fixTimestamps.php',
'FixUserRegistration' => __DIR__ . '/maintenance/fixUserRegistration.php',
'ForeignAPIFile' => __DIR__ . '/includes/filerepo/file/ForeignAPIFile.php',
'Reading needs to be enabled for the old or the new schema.'
);
Assert::parameter(
- ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) !== 0,
+ ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) !== 0,
'$mcrMigrationStage',
- 'Writing needs to be enabled for the old or the new schema.'
+ 'Writing needs to be enabled for the new schema.'
);
Assert::parameter(
( $mcrMigrationStage & SCHEMA_COMPAT_READ_OLD ) === 0
'$mcrMigrationStage',
'Cannot read the old schema when not also writing it.'
);
- Assert::parameter(
- ( $mcrMigrationStage & SCHEMA_COMPAT_READ_NEW ) === 0
- || ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) !== 0,
- '$mcrMigrationStage',
- 'Cannot read the new schema when not also writing it.'
- );
$this->loadBalancer = $loadBalancer;
$this->blobStore = $blobStore;
/**
* Trivial LoadBalancer that always returns an injected connection handle.
- *
- * Note that, while this LoadBalancer does not open any connections itself,
- * it still closes the injected connection at times, including during destruction.
- * It is therefore unsuitable for use in tests unless you have a Database instance
- * separate from the main test database (which is expected to stay open).
*/
class LoadBalancerSingle extends LoadBalancer {
/** @var IDatabase */
// Find text row again
$dbr = wfGetDB( DB_REPLICA );
- $oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
+ global $wgMultiContentRevisionSchemaMigrationStage;
+ if ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_READ_OLD ) {
+ $oldId = $dbr->selectField( 'revision', 'rev_text_id', [ 'rev_id' => $id ], __METHOD__ );
+ } else {
+ $res = $dbr->selectRow(
+ [ 'slots', 'content' ],
+ [ 'content_address' ],
+ [ 'slot_revision_id' => $id ],
+ __METHOD__,
+ [],
+ [ 'content' => [ 'INNER JOIN', [ 'content_id = slot_content_id' ] ] ]
+ );
+ // @phan-suppress-next-line PhanAccessMethodInternal
+ $blobStore = MediaWikiServices::getInstance()
+ ->getBlobStoreFactory()
+ ->newSqlBlobStore();
+ $oldId = $blobStore->getTextIdFromAddress( $res->content_address );
+ }
+
if ( !$oldId ) {
echo "Missing revision row for rev_id $id\n";
-
return;
}
/** @var ExternalStoreDB $storeObj */
$storeObj = $esFactory->getStore( 'DB' );
}
+ // @phan-suppress-next-line PhanAccessMethodInternal
+ $blobStore = MediaWikiServices::getInstance()
+ ->getBlobStoreFactory()
+ ->newSqlBlobStore();
# Get all articles by page_id
if ( !$maxPageId ) {
for ( $j = 0; $j < $thisChunkSize && $chunk->isHappy(); $j++ ) {
$oldid = $revs[$i + $j]->old_id;
- # Get text
- $text = Revision::getRevisionText( $revs[$i + $j] );
+ # Get text. We do not need the full `extractBlob` since the query is built
+ # to fetch non-externalstore blobs.
+ $text = $blobStore->decompressData(
+ $revs[$i + $j]->old_text,
+ explode( ',', $revs[$i + $j]->old_flags )
+ );
if ( $text === false ) {
$this->error( "\nError, unable to get text in old_id $oldid" );
+++ /dev/null
-<?php
-/**
- * Script to fix T22757.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Maintenance ExternalStorage
- */
-
-require_once __DIR__ . '/../Maintenance.php';
-
-/**
- * Maintenance script to fix T22757.
- *
- * @ingroup Maintenance ExternalStorage
- */
-class FixT22757 extends Maintenance {
- public $batchSize = 10000;
- public $mapCache = [];
- public $mapCacheSize = 0;
- public $maxMapCacheSize = 1000000;
-
- function __construct() {
- parent::__construct();
- $this->addDescription( 'Script to fix T22757 assuming that blob_tracking is intact' );
- $this->addOption( 'dry-run', 'Report only' );
- $this->addOption( 'start', 'old_id to start at', false, true );
- }
-
- function execute() {
- $dbr = $this->getDB( DB_REPLICA );
- $dbw = $this->getDB( DB_MASTER );
-
- $dryRun = $this->getOption( 'dry-run' );
- if ( $dryRun ) {
- print "Dry run only.\n";
- }
-
- $startId = $this->getOption( 'start', 0 );
- $numGood = 0;
- $numFixed = 0;
- $numBad = 0;
-
- $totalRevs = $dbr->selectField( 'text', 'MAX(old_id)', '', __METHOD__ );
-
- // In MySQL 4.1+, the binary field old_text has a non-working LOWER() function
- $lowerLeft = 'LOWER(CONVERT(LEFT(old_text,22) USING latin1))';
-
- while ( true ) {
- print "ID: $startId / $totalRevs\r";
-
- $res = $dbr->select(
- 'text',
- [ 'old_id', 'old_flags', 'old_text' ],
- [
- 'old_id > ' . intval( $startId ),
- 'old_flags LIKE \'%object%\' AND old_flags NOT LIKE \'%external%\'',
- "$lowerLeft = 'o:15:\"historyblobstub\"'",
- ],
- __METHOD__,
- [
- 'ORDER BY' => 'old_id',
- 'LIMIT' => $this->batchSize,
- ]
- );
-
- if ( !$res->numRows() ) {
- break;
- }
-
- $secondaryIds = [];
- $stubs = [];
-
- foreach ( $res as $row ) {
- $startId = $row->old_id;
-
- // Basic sanity checks
- $obj = unserialize( $row->old_text );
- if ( $obj === false ) {
- print "{$row->old_id}: unrecoverable: cannot unserialize\n";
- ++$numBad;
- continue;
- }
-
- if ( !is_object( $obj ) ) {
- print "{$row->old_id}: unrecoverable: unserialized to type " .
- gettype( $obj ) . ", possible double-serialization\n";
- ++$numBad;
- continue;
- }
-
- if ( strtolower( get_class( $obj ) ) !== 'historyblobstub' ) {
- print "{$row->old_id}: unrecoverable: unexpected object class " .
- get_class( $obj ) . "\n";
- ++$numBad;
- continue;
- }
-
- // Process flags
- $flags = explode( ',', $row->old_flags );
- if ( in_array( 'utf-8', $flags ) || in_array( 'utf8', $flags ) ) {
- $legacyEncoding = false;
- } else {
- $legacyEncoding = true;
- }
-
- // Queue the stub for future batch processing
- $id = intval( $obj->mOldId );
- $secondaryIds[] = $id;
- $stubs[$row->old_id] = [
- 'legacyEncoding' => $legacyEncoding,
- 'secondaryId' => $id,
- 'hash' => $obj->mHash,
- ];
- }
-
- $secondaryIds = array_unique( $secondaryIds );
-
- if ( !count( $secondaryIds ) ) {
- continue;
- }
-
- // Run the batch query on blob_tracking
- $res = $dbr->select(
- 'blob_tracking',
- '*',
- [
- 'bt_text_id' => $secondaryIds,
- ],
- __METHOD__
- );
- $trackedBlobs = [];
- foreach ( $res as $row ) {
- $trackedBlobs[$row->bt_text_id] = $row;
- }
-
- // Process the stubs
- foreach ( $stubs as $primaryId => $stub ) {
- $secondaryId = $stub['secondaryId'];
- if ( !isset( $trackedBlobs[$secondaryId] ) ) {
- // No tracked blob. Work out what went wrong
- $secondaryRow = $dbr->selectRow(
- 'text',
- [ 'old_flags', 'old_text' ],
- [ 'old_id' => $secondaryId ],
- __METHOD__
- );
- if ( !$secondaryRow ) {
- print "$primaryId: unrecoverable: secondary row is missing\n";
- ++$numBad;
- } elseif ( $this->isUnbrokenStub( $stub, $secondaryRow ) ) {
- // Not broken yet, and not in the tracked clusters so it won't get
- // broken by the current RCT run.
- ++$numGood;
- } elseif ( strpos( $secondaryRow->old_flags, 'external' ) !== false ) {
- print "$primaryId: unrecoverable: secondary gone to {$secondaryRow->old_text}\n";
- ++$numBad;
- } else {
- print "$primaryId: unrecoverable: miscellaneous corruption of secondary row\n";
- ++$numBad;
- }
- unset( $stubs[$primaryId] );
- continue;
- }
- $trackRow = $trackedBlobs[$secondaryId];
-
- // Check that the specified text really is available in the tracked source row
- $url = "DB://{$trackRow->bt_cluster}/{$trackRow->bt_blob_id}/{$stub['hash']}";
- $text = ExternalStore::fetchFromURL( $url );
- if ( $text === false ) {
- print "$primaryId: unrecoverable: source text missing\n";
- ++$numBad;
- unset( $stubs[$primaryId] );
- continue;
- }
- if ( md5( $text ) !== $stub['hash'] ) {
- print "$primaryId: unrecoverable: content hashes do not match\n";
- ++$numBad;
- unset( $stubs[$primaryId] );
- continue;
- }
-
- // Find the page_id and rev_id
- // The page is probably the same as the page of the secondary row
- $pageId = intval( $trackRow->bt_page );
- if ( !$pageId ) {
- $revId = $pageId = 0;
- } else {
- $revId = $this->findTextIdInPage( $pageId, $primaryId );
- if ( !$revId ) {
- // Actually an orphan
- $pageId = $revId = 0;
- }
- }
-
- $newFlags = $stub['legacyEncoding'] ? 'external' : 'external,utf-8';
-
- if ( !$dryRun ) {
- // Reset the text row to point to the original copy
- $this->beginTransaction( $dbw, __METHOD__ );
- $dbw->update(
- 'text',
- // SET
- [
- 'old_flags' => $newFlags,
- 'old_text' => $url
- ],
- // WHERE
- [ 'old_id' => $primaryId ],
- __METHOD__
- );
-
- // Add a blob_tracking row so that the new reference can be recompressed
- // without needing to run trackBlobs.php again
- $dbw->insert( 'blob_tracking',
- [
- 'bt_page' => $pageId,
- 'bt_rev_id' => $revId,
- 'bt_text_id' => $primaryId,
- 'bt_cluster' => $trackRow->bt_cluster,
- 'bt_blob_id' => $trackRow->bt_blob_id,
- 'bt_cgz_hash' => $stub['hash'],
- 'bt_new_url' => null,
- 'bt_moved' => 0,
- ],
- __METHOD__
- );
- $this->commitTransaction( $dbw, __METHOD__ );
- }
-
- print "$primaryId: resolved to $url\n";
- ++$numFixed;
- }
- }
-
- print "\n";
- print "Fixed: $numFixed\n";
- print "Unrecoverable: $numBad\n";
- print "Good stubs: $numGood\n";
- }
-
- function findTextIdInPage( $pageId, $textId ) {
- $ids = $this->getRevTextMap( $pageId );
- return $ids[$textId] ?? null;
- }
-
- function getRevTextMap( $pageId ) {
- if ( !isset( $this->mapCache[$pageId] ) ) {
- // Limit cache size
- while ( $this->mapCacheSize > $this->maxMapCacheSize ) {
- $key = key( $this->mapCache );
- $this->mapCacheSize -= count( $this->mapCache[$key] );
- unset( $this->mapCache[$key] );
- }
-
- $dbr = $this->getDB( DB_REPLICA );
- $map = [];
- $res = $dbr->select( 'revision',
- [ 'rev_id', 'rev_text_id' ],
- [ 'rev_page' => $pageId ],
- __METHOD__
- );
- foreach ( $res as $row ) {
- $map[$row->rev_text_id] = $row->rev_id;
- }
- $this->mapCache[$pageId] = $map;
- $this->mapCacheSize += count( $map );
- }
-
- return $this->mapCache[$pageId];
- }
-
- /**
- * This is based on part of HistoryBlobStub::getText().
- * Determine if the text can be retrieved from the row in the normal way.
- * @param array $stub
- * @param stdClass $secondaryRow
- * @return bool
- */
- function isUnbrokenStub( $stub, $secondaryRow ) {
- $flags = explode( ',', $secondaryRow->old_flags );
- $text = $secondaryRow->old_text;
- if ( in_array( 'external', $flags ) ) {
- $url = $text;
- Wikimedia\suppressWarnings();
- list( /* $proto */, $path ) = explode( '://', $url, 2 );
- Wikimedia\restoreWarnings();
-
- if ( $path == "" ) {
- return false;
- }
- $text = ExternalStore::fetchFromURL( $url );
- }
- if ( !in_array( 'object', $flags ) ) {
- return false;
- }
-
- if ( in_array( 'gzip', $flags ) ) {
- $obj = unserialize( gzinflate( $text ) );
- } else {
- $obj = unserialize( $text );
- }
-
- if ( !is_object( $obj ) ) {
- // Correct for old double-serialization bug.
- $obj = unserialize( $obj );
- }
-
- if ( !is_object( $obj ) ) {
- return false;
- }
-
- $obj->uncompress();
- $text = $obj->getItem( $stub['hash'] );
-
- return $text !== false;
- }
-}
-
-$maintClass = FixT22757::class;
-require_once RUN_MAINTENANCE_IF_MAIN;
* @ingroup Maintenance ExternalStorage
*/
+use MediaWiki\Storage\SqlBlobStore;
use Wikimedia\Rdbms\IMaintainableDatabase;
use MediaWiki\Logger\LegacyLogger;
use MediaWiki\MediaWikiServices;
public $debugLog, $infoLog, $criticalLog;
/** @var ExternalStoreDB */
public $store;
+ /** @var SqlBlobStore */
+ private $blobStore;
private static $optionsWithArgs = [
'procs',
$this->pageBlobClass = function_exists( 'xdiff_string_bdiff' ) ?
DiffHistoryBlob::class : ConcatenatedGzipHistoryBlob::class;
$this->orphanBlobClass = ConcatenatedGzipHistoryBlob::class;
+ // @phan-suppress-next-line PhanAccessMethodInternal
+ $this->blobStore = MediaWikiServices::getInstance()
+ ->getBlobStoreFactory()
+ ->newSqlBlobStore();
}
function debug( $msg ) {
}
$lastTextId = $row->bt_text_id;
// Load the text
- $text = Revision::getRevisionText( $row );
+ $text = $this->blobStore->expandBlob( $row->old_text, $row->old_flags );
if ( $text === false ) {
$this->critical( "Error loading {$row->bt_rev_id}/{$row->bt_text_id}" );
continue;
);
foreach ( $res as $row ) {
- $text = Revision::getRevisionText( $row );
+ $text = $this->blobStore->expandBlob( $row->old_text, $row->old_flags );
if ( $text === false ) {
$this->critical( "Error: cannot load revision text for old_id={$row->old_id}" );
continue;
'MediaWiki\Tests\Revision\RevisionSlotsTest' => "$testDir/phpunit/includes/Revision/RevisionSlotsTest.php",
'MediaWiki\Tests\Revision\RevisionRecordTests' => "$testDir/phpunit/includes/Revision/RevisionRecordTests.php",
'MediaWiki\Tests\Revision\RevisionStoreDbTestBase' => "$testDir/phpunit/includes/Revision/RevisionStoreDbTestBase.php",
- 'MediaWiki\Tests\Revision\PreMcrSchemaOverride' => "$testDir/phpunit/includes/Revision/PreMcrSchemaOverride.php",
'MediaWiki\Tests\Revision\RevisionStoreRecordTest' => "$testDir/phpunit/includes/Revision/RevisionStoreRecordTest.php",
# tests/phpunit/languages
+++ /dev/null
-<?php
-
-namespace MediaWiki\Tests\Revision;
-
-use Revision;
-
-/**
- * Tests RevisionStore against the pre-MCR, pre-ContentHandler DB schema.
- *
- * @covers \MediaWiki\Revision\RevisionStore
- *
- * @group RevisionStore
- * @group Storage
- * @group Database
- * @group medium
- */
-class NoContentModelRevisionStoreDbTest extends RevisionStoreDbTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function getContentHandlerUseDB() {
- return false;
- }
-
- protected function revisionToRow( Revision $rev, $options = [ 'page', 'user', 'comment' ] ) {
- $row = parent::revisionToRow( $rev, $options );
-
- $row->rev_text_id = (string)$rev->getTextId();
-
- return $row;
- }
-
- public function provideGetArchiveQueryInfo() {
- yield [
- [
- 'tables' => [ 'archive' ],
- 'fields' => array_merge(
- $this->getDefaultArchiveFields(),
- [
- 'ar_comment_text' => 'ar_comment',
- 'ar_comment_data' => 'NULL',
- 'ar_comment_cid' => 'NULL',
- 'ar_user_text' => 'ar_user_text',
- 'ar_user' => 'ar_user',
- 'ar_actor' => 'NULL',
- ]
- ),
- 'joins' => [],
- ]
- ];
- }
-
- public function provideGetQueryInfo() {
- yield [
- [],
- [
- 'tables' => [ 'revision' ],
- 'fields' => array_merge(
- $this->getDefaultQueryFields(),
- $this->getCommentQueryFields(),
- $this->getActorQueryFields()
- ),
- 'joins' => [],
- ]
- ];
- yield [
- [ 'page' ],
- [
- 'tables' => [ 'revision', 'page' ],
- 'fields' => array_merge(
- $this->getDefaultQueryFields(),
- $this->getCommentQueryFields(),
- $this->getActorQueryFields(),
- [
- 'page_namespace',
- 'page_title',
- 'page_id',
- 'page_latest',
- 'page_is_redirect',
- 'page_len',
- ]
- ),
- 'joins' => [
- 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
- ],
- ]
- ];
- yield [
- [ 'user' ],
- [
- 'tables' => [ 'revision', 'user' ],
- 'fields' => array_merge(
- $this->getDefaultQueryFields(),
- $this->getCommentQueryFields(),
- $this->getActorQueryFields(),
- [
- 'user_name',
- ]
- ),
- 'joins' => [
- 'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
- ],
- ]
- ];
- yield [
- [ 'text' ],
- [
- 'tables' => [ 'revision', 'text' ],
- 'fields' => array_merge(
- $this->getDefaultQueryFields(),
- $this->getCommentQueryFields(),
- $this->getActorQueryFields(),
- [
- 'old_text',
- 'old_flags',
- ]
- ),
- 'joins' => [
- 'text' => [ 'JOIN', [ 'rev_text_id=old_id' ] ],
- ],
- ]
- ];
- }
-
- public function provideGetSlotsQueryInfo() {
- $db = wfGetDB( DB_REPLICA );
-
- yield [
- [],
- [
- 'tables' => [
- 'slots' => 'revision',
- ],
- 'fields' => array_merge(
- [
- 'slot_revision_id' => 'slots.rev_id',
- 'slot_content_id' => 'NULL',
- 'slot_origin' => 'slots.rev_id',
- 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
- ]
- ),
- 'joins' => [],
- ]
- ];
- yield [
- [ 'content' ],
- [
- 'tables' => [
- 'slots' => 'revision',
- ],
- 'fields' => array_merge(
- [
- 'slot_revision_id' => 'slots.rev_id',
- 'slot_content_id' => 'NULL',
- 'slot_origin' => 'slots.rev_id',
- 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
- 'content_size' => 'slots.rev_len',
- 'content_sha1' => 'slots.rev_sha1',
- 'content_address' =>
- $db->buildConcat( [ $db->addQuotes( 'tt:' ), 'slots.rev_text_id' ] ),
- 'model_name' => 'NULL',
- ]
- ),
- 'joins' => [],
- ]
- ];
- }
-
- public function provideNewMutableRevisionFromArray() {
- foreach ( parent::provideNewMutableRevisionFromArray() as $case ) {
- yield $case;
- }
-
- yield 'Basic array, with page & id' => [
- [
- 'id' => 2,
- 'page' => 1,
- 'text_id' => 2,
- 'timestamp' => '20171017114835',
- 'user_text' => '111.0.1.2',
- 'user' => 0,
- 'minor_edit' => false,
- 'deleted' => 0,
- 'len' => 46,
- 'parent_id' => 1,
- 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
- 'comment' => 'Goat Comment!',
- ]
- ];
- }
-
- /**
- * Conditions to use together with getSlotsQueryInfo() when selecting slot rows for a given
- * revision.
- *
- * @return array
- */
- protected function getSlotRevisionConditions( $revId ) {
- return [ 'rev_id' => $revId ];
- }
-
-}
+++ /dev/null
-<?php
-
-namespace MediaWiki\Tests\Revision;
-
-use InvalidArgumentException;
-use MediaWiki\Revision\RevisionRecord;
-use Revision;
-use WikitextContent;
-
-/**
- * Tests RevisionStore against the pre-MCR DB schema.
- *
- * @covers \MediaWiki\Revision\RevisionStore
- *
- * @group RevisionStore
- * @group Storage
- * @group Database
- * @group medium
- */
-class PreMcrRevisionStoreDbTest extends RevisionStoreDbTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function revisionToRow( Revision $rev, $options = [ 'page', 'user', 'comment' ] ) {
- $row = parent::revisionToRow( $rev, $options );
-
- $row->rev_text_id = (string)$rev->getTextId();
- $row->rev_content_format = (string)$rev->getContentFormat();
- $row->rev_content_model = (string)$rev->getContentModel();
-
- return $row;
- }
-
- protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
- // Legacy schema is still being written
- $this->assertSelect(
- [ 'revision', 'text' ],
- [ 'count(*)' ],
- [ 'rev_id' => $rev->getId(), 'rev_text_id > 0' ],
- [ [ 1 ] ],
- [],
- [ 'text' => [ 'JOIN', [ 'rev_text_id = old_id' ] ] ]
- );
-
- parent::assertRevisionExistsInDatabase( $rev );
- }
-
- public function provideInsertRevisionOn_failures() {
- foreach ( parent::provideInsertRevisionOn_failures() as $case ) {
- yield $case;
- }
-
- yield 'slot that is not main slot' => [
- [
- 'content' => [
- 'main' => new WikitextContent( 'Chicken' ),
- 'lalala' => new WikitextContent( 'Duck' ),
- ],
- 'comment' => $this->getRandomCommentStoreComment(),
- 'timestamp' => '20171117010101',
- 'user' => true,
- ],
- new InvalidArgumentException( 'Only the main slot is supported' )
- ];
- }
-
- public function provideNewMutableRevisionFromArray() {
- foreach ( parent::provideNewMutableRevisionFromArray() as $case ) {
- yield $case;
- }
-
- yield 'Basic array, with page & id' => [
- [
- 'id' => 2,
- 'page' => 1,
- 'text_id' => 2,
- 'timestamp' => '20171017114835',
- 'user_text' => '111.0.1.2',
- 'user' => 0,
- 'minor_edit' => false,
- 'deleted' => 0,
- 'len' => 46,
- 'parent_id' => 1,
- 'sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
- 'comment' => 'Goat Comment!',
- 'content_format' => 'text/x-wiki',
- 'content_model' => 'wikitext',
- ]
- ];
- }
-
- /**
- * Conditions to use together with getSlotsQueryInfo() when selecting slot rows for a given
- * revision.
- *
- * @return array
- */
- protected function getSlotRevisionConditions( $revId ) {
- return [ 'rev_id' => $revId ];
- }
-
-}
+++ /dev/null
-<?php
-
-namespace MediaWiki\Tests\Revision;
-
-use Wikimedia\Rdbms\IMaintainableDatabase;
-use MediaWiki\DB\PatchFileLocation;
-
-/**
- * Trait providing schema overrides that allow tests to run against the pre-MCR database schema.
- */
-trait PreMcrSchemaOverride {
-
- use PatchFileLocation;
- use McrSchemaDetection;
-
- /**
- * @return int
- */
- protected function getMcrMigrationStage() {
- return MIGRATION_OLD;
- }
-
- /**
- * @return string[]
- */
- protected function getMcrTablesToReset() {
- return [];
- }
-
- /**
- * @return array[]
- */
- protected function getSchemaOverrides( IMaintainableDatabase $db ) {
- $overrides = [
- 'scripts' => [],
- 'drop' => [],
- 'create' => [],
- 'alter' => [],
- ];
-
- if ( $this->hasMcrTables( $db ) ) {
- $overrides['drop'] = [ 'slots', 'content', 'slot_roles', 'content_models', ];
- $overrides['scripts'][] = $this->getSqlPatchPath( $db, '/drop-mcr-tables', __DIR__ );
- }
-
- if ( !$this->hasPreMcrFields( $db ) ) {
- $overrides['alter'][] = 'revision';
- $overrides['scripts'][] = $this->getSqlPatchPath( $db, '/create-pre-mcr-fields', __DIR__ );
- }
-
- return $overrides;
- }
-
-}
],
]
];
- yield 'pre-MCR, no model' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [
- 'tables' => [
- 'archive',
- 'actor_ar_user' => 'actor',
- 'comment_ar_comment' => 'comment',
- ],
- 'fields' => array_merge(
- $this->getArchiveQueryFields( true ),
- $this->getNewActorQueryFields( 'ar' ),
- $this->getNewCommentQueryFields( 'ar' )
- ),
- 'joins' => [
- 'comment_ar_comment'
- => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
- 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
- ],
- ]
- ];
}
public function provideQueryInfo() {
],
]
];
- yield 'pre-MCR' => [
- [
- 'wgContentHandlerUseDB' => true,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [],
- [
- 'tables' => [
- 'revision',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getContentHandlerQueryFields( 'rev' ),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ]
- ];
- yield 'pre-MCR, page, user' => [
- [
- 'wgContentHandlerUseDB' => true,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [ 'page', 'user' ],
- [
- 'tables' => [
- 'revision', 'page', 'user',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getContentHandlerQueryFields( 'rev' ),
- $this->getPageQueryFields(),
- $this->getUserQueryFields(),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- 'joins' => [
- 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
- 'user' => [ 'LEFT JOIN', [
- 'actor_rev_user.actor_user != 0',
- 'user_id = actor_rev_user.actor_user',
- ] ],
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ]
- ];
- yield 'pre-MCR, no model' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [],
- [
- 'tables' => [
- 'revision',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ],
- ];
- yield 'pre-MCR, no model, page' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [ 'page' ],
- [
- 'tables' => [
- 'revision', 'page',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getPageQueryFields(),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- 'joins' => [
- 'page' => [ 'JOIN', [ 'page_id = rev_page' ], ],
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ],
- ];
- yield 'pre-MCR, no model, user' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [ 'user' ],
- [
- 'tables' => [
- 'revision', 'user',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getUserQueryFields(),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- 'joins' => [
- 'user' => [ 'LEFT JOIN', [
- 'actor_rev_user.actor_user != 0',
- 'user_id = actor_rev_user.actor_user',
- ] ],
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ],
- ];
- yield 'pre-MCR, no model, text' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [ 'text' ],
- [
- 'tables' => [
- 'revision', 'text',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getTextQueryFields(),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- 'joins' => [
- 'text' => [ 'JOIN', [ 'rev_text_id=old_id' ] ],
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ],
- ];
- yield 'pre-MCR, no model, text, page, user' => [
- [
- 'wgContentHandlerUseDB' => false,
- 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
- ],
- [ 'text', 'page', 'user' ],
- [
- 'tables' => [
- 'revision', 'page', 'user', 'text',
- 'temp_rev_comment' => 'revision_comment_temp',
- 'comment_rev_comment' => 'comment',
- 'temp_rev_user' => 'revision_actor_temp',
- 'actor_rev_user' => 'actor',
- ],
- 'fields' => array_merge(
- $this->getRevisionQueryFields( true ),
- $this->getPageQueryFields(),
- $this->getUserQueryFields(),
- $this->getTextQueryFields(),
- $this->getNewActorQueryFields( 'rev', true ),
- $this->getNewCommentQueryFields( 'rev' )
- ),
- 'joins' => [
- 'page' => [
- 'JOIN',
- [ 'page_id = rev_page' ],
- ],
- 'user' => [
- 'LEFT JOIN',
- [
- 'actor_rev_user.actor_user != 0',
- 'user_id = actor_rev_user.actor_user',
- ],
- ],
- 'text' => [
- 'JOIN',
- [ 'rev_text_id=old_id' ],
- ],
- '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' ],
- 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
- 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
- ],
- ],
- ];
}
public function provideSlotsQueryInfo() {
'joins' => [],
]
];
- yield 'pre-MCR' => [
- [
- 'wgMultiContentRevisionSchemaMigrationStage'
- => SCHEMA_COMPAT_OLD,
- ],
- [],
- [
- 'tables' => [
- 'revision',
- ],
- 'fields' => array_merge(
- [
- 'slot_revision_id' => 'rev_id',
- 'slot_content_id' => 'NULL',
- 'slot_origin' => 'rev_id',
- 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
- ]
- ),
- 'joins' => [],
- ]
- ];
- yield 'pre-MCR, content' => [
- [
- 'wgMultiContentRevisionSchemaMigrationStage'
- => SCHEMA_COMPAT_OLD,
- ],
- [ 'content' ],
- [
- 'tables' => [
- 'revision',
- ],
- 'fields' => array_merge(
- [
- 'slot_revision_id' => 'rev_id',
- 'slot_content_id' => 'NULL',
- 'slot_origin' => 'rev_id',
- 'role_name' => $db->addQuotes( SlotRecord::MAIN ),
- 'content_size' => 'rev_len',
- 'content_sha1' => 'rev_sha1',
- 'content_address' =>
- $db->buildConcat( [ $db->addQuotes( 'tt:' ), 'rev_text_id' ] ),
- 'rev_text_id' => 'rev_text_id',
- 'model_name' => 'rev_content_model',
- ]
- ),
- 'joins' => [],
- ]
- ];
}
/**
$this->getHashWANObjectCache(),
$this->getMockCommentStore(),
ActorMigration::newMigration(),
- MIGRATION_OLD,
+ MIGRATION_NEW,
new NullLogger(),
true
);
yield [ true ];
yield [ false ];
yield [ 'somewiki' ];
- yield [ 'somewiki', MIGRATION_OLD , false ];
- yield [ 'somewiki', MIGRATION_NEW , true ];
}
/**
*/
public function testGetRevisionStore(
$dbDomain,
- $mcrMigrationStage = MIGRATION_OLD,
+ $mcrMigrationStage = MIGRATION_NEW,
$contentHandlerUseDb = true
) {
$lbFactory = $this->getMockLoadBalancerFactory();
public function provideSetContentHandlerUseDB() {
return [
- // ContentHandlerUseDB can be true of false pre migration.
- [ false, SCHEMA_COMPAT_OLD, false ],
- [ true, SCHEMA_COMPAT_OLD, false ],
+ // ContentHandlerUseDB can be true or false pre migration.
// During and after migration it can not be false...
[ false, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, true ],
[ false, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, true ],
public function provideMigrationConstruction() {
return [
- [ SCHEMA_COMPAT_OLD, false ],
[ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, false ],
[ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, false ],
[ SCHEMA_COMPAT_NEW, false ],
+++ /dev/null
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests Revision against the pre-MCR, pre ContentHandler DB schema.
- *
- * @covers Revision
- *
- * @group Revision
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class RevisionNoContentModelDbTest extends RevisionDbTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function getContentHandlerUseDB() {
- return false;
- }
-
- public function provideGetTextId() {
- yield [ [], null ];
-
- $row = (object)[
- 'rev_id' => 7,
- 'rev_page' => 1, // should match actual page id
- 'rev_text_id' => 789,
- 'rev_timestamp' => '20180101000000',
- 'rev_len' => 7,
- 'rev_minor_edit' => 0,
- 'rev_deleted' => 0,
- 'rev_parent_id' => 0,
- 'rev_sha1' => 'deadbeef',
- 'rev_comment' => 'some comment',
- 'rev_comment_text' => 'some comment',
- 'rev_comment_data' => '{}',
- 'rev_user' => 17,
- 'rev_user_text' => 'some user',
- ];
-
- yield [ $row, 789 ];
- }
-
- public function provideGetRevisionText() {
- yield [
- [ 'text' ]
- ];
- }
-
-}
+++ /dev/null
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests Revision against the pre-MCR DB schema.
- *
- * @covers Revision
- *
- * @group Revision
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class RevisionPreMcrDbTest extends RevisionDbTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function getContentHandlerUseDB() {
- return true;
- }
-
- public function provideGetTextId() {
- yield [ [], null ];
-
- $row = (object)[
- 'rev_id' => 7,
- 'rev_page' => 1, // should match actual page id
- 'rev_text_id' => 789,
- 'rev_timestamp' => '20180101000000',
- 'rev_len' => 7,
- 'rev_minor_edit' => 0,
- 'rev_deleted' => 0,
- 'rev_parent_id' => 0,
- 'rev_sha1' => 'deadbeef',
- 'rev_comment' => 'some comment',
- 'rev_comment_text' => 'some comment',
- 'rev_comment_data' => '{}',
- 'rev_user' => 17,
- 'rev_user_text' => 'some user',
- ];
-
- yield [ $row, 789 ];
- }
-
- public function provideGetRevisionText() {
- yield [
- [ 'text' ]
- ];
- }
-}
+++ /dev/null
-<?php
-
-use MediaWiki\MediaWikiServices;
-use MediaWiki\Revision\SlotRecord;
-use MediaWiki\Storage\SqlBlobStore;
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Test class for page archiving, using the pre-MCR schema.
- *
- * @group ContentHandler
- * @group Database
- * ^--- important, causes temporary tables to be used instead of the real database
- *
- * @group medium
- * ^--- important, causes tests not to fail with timeout
- */
-class PageArchivePreMcrTest extends PageArchiveTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function getExpectedArchiveRows() {
- /** @var SqlBlobStore $blobStore */
- $blobStore = MediaWikiServices::getInstance()->getBlobStore();
-
- return [
- [
- 'ar_minor_edit' => '0',
- 'ar_user' => null,
- 'ar_user_text' => $this->ipEditor,
- 'ar_actor' => (string)User::newFromName( $this->ipEditor, false )->getActorId( $this->db ),
- 'ar_len' => '11',
- 'ar_deleted' => '0',
- 'ar_rev_id' => strval( $this->ipRev->getId() ),
- 'ar_timestamp' => $this->db->timestamp( $this->ipRev->getTimestamp() ),
- 'ar_sha1' => '0qdrpxl537ivfnx4gcpnzz0285yxryy',
- 'ar_page_id' => strval( $this->ipRev->getPageId() ),
- 'ar_comment_text' => 'just a test',
- 'ar_comment_data' => null,
- 'ar_comment_cid' => '2',
- 'ar_content_format' => null,
- 'ar_content_model' => null,
- 'ts_tags' => null,
- 'ar_id' => '2',
- 'ar_namespace' => '0',
- 'ar_title' => 'PageArchiveTest_thePage',
- 'ar_text_id' => (string)$blobStore->getTextIdFromAddress(
- $this->ipRev->getSlot( SlotRecord::MAIN )->getAddress()
- ),
- 'ar_parent_id' => strval( $this->ipRev->getParentId() ),
- ],
- [
- 'ar_minor_edit' => '0',
- 'ar_user' => (string)$this->getTestUser()->getUser()->getId(),
- 'ar_user_text' => $this->getTestUser()->getUser()->getName(),
- 'ar_actor' => (string)$this->getTestUser()->getUser()->getActorId(),
- 'ar_len' => '7',
- 'ar_deleted' => '0',
- 'ar_rev_id' => strval( $this->firstRev->getId() ),
- 'ar_timestamp' => $this->db->timestamp( $this->firstRev->getTimestamp() ),
- 'ar_sha1' => 'pr0s8e18148pxhgjfa0gjrvpy8fiyxc',
- 'ar_page_id' => strval( $this->firstRev->getPageId() ),
- 'ar_comment_text' => 'testing',
- 'ar_comment_data' => null,
- 'ar_comment_cid' => '1',
- 'ar_content_format' => null,
- 'ar_content_model' => null,
- 'ts_tags' => null,
- 'ar_id' => '1',
- 'ar_namespace' => '0',
- 'ar_title' => 'PageArchiveTest_thePage',
- 'ar_text_id' => (string)$blobStore->getTextIdFromAddress(
- $this->firstRev->getSlot( SlotRecord::MAIN )->getAddress()
- ),
- 'ar_parent_id' => '0',
- ],
- ];
- }
-
-}
+++ /dev/null
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests WikiPage against the pre-MCR, pre ContentHandler DB schema.
- *
- * @covers WikiPage
- *
- * @group WikiPage
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class WikiPageNoContentModelDbTest extends WikiPageDbTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function getContentHandlerUseDB() {
- return false;
- }
-
- public function testGetDeletionUpdates() {
- $mainContent1 = new WikitextContent( '' );
-
- $title = Title::makeTitle( $this->getDefaultWikitextNS(), __METHOD__ );
- $page = new WikiPage( $title );
- $page = $this->createPage(
- $page,
- [ 'main' => $mainContent1 ]
- );
-
- $dataUpdates = $page->getDeletionUpdates( $page->getRevisionRecord() );
- $this->assertNotEmpty( $dataUpdates );
-
- $updateNames = array_map( function ( $du ) {
- return isset( $du->_name ) ? $du->_name : get_class( $du );
- }, $dataUpdates );
-
- $this->assertContains( LinksDeletionUpdate::class, $updateNames );
- }
-
-}
+++ /dev/null
-<?php
-
-use MediaWiki\Tests\Revision\PreMcrSchemaOverride;
-
-/**
- * Tests WikiPage against the pre-MCR DB schema.
- *
- * @covers WikiPage
- *
- * @group WikiPage
- * @group Storage
- * @group ContentHandler
- * @group Database
- * @group medium
- */
-class WikiPagePreMcrDbTest extends WikiPageDbTestBase {
-
- use PreMcrSchemaOverride;
-
- protected function getContentHandlerUseDB() {
- return true;
- }
-
- /**
- * @covers WikiPage::getContentModel
- */
- public function testGetContentModel() {
- $page = $this->createPage(
- __METHOD__,
- "some text",
- CONTENT_MODEL_JAVASCRIPT
- );
-
- $page = new WikiPage( $page->getTitle() );
- $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $page->getContentModel() );
- }
-
- /**
- * @covers WikiPage::getContentHandler
- */
- public function testGetContentHandler() {
- $page = $this->createPage(
- __METHOD__,
- "some text",
- CONTENT_MODEL_JAVASCRIPT
- );
-
- $page = new WikiPage( $page->getTitle() );
- $this->assertEquals( JavaScriptContentHandler::class, get_class( $page->getContentHandler() ) );
- }
-
-}