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