3 use MediaWiki\MediaWikiServices
;
4 use Wikimedia\Rdbms\IMaintainableDatabase
;
5 use Wikimedia\ScopedCallback
;
6 use Wikimedia\TestingAccessWrapper
;
10 * @covers CommentStore
11 * @covers CommentStoreComment
13 class CommentStoreTest
extends MediaWikiLangTestCase
{
15 protected $tablesUsed = [
17 'revision_comment_temp',
22 protected function getSchemaOverrides( IMaintainableDatabase
$db ) {
25 __DIR__
. '/CommentStoreTest.sql',
28 'create' => [ 'commentstore1', 'commentstore2', 'commentstore2_temp' ],
34 * Create a store for a particular stage
36 * @return CommentStore
38 protected function makeStore( $stage ) {
39 $store = new CommentStore( MediaWikiServices
::getInstance()->getContentLanguage(), $stage );
41 TestingAccessWrapper
::newFromObject( $store )->tempTables +
= [ 'cs2_comment' => [
42 'table' => 'commentstore2_temp',
44 'field' => 'cs2t_comment_id',
46 'stage' => MIGRATION_OLD
,
47 'deprecatedIn' => null,
54 * Create a store for a particular stage and key (for testing deprecated behaviour)
57 * @return CommentStore
59 protected function makeStoreWithKey( $stage, $key ) {
60 $this->hideDeprecated( 'CommentStore::newKey' );
61 $store = CommentStore
::newKey( $key );
62 TestingAccessWrapper
::newFromObject( $store )->stage
= $stage;
64 TestingAccessWrapper
::newFromObject( $store )->tempTables +
= [ 'cs2_comment' => [
65 'table' => 'commentstore2_temp',
67 'field' => 'cs2t_comment_id',
69 'stage' => MIGRATION_OLD
,
70 'deprecatedIn' => null,
77 * @dataProvider provideConstructor
79 * @param string|null $exceptionMsg
81 public function testConstructor( $stage, $exceptionMsg ) {
83 $m = new CommentStore( Language
::factory( 'qqx' ), $stage );
84 if ( $exceptionMsg !== null ) {
85 $this->fail( 'Expected exception not thrown' );
87 $this->assertInstanceOf( CommentStore
::class, $m );
88 } catch ( InvalidArgumentException
$ex ) {
89 $this->assertSame( $exceptionMsg, $ex->getMessage() );
93 public static function provideConstructor() {
95 [ 0, '$stage must include a write mode' ],
96 [ SCHEMA_COMPAT_READ_OLD
, '$stage must include a write mode' ],
97 [ SCHEMA_COMPAT_READ_NEW
, '$stage must include a write mode' ],
98 [ SCHEMA_COMPAT_READ_BOTH
, '$stage must include a write mode' ],
100 [ SCHEMA_COMPAT_WRITE_OLD
, '$stage must include a read mode' ],
101 [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_OLD
, null ],
102 [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_NEW
, null ],
103 [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_BOTH
, null ],
105 [ SCHEMA_COMPAT_WRITE_NEW
, '$stage must include a read mode' ],
106 [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_OLD
, null ],
107 [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_NEW
, null ],
108 [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_BOTH
, null ],
110 [ SCHEMA_COMPAT_WRITE_BOTH
, '$stage must include a read mode' ],
111 [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, null ],
112 [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, null ],
113 [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_BOTH
, null ],
118 * @dataProvider provideGetFields
121 * @param array $expect
123 public function testGetFields_withKeyConstruction( $stage, $key, $expect ) {
124 $store = $this->makeStoreWithKey( $stage, $key );
125 $result = $store->getFields();
126 $this->assertEquals( $expect, $result );
130 * @dataProvider provideGetFields
133 * @param array $expect
135 public function testGetFields( $stage, $key, $expect ) {
136 $store = $this->makeStore( $stage );
137 $result = $store->getFields( $key );
138 $this->assertEquals( $expect, $result );
141 public static function provideGetFields() {
143 'Simple table, old' => [
144 MIGRATION_OLD
, 'ipb_reason',
145 [ 'ipb_reason_text' => 'ipb_reason', 'ipb_reason_data' => 'NULL', 'ipb_reason_cid' => 'NULL' ],
147 'Simple table, write-both' => [
148 MIGRATION_WRITE_BOTH
, 'ipb_reason',
149 [ 'ipb_reason_old' => 'ipb_reason', 'ipb_reason_id' => 'ipb_reason_id' ],
151 'Simple table, write-new' => [
152 MIGRATION_WRITE_NEW
, 'ipb_reason',
153 [ 'ipb_reason_old' => 'ipb_reason', 'ipb_reason_id' => 'ipb_reason_id' ],
155 'Simple table, new' => [
156 MIGRATION_NEW
, 'ipb_reason',
157 [ 'ipb_reason_id' => 'ipb_reason_id' ],
159 'Simple table, write-both/read-old' => [
160 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, 'ipb_reason',
161 [ 'ipb_reason_text' => 'ipb_reason', 'ipb_reason_data' => 'NULL', 'ipb_reason_cid' => 'NULL' ],
163 'Simple table, write-both/read-new' => [
164 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, 'ipb_reason',
165 [ 'ipb_reason_id' => 'ipb_reason_id' ],
169 MIGRATION_OLD
, 'rev_comment',
171 'rev_comment_text' => 'rev_comment',
172 'rev_comment_data' => 'NULL',
173 'rev_comment_cid' => 'NULL',
176 'Revision, write-both' => [
177 MIGRATION_WRITE_BOTH
, 'rev_comment',
178 [ 'rev_comment_old' => 'rev_comment', 'rev_comment_pk' => 'rev_id' ],
180 'Revision, write-new' => [
181 MIGRATION_WRITE_NEW
, 'rev_comment',
182 [ 'rev_comment_old' => 'rev_comment', 'rev_comment_pk' => 'rev_id' ],
185 MIGRATION_NEW
, 'rev_comment',
186 [ 'rev_comment_pk' => 'rev_id' ],
188 'Revision, write-both/read-old' => [
189 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, 'rev_comment',
191 'rev_comment_text' => 'rev_comment',
192 'rev_comment_data' => 'NULL',
193 'rev_comment_cid' => 'NULL',
196 'Revision, write-both/read-new' => [
197 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, 'rev_comment',
198 [ 'rev_comment_pk' => 'rev_id' ],
202 MIGRATION_OLD
, 'img_description',
204 'img_description_text' => 'img_description',
205 'img_description_data' => 'NULL',
206 'img_description_cid' => 'NULL',
209 'Image, write-both' => [
210 MIGRATION_WRITE_BOTH
, 'img_description',
212 'img_description_old' => 'img_description',
213 'img_description_id' => 'img_description_id'
216 'Image, write-new' => [
217 MIGRATION_WRITE_NEW
, 'img_description',
219 'img_description_old' => 'img_description',
220 'img_description_id' => 'img_description_id'
224 MIGRATION_NEW
, 'img_description',
226 'img_description_id' => 'img_description_id'
229 'Image, write-both/read-old' => [
230 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, 'img_description',
232 'img_description_text' => 'img_description',
233 'img_description_data' => 'NULL',
234 'img_description_cid' => 'NULL',
237 'Image, write-both/read-new' => [
238 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, 'img_description',
240 'img_description_id' => 'img_description_id'
247 * @dataProvider provideGetJoin
250 * @param array $expect
252 public function testGetJoin_withKeyConstruction( $stage, $key, $expect ) {
253 $store = $this->makeStoreWithKey( $stage, $key );
254 $result = $store->getJoin();
255 $this->assertEquals( $expect, $result );
259 * @dataProvider provideGetJoin
262 * @param array $expect
264 public function testGetJoin( $stage, $key, $expect ) {
265 $store = $this->makeStore( $stage );
266 $result = $store->getJoin( $key );
267 $this->assertEquals( $expect, $result );
270 public static function provideGetJoin() {
272 'Simple table, old' => [
273 MIGRATION_OLD
, 'ipb_reason', [
276 'ipb_reason_text' => 'ipb_reason',
277 'ipb_reason_data' => 'NULL',
278 'ipb_reason_cid' => 'NULL',
283 'Simple table, write-both' => [
284 MIGRATION_WRITE_BOTH
, 'ipb_reason', [
285 'tables' => [ 'comment_ipb_reason' => 'comment' ],
287 'ipb_reason_text' => 'COALESCE( comment_ipb_reason.comment_text, ipb_reason )',
288 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
289 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
292 'comment_ipb_reason' => [ 'LEFT JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
296 'Simple table, write-new' => [
297 MIGRATION_WRITE_NEW
, 'ipb_reason', [
298 'tables' => [ 'comment_ipb_reason' => 'comment' ],
300 'ipb_reason_text' => 'COALESCE( comment_ipb_reason.comment_text, ipb_reason )',
301 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
302 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
305 'comment_ipb_reason' => [ 'LEFT JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
309 'Simple table, new' => [
310 MIGRATION_NEW
, 'ipb_reason', [
311 'tables' => [ 'comment_ipb_reason' => 'comment' ],
313 'ipb_reason_text' => 'comment_ipb_reason.comment_text',
314 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
315 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
318 'comment_ipb_reason' => [ 'JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
322 'Simple table, write-both/read-old' => [
323 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, 'ipb_reason', [
326 'ipb_reason_text' => 'ipb_reason',
327 'ipb_reason_data' => 'NULL',
328 'ipb_reason_cid' => 'NULL',
333 'Simple table, write-both/read-new' => [
334 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, 'ipb_reason', [
335 'tables' => [ 'comment_ipb_reason' => 'comment' ],
337 'ipb_reason_text' => 'comment_ipb_reason.comment_text',
338 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
339 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
342 'comment_ipb_reason' => [ 'JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
348 MIGRATION_OLD
, 'rev_comment', [
351 'rev_comment_text' => 'rev_comment',
352 'rev_comment_data' => 'NULL',
353 'rev_comment_cid' => 'NULL',
358 'Revision, write-both' => [
359 MIGRATION_WRITE_BOTH
, 'rev_comment', [
361 'temp_rev_comment' => 'revision_comment_temp',
362 'comment_rev_comment' => 'comment',
365 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
366 'rev_comment_data' => 'comment_rev_comment.comment_data',
367 'rev_comment_cid' => 'comment_rev_comment.comment_id',
370 'temp_rev_comment' => [ 'LEFT JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
371 'comment_rev_comment' => [ 'LEFT JOIN',
372 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
376 'Revision, write-new' => [
377 MIGRATION_WRITE_NEW
, 'rev_comment', [
379 'temp_rev_comment' => 'revision_comment_temp',
380 'comment_rev_comment' => 'comment',
383 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
384 'rev_comment_data' => 'comment_rev_comment.comment_data',
385 'rev_comment_cid' => 'comment_rev_comment.comment_id',
388 'temp_rev_comment' => [ 'LEFT JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
389 'comment_rev_comment' => [ 'LEFT JOIN',
390 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
395 MIGRATION_NEW
, 'rev_comment', [
397 'temp_rev_comment' => 'revision_comment_temp',
398 'comment_rev_comment' => 'comment',
401 'rev_comment_text' => 'comment_rev_comment.comment_text',
402 'rev_comment_data' => 'comment_rev_comment.comment_data',
403 'rev_comment_cid' => 'comment_rev_comment.comment_id',
406 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
407 'comment_rev_comment' => [ 'JOIN',
408 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
412 'Revision, write-both/read-old' => [
413 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, 'rev_comment', [
416 'rev_comment_text' => 'rev_comment',
417 'rev_comment_data' => 'NULL',
418 'rev_comment_cid' => 'NULL',
423 'Revision, write-both/read-new' => [
424 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, 'rev_comment', [
426 'temp_rev_comment' => 'revision_comment_temp',
427 'comment_rev_comment' => 'comment',
430 'rev_comment_text' => 'comment_rev_comment.comment_text',
431 'rev_comment_data' => 'comment_rev_comment.comment_data',
432 'rev_comment_cid' => 'comment_rev_comment.comment_id',
435 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
436 'comment_rev_comment' => [ 'JOIN',
437 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
443 MIGRATION_OLD
, 'img_description', [
446 'img_description_text' => 'img_description',
447 'img_description_data' => 'NULL',
448 'img_description_cid' => 'NULL',
453 'Image, write-both' => [
454 MIGRATION_WRITE_BOTH
, 'img_description', [
456 'comment_img_description' => 'comment',
459 'img_description_text' => 'COALESCE( comment_img_description.comment_text, img_description )',
460 'img_description_data' => 'comment_img_description.comment_data',
461 'img_description_cid' => 'comment_img_description.comment_id',
464 'comment_img_description' => [ 'LEFT JOIN',
465 'comment_img_description.comment_id = img_description_id',
470 'Image, write-new' => [
471 MIGRATION_WRITE_NEW
, 'img_description', [
473 'comment_img_description' => 'comment',
476 'img_description_text' => 'COALESCE( comment_img_description.comment_text, img_description )',
477 'img_description_data' => 'comment_img_description.comment_data',
478 'img_description_cid' => 'comment_img_description.comment_id',
481 'comment_img_description' => [ 'LEFT JOIN',
482 'comment_img_description.comment_id = img_description_id',
488 MIGRATION_NEW
, 'img_description', [
490 'comment_img_description' => 'comment',
493 'img_description_text' => 'comment_img_description.comment_text',
494 'img_description_data' => 'comment_img_description.comment_data',
495 'img_description_cid' => 'comment_img_description.comment_id',
498 'comment_img_description' => [ 'JOIN',
499 'comment_img_description.comment_id = img_description_id',
504 'Image, write-both/read-old' => [
505 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
, 'img_description', [
508 'img_description_text' => 'img_description',
509 'img_description_data' => 'NULL',
510 'img_description_cid' => 'NULL',
515 'Image, write-both/read-new' => [
516 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, 'img_description', [
518 'comment_img_description' => 'comment',
521 'img_description_text' => 'comment_img_description.comment_text',
522 'img_description_data' => 'comment_img_description.comment_data',
523 'img_description_cid' => 'comment_img_description.comment_id',
526 'comment_img_description' => [ 'JOIN',
527 'comment_img_description.comment_id = img_description_id',
535 private function assertComment( $expect, $actual, $from ) {
536 $this->assertSame( $expect['text'], $actual->text
, "text $from" );
537 $this->assertInstanceOf( get_class( $expect['message'] ), $actual->message
,
538 "message class $from" );
539 $this->assertSame( $expect['message']->getKeysToTry(), $actual->message
->getKeysToTry(),
540 "message keys $from" );
541 $this->assertEquals( $expect['message']->text(), $actual->message
->text(),
542 "message rendering $from" );
543 $this->assertEquals( $expect['text'], $actual->message
->text(),
544 "message rendering and text $from" );
545 $this->assertEquals( $expect['data'], $actual->data
, "data $from" );
549 * @dataProvider provideInsertRoundTrip
550 * @param string $table
553 * @param string|Message $comment
554 * @param array|null $data
555 * @param array $expect
557 public function testInsertRoundTrip( $table, $key, $pk, $comment, $data, $expect ) {
561 'text' => $expect['text'],
562 'message' => new RawMessage( '$1', [ Message
::plaintextParam( $expect['text'] ) ] ),
567 MIGRATION_OLD
=> [ MIGRATION_OLD
, MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
],
568 MIGRATION_WRITE_BOTH
=> [ MIGRATION_OLD
, MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
,
570 MIGRATION_WRITE_NEW
=> [ MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
, MIGRATION_NEW
],
571 MIGRATION_NEW
=> [ MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
, MIGRATION_NEW
],
573 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
=> [
574 MIGRATION_OLD
, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
575 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, MIGRATION_NEW
577 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
=> [
578 MIGRATION_OLD
, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
579 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, MIGRATION_NEW
583 foreach ( $stages as $writeStage => $possibleReadStages ) {
584 $wstore = $this->makeStore( $writeStage );
585 $usesTemp = $key === 'cs2_comment';
588 list( $fields, $callback ) = $wstore->insertWithTempTable(
589 $this->db
, $key, $comment, $data
592 $fields = $wstore->insert( $this->db
, $key, $comment, $data );
595 if ( $writeStage & SCHEMA_COMPAT_WRITE_OLD
) {
596 $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
598 $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
600 if ( ( $writeStage & SCHEMA_COMPAT_WRITE_NEW
) && !$usesTemp ) {
601 $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
603 $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
606 $this->db
->insert( $table, [ $pk => ++
$id ] +
$fields, __METHOD__
);
611 foreach ( $possibleReadStages as $readStage ) {
612 $rstore = $this->makeStore( $readStage );
614 $fieldRow = $this->db
->selectRow(
616 $rstore->getFields( $key ),
621 $queryInfo = $rstore->getJoin( $key );
622 $joinRow = $this->db
->selectRow(
623 [ $table ] +
$queryInfo['tables'],
624 $queryInfo['fields'],
631 $expectForCombination = (
632 ( $writeStage & SCHEMA_COMPAT_WRITE_BOTH
) === SCHEMA_COMPAT_WRITE_OLD ||
633 ( $readStage & SCHEMA_COMPAT_READ_BOTH
) === SCHEMA_COMPAT_READ_OLD
634 ) ?
$expectOld : $expect;
635 $this->assertComment(
636 $expectForCombination,
637 $rstore->getCommentLegacy( $this->db
, $key, $fieldRow ),
638 "w=$writeStage, r=$readStage, from getFields()"
640 $this->assertComment(
641 $expectForCombination,
642 $rstore->getComment( $key, $joinRow ),
643 "w=$writeStage, r=$readStage, from getJoin()"
650 * @dataProvider provideInsertRoundTrip
651 * @param string $table
654 * @param string|Message $comment
655 * @param array|null $data
656 * @param array $expect
658 public function testInsertRoundTrip_withKeyConstruction(
659 $table, $key, $pk, $comment, $data, $expect
664 'text' => $expect['text'],
665 'message' => new RawMessage( '$1', [ Message
::plaintextParam( $expect['text'] ) ] ),
670 MIGRATION_OLD
=> [ MIGRATION_OLD
, MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
],
671 MIGRATION_WRITE_BOTH
=> [ MIGRATION_OLD
, MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
,
673 MIGRATION_WRITE_NEW
=> [ MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
, MIGRATION_NEW
],
674 MIGRATION_NEW
=> [ MIGRATION_WRITE_BOTH
, MIGRATION_WRITE_NEW
, MIGRATION_NEW
],
676 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
=> [
677 MIGRATION_OLD
, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
678 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, MIGRATION_NEW
680 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
=> [
681 MIGRATION_OLD
, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
,
682 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
, MIGRATION_NEW
686 foreach ( $stages as $writeStage => $possibleReadStages ) {
687 $wstore = $this->makeStoreWithKey( $writeStage, $key );
688 $usesTemp = $key === 'cs2_comment';
691 list( $fields, $callback ) = $wstore->insertWithTempTable(
692 $this->db
, $comment, $data
695 $fields = $wstore->insert( $this->db
, $comment, $data );
698 if ( $writeStage & SCHEMA_COMPAT_WRITE_OLD
) {
699 $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
701 $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
703 if ( ( $writeStage & SCHEMA_COMPAT_WRITE_NEW
) && !$usesTemp ) {
704 $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
706 $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
709 $this->db
->insert( $table, [ $pk => ++
$id ] +
$fields, __METHOD__
);
714 foreach ( $possibleReadStages as $readStage ) {
715 $rstore = $this->makeStoreWithKey( $readStage, $key );
717 $fieldRow = $this->db
->selectRow(
719 $rstore->getFields(),
724 $queryInfo = $rstore->getJoin();
725 $joinRow = $this->db
->selectRow(
726 [ $table ] +
$queryInfo['tables'],
727 $queryInfo['fields'],
734 $expectForCombination = (
735 ( $writeStage & SCHEMA_COMPAT_WRITE_BOTH
) === SCHEMA_COMPAT_WRITE_OLD ||
736 ( $readStage & SCHEMA_COMPAT_READ_BOTH
) === SCHEMA_COMPAT_READ_OLD
737 ) ?
$expectOld : $expect;
738 $this->assertComment(
739 $expectForCombination,
740 $rstore->getCommentLegacy( $this->db
, $fieldRow ),
741 "w=$writeStage, r=$readStage, from getFields()"
743 $this->assertComment(
744 $expectForCombination,
745 $rstore->getComment( $joinRow ),
746 "w=$writeStage, r=$readStage, from getJoin()"
752 public static function provideInsertRoundTrip() {
753 $db = wfGetDB( DB_REPLICA
); // for timestamps
755 $msgComment = new Message( 'parentheses', [ 'message comment' ] );
756 $textCommentMsg = new RawMessage( '$1', [ Message
::plaintextParam( '{{text}} comment' ) ] );
757 $nestedMsgComment = new Message( [ 'parentheses', 'rawmessage' ], [ new Message( 'mainpage' ) ] );
758 $comStoreComment = new CommentStoreComment(
759 null, 'comment store comment', null, [ 'foo' => 'bar' ]
763 'Simple table, text comment' => [
764 'commentstore1', 'cs1_comment', 'cs1_id', '{{text}} comment', null, [
765 'text' => '{{text}} comment',
766 'message' => $textCommentMsg,
770 'Simple table, text comment with data' => [
771 'commentstore1', 'cs1_comment', 'cs1_id', '{{text}} comment', [ 'message' => 42 ], [
772 'text' => '{{text}} comment',
773 'message' => $textCommentMsg,
774 'data' => [ 'message' => 42 ],
777 'Simple table, message comment' => [
778 'commentstore1', 'cs1_comment', 'cs1_id', $msgComment, null, [
779 'text' => '(message comment)',
780 'message' => $msgComment,
784 'Simple table, message comment with data' => [
785 'commentstore1', 'cs1_comment', 'cs1_id', $msgComment, [ 'message' => 42 ], [
786 'text' => '(message comment)',
787 'message' => $msgComment,
788 'data' => [ 'message' => 42 ],
791 'Simple table, nested message comment' => [
792 'commentstore1', 'cs1_comment', 'cs1_id', $nestedMsgComment, null, [
793 'text' => '(Main Page)',
794 'message' => $nestedMsgComment,
798 'Simple table, CommentStoreComment' => [
799 'commentstore1', 'cs1_comment', 'cs1_id', clone $comStoreComment, [ 'baz' => 'baz' ], [
800 'text' => 'comment store comment',
801 'message' => $comStoreComment->message
,
802 'data' => [ 'foo' => 'bar' ],
806 'Revision, text comment' => [
807 'commentstore2', 'cs2_comment', 'cs2_id', '{{text}} comment', null, [
808 'text' => '{{text}} comment',
809 'message' => $textCommentMsg,
813 'Revision, text comment with data' => [
814 'commentstore2', 'cs2_comment', 'cs2_id', '{{text}} comment', [ 'message' => 42 ], [
815 'text' => '{{text}} comment',
816 'message' => $textCommentMsg,
817 'data' => [ 'message' => 42 ],
820 'Revision, message comment' => [
821 'commentstore2', 'cs2_comment', 'cs2_id', $msgComment, null, [
822 'text' => '(message comment)',
823 'message' => $msgComment,
827 'Revision, message comment with data' => [
828 'commentstore2', 'cs2_comment', 'cs2_id', $msgComment, [ 'message' => 42 ], [
829 'text' => '(message comment)',
830 'message' => $msgComment,
831 'data' => [ 'message' => 42 ],
834 'Revision, nested message comment' => [
835 'commentstore2', 'cs2_comment', 'cs2_id', $nestedMsgComment, null, [
836 'text' => '(Main Page)',
837 'message' => $nestedMsgComment,
841 'Revision, CommentStoreComment' => [
842 'commentstore2', 'cs2_comment', 'cs2_id', clone $comStoreComment, [ 'baz' => 'baz' ], [
843 'text' => 'comment store comment',
844 'message' => $comStoreComment->message
,
845 'data' => [ 'foo' => 'bar' ],
851 public function testGetCommentErrors() {
852 Wikimedia\
suppressWarnings();
853 $reset = new ScopedCallback( 'Wikimedia\restoreWarnings' );
855 $store = $this->makeStore( MIGRATION_OLD
);
856 $res = $store->getComment( 'dummy', [ 'dummy' => 'comment' ] );
857 $this->assertSame( '', $res->text
);
858 $res = $store->getComment( 'dummy', [ 'dummy' => 'comment' ], true );
859 $this->assertSame( 'comment', $res->text
);
861 $store = $this->makeStore( MIGRATION_NEW
);
863 $store->getComment( 'dummy', [ 'dummy' => 'comment' ] );
864 $this->fail( 'Expected exception not thrown' );
865 } catch ( InvalidArgumentException
$ex ) {
866 $this->assertSame( '$row does not contain fields needed for comment dummy', $ex->getMessage() );
868 $res = $store->getComment( 'dummy', [ 'dummy' => 'comment' ], true );
869 $this->assertSame( 'comment', $res->text
);
871 $store->getComment( 'dummy', [ 'dummy_id' => 1 ] );
872 $this->fail( 'Expected exception not thrown' );
873 } catch ( InvalidArgumentException
$ex ) {
875 '$row does not contain fields needed for comment dummy and getComment(), '
876 . 'but does have fields for getCommentLegacy()',
881 $store = $this->makeStore( MIGRATION_NEW
);
883 $store->getComment( 'rev_comment', [ 'rev_comment' => 'comment' ] );
884 $this->fail( 'Expected exception not thrown' );
885 } catch ( InvalidArgumentException
$ex ) {
887 '$row does not contain fields needed for comment rev_comment', $ex->getMessage()
890 $res = $store->getComment( 'rev_comment', [ 'rev_comment' => 'comment' ], true );
891 $this->assertSame( 'comment', $res->text
);
893 $store->getComment( 'rev_comment', [ 'rev_comment_pk' => 1 ] );
894 $this->fail( 'Expected exception not thrown' );
895 } catch ( InvalidArgumentException
$ex ) {
897 '$row does not contain fields needed for comment rev_comment and getComment(), '
898 . 'but does have fields for getCommentLegacy()',
904 public static function provideStages() {
906 'MIGRATION_OLD' => [ MIGRATION_OLD
],
907 'MIGRATION_WRITE_BOTH' => [ MIGRATION_WRITE_BOTH
],
908 'MIGRATION_WRITE_NEW' => [ MIGRATION_WRITE_NEW
],
909 'MIGRATION_NEW' => [ MIGRATION_NEW
],
911 'SCHEMA_COMPAT write-both/read-old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
],
912 'SCHEMA_COMPAT write-both/read-new' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
],
917 * @dataProvider provideStages
919 * @expectedException InvalidArgumentException
920 * @expectedExceptionMessage Must use insertWithTempTable() for rev_comment
922 public function testInsertWrong( $stage ) {
923 $store = $this->makeStore( $stage );
924 $store->insert( $this->db
, 'rev_comment', 'foo' );
928 * @dataProvider provideStages
930 * @expectedException InvalidArgumentException
931 * @expectedExceptionMessage Must use insert() for ipb_reason
933 public function testInsertWithTempTableWrong( $stage ) {
934 $store = $this->makeStore( $stage );
935 $store->insertWithTempTable( $this->db
, 'ipb_reason', 'foo' );
939 * @dataProvider provideStages
942 public function testInsertWithTempTableDeprecated( $stage ) {
943 $store = $this->makeStore( $stage );
944 $wrap = TestingAccessWrapper
::newFromObject( $store );
945 $wrap->tempTables +
= [ 'ipb_reason' => [
946 'stage' => MIGRATION_NEW
,
947 'deprecatedIn' => '1.30',
950 $this->hideDeprecated( 'CommentStore::insertWithTempTable for ipb_reason' );
951 list( $fields, $callback ) = $store->insertWithTempTable( $this->db
, 'ipb_reason', 'foo' );
952 $this->assertTrue( is_callable( $callback ) );
955 public function testInsertTruncation() {
956 $comment = str_repeat( '💣', 16400 );
957 $truncated1 = str_repeat( '💣', 63 ) . '...';
958 $truncated2 = str_repeat( '💣', CommentStore
::COMMENT_CHARACTER_LIMIT
- 3 ) . '...';
960 $store = $this->makeStore( MIGRATION_WRITE_BOTH
);
961 $fields = $store->insert( $this->db
, 'ipb_reason', $comment );
962 $this->assertSame( $truncated1, $fields['ipb_reason'] );
963 $stored = $this->db
->selectField(
964 'comment', 'comment_text', [ 'comment_id' => $fields['ipb_reason_id'] ], __METHOD__
966 $this->assertSame( $truncated2, $stored );
970 * @expectedException OverflowException
971 * @expectedExceptionMessage Comment data is too long (65611 bytes, maximum is 65535)
973 public function testInsertTooMuchData() {
974 $store = $this->makeStore( MIGRATION_WRITE_BOTH
);
975 $store->insert( $this->db
, 'ipb_reason', 'foo', [
976 'long' => str_repeat( '💣', 16400 )
980 public function testGetStore() {
981 $this->assertInstanceOf( CommentStore
::class, CommentStore
::getStore() );
984 public function testNewKey() {
985 $this->hideDeprecated( 'CommentStore::newKey' );
986 $this->assertInstanceOf( CommentStore
::class, CommentStore
::newKey( 'dummy' ) );