Merge "RELEASE-NOTES to HISTORY for 1.27.4/1.28.3/1.29.2"
[lhc/web/wiklou.git] / tests / phpunit / includes / RevisionTest.php
1 <?php
2
3 use Wikimedia\TestingAccessWrapper;
4
5 /**
6 * Test cases in RevisionTest should not interact with the Database.
7 * For test cases that need Database interaction see RevisionDbTestBase.
8 */
9 class RevisionTest extends MediaWikiTestCase {
10
11 public function provideConstructFromArray() {
12 yield 'with text' => [
13 [
14 'text' => 'hello world.',
15 'content_model' => CONTENT_MODEL_JAVASCRIPT
16 ],
17 ];
18 yield 'with content' => [
19 [
20 'content' => new JavaScriptContent( 'hellow world.' )
21 ],
22 ];
23 }
24
25 /**
26 * @dataProvider provideConstructFromArray
27 * @covers Revision::__construct
28 * @covers Revision::constructFromRowArray
29 */
30 public function testConstructFromArray( array $rowArray ) {
31 $rev = new Revision( $rowArray );
32 $this->assertNotNull( $rev->getContent(), 'no content object available' );
33 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContent()->getModel() );
34 $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $rev->getContentModel() );
35 }
36
37 public function provideConstructFromArray_userSetAsExpected() {
38 yield 'no user defaults to wgUser' => [
39 [
40 'content' => new JavaScriptContent( 'hello world.' ),
41 ],
42 null,
43 null,
44 ];
45 yield 'user text and id' => [
46 [
47 'content' => new JavaScriptContent( 'hello world.' ),
48 'user_text' => 'SomeTextUserName',
49 'user' => 99,
50
51 ],
52 99,
53 'SomeTextUserName',
54 ];
55 // Note: the below XXX test cases are odd and probably result in unexpected behaviour if used
56 // in production code.
57 yield 'XXX: user text only' => [
58 [
59 'content' => new JavaScriptContent( 'hello world.' ),
60 'user_text' => '111.111.111.111',
61 ],
62 null,
63 '111.111.111.111',
64 ];
65 yield 'XXX: user id only' => [
66 [
67 'content' => new JavaScriptContent( 'hello world.' ),
68 'user' => 9989,
69 ],
70 9989,
71 null,
72 ];
73 }
74
75 /**
76 * @dataProvider provideConstructFromArray_userSetAsExpected
77 * @covers Revision::__construct
78 * @covers Revision::constructFromRowArray
79 *
80 * @param array $rowArray
81 * @param mixed $expectedUserId null to expect the current wgUser ID
82 * @param mixed $expectedUserName null to expect the current wgUser name
83 */
84 public function testConstructFromArray_userSetAsExpected(
85 array $rowArray,
86 $expectedUserId,
87 $expectedUserName
88 ) {
89 $testUser = $this->getTestUser()->getUser();
90 $this->setMwGlobals( 'wgUser', $testUser );
91 if ( $expectedUserId === null ) {
92 $expectedUserId = $testUser->getId();
93 }
94 if ( $expectedUserName === null ) {
95 $expectedUserName = $testUser->getName();
96 }
97
98 $rev = new Revision( $rowArray );
99 $this->assertEquals( $expectedUserId, $rev->getUser() );
100 $this->assertEquals( $expectedUserName, $rev->getUserText() );
101 }
102
103 public function provideConstructFromArrayThrowsExceptions() {
104 yield 'content and text_id both not empty' => [
105 [
106 'content' => new WikitextContent( 'GOAT' ),
107 'text_id' => 'someid',
108 ],
109 new MWException( "Text already stored in external store (id someid), " .
110 "can't serialize content object" )
111 ];
112 yield 'with bad content object (class)' => [
113 [ 'content' => new stdClass() ],
114 new MWException( '`content` field must contain a Content object.' )
115 ];
116 yield 'with bad content object (string)' => [
117 [ 'content' => 'ImAGoat' ],
118 new MWException( '`content` field must contain a Content object.' )
119 ];
120 yield 'bad row format' => [
121 'imastring, not a row',
122 new MWException( 'Revision constructor passed invalid row format.' )
123 ];
124 }
125
126 /**
127 * @dataProvider provideConstructFromArrayThrowsExceptions
128 * @covers Revision::__construct
129 * @covers Revision::constructFromRowArray
130 */
131 public function testConstructFromArrayThrowsExceptions( $rowArray, Exception $expectedException ) {
132 $this->setExpectedException(
133 get_class( $expectedException ),
134 $expectedException->getMessage(),
135 $expectedException->getCode()
136 );
137 new Revision( $rowArray );
138 }
139
140 public function provideConstructFromRow() {
141 yield 'Full construction' => [
142 [
143 'rev_id' => '2',
144 'rev_page' => '1',
145 'rev_text_id' => '2',
146 'rev_timestamp' => '20171017114835',
147 'rev_user_text' => '127.0.0.1',
148 'rev_user' => '0',
149 'rev_minor_edit' => '0',
150 'rev_deleted' => '0',
151 'rev_len' => '46',
152 'rev_parent_id' => '1',
153 'rev_sha1' => 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z',
154 'rev_comment_text' => 'Goat Comment!',
155 'rev_comment_data' => null,
156 'rev_comment_cid' => null,
157 'rev_content_format' => 'GOATFORMAT',
158 'rev_content_model' => 'GOATMODEL',
159 ],
160 function ( RevisionTest $testCase, Revision $rev ) {
161 $testCase->assertSame( 2, $rev->getId() );
162 $testCase->assertSame( 1, $rev->getPage() );
163 $testCase->assertSame( 2, $rev->getTextId() );
164 $testCase->assertSame( '20171017114835', $rev->getTimestamp() );
165 $testCase->assertSame( '127.0.0.1', $rev->getUserText() );
166 $testCase->assertSame( 0, $rev->getUser() );
167 $testCase->assertSame( false, $rev->isMinor() );
168 $testCase->assertSame( false, $rev->isDeleted( Revision::DELETED_TEXT ) );
169 $testCase->assertSame( 46, $rev->getSize() );
170 $testCase->assertSame( 1, $rev->getParentId() );
171 $testCase->assertSame( 'rdqbbzs3pkhihgbs8qf2q9jsvheag5z', $rev->getSha1() );
172 $testCase->assertSame( 'Goat Comment!', $rev->getComment() );
173 $testCase->assertSame( 'GOATFORMAT', $rev->getContentFormat() );
174 $testCase->assertSame( 'GOATMODEL', $rev->getContentModel() );
175 }
176 ];
177 yield 'null fields' => [
178 [
179 'rev_id' => '2',
180 'rev_page' => '1',
181 'rev_text_id' => '2',
182 'rev_timestamp' => '20171017114835',
183 'rev_user_text' => '127.0.0.1',
184 'rev_user' => '0',
185 'rev_minor_edit' => '0',
186 'rev_deleted' => '0',
187 'rev_comment_text' => 'Goat Comment!',
188 'rev_comment_data' => null,
189 'rev_comment_cid' => null,
190 ],
191 function ( RevisionTest $testCase, Revision $rev ) {
192 $testCase->assertNull( $rev->getSize() );
193 $testCase->assertNull( $rev->getParentId() );
194 $testCase->assertNull( $rev->getSha1() );
195 $testCase->assertSame( 'text/x-wiki', $rev->getContentFormat() );
196 $testCase->assertSame( 'wikitext', $rev->getContentModel() );
197 }
198 ];
199 }
200
201 /**
202 * @dataProvider provideConstructFromRow
203 * @covers Revision::__construct
204 * @covers Revision::constructFromDbRowObject
205 */
206 public function testConstructFromRow( array $arrayData, $assertions ) {
207 $row = (object)$arrayData;
208 $rev = new Revision( $row );
209 $assertions( $this, $rev );
210 }
211
212 public function provideGetRevisionText() {
213 yield 'Generic test' => [
214 'This is a goat of revision text.',
215 [
216 'old_flags' => '',
217 'old_text' => 'This is a goat of revision text.',
218 ],
219 ];
220 }
221
222 public function provideGetId() {
223 yield [
224 [],
225 null
226 ];
227 yield [
228 [ 'id' => 998 ],
229 998
230 ];
231 }
232
233 /**
234 * @dataProvider provideGetId
235 * @covers Revision::getId
236 */
237 public function testGetId( $rowArray, $expectedId ) {
238 $rev = new Revision( $rowArray );
239 $this->assertEquals( $expectedId, $rev->getId() );
240 }
241
242 public function provideSetId() {
243 yield [ '123', 123 ];
244 yield [ 456, 456 ];
245 }
246
247 /**
248 * @dataProvider provideSetId
249 * @covers Revision::setId
250 */
251 public function testSetId( $input, $expected ) {
252 $rev = new Revision( [] );
253 $rev->setId( $input );
254 $this->assertSame( $expected, $rev->getId() );
255 }
256
257 public function provideSetUserIdAndName() {
258 yield [ '123', 123, 'GOaT' ];
259 yield [ 456, 456, 'GOaT' ];
260 }
261
262 /**
263 * @dataProvider provideSetUserIdAndName
264 * @covers Revision::setUserIdAndName
265 */
266 public function testSetUserIdAndName( $inputId, $expectedId, $name ) {
267 $rev = new Revision( [] );
268 $rev->setUserIdAndName( $inputId, $name );
269 $this->assertSame( $expectedId, $rev->getUser( Revision::RAW ) );
270 $this->assertEquals( $name, $rev->getUserText( Revision::RAW ) );
271 }
272
273 public function provideGetTextId() {
274 yield [ [], null ];
275 yield [ [ 'text_id' => '123' ], 123 ];
276 yield [ [ 'text_id' => 456 ], 456 ];
277 }
278
279 /**
280 * @dataProvider provideGetTextId
281 * @covers Revision::getTextId()
282 */
283 public function testGetTextId( $rowArray, $expected ) {
284 $rev = new Revision( $rowArray );
285 $this->assertSame( $expected, $rev->getTextId() );
286 }
287
288 public function provideGetParentId() {
289 yield [ [], null ];
290 yield [ [ 'parent_id' => '123' ], 123 ];
291 yield [ [ 'parent_id' => 456 ], 456 ];
292 }
293
294 /**
295 * @dataProvider provideGetParentId
296 * @covers Revision::getParentId()
297 */
298 public function testGetParentId( $rowArray, $expected ) {
299 $rev = new Revision( $rowArray );
300 $this->assertSame( $expected, $rev->getParentId() );
301 }
302
303 /**
304 * @covers Revision::getRevisionText
305 * @dataProvider provideGetRevisionText
306 */
307 public function testGetRevisionText( $expected, $rowData, $prefix = 'old_', $wiki = false ) {
308 $this->assertEquals(
309 $expected,
310 Revision::getRevisionText( (object)$rowData, $prefix, $wiki ) );
311 }
312
313 public function provideGetRevisionTextWithZlibExtension() {
314 yield 'Generic gzip test' => [
315 'This is a small goat of revision text.',
316 [
317 'old_flags' => 'gzip',
318 'old_text' => gzdeflate( 'This is a small goat of revision text.' ),
319 ],
320 ];
321 }
322
323 /**
324 * @covers Revision::getRevisionText
325 * @dataProvider provideGetRevisionTextWithZlibExtension
326 */
327 public function testGetRevisionWithZlibExtension( $expected, $rowData ) {
328 $this->checkPHPExtension( 'zlib' );
329 $this->testGetRevisionText( $expected, $rowData );
330 }
331
332 public function provideGetRevisionTextWithLegacyEncoding() {
333 yield 'Utf8Native' => [
334 "Wiki est l'\xc3\xa9cole superieur !",
335 'iso-8859-1',
336 [
337 'old_flags' => 'utf-8',
338 'old_text' => "Wiki est l'\xc3\xa9cole superieur !",
339 ]
340 ];
341 yield 'Utf8Legacy' => [
342 "Wiki est l'\xc3\xa9cole superieur !",
343 'iso-8859-1',
344 [
345 'old_flags' => '',
346 'old_text' => "Wiki est l'\xe9cole superieur !",
347 ]
348 ];
349 }
350
351 /**
352 * @covers Revision::getRevisionText
353 * @dataProvider provideGetRevisionTextWithLegacyEncoding
354 */
355 public function testGetRevisionWithLegacyEncoding( $expected, $encoding, $rowData ) {
356 $this->setMwGlobals( 'wgLegacyEncoding', $encoding );
357 $this->testGetRevisionText( $expected, $rowData );
358 }
359
360 public function provideGetRevisionTextWithGzipAndLegacyEncoding() {
361 /**
362 * WARNING!
363 * Do not set the external flag!
364 * Otherwise, getRevisionText will hit the live database (if ExternalStore is enabled)!
365 */
366 yield 'Utf8NativeGzip' => [
367 "Wiki est l'\xc3\xa9cole superieur !",
368 'iso-8859-1',
369 [
370 'old_flags' => 'gzip,utf-8',
371 'old_text' => gzdeflate( "Wiki est l'\xc3\xa9cole superieur !" ),
372 ]
373 ];
374 yield 'Utf8LegacyGzip' => [
375 "Wiki est l'\xc3\xa9cole superieur !",
376 'iso-8859-1',
377 [
378 'old_flags' => 'gzip',
379 'old_text' => gzdeflate( "Wiki est l'\xe9cole superieur !" ),
380 ]
381 ];
382 }
383
384 /**
385 * @covers Revision::getRevisionText
386 * @dataProvider provideGetRevisionTextWithGzipAndLegacyEncoding
387 */
388 public function testGetRevisionWithGzipAndLegacyEncoding( $expected, $encoding, $rowData ) {
389 $this->checkPHPExtension( 'zlib' );
390 $this->setMwGlobals( 'wgLegacyEncoding', $encoding );
391 $this->testGetRevisionText( $expected, $rowData );
392 }
393
394 /**
395 * @covers Revision::compressRevisionText
396 */
397 public function testCompressRevisionTextUtf8() {
398 $row = new stdClass;
399 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
400 $row->old_flags = Revision::compressRevisionText( $row->old_text );
401 $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
402 "Flags should contain 'utf-8'" );
403 $this->assertFalse( false !== strpos( $row->old_flags, 'gzip' ),
404 "Flags should not contain 'gzip'" );
405 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
406 $row->old_text, "Direct check" );
407 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
408 Revision::getRevisionText( $row ), "getRevisionText" );
409 }
410
411 /**
412 * @covers Revision::compressRevisionText
413 */
414 public function testCompressRevisionTextUtf8Gzip() {
415 $this->checkPHPExtension( 'zlib' );
416 $this->setMwGlobals( 'wgCompressRevisions', true );
417
418 $row = new stdClass;
419 $row->old_text = "Wiki est l'\xc3\xa9cole superieur !";
420 $row->old_flags = Revision::compressRevisionText( $row->old_text );
421 $this->assertTrue( false !== strpos( $row->old_flags, 'utf-8' ),
422 "Flags should contain 'utf-8'" );
423 $this->assertTrue( false !== strpos( $row->old_flags, 'gzip' ),
424 "Flags should contain 'gzip'" );
425 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
426 gzinflate( $row->old_text ), "Direct check" );
427 $this->assertEquals( "Wiki est l'\xc3\xa9cole superieur !",
428 Revision::getRevisionText( $row ), "getRevisionText" );
429 }
430
431 public function provideFetchFromConds() {
432 yield [ 0, [] ];
433 yield [ Revision::READ_LOCKING, [ 'FOR UPDATE' ] ];
434 }
435
436 /**
437 * @dataProvider provideFetchFromConds
438 * @covers Revision::fetchFromConds
439 */
440 public function testFetchFromConds( $flags, array $options ) {
441 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
442 $conditions = [ 'conditionsArray' ];
443
444 $db = $this->getMock( IDatabase::class );
445 $db->expects( $this->once() )
446 ->method( 'selectRow' )
447 ->with(
448 $this->equalTo( [ 'revision', 'page', 'user' ] ),
449 // We don't really care about the fields are they come from the selectField methods
450 $this->isType( 'array' ),
451 $this->equalTo( $conditions ),
452 // Method name
453 $this->equalTo( 'Revision::fetchFromConds' ),
454 $this->equalTo( $options ),
455 // We don't really care about the join conds are they come from the joinCond methods
456 $this->isType( 'array' )
457 )
458 ->willReturn( 'RETURNVALUE' );
459
460 $wrapper = TestingAccessWrapper::newFromClass( Revision::class );
461 $result = $wrapper->fetchFromConds( $db, $conditions, $flags );
462
463 $this->assertEquals( 'RETURNVALUE', $result );
464 }
465
466 public function provideDecompressRevisionText() {
467 yield '(no legacy encoding), false in false out' => [ false, false, [], false ];
468 yield '(no legacy encoding), empty in empty out' => [ false, '', [], '' ];
469 yield '(no legacy encoding), empty in empty out' => [ false, 'A', [], 'A' ];
470 yield '(no legacy encoding), string in with gzip flag returns string' => [
471 // gzip string below generated with gzdeflate( 'AAAABBAAA' )
472 false, "sttttr\002\022\000", [ 'gzip' ], 'AAAABBAAA',
473 ];
474 yield '(no legacy encoding), string in with object flag returns false' => [
475 // gzip string below generated with serialize( 'JOJO' )
476 false, "s:4:\"JOJO\";", [ 'object' ], false,
477 ];
478 yield '(no legacy encoding), serialized object in with object flag returns string' => [
479 false,
480 // Using a TitleValue object as it has a getText method (which is needed)
481 serialize( new TitleValue( 0, 'HHJJDDFF' ) ),
482 [ 'object' ],
483 'HHJJDDFF',
484 ];
485 yield '(no legacy encoding), serialized object in with object & gzip flag returns string' => [
486 false,
487 // Using a TitleValue object as it has a getText method (which is needed)
488 gzdeflate( serialize( new TitleValue( 0, '8219JJJ840' ) ) ),
489 [ 'object', 'gzip' ],
490 '8219JJJ840',
491 ];
492 yield '(ISO-8859-1 encoding), string in string out' => [
493 'ISO-8859-1',
494 iconv( 'utf8', 'ISO-8859-1', "1®Àþ1" ),
495 [],
496 '1®Àþ1',
497 ];
498 yield '(ISO-8859-1 encoding), serialized object in with gzip flags returns string' => [
499 'ISO-8859-1',
500 gzdeflate( iconv( 'utf8', 'ISO-8859-1', "4®Àþ4" ) ),
501 [ 'gzip' ],
502 '4®Àþ4',
503 ];
504 yield '(ISO-8859-1 encoding), serialized object in with object flags returns string' => [
505 'ISO-8859-1',
506 serialize( new TitleValue( 0, iconv( 'utf8', 'ISO-8859-1', "3®Àþ3" ) ) ),
507 [ 'object' ],
508 '3®Àþ3',
509 ];
510 yield '(ISO-8859-1 encoding), serialized object in with object & gzip flags returns string' => [
511 'ISO-8859-1',
512 gzdeflate( serialize( new TitleValue( 0, iconv( 'utf8', 'ISO-8859-1', "2®Àþ2" ) ) ) ),
513 [ 'gzip', 'object' ],
514 '2®Àþ2',
515 ];
516 }
517
518 /**
519 * @dataProvider provideDecompressRevisionText
520 * @covers Revision::decompressRevisionText
521 *
522 * @param bool $legacyEncoding
523 * @param mixed $text
524 * @param array $flags
525 * @param mixed $expected
526 */
527 public function testDecompressRevisionText( $legacyEncoding, $text, $flags, $expected ) {
528 $this->setMwGlobals( 'wgLegacyEncoding', $legacyEncoding );
529 $this->setMwGlobals( 'wgLanguageCode', 'en' );
530 $this->assertSame(
531 $expected,
532 Revision::decompressRevisionText( $text, $flags )
533 );
534 }
535
536 /**
537 * @covers Revision::getRevisionText
538 */
539 public function testGetRevisionText_returnsFalseWhenNoTextField() {
540 $this->assertFalse( Revision::getRevisionText( new stdClass() ) );
541 }
542
543 public function provideTestGetRevisionText_returnsDecompressedTextFieldWhenNotExternal() {
544 yield 'Just text' => [
545 (object)[ 'old_text' => 'SomeText' ],
546 'old_',
547 'SomeText'
548 ];
549 // gzip string below generated with gzdeflate( 'AAAABBAAA' )
550 yield 'gzip text' => [
551 (object)[
552 'old_text' => "sttttr\002\022\000",
553 'old_flags' => 'gzip'
554 ],
555 'old_',
556 'AAAABBAAA'
557 ];
558 yield 'gzip text and different prefix' => [
559 (object)[
560 'jojo_text' => "sttttr\002\022\000",
561 'jojo_flags' => 'gzip'
562 ],
563 'jojo_',
564 'AAAABBAAA'
565 ];
566 }
567
568 /**
569 * @dataProvider provideTestGetRevisionText_returnsDecompressedTextFieldWhenNotExternal
570 * @covers Revision::getRevisionText
571 */
572 public function testGetRevisionText_returnsDecompressedTextFieldWhenNotExternal(
573 $row,
574 $prefix,
575 $expected
576 ) {
577 $this->assertSame( $expected, Revision::getRevisionText( $row, $prefix ) );
578 }
579
580 public function provideTestGetRevisionText_external_returnsFalseWhenNotEnoughUrlParts() {
581 yield 'Just some text' => [ 'someNonUrlText' ];
582 yield 'No second URL part' => [ 'someProtocol://' ];
583 }
584
585 /**
586 * @dataProvider provideTestGetRevisionText_external_returnsFalseWhenNotEnoughUrlParts
587 * @covers Revision::getRevisionText
588 */
589 public function testGetRevisionText_external_returnsFalseWhenNotEnoughUrlParts(
590 $text
591 ) {
592 $this->assertFalse(
593 Revision::getRevisionText(
594 (object)[
595 'old_text' => $text,
596 'old_flags' => 'external',
597 ]
598 )
599 );
600 }
601
602 /**
603 * @covers Revision::getRevisionText
604 */
605 public function testGetRevisionText_external_noOldId() {
606 $this->setService(
607 'ExternalStoreFactory',
608 new ExternalStoreFactory( [ 'ForTesting' ] )
609 );
610 $this->assertSame(
611 'AAAABBAAA',
612 Revision::getRevisionText(
613 (object)[
614 'old_text' => 'ForTesting://cluster1/12345',
615 'old_flags' => 'external,gzip',
616 ]
617 )
618 );
619 }
620
621 /**
622 * @covers Revision::getRevisionText
623 */
624 public function testGetRevisionText_external_oldId() {
625 $cache = new WANObjectCache( [ 'cache' => new HashBagOStuff() ] );
626 $this->setService( 'MainWANObjectCache', $cache );
627 $this->setService(
628 'ExternalStoreFactory',
629 new ExternalStoreFactory( [ 'ForTesting' ] )
630 );
631
632 $cacheKey = $cache->makeKey( 'revisiontext', 'textid', '7777' );
633
634 $this->assertSame(
635 'AAAABBAAA',
636 Revision::getRevisionText(
637 (object)[
638 'old_text' => 'ForTesting://cluster1/12345',
639 'old_flags' => 'external,gzip',
640 'old_id' => '7777',
641 ]
642 )
643 );
644 $this->assertSame( 'AAAABBAAA', $cache->get( $cacheKey ) );
645 }
646
647 /**
648 * @covers Revision::userJoinCond
649 */
650 public function testUserJoinCond() {
651 $this->hideDeprecated( 'Revision::userJoinCond' );
652 $this->assertEquals(
653 [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
654 Revision::userJoinCond()
655 );
656 }
657
658 /**
659 * @covers Revision::pageJoinCond
660 */
661 public function testPageJoinCond() {
662 $this->hideDeprecated( 'Revision::pageJoinCond' );
663 $this->assertEquals(
664 [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
665 Revision::pageJoinCond()
666 );
667 }
668
669 public function provideSelectFields() {
670 yield [
671 true,
672 [
673 'rev_id',
674 'rev_page',
675 'rev_text_id',
676 'rev_timestamp',
677 'rev_user_text',
678 'rev_user',
679 'rev_minor_edit',
680 'rev_deleted',
681 'rev_len',
682 'rev_parent_id',
683 'rev_sha1',
684 'rev_comment_text' => 'rev_comment',
685 'rev_comment_data' => 'NULL',
686 'rev_comment_cid' => 'NULL',
687 'rev_content_format',
688 'rev_content_model',
689 ]
690 ];
691 yield [
692 false,
693 [
694 'rev_id',
695 'rev_page',
696 'rev_text_id',
697 'rev_timestamp',
698 'rev_user_text',
699 'rev_user',
700 'rev_minor_edit',
701 'rev_deleted',
702 'rev_len',
703 'rev_parent_id',
704 'rev_sha1',
705 'rev_comment_text' => 'rev_comment',
706 'rev_comment_data' => 'NULL',
707 'rev_comment_cid' => 'NULL',
708 ]
709 ];
710 }
711
712 /**
713 * @dataProvider provideSelectFields
714 * @covers Revision::selectFields
715 * @todo a true unit test would mock CommentStore
716 */
717 public function testSelectFields( $contentHandlerUseDB, $expected ) {
718 $this->hideDeprecated( 'Revision::selectFields' );
719 $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
720 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
721 $this->assertEquals( $expected, Revision::selectFields() );
722 }
723
724 public function provideSelectArchiveFields() {
725 yield [
726 true,
727 [
728 'ar_id',
729 'ar_page_id',
730 'ar_rev_id',
731 'ar_text',
732 'ar_text_id',
733 'ar_timestamp',
734 'ar_user_text',
735 'ar_user',
736 'ar_minor_edit',
737 'ar_deleted',
738 'ar_len',
739 'ar_parent_id',
740 'ar_sha1',
741 'ar_comment_text' => 'ar_comment',
742 'ar_comment_data' => 'NULL',
743 'ar_comment_cid' => 'NULL',
744 'ar_content_format',
745 'ar_content_model',
746 ]
747 ];
748 yield [
749 false,
750 [
751 'ar_id',
752 'ar_page_id',
753 'ar_rev_id',
754 'ar_text',
755 'ar_text_id',
756 'ar_timestamp',
757 'ar_user_text',
758 'ar_user',
759 'ar_minor_edit',
760 'ar_deleted',
761 'ar_len',
762 'ar_parent_id',
763 'ar_sha1',
764 'ar_comment_text' => 'ar_comment',
765 'ar_comment_data' => 'NULL',
766 'ar_comment_cid' => 'NULL',
767 ]
768 ];
769 }
770
771 /**
772 * @dataProvider provideSelectArchiveFields
773 * @covers Revision::selectArchiveFields
774 * @todo a true unit test would mock CommentStore
775 */
776 public function testSelectArchiveFields( $contentHandlerUseDB, $expected ) {
777 $this->hideDeprecated( 'Revision::selectArchiveFields' );
778 $this->setMwGlobals( 'wgContentHandlerUseDB', $contentHandlerUseDB );
779 $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
780 $this->assertEquals( $expected, Revision::selectArchiveFields() );
781 }
782
783 /**
784 * @covers Revision::selectTextFields
785 */
786 public function testSelectTextFields() {
787 $this->hideDeprecated( 'Revision::selectTextFields' );
788 $this->assertEquals(
789 [
790 'old_text',
791 'old_flags',
792 ],
793 Revision::selectTextFields()
794 );
795 }
796
797 /**
798 * @covers Revision::selectPageFields
799 */
800 public function testSelectPageFields() {
801 $this->hideDeprecated( 'Revision::selectPageFields' );
802 $this->assertEquals(
803 [
804 'page_namespace',
805 'page_title',
806 'page_id',
807 'page_latest',
808 'page_is_redirect',
809 'page_len',
810 ],
811 Revision::selectPageFields()
812 );
813 }
814
815 /**
816 * @covers Revision::selectUserFields
817 */
818 public function testSelectUserFields() {
819 $this->hideDeprecated( 'Revision::selectUserFields' );
820 $this->assertEquals(
821 [
822 'user_name',
823 ],
824 Revision::selectUserFields()
825 );
826 }
827
828 public function provideGetArchiveQueryInfo() {
829 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD' => [
830 [
831 'wgContentHandlerUseDB' => false,
832 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
833 ],
834 [
835 'tables' => [ 'archive' ],
836 'fields' => [
837 'ar_id',
838 'ar_page_id',
839 'ar_rev_id',
840 'ar_text',
841 'ar_text_id',
842 'ar_timestamp',
843 'ar_user_text',
844 'ar_user',
845 'ar_minor_edit',
846 'ar_deleted',
847 'ar_len',
848 'ar_parent_id',
849 'ar_sha1',
850 'ar_comment_text' => 'ar_comment',
851 'ar_comment_data' => 'NULL',
852 'ar_comment_cid' => 'NULL',
853 ],
854 'joins' => [],
855 ]
856 ];
857 yield 'wgContentHandlerUseDB true, wgCommentTableSchemaMigrationStage OLD' => [
858 [
859 'wgContentHandlerUseDB' => true,
860 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
861 ],
862 [
863 'tables' => [ 'archive' ],
864 'fields' => [
865 'ar_id',
866 'ar_page_id',
867 'ar_rev_id',
868 'ar_text',
869 'ar_text_id',
870 'ar_timestamp',
871 'ar_user_text',
872 'ar_user',
873 'ar_minor_edit',
874 'ar_deleted',
875 'ar_len',
876 'ar_parent_id',
877 'ar_sha1',
878 'ar_comment_text' => 'ar_comment',
879 'ar_comment_data' => 'NULL',
880 'ar_comment_cid' => 'NULL',
881 'ar_content_format',
882 'ar_content_model',
883 ],
884 'joins' => [],
885 ]
886 ];
887 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_BOTH' => [
888 [
889 'wgContentHandlerUseDB' => false,
890 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH,
891 ],
892 [
893 'tables' => [
894 'archive',
895 'comment_ar_comment' => 'comment',
896 ],
897 'fields' => [
898 'ar_id',
899 'ar_page_id',
900 'ar_rev_id',
901 'ar_text',
902 'ar_text_id',
903 'ar_timestamp',
904 'ar_user_text',
905 'ar_user',
906 'ar_minor_edit',
907 'ar_deleted',
908 'ar_len',
909 'ar_parent_id',
910 'ar_sha1',
911 'ar_comment_text' => 'COALESCE( comment_ar_comment.comment_text, ar_comment )',
912 'ar_comment_data' => 'comment_ar_comment.comment_data',
913 'ar_comment_cid' => 'comment_ar_comment.comment_id',
914 ],
915 'joins' => [
916 'comment_ar_comment' => [
917 'LEFT JOIN',
918 'comment_ar_comment.comment_id = ar_comment_id',
919 ],
920 ],
921 ]
922 ];
923 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_NEW' => [
924 [
925 'wgContentHandlerUseDB' => false,
926 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_NEW,
927 ],
928 [
929 'tables' => [
930 'archive',
931 'comment_ar_comment' => 'comment',
932 ],
933 'fields' => [
934 'ar_id',
935 'ar_page_id',
936 'ar_rev_id',
937 'ar_text',
938 'ar_text_id',
939 'ar_timestamp',
940 'ar_user_text',
941 'ar_user',
942 'ar_minor_edit',
943 'ar_deleted',
944 'ar_len',
945 'ar_parent_id',
946 'ar_sha1',
947 'ar_comment_text' => 'COALESCE( comment_ar_comment.comment_text, ar_comment )',
948 'ar_comment_data' => 'comment_ar_comment.comment_data',
949 'ar_comment_cid' => 'comment_ar_comment.comment_id',
950 ],
951 'joins' => [
952 'comment_ar_comment' => [
953 'LEFT JOIN',
954 'comment_ar_comment.comment_id = ar_comment_id',
955 ],
956 ],
957 ]
958 ];
959 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage NEW' => [
960 [
961 'wgContentHandlerUseDB' => false,
962 'wgCommentTableSchemaMigrationStage' => MIGRATION_NEW,
963 ],
964 [
965 'tables' => [
966 'archive',
967 'comment_ar_comment' => 'comment',
968 ],
969 'fields' => [
970 'ar_id',
971 'ar_page_id',
972 'ar_rev_id',
973 'ar_text',
974 'ar_text_id',
975 'ar_timestamp',
976 'ar_user_text',
977 'ar_user',
978 'ar_minor_edit',
979 'ar_deleted',
980 'ar_len',
981 'ar_parent_id',
982 'ar_sha1',
983 'ar_comment_text' => 'comment_ar_comment.comment_text',
984 'ar_comment_data' => 'comment_ar_comment.comment_data',
985 'ar_comment_cid' => 'comment_ar_comment.comment_id',
986 ],
987 'joins' => [
988 'comment_ar_comment' => [
989 'JOIN',
990 'comment_ar_comment.comment_id = ar_comment_id',
991 ],
992 ],
993 ]
994 ];
995 }
996
997 /**
998 * @covers Revision::getArchiveQueryInfo
999 * @dataProvider provideGetArchiveQueryInfo
1000 */
1001 public function testGetArchiveQueryInfo( $globals, $expected ) {
1002 $this->setMwGlobals( $globals );
1003 $this->assertEquals(
1004 $expected,
1005 Revision::getArchiveQueryInfo()
1006 );
1007 }
1008
1009 public function provideGetQueryInfo() {
1010 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts none' => [
1011 [
1012 'wgContentHandlerUseDB' => false,
1013 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1014 ],
1015 [],
1016 [
1017 'tables' => [ 'revision' ],
1018 'fields' => [
1019 'rev_id',
1020 'rev_page',
1021 'rev_text_id',
1022 'rev_timestamp',
1023 'rev_user_text',
1024 'rev_user',
1025 'rev_minor_edit',
1026 'rev_deleted',
1027 'rev_len',
1028 'rev_parent_id',
1029 'rev_sha1',
1030 'rev_comment_text' => 'rev_comment',
1031 'rev_comment_data' => 'NULL',
1032 'rev_comment_cid' => 'NULL',
1033 ],
1034 'joins' => [],
1035 ],
1036 ];
1037 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts page' => [
1038 [
1039 'wgContentHandlerUseDB' => false,
1040 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1041 ],
1042 [ 'page' ],
1043 [
1044 'tables' => [ 'revision', 'page' ],
1045 'fields' => [
1046 'rev_id',
1047 'rev_page',
1048 'rev_text_id',
1049 'rev_timestamp',
1050 'rev_user_text',
1051 'rev_user',
1052 'rev_minor_edit',
1053 'rev_deleted',
1054 'rev_len',
1055 'rev_parent_id',
1056 'rev_sha1',
1057 'rev_comment_text' => 'rev_comment',
1058 'rev_comment_data' => 'NULL',
1059 'rev_comment_cid' => 'NULL',
1060 'page_namespace',
1061 'page_title',
1062 'page_id',
1063 'page_latest',
1064 'page_is_redirect',
1065 'page_len',
1066 ],
1067 'joins' => [
1068 'page' => [
1069 'INNER JOIN',
1070 [ 'page_id = rev_page' ],
1071 ],
1072 ],
1073 ],
1074 ];
1075 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts user' => [
1076 [
1077 'wgContentHandlerUseDB' => false,
1078 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1079 ],
1080 [ 'user' ],
1081 [
1082 'tables' => [ 'revision', 'user' ],
1083 'fields' => [
1084 'rev_id',
1085 'rev_page',
1086 'rev_text_id',
1087 'rev_timestamp',
1088 'rev_user_text',
1089 'rev_user',
1090 'rev_minor_edit',
1091 'rev_deleted',
1092 'rev_len',
1093 'rev_parent_id',
1094 'rev_sha1',
1095 'rev_comment_text' => 'rev_comment',
1096 'rev_comment_data' => 'NULL',
1097 'rev_comment_cid' => 'NULL',
1098 'user_name',
1099 ],
1100 'joins' => [
1101 'user' => [
1102 'LEFT JOIN',
1103 [
1104 'rev_user != 0',
1105 'user_id = rev_user',
1106 ],
1107 ],
1108 ],
1109 ],
1110 ];
1111 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts text' => [
1112 [
1113 'wgContentHandlerUseDB' => false,
1114 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1115 ],
1116 [ 'text' ],
1117 [
1118 'tables' => [ 'revision', 'text' ],
1119 'fields' => [
1120 'rev_id',
1121 'rev_page',
1122 'rev_text_id',
1123 'rev_timestamp',
1124 'rev_user_text',
1125 'rev_user',
1126 'rev_minor_edit',
1127 'rev_deleted',
1128 'rev_len',
1129 'rev_parent_id',
1130 'rev_sha1',
1131 'rev_comment_text' => 'rev_comment',
1132 'rev_comment_data' => 'NULL',
1133 'rev_comment_cid' => 'NULL',
1134 'old_text',
1135 'old_flags',
1136 ],
1137 'joins' => [
1138 'text' => [
1139 'INNER JOIN',
1140 [ 'rev_text_id=old_id' ],
1141 ],
1142 ],
1143 ],
1144 ];
1145 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage OLD, opts 3' => [
1146 [
1147 'wgContentHandlerUseDB' => false,
1148 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1149 ],
1150 [ 'text', 'page', 'user' ],
1151 [
1152 'tables' => [ 'revision', 'page', 'user', 'text' ],
1153 'fields' => [
1154 'rev_id',
1155 'rev_page',
1156 'rev_text_id',
1157 'rev_timestamp',
1158 'rev_user_text',
1159 'rev_user',
1160 'rev_minor_edit',
1161 'rev_deleted',
1162 'rev_len',
1163 'rev_parent_id',
1164 'rev_sha1',
1165 'rev_comment_text' => 'rev_comment',
1166 'rev_comment_data' => 'NULL',
1167 'rev_comment_cid' => 'NULL',
1168 'page_namespace',
1169 'page_title',
1170 'page_id',
1171 'page_latest',
1172 'page_is_redirect',
1173 'page_len',
1174 'user_name',
1175 'old_text',
1176 'old_flags',
1177 ],
1178 'joins' => [
1179 'page' => [
1180 'INNER JOIN',
1181 [ 'page_id = rev_page' ],
1182 ],
1183 'user' => [
1184 'LEFT JOIN',
1185 [
1186 'rev_user != 0',
1187 'user_id = rev_user',
1188 ],
1189 ],
1190 'text' => [
1191 'INNER JOIN',
1192 [ 'rev_text_id=old_id' ],
1193 ],
1194 ],
1195 ],
1196 ];
1197 yield 'wgContentHandlerUseDB true, wgCommentTableSchemaMigrationStage OLD, opts none' => [
1198 [
1199 'wgContentHandlerUseDB' => true,
1200 'wgCommentTableSchemaMigrationStage' => MIGRATION_OLD,
1201 ],
1202 [],
1203 [
1204 'tables' => [ 'revision' ],
1205 'fields' => [
1206 'rev_id',
1207 'rev_page',
1208 'rev_text_id',
1209 'rev_timestamp',
1210 'rev_user_text',
1211 'rev_user',
1212 'rev_minor_edit',
1213 'rev_deleted',
1214 'rev_len',
1215 'rev_parent_id',
1216 'rev_sha1',
1217 'rev_comment_text' => 'rev_comment',
1218 'rev_comment_data' => 'NULL',
1219 'rev_comment_cid' => 'NULL',
1220 'rev_content_format',
1221 'rev_content_model',
1222 ],
1223 'joins' => [],
1224 ],
1225 ];
1226 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_BOTH, opts none' => [
1227 [
1228 'wgContentHandlerUseDB' => false,
1229 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_BOTH,
1230 ],
1231 [],
1232 [
1233 'tables' => [
1234 'revision',
1235 'temp_rev_comment' => 'revision_comment_temp',
1236 'comment_rev_comment' => 'comment',
1237 ],
1238 'fields' => [
1239 'rev_id',
1240 'rev_page',
1241 'rev_text_id',
1242 'rev_timestamp',
1243 'rev_user_text',
1244 'rev_user',
1245 'rev_minor_edit',
1246 'rev_deleted',
1247 'rev_len',
1248 'rev_parent_id',
1249 'rev_sha1',
1250 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
1251 'rev_comment_data' => 'comment_rev_comment.comment_data',
1252 'rev_comment_cid' => 'comment_rev_comment.comment_id',
1253 ],
1254 'joins' => [
1255 'temp_rev_comment' => [
1256 'LEFT JOIN',
1257 'temp_rev_comment.revcomment_rev = rev_id',
1258 ],
1259 'comment_rev_comment' => [
1260 'LEFT JOIN',
1261 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
1262 ],
1263 ],
1264 ],
1265 ];
1266 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage WRITE_NEW, opts none' => [
1267 [
1268 'wgContentHandlerUseDB' => false,
1269 'wgCommentTableSchemaMigrationStage' => MIGRATION_WRITE_NEW,
1270 ],
1271 [],
1272 [
1273 'tables' => [
1274 'revision',
1275 'temp_rev_comment' => 'revision_comment_temp',
1276 'comment_rev_comment' => 'comment',
1277 ],
1278 'fields' => [
1279 'rev_id',
1280 'rev_page',
1281 'rev_text_id',
1282 'rev_timestamp',
1283 'rev_user_text',
1284 'rev_user',
1285 'rev_minor_edit',
1286 'rev_deleted',
1287 'rev_len',
1288 'rev_parent_id',
1289 'rev_sha1',
1290 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
1291 'rev_comment_data' => 'comment_rev_comment.comment_data',
1292 'rev_comment_cid' => 'comment_rev_comment.comment_id',
1293 ],
1294 'joins' => [
1295 'temp_rev_comment' => [
1296 'LEFT JOIN',
1297 'temp_rev_comment.revcomment_rev = rev_id',
1298 ],
1299 'comment_rev_comment' => [
1300 'LEFT JOIN',
1301 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
1302 ],
1303 ],
1304 ],
1305 ];
1306 yield 'wgContentHandlerUseDB false, wgCommentTableSchemaMigrationStage NEW, opts none' => [
1307 [
1308 'wgContentHandlerUseDB' => false,
1309 'wgCommentTableSchemaMigrationStage' => MIGRATION_NEW,
1310 ],
1311 [],
1312 [
1313 'tables' => [
1314 'revision',
1315 'temp_rev_comment' => 'revision_comment_temp',
1316 'comment_rev_comment' => 'comment',
1317 ],
1318 'fields' => [
1319 'rev_id',
1320 'rev_page',
1321 'rev_text_id',
1322 'rev_timestamp',
1323 'rev_user_text',
1324 'rev_user',
1325 'rev_minor_edit',
1326 'rev_deleted',
1327 'rev_len',
1328 'rev_parent_id',
1329 'rev_sha1',
1330 'rev_comment_text' => 'comment_rev_comment.comment_text',
1331 'rev_comment_data' => 'comment_rev_comment.comment_data',
1332 'rev_comment_cid' => 'comment_rev_comment.comment_id',
1333 ],
1334 'joins' => [
1335 'temp_rev_comment' => [
1336 'JOIN',
1337 'temp_rev_comment.revcomment_rev = rev_id',
1338 ],
1339 'comment_rev_comment' => [
1340 'JOIN',
1341 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id',
1342 ],
1343 ],
1344 ],
1345 ];
1346 }
1347
1348 /**
1349 * @covers Revision::getQueryInfo
1350 * @dataProvider provideGetQueryInfo
1351 */
1352 public function testGetQueryInfo( $globals, $options, $expected ) {
1353 $this->setMwGlobals( $globals );
1354 $this->assertEquals(
1355 $expected,
1356 Revision::getQueryInfo( $options )
1357 );
1358 }
1359
1360 }