Merge "resourceloader: Restore the comment about <script> vs XHR loading"
[lhc/web/wiklou.git] / tests / phpunit / includes / CommentStoreTest.php
1 <?php
2
3 use MediaWiki\MediaWikiServices;
4 use Wikimedia\ScopedCallback;
5 use Wikimedia\TestingAccessWrapper;
6
7 /**
8 * @group Database
9 * @covers CommentStore
10 * @covers CommentStoreComment
11 */
12 class CommentStoreTest extends MediaWikiLangTestCase {
13
14 protected $tablesUsed = [
15 'revision',
16 'revision_comment_temp',
17 'ipblocks',
18 'comment',
19 ];
20
21 protected function getSchemaOverrides( IMaintainableDatabase $db ) {
22 return [
23 'scripts' => [
24 __DIR__ . '/CommentStoreTest.sql',
25 ],
26 'drop' => [],
27 'create' => [ 'commentstore1', 'commentstore2', 'commentstore2_temp' ],
28 'alter' => [],
29 ];
30 }
31
32 /**
33 * Create a store for a particular stage
34 * @param int $stage
35 * @return CommentStore
36 */
37 protected function makeStore( $stage ) {
38 $store = new CommentStore( MediaWikiServices::getInstance()->getContentLanguage(), $stage );
39
40 TestingAccessWrapper::newFromObject( $store )->tempTables += [ 'cs2_comment' => [
41 'table' => 'commentstore2_temp',
42 'pk' => 'cs2t_id',
43 'field' => 'cs2t_comment_id',
44 'joinPK' => 'cs2_id',
45 'stage' => MIGRATION_OLD,
46 'deprecatedIn' => null,
47 ] ];
48
49 return $store;
50 }
51
52 /**
53 * Create a store for a particular stage and key (for testing deprecated behaviour)
54 * @param int $stage
55 * @param string $key
56 * @return CommentStore
57 */
58 protected function makeStoreWithKey( $stage, $key ) {
59 $this->hideDeprecated( 'CommentStore::newKey' );
60 $store = CommentStore::newKey( $key );
61 TestingAccessWrapper::newFromObject( $store )->stage = $stage;
62
63 TestingAccessWrapper::newFromObject( $store )->tempTables += [ 'cs2_comment' => [
64 'table' => 'commentstore2_temp',
65 'pk' => 'cs2t_id',
66 'field' => 'cs2t_comment_id',
67 'joinPK' => 'cs2_id',
68 'stage' => MIGRATION_OLD,
69 'deprecatedIn' => null,
70 ] ];
71
72 return $store;
73 }
74
75 /**
76 * @dataProvider provideGetFields
77 * @param int $stage
78 * @param string $key
79 * @param array $expect
80 */
81 public function testGetFields_withKeyConstruction( $stage, $key, $expect ) {
82 $store = $this->makeStoreWithKey( $stage, $key );
83 $result = $store->getFields();
84 $this->assertEquals( $expect, $result );
85 }
86
87 /**
88 * @dataProvider provideGetFields
89 * @param int $stage
90 * @param string $key
91 * @param array $expect
92 */
93 public function testGetFields( $stage, $key, $expect ) {
94 $store = $this->makeStore( $stage );
95 $result = $store->getFields( $key );
96 $this->assertEquals( $expect, $result );
97 }
98
99 public static function provideGetFields() {
100 return [
101 'Simple table, old' => [
102 MIGRATION_OLD, 'ipb_reason',
103 [ 'ipb_reason_text' => 'ipb_reason', 'ipb_reason_data' => 'NULL', 'ipb_reason_cid' => 'NULL' ],
104 ],
105 'Simple table, write-both' => [
106 MIGRATION_WRITE_BOTH, 'ipb_reason',
107 [ 'ipb_reason_old' => 'ipb_reason', 'ipb_reason_id' => 'ipb_reason_id' ],
108 ],
109 'Simple table, write-new' => [
110 MIGRATION_WRITE_NEW, 'ipb_reason',
111 [ 'ipb_reason_old' => 'ipb_reason', 'ipb_reason_id' => 'ipb_reason_id' ],
112 ],
113 'Simple table, new' => [
114 MIGRATION_NEW, 'ipb_reason',
115 [ 'ipb_reason_id' => 'ipb_reason_id' ],
116 ],
117
118 'Revision, old' => [
119 MIGRATION_OLD, 'rev_comment',
120 [
121 'rev_comment_text' => 'rev_comment',
122 'rev_comment_data' => 'NULL',
123 'rev_comment_cid' => 'NULL',
124 ],
125 ],
126 'Revision, write-both' => [
127 MIGRATION_WRITE_BOTH, 'rev_comment',
128 [ 'rev_comment_old' => 'rev_comment', 'rev_comment_pk' => 'rev_id' ],
129 ],
130 'Revision, write-new' => [
131 MIGRATION_WRITE_NEW, 'rev_comment',
132 [ 'rev_comment_old' => 'rev_comment', 'rev_comment_pk' => 'rev_id' ],
133 ],
134 'Revision, new' => [
135 MIGRATION_NEW, 'rev_comment',
136 [ 'rev_comment_pk' => 'rev_id' ],
137 ],
138
139 'Image, old' => [
140 MIGRATION_OLD, 'img_description',
141 [
142 'img_description_text' => 'img_description',
143 'img_description_data' => 'NULL',
144 'img_description_cid' => 'NULL',
145 ],
146 ],
147 'Image, write-both' => [
148 MIGRATION_WRITE_BOTH, 'img_description',
149 [
150 'img_description_old' => 'img_description',
151 'img_description_id' => 'img_description_id'
152 ],
153 ],
154 'Image, write-new' => [
155 MIGRATION_WRITE_NEW, 'img_description',
156 [
157 'img_description_old' => 'img_description',
158 'img_description_id' => 'img_description_id'
159 ],
160 ],
161 'Image, new' => [
162 MIGRATION_NEW, 'img_description',
163 [
164 'img_description_id' => 'img_description_id'
165 ],
166 ],
167 ];
168 }
169
170 /**
171 * @dataProvider provideGetJoin
172 * @param int $stage
173 * @param string $key
174 * @param array $expect
175 */
176 public function testGetJoin_withKeyConstruction( $stage, $key, $expect ) {
177 $store = $this->makeStoreWithKey( $stage, $key );
178 $result = $store->getJoin();
179 $this->assertEquals( $expect, $result );
180 }
181
182 /**
183 * @dataProvider provideGetJoin
184 * @param int $stage
185 * @param string $key
186 * @param array $expect
187 */
188 public function testGetJoin( $stage, $key, $expect ) {
189 $store = $this->makeStore( $stage );
190 $result = $store->getJoin( $key );
191 $this->assertEquals( $expect, $result );
192 }
193
194 public static function provideGetJoin() {
195 return [
196 'Simple table, old' => [
197 MIGRATION_OLD, 'ipb_reason', [
198 'tables' => [],
199 'fields' => [
200 'ipb_reason_text' => 'ipb_reason',
201 'ipb_reason_data' => 'NULL',
202 'ipb_reason_cid' => 'NULL',
203 ],
204 'joins' => [],
205 ],
206 ],
207 'Simple table, write-both' => [
208 MIGRATION_WRITE_BOTH, 'ipb_reason', [
209 'tables' => [ 'comment_ipb_reason' => 'comment' ],
210 'fields' => [
211 'ipb_reason_text' => 'COALESCE( comment_ipb_reason.comment_text, ipb_reason )',
212 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
213 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
214 ],
215 'joins' => [
216 'comment_ipb_reason' => [ 'LEFT JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
217 ],
218 ],
219 ],
220 'Simple table, write-new' => [
221 MIGRATION_WRITE_NEW, 'ipb_reason', [
222 'tables' => [ 'comment_ipb_reason' => 'comment' ],
223 'fields' => [
224 'ipb_reason_text' => 'COALESCE( comment_ipb_reason.comment_text, ipb_reason )',
225 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
226 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
227 ],
228 'joins' => [
229 'comment_ipb_reason' => [ 'LEFT JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
230 ],
231 ],
232 ],
233 'Simple table, new' => [
234 MIGRATION_NEW, 'ipb_reason', [
235 'tables' => [ 'comment_ipb_reason' => 'comment' ],
236 'fields' => [
237 'ipb_reason_text' => 'comment_ipb_reason.comment_text',
238 'ipb_reason_data' => 'comment_ipb_reason.comment_data',
239 'ipb_reason_cid' => 'comment_ipb_reason.comment_id',
240 ],
241 'joins' => [
242 'comment_ipb_reason' => [ 'JOIN', 'comment_ipb_reason.comment_id = ipb_reason_id' ],
243 ],
244 ],
245 ],
246
247 'Revision, old' => [
248 MIGRATION_OLD, 'rev_comment', [
249 'tables' => [],
250 'fields' => [
251 'rev_comment_text' => 'rev_comment',
252 'rev_comment_data' => 'NULL',
253 'rev_comment_cid' => 'NULL',
254 ],
255 'joins' => [],
256 ],
257 ],
258 'Revision, write-both' => [
259 MIGRATION_WRITE_BOTH, 'rev_comment', [
260 'tables' => [
261 'temp_rev_comment' => 'revision_comment_temp',
262 'comment_rev_comment' => 'comment',
263 ],
264 'fields' => [
265 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
266 'rev_comment_data' => 'comment_rev_comment.comment_data',
267 'rev_comment_cid' => 'comment_rev_comment.comment_id',
268 ],
269 'joins' => [
270 'temp_rev_comment' => [ 'LEFT JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
271 'comment_rev_comment' => [ 'LEFT JOIN',
272 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
273 ],
274 ],
275 ],
276 'Revision, write-new' => [
277 MIGRATION_WRITE_NEW, 'rev_comment', [
278 'tables' => [
279 'temp_rev_comment' => 'revision_comment_temp',
280 'comment_rev_comment' => 'comment',
281 ],
282 'fields' => [
283 'rev_comment_text' => 'COALESCE( comment_rev_comment.comment_text, rev_comment )',
284 'rev_comment_data' => 'comment_rev_comment.comment_data',
285 'rev_comment_cid' => 'comment_rev_comment.comment_id',
286 ],
287 'joins' => [
288 'temp_rev_comment' => [ 'LEFT JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
289 'comment_rev_comment' => [ 'LEFT JOIN',
290 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
291 ],
292 ],
293 ],
294 'Revision, new' => [
295 MIGRATION_NEW, 'rev_comment', [
296 'tables' => [
297 'temp_rev_comment' => 'revision_comment_temp',
298 'comment_rev_comment' => 'comment',
299 ],
300 'fields' => [
301 'rev_comment_text' => 'comment_rev_comment.comment_text',
302 'rev_comment_data' => 'comment_rev_comment.comment_data',
303 'rev_comment_cid' => 'comment_rev_comment.comment_id',
304 ],
305 'joins' => [
306 'temp_rev_comment' => [ 'JOIN', 'temp_rev_comment.revcomment_rev = rev_id' ],
307 'comment_rev_comment' => [ 'JOIN',
308 'comment_rev_comment.comment_id = temp_rev_comment.revcomment_comment_id' ],
309 ],
310 ],
311 ],
312
313 'Image, old' => [
314 MIGRATION_OLD, 'img_description', [
315 'tables' => [],
316 'fields' => [
317 'img_description_text' => 'img_description',
318 'img_description_data' => 'NULL',
319 'img_description_cid' => 'NULL',
320 ],
321 'joins' => [],
322 ],
323 ],
324 'Image, write-both' => [
325 MIGRATION_WRITE_BOTH, 'img_description', [
326 'tables' => [
327 'comment_img_description' => 'comment',
328 ],
329 'fields' => [
330 'img_description_text' => 'COALESCE( comment_img_description.comment_text, img_description )',
331 'img_description_data' => 'comment_img_description.comment_data',
332 'img_description_cid' => 'comment_img_description.comment_id',
333 ],
334 'joins' => [
335 'comment_img_description' => [ 'LEFT JOIN',
336 'comment_img_description.comment_id = img_description_id',
337 ],
338 ],
339 ],
340 ],
341 'Image, write-new' => [
342 MIGRATION_WRITE_NEW, 'img_description', [
343 'tables' => [
344 'comment_img_description' => 'comment',
345 ],
346 'fields' => [
347 'img_description_text' => 'COALESCE( comment_img_description.comment_text, img_description )',
348 'img_description_data' => 'comment_img_description.comment_data',
349 'img_description_cid' => 'comment_img_description.comment_id',
350 ],
351 'joins' => [
352 'comment_img_description' => [ 'LEFT JOIN',
353 'comment_img_description.comment_id = img_description_id',
354 ],
355 ],
356 ],
357 ],
358 'Image, new' => [
359 MIGRATION_NEW, 'img_description', [
360 'tables' => [
361 'comment_img_description' => 'comment',
362 ],
363 'fields' => [
364 'img_description_text' => 'comment_img_description.comment_text',
365 'img_description_data' => 'comment_img_description.comment_data',
366 'img_description_cid' => 'comment_img_description.comment_id',
367 ],
368 'joins' => [
369 'comment_img_description' => [ 'JOIN',
370 'comment_img_description.comment_id = img_description_id',
371 ],
372 ],
373 ],
374 ],
375 ];
376 }
377
378 private function assertComment( $expect, $actual, $from ) {
379 $this->assertSame( $expect['text'], $actual->text, "text $from" );
380 $this->assertInstanceOf( get_class( $expect['message'] ), $actual->message,
381 "message class $from" );
382 $this->assertSame( $expect['message']->getKeysToTry(), $actual->message->getKeysToTry(),
383 "message keys $from" );
384 $this->assertEquals( $expect['message']->text(), $actual->message->text(),
385 "message rendering $from" );
386 $this->assertEquals( $expect['text'], $actual->message->text(),
387 "message rendering and text $from" );
388 $this->assertEquals( $expect['data'], $actual->data, "data $from" );
389 }
390
391 /**
392 * @dataProvider provideInsertRoundTrip
393 * @param string $table
394 * @param string $key
395 * @param string $pk
396 * @param string|Message $comment
397 * @param array|null $data
398 * @param array $expect
399 */
400 public function testInsertRoundTrip( $table, $key, $pk, $comment, $data, $expect ) {
401 static $id = 1;
402
403 $expectOld = [
404 'text' => $expect['text'],
405 'message' => new RawMessage( '$1', [ Message::plaintextParam( $expect['text'] ) ] ),
406 'data' => null,
407 ];
408
409 $stages = [
410 MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
411 MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW,
412 MIGRATION_NEW ],
413 MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
414 MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
415 ];
416
417 foreach ( $stages as $writeStage => $possibleReadStages ) {
418 $wstore = $this->makeStore( $writeStage );
419 $usesTemp = $key === 'cs2_comment';
420
421 if ( $usesTemp ) {
422 list( $fields, $callback ) = $wstore->insertWithTempTable(
423 $this->db, $key, $comment, $data
424 );
425 } else {
426 $fields = $wstore->insert( $this->db, $key, $comment, $data );
427 }
428
429 if ( $writeStage <= MIGRATION_WRITE_BOTH ) {
430 $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
431 } else {
432 $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
433 }
434 if ( $writeStage >= MIGRATION_WRITE_BOTH && !$usesTemp ) {
435 $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
436 } else {
437 $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
438 }
439
440 $this->db->insert( $table, [ $pk => ++$id ] + $fields, __METHOD__ );
441 if ( $usesTemp ) {
442 $callback( $id );
443 }
444
445 foreach ( $possibleReadStages as $readStage ) {
446 $rstore = $this->makeStore( $readStage );
447
448 $fieldRow = $this->db->selectRow(
449 $table,
450 $rstore->getFields( $key ),
451 [ $pk => $id ],
452 __METHOD__
453 );
454
455 $queryInfo = $rstore->getJoin( $key );
456 $joinRow = $this->db->selectRow(
457 [ $table ] + $queryInfo['tables'],
458 $queryInfo['fields'],
459 [ $pk => $id ],
460 __METHOD__,
461 [],
462 $queryInfo['joins']
463 );
464
465 $this->assertComment(
466 $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
467 $rstore->getCommentLegacy( $this->db, $key, $fieldRow ),
468 "w=$writeStage, r=$readStage, from getFields()"
469 );
470 $this->assertComment(
471 $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
472 $rstore->getComment( $key, $joinRow ),
473 "w=$writeStage, r=$readStage, from getJoin()"
474 );
475 }
476 }
477 }
478
479 /**
480 * @dataProvider provideInsertRoundTrip
481 * @param string $table
482 * @param string $key
483 * @param string $pk
484 * @param string|Message $comment
485 * @param array|null $data
486 * @param array $expect
487 */
488 public function testInsertRoundTrip_withKeyConstruction(
489 $table, $key, $pk, $comment, $data, $expect
490 ) {
491 static $id = 1000;
492
493 $expectOld = [
494 'text' => $expect['text'],
495 'message' => new RawMessage( '$1', [ Message::plaintextParam( $expect['text'] ) ] ),
496 'data' => null,
497 ];
498
499 $stages = [
500 MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
501 MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW,
502 MIGRATION_NEW ],
503 MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
504 MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
505 ];
506
507 foreach ( $stages as $writeStage => $possibleReadStages ) {
508 $wstore = $this->makeStoreWithKey( $writeStage, $key );
509 $usesTemp = $key === 'cs2_comment';
510
511 if ( $usesTemp ) {
512 list( $fields, $callback ) = $wstore->insertWithTempTable(
513 $this->db, $comment, $data
514 );
515 } else {
516 $fields = $wstore->insert( $this->db, $comment, $data );
517 }
518
519 if ( $writeStage <= MIGRATION_WRITE_BOTH ) {
520 $this->assertSame( $expect['text'], $fields[$key], "old field, stage=$writeStage" );
521 } else {
522 $this->assertArrayNotHasKey( $key, $fields, "old field, stage=$writeStage" );
523 }
524 if ( $writeStage >= MIGRATION_WRITE_BOTH && !$usesTemp ) {
525 $this->assertArrayHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
526 } else {
527 $this->assertArrayNotHasKey( "{$key}_id", $fields, "new field, stage=$writeStage" );
528 }
529
530 $this->db->insert( $table, [ $pk => ++$id ] + $fields, __METHOD__ );
531 if ( $usesTemp ) {
532 $callback( $id );
533 }
534
535 foreach ( $possibleReadStages as $readStage ) {
536 $rstore = $this->makeStoreWithKey( $readStage, $key );
537
538 $fieldRow = $this->db->selectRow(
539 $table,
540 $rstore->getFields(),
541 [ $pk => $id ],
542 __METHOD__
543 );
544
545 $queryInfo = $rstore->getJoin();
546 $joinRow = $this->db->selectRow(
547 [ $table ] + $queryInfo['tables'],
548 $queryInfo['fields'],
549 [ $pk => $id ],
550 __METHOD__,
551 [],
552 $queryInfo['joins']
553 );
554
555 $this->assertComment(
556 $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
557 $rstore->getCommentLegacy( $this->db, $fieldRow ),
558 "w=$writeStage, r=$readStage, from getFields()"
559 );
560 $this->assertComment(
561 $writeStage === MIGRATION_OLD || $readStage === MIGRATION_OLD ? $expectOld : $expect,
562 $rstore->getComment( $joinRow ),
563 "w=$writeStage, r=$readStage, from getJoin()"
564 );
565 }
566 }
567 }
568
569 public static function provideInsertRoundTrip() {
570 $db = wfGetDB( DB_REPLICA ); // for timestamps
571
572 $msgComment = new Message( 'parentheses', [ 'message comment' ] );
573 $textCommentMsg = new RawMessage( '$1', [ Message::plaintextParam( '{{text}} comment' ) ] );
574 $nestedMsgComment = new Message( [ 'parentheses', 'rawmessage' ], [ new Message( 'mainpage' ) ] );
575 $comStoreComment = new CommentStoreComment(
576 null, 'comment store comment', null, [ 'foo' => 'bar' ]
577 );
578
579 return [
580 'Simple table, text comment' => [
581 'commentstore1', 'cs1_comment', 'cs1_id', '{{text}} comment', null, [
582 'text' => '{{text}} comment',
583 'message' => $textCommentMsg,
584 'data' => null,
585 ]
586 ],
587 'Simple table, text comment with data' => [
588 'commentstore1', 'cs1_comment', 'cs1_id', '{{text}} comment', [ 'message' => 42 ], [
589 'text' => '{{text}} comment',
590 'message' => $textCommentMsg,
591 'data' => [ 'message' => 42 ],
592 ]
593 ],
594 'Simple table, message comment' => [
595 'commentstore1', 'cs1_comment', 'cs1_id', $msgComment, null, [
596 'text' => '(message comment)',
597 'message' => $msgComment,
598 'data' => null,
599 ]
600 ],
601 'Simple table, message comment with data' => [
602 'commentstore1', 'cs1_comment', 'cs1_id', $msgComment, [ 'message' => 42 ], [
603 'text' => '(message comment)',
604 'message' => $msgComment,
605 'data' => [ 'message' => 42 ],
606 ]
607 ],
608 'Simple table, nested message comment' => [
609 'commentstore1', 'cs1_comment', 'cs1_id', $nestedMsgComment, null, [
610 'text' => '(Main Page)',
611 'message' => $nestedMsgComment,
612 'data' => null,
613 ]
614 ],
615 'Simple table, CommentStoreComment' => [
616 'commentstore1', 'cs1_comment', 'cs1_id', clone $comStoreComment, [ 'baz' => 'baz' ], [
617 'text' => 'comment store comment',
618 'message' => $comStoreComment->message,
619 'data' => [ 'foo' => 'bar' ],
620 ]
621 ],
622
623 'Revision, text comment' => [
624 'commentstore2', 'cs2_comment', 'cs2_id', '{{text}} comment', null, [
625 'text' => '{{text}} comment',
626 'message' => $textCommentMsg,
627 'data' => null,
628 ]
629 ],
630 'Revision, text comment with data' => [
631 'commentstore2', 'cs2_comment', 'cs2_id', '{{text}} comment', [ 'message' => 42 ], [
632 'text' => '{{text}} comment',
633 'message' => $textCommentMsg,
634 'data' => [ 'message' => 42 ],
635 ]
636 ],
637 'Revision, message comment' => [
638 'commentstore2', 'cs2_comment', 'cs2_id', $msgComment, null, [
639 'text' => '(message comment)',
640 'message' => $msgComment,
641 'data' => null,
642 ]
643 ],
644 'Revision, message comment with data' => [
645 'commentstore2', 'cs2_comment', 'cs2_id', $msgComment, [ 'message' => 42 ], [
646 'text' => '(message comment)',
647 'message' => $msgComment,
648 'data' => [ 'message' => 42 ],
649 ]
650 ],
651 'Revision, nested message comment' => [
652 'commentstore2', 'cs2_comment', 'cs2_id', $nestedMsgComment, null, [
653 'text' => '(Main Page)',
654 'message' => $nestedMsgComment,
655 'data' => null,
656 ]
657 ],
658 'Revision, CommentStoreComment' => [
659 'commentstore2', 'cs2_comment', 'cs2_id', clone $comStoreComment, [ 'baz' => 'baz' ], [
660 'text' => 'comment store comment',
661 'message' => $comStoreComment->message,
662 'data' => [ 'foo' => 'bar' ],
663 ]
664 ],
665 ];
666 }
667
668 public function testGetCommentErrors() {
669 Wikimedia\suppressWarnings();
670 $reset = new ScopedCallback( 'Wikimedia\restoreWarnings' );
671
672 $store = $this->makeStore( MIGRATION_OLD );
673 $res = $store->getComment( 'dummy', [ 'dummy' => 'comment' ] );
674 $this->assertSame( '', $res->text );
675 $res = $store->getComment( 'dummy', [ 'dummy' => 'comment' ], true );
676 $this->assertSame( 'comment', $res->text );
677
678 $store = $this->makeStore( MIGRATION_NEW );
679 try {
680 $store->getComment( 'dummy', [ 'dummy' => 'comment' ] );
681 $this->fail( 'Expected exception not thrown' );
682 } catch ( InvalidArgumentException $ex ) {
683 $this->assertSame( '$row does not contain fields needed for comment dummy', $ex->getMessage() );
684 }
685 $res = $store->getComment( 'dummy', [ 'dummy' => 'comment' ], true );
686 $this->assertSame( 'comment', $res->text );
687 try {
688 $store->getComment( 'dummy', [ 'dummy_id' => 1 ] );
689 $this->fail( 'Expected exception not thrown' );
690 } catch ( InvalidArgumentException $ex ) {
691 $this->assertSame(
692 '$row does not contain fields needed for comment dummy and getComment(), '
693 . 'but does have fields for getCommentLegacy()',
694 $ex->getMessage()
695 );
696 }
697
698 $store = $this->makeStore( MIGRATION_NEW );
699 try {
700 $store->getComment( 'rev_comment', [ 'rev_comment' => 'comment' ] );
701 $this->fail( 'Expected exception not thrown' );
702 } catch ( InvalidArgumentException $ex ) {
703 $this->assertSame(
704 '$row does not contain fields needed for comment rev_comment', $ex->getMessage()
705 );
706 }
707 $res = $store->getComment( 'rev_comment', [ 'rev_comment' => 'comment' ], true );
708 $this->assertSame( 'comment', $res->text );
709 try {
710 $store->getComment( 'rev_comment', [ 'rev_comment_pk' => 1 ] );
711 $this->fail( 'Expected exception not thrown' );
712 } catch ( InvalidArgumentException $ex ) {
713 $this->assertSame(
714 '$row does not contain fields needed for comment rev_comment and getComment(), '
715 . 'but does have fields for getCommentLegacy()',
716 $ex->getMessage()
717 );
718 }
719 }
720
721 public static function provideStages() {
722 return [
723 'MIGRATION_OLD' => [ MIGRATION_OLD ],
724 'MIGRATION_WRITE_BOTH' => [ MIGRATION_WRITE_BOTH ],
725 'MIGRATION_WRITE_NEW' => [ MIGRATION_WRITE_NEW ],
726 'MIGRATION_NEW' => [ MIGRATION_NEW ],
727 ];
728 }
729
730 /**
731 * @dataProvider provideStages
732 * @param int $stage
733 * @expectedException InvalidArgumentException
734 * @expectedExceptionMessage Must use insertWithTempTable() for rev_comment
735 */
736 public function testInsertWrong( $stage ) {
737 $store = $this->makeStore( $stage );
738 $store->insert( $this->db, 'rev_comment', 'foo' );
739 }
740
741 /**
742 * @dataProvider provideStages
743 * @param int $stage
744 * @expectedException InvalidArgumentException
745 * @expectedExceptionMessage Must use insert() for ipb_reason
746 */
747 public function testInsertWithTempTableWrong( $stage ) {
748 $store = $this->makeStore( $stage );
749 $store->insertWithTempTable( $this->db, 'ipb_reason', 'foo' );
750 }
751
752 /**
753 * @dataProvider provideStages
754 * @param int $stage
755 */
756 public function testInsertWithTempTableDeprecated( $stage ) {
757 $store = $this->makeStore( $stage );
758 $wrap = TestingAccessWrapper::newFromObject( $store );
759 $wrap->tempTables += [ 'ipb_reason' => [
760 'stage' => MIGRATION_NEW,
761 'deprecatedIn' => '1.30',
762 ] ];
763
764 $this->hideDeprecated( 'CommentStore::insertWithTempTable for ipb_reason' );
765 list( $fields, $callback ) = $store->insertWithTempTable( $this->db, 'ipb_reason', 'foo' );
766 $this->assertTrue( is_callable( $callback ) );
767 }
768
769 public function testInsertTruncation() {
770 $comment = str_repeat( '💣', 16400 );
771 $truncated1 = str_repeat( '💣', 63 ) . '...';
772 $truncated2 = str_repeat( '💣', CommentStore::COMMENT_CHARACTER_LIMIT - 3 ) . '...';
773
774 $store = $this->makeStore( MIGRATION_WRITE_BOTH );
775 $fields = $store->insert( $this->db, 'ipb_reason', $comment );
776 $this->assertSame( $truncated1, $fields['ipb_reason'] );
777 $stored = $this->db->selectField(
778 'comment', 'comment_text', [ 'comment_id' => $fields['ipb_reason_id'] ], __METHOD__
779 );
780 $this->assertSame( $truncated2, $stored );
781 }
782
783 /**
784 * @expectedException OverflowException
785 * @expectedExceptionMessage Comment data is too long (65611 bytes, maximum is 65535)
786 */
787 public function testInsertTooMuchData() {
788 $store = $this->makeStore( MIGRATION_WRITE_BOTH );
789 $store->insert( $this->db, 'ipb_reason', 'foo', [
790 'long' => str_repeat( '💣', 16400 )
791 ] );
792 }
793
794 public function testGetStore() {
795 $this->assertInstanceOf( CommentStore::class, CommentStore::getStore() );
796 }
797
798 public function testNewKey() {
799 $this->hideDeprecated( 'CommentStore::newKey' );
800 $this->assertInstanceOf( CommentStore::class, CommentStore::newKey( 'dummy' ) );
801 }
802
803 }