3 use Wikimedia\ScopedCallback
;
4 use Wikimedia\TestingAccessWrapper
;
9 * @covers CommentStoreComment
11 class CommentStoreTest
extends MediaWikiLangTestCase
{
13 protected $tablesUsed = [
15 'revision_comment_temp',
21 * Create a store for a particular stage
24 * @return CommentStore
26 protected function makeStore( $stage, $key ) {
27 $store = new CommentStore( $key );
28 TestingAccessWrapper
::newFromObject( $store )->stage
= $stage;
33 * @dataProvider provideGetFields
36 * @param array $expect
38 public function testGetFields( $stage, $key, $expect ) {
39 $store = $this->makeStore( $stage, $key );
40 $result = $store->getFields();
41 $this->assertEquals( $expect, $result );
44 public static function provideGetFields() {
46 'Simple table, old' => [
47 MIGRATION_OLD
, 'ipb_reason',
48 [ 'ipb_reason_text' => 'ipb_reason', 'ipb_reason_data' => 'NULL', 'ipb_reason_cid' => 'NULL' ],
50 'Simple table, write-both' => [
51 MIGRATION_WRITE_BOTH
, 'ipb_reason',
52 [ 'ipb_reason_old' => 'ipb_reason', 'ipb_reason_id' => 'ipb_reason_id' ],
54 'Simple table, write-new' => [
55 MIGRATION_WRITE_NEW
, 'ipb_reason',
56 [ 'ipb_reason_old' => 'ipb_reason', 'ipb_reason_id' => 'ipb_reason_id' ],
58 'Simple table, new' => [
59 MIGRATION_NEW
, 'ipb_reason',
60 [ 'ipb_reason_id' => 'ipb_reason_id' ],
64 MIGRATION_OLD
, 'rev_comment',
66 'rev_comment_text' => 'rev_comment',
67 'rev_comment_data' => 'NULL',
68 'rev_comment_cid' => 'NULL',
71 'Revision, write-both' => [
72 MIGRATION_WRITE_BOTH
, 'rev_comment',
73 [ 'rev_comment_old' => 'rev_comment', 'rev_comment_pk' => 'rev_id' ],
75 'Revision, write-new' => [
76 MIGRATION_WRITE_NEW
, 'rev_comment',
77 [ 'rev_comment_old' => 'rev_comment', 'rev_comment_pk' => 'rev_id' ],
80 MIGRATION_NEW
, 'rev_comment',
81 [ 'rev_comment_pk' => 'rev_id' ],
85 MIGRATION_OLD
, 'img_description',
87 'img_description_text' => 'img_description',
88 'img_description_data' => 'NULL',
89 'img_description_cid' => 'NULL',
92 'Image, write-both' => [
93 MIGRATION_WRITE_BOTH
, 'img_description',
94 [ 'img_description_old' => 'img_description', 'img_description_pk' => 'img_name' ],
96 'Image, write-new' => [
97 MIGRATION_WRITE_NEW
, 'img_description',
98 [ 'img_description_old' => 'img_description', 'img_description_pk' => 'img_name' ],
101 MIGRATION_NEW
, 'img_description',
102 [ 'img_description_pk' => 'img_name' ],
108 * @dataProvider provideGetJoin
111 * @param array $expect
113 public function testGetJoin( $stage, $key, $expect ) {
114 $store = $this->makeStore( $stage, $key );
115 $result = $store->getJoin();
116 $this->assertEquals( $expect, $result );
119 public static function provideGetJoin() {
121 'Simple table, old' => [
122 MIGRATION_OLD
, 'ipb_reason', [
125 'ipb_reason_text' => 'ipb_reason',
126 'ipb_reason_data' => 'NULL',
127 'ipb_reason_cid' => 'NULL',
132 'Simple table, write-both' => [
133 MIGRATION_WRITE_BOTH
, 'ipb_reason', [
134 'tables' => [ 'comment_ipb_reason' => 'comment' ],
136 'ipb_reason_text' => 'COALESCE( comment_ipb_reason.comment_text, ipb_reason )',
137 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
138 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
141 'comment_ipb_reason' => [ 'LEFT JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
145 'Simple table, write-new' => [
146 MIGRATION_WRITE_NEW
, 'ipb_reason', [
147 'tables' => [ 'comment_ipb_reason' => 'comment' ],
149 'ipb_reason_text' => 'COALESCE( comment_ipb_reason.comment_text, ipb_reason )',
150 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
151 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
154 'comment_ipb_reason' => [ 'LEFT JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
158 'Simple table, new' => [
159 MIGRATION_NEW
, 'ipb_reason', [
160 'tables' => [ 'comment_ipb_reason' => 'comment' ],
162 'ipb_reason_text' => 'comment_ipb_reason.comment_text',
163 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
164 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
167 'comment_ipb_reason' => [ 'JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
173 MIGRATION_OLD
, 'rev_comment', [
176 'rev_comment_text' => 'rev_comment',
177 'rev_comment_data' => 'NULL',
178 'rev_comment_cid' => 'NULL',
183 'Revision, write-both' => [
184 MIGRATION_WRITE_BOTH
, 'rev_comment', [
186 'temp_rev_comment' => 'revision_comment_temp',
187 'comment_rev_comment' => 'comment',
190 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
191 'rev_comment_data' => 'comment_rev_comment.comment_data',
192 'rev_comment_cid' => 'comment_rev_comment.comment_id',
195 'temp_rev_comment' => [ 'LEFT JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
196 'comment_rev_comment' => [ 'LEFT JOIN',
197 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
201 'Revision, write-new' => [
202 MIGRATION_WRITE_NEW
, 'rev_comment', [
204 'temp_rev_comment' => 'revision_comment_temp',
205 'comment_rev_comment' => 'comment',
208 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
209 'rev_comment_data' => 'comment_rev_comment.comment_data',
210 'rev_comment_cid' => 'comment_rev_comment.comment_id',
213 'temp_rev_comment' => [ 'LEFT JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
214 'comment_rev_comment' => [ 'LEFT JOIN',
215 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
220 MIGRATION_NEW
, 'rev_comment', [
222 'temp_rev_comment' => 'revision_comment_temp',
223 'comment_rev_comment' => 'comment',
226 'rev_comment_text' => 'comment_rev_comment.comment_text',
227 'rev_comment_data' => 'comment_rev_comment.comment_data',
228 'rev_comment_cid' => 'comment_rev_comment.comment_id',
231 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
232 'comment_rev_comment' => [ 'JOIN',
233 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
239 MIGRATION_OLD
, 'img_description', [
242 'img_description_text' => 'img_description',
243 'img_description_data' => 'NULL',
244 'img_description_cid' => 'NULL',
249 'Image, write-both' => [
250 MIGRATION_WRITE_BOTH
, 'img_description', [
252 'temp_img_description' => 'image_comment_temp',
253 'comment_img_description' => 'comment',
256 'img_description_text' => 'COALESCE( comment_img_description.comment_text, img_description )',
257 'img_description_data' => 'comment_img_description.comment_data',
258 'img_description_cid' => 'comment_img_description.comment_id',
261 'temp_img_description' => [ 'LEFT JOIN', 'temp_img_description.imgcomment_name = img_name' ],
262 'comment_img_description' => [ 'LEFT JOIN',
263 'comment_img_description.comment_id = temp_img_description.imgcomment_description_id' ],
267 'Image, write-new' => [
268 MIGRATION_WRITE_NEW
, 'img_description', [
270 'temp_img_description' => 'image_comment_temp',
271 'comment_img_description' => 'comment',
274 'img_description_text' => 'COALESCE( comment_img_description.comment_text, img_description )',
275 'img_description_data' => 'comment_img_description.comment_data',
276 'img_description_cid' => 'comment_img_description.comment_id',
279 'temp_img_description' => [ 'LEFT JOIN', 'temp_img_description.imgcomment_name = img_name' ],
280 'comment_img_description' => [ 'LEFT JOIN',
281 'comment_img_description.comment_id = temp_img_description.imgcomment_description_id' ],
286 MIGRATION_NEW
, 'img_description', [
288 'temp_img_description' => 'image_comment_temp',
289 'comment_img_description' => 'comment',
292 'img_description_text' => 'comment_img_description.comment_text',
293 'img_description_data' => 'comment_img_description.comment_data',
294 'img_description_cid' => 'comment_img_description.comment_id',
297 'temp_img_description' => [ 'JOIN', 'temp_img_description.imgcomment_name = img_name' ],
298 'comment_img_description' => [ 'JOIN',
299 'comment_img_description.comment_id = temp_img_description.imgcomment_description_id' ],
306 private function assertComment( $expect, $actual, $from ) {
307 $this->assertSame( $expect['text'], $actual->text
, "text $from" );
308 $this->assertInstanceOf( get_class( $expect['message'] ), $actual->message
,
309 "message class $from" );
310 $this->assertSame( $expect['message']->getKeysToTry(), $actual->message
->getKeysToTry(),
311 "message keys $from" );
312 $this->assertEquals( $expect['message']->text(), $actual->message
->text(),
313 "message rendering $from" );
314 $this->assertEquals( $expect['data'], $actual->data
, "data $from" );
318 * @dataProvider provideInsertRoundTrip
319 * @param string $table
322 * @param string $extraFields
323 * @param string|Message $comment
324 * @param array|null $data
325 * @param array $expect
327 public function testInsertRoundTrip( $table, $key, $pk, $extraFields, $comment, $data, $expect ) {
329 'text' => $expect['text'],
330 'message' => new RawMessage( '$1', [ $expect['text'] ] ),
335 MIGRATION_OLD
=> [ MIGRATION_OLD
, MIGRATION_WRITE_NEW
],
336 MIGRATION_WRITE_BOTH
=> [ MIGRATION_OLD
, MIGRATION_NEW
],
337 MIGRATION_WRITE_NEW
=> [ MIGRATION_WRITE_BOTH
, MIGRATION_NEW
],
338 MIGRATION_NEW
=> [ MIGRATION_WRITE_BOTH
, MIGRATION_NEW
],
341 foreach ( $stages as $writeStage => $readRange ) {
342 if ( $key === 'ipb_reason' ) {
343 $extraFields['ipb_address'] = __CLASS__
. "#$writeStage";
346 $wstore = $this->makeStore( $writeStage, $key );
347 $usesTemp = $key === 'rev_comment';
350 list( $fields, $callback ) = $wstore->insertWithTempTable( $this->db
, $comment, $data );
352 $fields = $wstore->insert( $this->db
, $comment, $data );
355 if ( $writeStage <= MIGRATION_WRITE_BOTH
) {
356 $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
358 $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
360 if ( $writeStage >= MIGRATION_WRITE_BOTH
&& !$usesTemp ) {
361 $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
363 $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
366 $extraFields[$pk] = $this->db
->nextSequenceValue( "{$table}_{$pk}_seq" );
367 $this->db
->insert( $table, $extraFields +
$fields, __METHOD__
);
368 $id = $this->db
->insertId();
373 for ( $readStage = $readRange[0]; $readStage <= $readRange[1]; $readStage++
) {
374 $rstore = $this->makeStore( $readStage, $key );
376 $fieldRow = $this->db
->selectRow(
378 $rstore->getFields(),
383 $queryInfo = $rstore->getJoin();
384 $joinRow = $this->db
->selectRow(
385 [ $table ] +
$queryInfo['tables'],
386 $queryInfo['fields'],
393 $this->assertComment(
394 $writeStage === MIGRATION_OLD ||
$readStage === MIGRATION_OLD ?
$expectOld : $expect,
395 $rstore->getCommentLegacy( $this->db
, $fieldRow ),
396 "w=$writeStage, r=$readStage, from getFields()"
398 $this->assertComment(
399 $writeStage === MIGRATION_OLD ||
$readStage === MIGRATION_OLD ?
$expectOld : $expect,
400 $rstore->getComment( $joinRow ),
401 "w=$writeStage, r=$readStage, from getJoin()"
407 public static function provideInsertRoundTrip() {
408 $db = wfGetDB( DB_REPLICA
); // for timestamps
410 $msgComment = new Message( 'parentheses', [ 'message comment' ] );
411 $textCommentMsg = new RawMessage( '$1', [ 'text comment' ] );
412 $nestedMsgComment = new Message( [ 'parentheses', 'rawmessage' ], [ new Message( 'mainpage' ) ] );
414 'ipb_range_start' => '',
415 'ipb_range_end' => '',
417 'ipb_timestamp' => $db->timestamp(),
418 'ipb_expiry' => $db->getInfinity(),
425 'rev_user_text' => '',
426 'rev_timestamp' => $db->timestamp(),
428 $comStoreComment = new CommentStoreComment(
429 null, 'comment store comment', null, [ 'foo' => 'bar' ]
433 'Simple table, text comment' => [
434 'ipblocks', 'ipb_reason', 'ipb_id', $ipbfields, 'text comment', null, [
435 'text' => 'text comment',
436 'message' => $textCommentMsg,
440 'Simple table, text comment with data' => [
441 'ipblocks', 'ipb_reason', 'ipb_id', $ipbfields, 'text comment', [ 'message' => 42 ], [
442 'text' => 'text comment',
443 'message' => $textCommentMsg,
444 'data' => [ 'message' => 42 ],
447 'Simple table, message comment' => [
448 'ipblocks', 'ipb_reason', 'ipb_id', $ipbfields, $msgComment, null, [
449 'text' => '(message comment)',
450 'message' => $msgComment,
454 'Simple table, message comment with data' => [
455 'ipblocks', 'ipb_reason', 'ipb_id', $ipbfields, $msgComment, [ 'message' => 42 ], [
456 'text' => '(message comment)',
457 'message' => $msgComment,
458 'data' => [ 'message' => 42 ],
461 'Simple table, nested message comment' => [
462 'ipblocks', 'ipb_reason', 'ipb_id', $ipbfields, $nestedMsgComment, null, [
463 'text' => '(Main Page)',
464 'message' => $nestedMsgComment,
468 'Simple table, CommentStoreComment' => [
469 'ipblocks', 'ipb_reason', 'ipb_id', $ipbfields, clone $comStoreComment, [ 'baz' => 'baz' ], [
470 'text' => 'comment store comment',
471 'message' => $comStoreComment->message
,
472 'data' => [ 'foo' => 'bar' ],
476 'Revision, text comment' => [
477 'revision', 'rev_comment', 'rev_id', $revfields, 'text comment', null, [
478 'text' => 'text comment',
479 'message' => $textCommentMsg,
483 'Revision, text comment with data' => [
484 'revision', 'rev_comment', 'rev_id', $revfields, 'text comment', [ 'message' => 42 ], [
485 'text' => 'text comment',
486 'message' => $textCommentMsg,
487 'data' => [ 'message' => 42 ],
490 'Revision, message comment' => [
491 'revision', 'rev_comment', 'rev_id', $revfields, $msgComment, null, [
492 'text' => '(message comment)',
493 'message' => $msgComment,
497 'Revision, message comment with data' => [
498 'revision', 'rev_comment', 'rev_id', $revfields, $msgComment, [ 'message' => 42 ], [
499 'text' => '(message comment)',
500 'message' => $msgComment,
501 'data' => [ 'message' => 42 ],
504 'Revision, nested message comment' => [
505 'revision', 'rev_comment', 'rev_id', $revfields, $nestedMsgComment, null, [
506 'text' => '(Main Page)',
507 'message' => $nestedMsgComment,
511 'Revision, CommentStoreComment' => [
512 'revision', 'rev_comment', 'rev_id', $revfields, clone $comStoreComment, [ 'baz' => 'baz' ], [
513 'text' => 'comment store comment',
514 'message' => $comStoreComment->message
,
515 'data' => [ 'foo' => 'bar' ],
521 public function testGetCommentErrors() {
522 MediaWiki\
suppressWarnings();
523 $reset = new ScopedCallback( 'MediaWiki\restoreWarnings' );
525 $store = $this->makeStore( MIGRATION_OLD
, 'dummy' );
526 $res = $store->getComment( [ 'dummy' => 'comment' ] );
527 $this->assertSame( '', $res->text
);
528 $res = $store->getComment( [ 'dummy' => 'comment' ], true );
529 $this->assertSame( 'comment', $res->text
);
531 $store = $this->makeStore( MIGRATION_NEW
, 'dummy' );
533 $store->getComment( [ 'dummy' => 'comment' ] );
534 $this->fail( 'Expected exception not thrown' );
535 } catch ( InvalidArgumentException
$ex ) {
536 $this->assertSame( '$row does not contain fields needed for comment dummy', $ex->getMessage() );
538 $res = $store->getComment( [ 'dummy' => 'comment' ], true );
539 $this->assertSame( 'comment', $res->text
);
541 $store->getComment( [ 'dummy_id' => 1 ] );
542 $this->fail( 'Expected exception not thrown' );
543 } catch ( InvalidArgumentException
$ex ) {
545 '$row does not contain fields needed for comment dummy and getComment(), '
546 . 'but does have fields for getCommentLegacy()',
551 $store = $this->makeStore( MIGRATION_NEW
, 'rev_comment' );
553 $store->getComment( [ 'rev_comment' => 'comment' ] );
554 $this->fail( 'Expected exception not thrown' );
555 } catch ( InvalidArgumentException
$ex ) {
557 '$row does not contain fields needed for comment rev_comment', $ex->getMessage()
560 $res = $store->getComment( [ 'rev_comment' => 'comment' ], true );
561 $this->assertSame( 'comment', $res->text
);
563 $store->getComment( [ 'rev_comment_pk' => 1 ] );
564 $this->fail( 'Expected exception not thrown' );
565 } catch ( InvalidArgumentException
$ex ) {
567 '$row does not contain fields needed for comment rev_comment and getComment(), '
568 . 'but does have fields for getCommentLegacy()',
574 public static function provideStages() {
576 'MIGRATION_OLD' => [ MIGRATION_OLD
],
577 'MIGRATION_WRITE_BOTH' => [ MIGRATION_WRITE_BOTH
],
578 'MIGRATION_WRITE_NEW' => [ MIGRATION_WRITE_NEW
],
579 'MIGRATION_NEW' => [ MIGRATION_NEW
],
584 * @dataProvider provideStages
586 * @expectedException InvalidArgumentException
587 * @expectedExceptionMessage Must use insertWithTempTable() for rev_comment
589 public function testInsertWrong( $stage ) {
590 $store = $this->makeStore( $stage, 'rev_comment' );
591 $store->insert( $this->db
, 'foo' );
595 * @dataProvider provideStages
597 * @expectedException InvalidArgumentException
598 * @expectedExceptionMessage Must use insert() for ipb_reason
600 public function testInsertWithTempTableWrong( $stage ) {
601 $store = $this->makeStore( $stage, 'ipb_reason' );
602 $store->insertWithTempTable( $this->db
, 'foo' );
606 * @dataProvider provideStages
609 public function testInsertWithTempTableDeprecated( $stage ) {
610 $wrap = TestingAccessWrapper
::newFromClass( CommentStore
::class );
611 $wrap->formerTempTables +
= [ 'ipb_reason' => '1.30' ];
613 $this->hideDeprecated( 'CommentStore::insertWithTempTable for ipb_reason' );
614 $store = $this->makeStore( $stage, 'ipb_reason' );
615 list( $fields, $callback ) = $store->insertWithTempTable( $this->db
, 'foo' );
616 $this->assertTrue( is_callable( $callback ) );
619 public function testConstructor() {
620 $this->assertInstanceOf( CommentStore
::class, CommentStore
::newKey( 'dummy' ) );