3 namespace MediaWiki\Tests\Revision
;
5 use MediaWiki\MediaWikiServices
;
6 use MediaWiki\Revision\SlotRecord
;
11 * Tests RevisionStore against the post-migration MCR DB schema.
13 * @group RevisionStore
17 class RevisionQueryInfoTest
extends MediaWikiTestCase
{
19 protected function getRevisionQueryFields( $returnTextIdField = true ) {
30 if ( $returnTextIdField ) {
31 $fields[] = 'rev_text_id';
36 protected function getArchiveQueryFields( $returnTextFields = true ) {
50 if ( $returnTextFields ) {
51 $fields[] = 'ar_text_id';
56 protected function getNewCommentQueryFields( $prefix ) {
58 "{$prefix}_comment_text" => "comment_{$prefix}_comment.comment_text",
59 "{$prefix}_comment_data" => "comment_{$prefix}_comment.comment_data",
60 "{$prefix}_comment_cid" => "comment_{$prefix}_comment.comment_id",
64 protected function getNewActorQueryFields( $prefix, $tmp = false ) {
66 "{$prefix}_user" => "actor_{$prefix}_user.actor_user",
67 "{$prefix}_user_text" => "actor_{$prefix}_user.actor_name",
68 "{$prefix}_actor" => $tmp ?
"temp_{$prefix}_user.{$prefix}actor_actor" : "{$prefix}_actor",
72 protected function getTextQueryFields() {
79 protected function getPageQueryFields() {
90 protected function getUserQueryFields() {
96 protected function getContentHandlerQueryFields( $prefix ) {
98 "{$prefix}_content_format",
99 "{$prefix}_content_model",
103 public function provideArchiveQueryInfo() {
104 yield
'MCR, comment, actor' => [
106 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW
,
111 'actor_ar_user' => 'actor',
112 'comment_ar_comment' => 'comment',
114 'fields' => array_merge(
115 $this->getArchiveQueryFields( false ),
116 $this->getNewActorQueryFields( 'ar' ),
117 $this->getNewCommentQueryFields( 'ar' )
121 => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
122 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
126 yield
'read-new MCR, comment, actor' => [
128 'wgContentHandlerUseDB' => true,
129 'wgMultiContentRevisionSchemaMigrationStage'
130 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
,
135 'actor_ar_user' => 'actor',
136 'comment_ar_comment' => 'comment',
138 'fields' => array_merge(
139 $this->getArchiveQueryFields( false ),
140 $this->getNewActorQueryFields( 'ar' ),
141 $this->getNewCommentQueryFields( 'ar' )
145 => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
146 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
150 yield
'MCR write-both/read-old' => [
152 'wgContentHandlerUseDB' => true,
153 'wgMultiContentRevisionSchemaMigrationStage'
154 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
159 'actor_ar_user' => 'actor',
160 'comment_ar_comment' => 'comment',
162 'fields' => array_merge(
163 $this->getArchiveQueryFields( true ),
164 $this->getContentHandlerQueryFields( 'ar' ),
165 $this->getNewActorQueryFields( 'ar' ),
166 $this->getNewCommentQueryFields( 'ar' )
170 => [ 'JOIN', 'comment_ar_comment.comment_id = ar_comment_id' ],
171 'actor_ar_user' => [ 'JOIN', 'actor_ar_user.actor_id = ar_actor' ],
177 public function provideQueryInfo() {
178 // TODO: more option variations
179 yield
'MCR, page, user, comment, actor' => [
181 'wgContentHandlerUseDB' => true,
182 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW
,
190 'temp_rev_user' => 'revision_actor_temp',
191 'temp_rev_comment' => 'revision_comment_temp',
192 'actor_rev_user' => 'actor',
193 'comment_rev_comment' => 'comment',
195 'fields' => array_merge(
196 $this->getRevisionQueryFields( false ),
197 $this->getPageQueryFields(),
198 $this->getUserQueryFields(),
199 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
200 $this->getNewCommentQueryFields( 'rev' )
203 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
206 [ 'actor_rev_user.actor_user != 0', 'user_id = actor_rev_user.actor_user' ],
208 'comment_rev_comment' => [
210 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
212 'actor_rev_user' => [
214 'actor_rev_user.actor_id = temp_rev_user.revactor_actor',
216 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
217 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
218 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
219 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
223 yield
'MCR read-new, page, user, comment, actor' => [
225 'wgContentHandlerUseDB' => true,
226 'wgMultiContentRevisionSchemaMigrationStage'
227 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
,
235 'temp_rev_user' => 'revision_actor_temp',
236 'temp_rev_comment' => 'revision_comment_temp',
237 'actor_rev_user' => 'actor',
238 'comment_rev_comment' => 'comment',
240 'fields' => array_merge(
241 $this->getRevisionQueryFields( false ),
242 $this->getPageQueryFields(),
243 $this->getUserQueryFields(),
244 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
245 $this->getNewCommentQueryFields( 'rev' )
248 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
252 'actor_rev_user.actor_user != 0',
253 'user_id = actor_rev_user.actor_user',
256 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
257 'comment_rev_comment'
258 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
259 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
260 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
264 yield
'MCR read-new' => [
266 'wgContentHandlerUseDB' => true,
267 'wgMultiContentRevisionSchemaMigrationStage'
268 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
,
276 'temp_rev_user' => 'revision_actor_temp',
277 'temp_rev_comment' => 'revision_comment_temp',
278 'actor_rev_user' => 'actor',
279 'comment_rev_comment' => 'comment',
281 'fields' => array_merge(
282 $this->getRevisionQueryFields( false ),
283 $this->getPageQueryFields(),
284 $this->getUserQueryFields(),
285 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
286 $this->getNewCommentQueryFields( 'rev' )
289 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
293 'actor_rev_user.actor_user != 0',
294 'user_id = actor_rev_user.actor_user'
297 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
298 'comment_rev_comment'
299 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
300 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
301 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
305 yield
'MCR write-both/read-old' => [
307 'wgContentHandlerUseDB' => true,
308 'wgMultiContentRevisionSchemaMigrationStage'
309 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
315 'temp_rev_comment' => 'revision_comment_temp',
316 'comment_rev_comment' => 'comment',
317 'temp_rev_user' => 'revision_actor_temp',
318 'actor_rev_user' => 'actor',
320 'fields' => array_merge(
321 $this->getRevisionQueryFields( true ),
322 $this->getContentHandlerQueryFields( 'rev' ),
323 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
324 $this->getNewCommentQueryFields( 'rev' )
327 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
328 'comment_rev_comment'
329 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
330 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
331 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
335 yield
'MCR write-both/read-old, page, user' => [
337 'wgContentHandlerUseDB' => true,
338 'wgMultiContentRevisionSchemaMigrationStage'
339 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
347 'temp_rev_comment' => 'revision_comment_temp',
348 'comment_rev_comment' => 'comment',
349 'temp_rev_user' => 'revision_actor_temp',
350 'actor_rev_user' => 'actor',
352 'fields' => array_merge(
353 $this->getRevisionQueryFields( true ),
354 $this->getContentHandlerQueryFields( 'rev' ),
355 $this->getUserQueryFields(),
356 $this->getPageQueryFields(),
357 $this->getNewActorQueryFields( 'rev', 'temp_rev_user.revactor_actor' ),
358 $this->getNewCommentQueryFields( 'rev' )
361 'page' => [ 'JOIN', [ 'page_id = rev_page' ] ],
365 'actor_rev_user.actor_user != 0',
366 'user_id = actor_rev_user.actor_user',
369 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
370 'comment_rev_comment'
371 => [ 'JOIN', 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
372 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
373 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
379 public function provideSlotsQueryInfo() {
380 yield
'MCR, no options' => [
382 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW
,
398 yield
'MCR, role option' => [
400 'wgMultiContentRevisionSchemaMigrationStage' => SCHEMA_COMPAT_NEW
,
416 'slot_roles' => [ 'LEFT JOIN', [ 'slot_role_id = role_id' ] ],
420 yield
'MCR read-new, content option' => [
422 'wgMultiContentRevisionSchemaMigrationStage'
423 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
,
442 'content' => [ 'JOIN', [ 'slot_content_id = content_id' ] ],
446 yield
'MCR read-new, content and model options' => [
448 'wgMultiContentRevisionSchemaMigrationStage'
449 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
,
451 [ 'content', 'model' ],
470 'content' => [ 'JOIN', [ 'slot_content_id = content_id' ] ],
471 'content_models' => [ 'LEFT JOIN', [ 'content_model = model_id' ] ],
476 $db = wfGetDB( DB_REPLICA
);
478 yield
'MCR write-both/read-old' => [
480 'wgMultiContentRevisionSchemaMigrationStage'
481 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
488 'fields' => array_merge(
490 'slot_revision_id' => 'rev_id',
491 'slot_content_id' => 'NULL',
492 'slot_origin' => 'rev_id',
493 'role_name' => $db->addQuotes( SlotRecord
::MAIN
),
499 yield
'MCR write-both/read-old, content' => [
501 'wgMultiContentRevisionSchemaMigrationStage'
502 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
509 'fields' => array_merge(
511 'slot_revision_id' => 'rev_id',
512 'slot_content_id' => 'NULL',
513 'slot_origin' => 'rev_id',
514 'role_name' => $db->addQuotes( SlotRecord
::MAIN
),
515 'content_size' => 'rev_len',
516 'content_sha1' => 'rev_sha1',
517 'content_address' => $db->buildConcat( [
518 $db->addQuotes( 'tt:' ), 'rev_text_id' ] ),
519 'rev_text_id' => 'rev_text_id',
520 'model_name' => 'rev_content_model',
526 yield
'MCR write-both/read-old, content, model, role' => [
528 'wgMultiContentRevisionSchemaMigrationStage'
529 => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
531 [ 'content', 'model', 'role' ],
536 'fields' => array_merge(
538 'slot_revision_id' => 'rev_id',
539 'slot_content_id' => 'NULL',
540 'slot_origin' => 'rev_id',
541 'role_name' => $db->addQuotes( SlotRecord
::MAIN
),
542 'content_size' => 'rev_len',
543 'content_sha1' => 'rev_sha1',
544 'content_address' => $db->buildConcat( [
545 $db->addQuotes( 'tt:' ), 'rev_text_id' ] ),
546 'rev_text_id' => 'rev_text_id',
547 'model_name' => 'rev_content_model',
556 * @covers Revision::getArchiveQueryInfo
557 * @dataProvider provideArchiveQueryInfo
559 public function testRevisionGetArchiveQueryInfo( $migrationStageSettings, $expected ) {
560 $this->setMwGlobals( $migrationStageSettings );
562 $queryInfo = Revision
::getArchiveQueryInfo();
563 $this->assertQueryInfoEquals( $expected, $queryInfo );
567 * @covers Revision::getQueryInfo
568 * @dataProvider provideQueryInfo
570 public function testRevisionGetQueryInfo( $migrationStageSettings, $options, $expected ) {
571 $this->setMwGlobals( $migrationStageSettings );
573 $queryInfo = Revision
::getQueryInfo( $options );
574 $this->assertQueryInfoEquals( $expected, $queryInfo );
578 * @dataProvider provideQueryInfo
579 * @covers \MediaWiki\Revision\RevisionStore::getQueryInfo
581 public function testRevisionStoreGetQueryInfo( $migrationStageSettings, $options, $expected ) {
582 $this->setMwGlobals( $migrationStageSettings );
584 $store = MediaWikiServices
::getInstance()->getRevisionStore();
586 $queryInfo = $store->getQueryInfo( $options );
587 $this->assertQueryInfoEquals( $expected, $queryInfo );
591 * @dataProvider provideSlotsQueryInfo
592 * @covers \MediaWiki\Revision\RevisionStore::getSlotsQueryInfo
594 public function testRevisionStoreGetSlotsQueryInfo(
595 $migrationStageSettings,
599 $this->setMwGlobals( $migrationStageSettings );
601 $store = MediaWikiServices
::getInstance()->getRevisionStore();
603 $queryInfo = $store->getSlotsQueryInfo( $options );
604 $this->assertQueryInfoEquals( $expected, $queryInfo );
608 * @dataProvider provideArchiveQueryInfo
609 * @covers \MediaWiki\Revision\RevisionStore::getArchiveQueryInfo
611 public function testRevisionStoreGetArchiveQueryInfo( $migrationStageSettings, $expected ) {
612 $this->setMwGlobals( $migrationStageSettings );
614 $store = MediaWikiServices
::getInstance()->getRevisionStore();
616 $queryInfo = $store->getArchiveQueryInfo();
617 $this->assertQueryInfoEquals( $expected, $queryInfo );
620 private function assertQueryInfoEquals( $expected, $queryInfo ) {
621 $this->assertArrayEqualsIgnoringIntKeyOrder(
623 $queryInfo['tables'],
626 $this->assertArrayEqualsIgnoringIntKeyOrder(
628 $queryInfo['fields'],
631 $this->assertArrayEqualsIgnoringIntKeyOrder(
639 * Assert that the two arrays passed are equal, ignoring the order of the values that integer
642 * Note: Failures of this assertion can be slightly confusing as the arrays are actually
643 * split into a string key array and an int key array before assertions occur.
645 * @param array $expected
646 * @param array $actual
648 private function assertArrayEqualsIgnoringIntKeyOrder(
653 $this->objectAssociativeSort( $expected );
654 $this->objectAssociativeSort( $actual );
656 // Separate the int key values from the string key values so that assertion failures are
657 // easier to understand.
658 $expectedIntKeyValues = [];
659 $actualIntKeyValues = [];
661 // Remove all int keys and re add them at the end after sorting by value
662 // This will result in all int keys being in the same order with same ints at the end of
664 foreach ( $expected as $key => $value ) {
665 if ( is_int( $key ) ) {
666 unset( $expected[$key] );
667 $expectedIntKeyValues[] = $value;
670 foreach ( $actual as $key => $value ) {
671 if ( is_int( $key ) ) {
672 unset( $actual[$key] );
673 $actualIntKeyValues[] = $value;
677 $this->objectAssociativeSort( $expected );
678 $this->objectAssociativeSort( $actual );
680 $this->objectAssociativeSort( $expectedIntKeyValues );
681 $this->objectAssociativeSort( $actualIntKeyValues );
683 $this->assertEquals( $expected, $actual, $message );
684 $this->assertEquals( $expectedIntKeyValues, $actualIntKeyValues, $message );