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