Merge "Selenium: replace UserLoginPage with BlankPage where possible"
[lhc/web/wiklou.git] / tests / phpunit / includes / ActorMigrationTest.php
1 <?php
2
3 use MediaWiki\User\UserIdentity;
4 use MediaWiki\MediaWikiServices;
5 use Wikimedia\TestingAccessWrapper;
6
7 /**
8 * @group Database
9 * @covers ActorMigration
10 */
11 class ActorMigrationTest extends MediaWikiLangTestCase {
12
13 protected $tablesUsed = [
14 'revision',
15 'revision_actor_temp',
16 'ipblocks',
17 'recentchanges',
18 'actor',
19 ];
20
21 /**
22 * @dataProvider provideConstructor
23 * @param int $stage
24 * @param string|null $exceptionMsg
25 */
26 public function testConstructor( $stage, $exceptionMsg ) {
27 try {
28 $m = new ActorMigration( $stage );
29 if ( $exceptionMsg !== null ) {
30 $this->fail( 'Expected exception not thrown' );
31 }
32 $this->assertInstanceOf( ActorMigration::class, $m );
33 } catch ( InvalidArgumentException $ex ) {
34 $this->assertSame( $exceptionMsg, $ex->getMessage() );
35 }
36 }
37
38 public static function provideConstructor() {
39 return [
40 [ 0, '$stage must include a write mode' ],
41 [ SCHEMA_COMPAT_READ_OLD, '$stage must include a write mode' ],
42 [ SCHEMA_COMPAT_READ_NEW, '$stage must include a write mode' ],
43 [ SCHEMA_COMPAT_READ_BOTH, '$stage must include a write mode' ],
44
45 [ SCHEMA_COMPAT_WRITE_OLD, '$stage must include a read mode' ],
46 [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_OLD, null ],
47 [
48 SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_NEW,
49 'Cannot read the new schema without also writing it'
50 ],
51 [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_BOTH, 'Cannot read both schemas' ],
52
53 [ SCHEMA_COMPAT_WRITE_NEW, '$stage must include a read mode' ],
54 [
55 SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_OLD,
56 'Cannot read the old schema without also writing it'
57 ],
58 [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_NEW, null ],
59 [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_BOTH, 'Cannot read both schemas' ],
60
61 [ SCHEMA_COMPAT_WRITE_BOTH, '$stage must include a read mode' ],
62 [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, null ],
63 [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, null ],
64 [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_BOTH, 'Cannot read both schemas' ],
65 ];
66 }
67
68 /**
69 * @dataProvider provideGetJoin
70 * @param int $stage
71 * @param string $key
72 * @param array $expect
73 */
74 public function testGetJoin( $stage, $key, $expect ) {
75 $m = new ActorMigration( $stage );
76 $result = $m->getJoin( $key );
77 $this->assertEquals( $expect, $result );
78 }
79
80 public static function provideGetJoin() {
81 return [
82 'Simple table, old' => [
83 SCHEMA_COMPAT_OLD, 'rc_user', [
84 'tables' => [],
85 'fields' => [
86 'rc_user' => 'rc_user',
87 'rc_user_text' => 'rc_user_text',
88 'rc_actor' => 'NULL',
89 ],
90 'joins' => [],
91 ],
92 ],
93 'Simple table, read-old' => [
94 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', [
95 'tables' => [],
96 'fields' => [
97 'rc_user' => 'rc_user',
98 'rc_user_text' => 'rc_user_text',
99 'rc_actor' => 'NULL',
100 ],
101 'joins' => [],
102 ],
103 ],
104 'Simple table, read-new' => [
105 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', [
106 'tables' => [ 'actor_rc_user' => 'actor' ],
107 'fields' => [
108 'rc_user' => 'actor_rc_user.actor_user',
109 'rc_user_text' => 'actor_rc_user.actor_name',
110 'rc_actor' => 'rc_actor',
111 ],
112 'joins' => [
113 'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
114 ],
115 ],
116 ],
117 'Simple table, new' => [
118 SCHEMA_COMPAT_NEW, 'rc_user', [
119 'tables' => [ 'actor_rc_user' => 'actor' ],
120 'fields' => [
121 'rc_user' => 'actor_rc_user.actor_user',
122 'rc_user_text' => 'actor_rc_user.actor_name',
123 'rc_actor' => 'rc_actor',
124 ],
125 'joins' => [
126 'actor_rc_user' => [ 'JOIN', 'actor_rc_user.actor_id = rc_actor' ],
127 ],
128 ],
129 ],
130
131 'ipblocks, old' => [
132 SCHEMA_COMPAT_OLD, 'ipb_by', [
133 'tables' => [],
134 'fields' => [
135 'ipb_by' => 'ipb_by',
136 'ipb_by_text' => 'ipb_by_text',
137 'ipb_by_actor' => 'NULL',
138 ],
139 'joins' => [],
140 ],
141 ],
142 'ipblocks, read-old' => [
143 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_by', [
144 'tables' => [],
145 'fields' => [
146 'ipb_by' => 'ipb_by',
147 'ipb_by_text' => 'ipb_by_text',
148 'ipb_by_actor' => 'NULL',
149 ],
150 'joins' => [],
151 ],
152 ],
153 'ipblocks, read-new' => [
154 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_by', [
155 'tables' => [ 'actor_ipb_by' => 'actor' ],
156 'fields' => [
157 'ipb_by' => 'actor_ipb_by.actor_user',
158 'ipb_by_text' => 'actor_ipb_by.actor_name',
159 'ipb_by_actor' => 'ipb_by_actor',
160 ],
161 'joins' => [
162 'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
163 ],
164 ],
165 ],
166 'ipblocks, new' => [
167 SCHEMA_COMPAT_NEW, 'ipb_by', [
168 'tables' => [ 'actor_ipb_by' => 'actor' ],
169 'fields' => [
170 'ipb_by' => 'actor_ipb_by.actor_user',
171 'ipb_by_text' => 'actor_ipb_by.actor_name',
172 'ipb_by_actor' => 'ipb_by_actor',
173 ],
174 'joins' => [
175 'actor_ipb_by' => [ 'JOIN', 'actor_ipb_by.actor_id = ipb_by_actor' ],
176 ],
177 ],
178 ],
179
180 'Revision, old' => [
181 SCHEMA_COMPAT_OLD, 'rev_user', [
182 'tables' => [],
183 'fields' => [
184 'rev_user' => 'rev_user',
185 'rev_user_text' => 'rev_user_text',
186 'rev_actor' => 'NULL',
187 ],
188 'joins' => [],
189 ],
190 ],
191 'Revision, read-old' => [
192 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_user', [
193 'tables' => [],
194 'fields' => [
195 'rev_user' => 'rev_user',
196 'rev_user_text' => 'rev_user_text',
197 'rev_actor' => 'NULL',
198 ],
199 'joins' => [],
200 ],
201 ],
202 'Revision, read-new' => [
203 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_user', [
204 'tables' => [
205 'temp_rev_user' => 'revision_actor_temp',
206 'actor_rev_user' => 'actor',
207 ],
208 'fields' => [
209 'rev_user' => 'actor_rev_user.actor_user',
210 'rev_user_text' => 'actor_rev_user.actor_name',
211 'rev_actor' => 'temp_rev_user.revactor_actor',
212 ],
213 'joins' => [
214 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
215 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
216 ],
217 ],
218 ],
219 'Revision, new' => [
220 SCHEMA_COMPAT_NEW, 'rev_user', [
221 'tables' => [
222 'temp_rev_user' => 'revision_actor_temp',
223 'actor_rev_user' => 'actor',
224 ],
225 'fields' => [
226 'rev_user' => 'actor_rev_user.actor_user',
227 'rev_user_text' => 'actor_rev_user.actor_name',
228 'rev_actor' => 'temp_rev_user.revactor_actor',
229 ],
230 'joins' => [
231 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
232 'actor_rev_user' => [ 'JOIN', 'actor_rev_user.actor_id = temp_rev_user.revactor_actor' ],
233 ],
234 ],
235 ],
236 ];
237 }
238
239 /**
240 * @dataProvider provideGetWhere
241 * @param int $stage
242 * @param string $key
243 * @param UserIdentity[] $users
244 * @param bool $useId
245 * @param array $expect
246 */
247 public function testGetWhere( $stage, $key, $users, $useId, $expect ) {
248 $expect['conds'] = '(' . implode( ') OR (', $expect['orconds'] ) . ')';
249
250 if ( count( $users ) === 1 ) {
251 $users = reset( $users );
252 }
253
254 $m = new ActorMigration( $stage );
255 $result = $m->getWhere( $this->db, $key, $users, $useId );
256 $this->assertEquals( $expect, $result );
257 }
258
259 public function provideGetWhere() {
260 $makeUserIdentity = function ( $id, $name, $actor ) {
261 $u = $this->getMock( UserIdentity::class );
262 $u->method( 'getId' )->willReturn( $id );
263 $u->method( 'getName' )->willReturn( $name );
264 $u->method( 'getActorId' )->willReturn( $actor );
265 return $u;
266 };
267
268 $genericUser = [ $makeUserIdentity( 1, 'User1', 11 ) ];
269 $complicatedUsers = [
270 $makeUserIdentity( 1, 'User1', 11 ),
271 $makeUserIdentity( 2, 'User2', 12 ),
272 $makeUserIdentity( 3, 'User3', 0 ),
273 $makeUserIdentity( 0, '192.168.12.34', 34 ),
274 $makeUserIdentity( 0, '192.168.12.35', 0 ),
275 ];
276
277 return [
278 'Simple table, old' => [
279 SCHEMA_COMPAT_OLD, 'rc_user', $genericUser, true, [
280 'tables' => [],
281 'orconds' => [ 'userid' => "rc_user = '1'" ],
282 'joins' => [],
283 ],
284 ],
285 'Simple table, read-old' => [
286 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $genericUser, true, [
287 'tables' => [],
288 'orconds' => [ 'userid' => "rc_user = '1'" ],
289 'joins' => [],
290 ],
291 ],
292 'Simple table, read-new' => [
293 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $genericUser, true, [
294 'tables' => [],
295 'orconds' => [ 'actor' => "rc_actor = '11'" ],
296 'joins' => [],
297 ],
298 ],
299 'Simple table, new' => [
300 SCHEMA_COMPAT_NEW, 'rc_user', $genericUser, true, [
301 'tables' => [],
302 'orconds' => [ 'actor' => "rc_actor = '11'" ],
303 'joins' => [],
304 ],
305 ],
306
307 'ipblocks, old' => [
308 SCHEMA_COMPAT_OLD, 'ipb_by', $genericUser, true, [
309 'tables' => [],
310 'orconds' => [ 'userid' => "ipb_by = '1'" ],
311 'joins' => [],
312 ],
313 ],
314 'ipblocks, read-old' => [
315 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'ipb_by', $genericUser, true, [
316 'tables' => [],
317 'orconds' => [ 'userid' => "ipb_by = '1'" ],
318 'joins' => [],
319 ],
320 ],
321 'ipblocks, read-new' => [
322 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'ipb_by', $genericUser, true, [
323 'tables' => [],
324 'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
325 'joins' => [],
326 ],
327 ],
328 'ipblocks, new' => [
329 SCHEMA_COMPAT_NEW, 'ipb_by', $genericUser, true, [
330 'tables' => [],
331 'orconds' => [ 'actor' => "ipb_by_actor = '11'" ],
332 'joins' => [],
333 ],
334 ],
335
336 'Revision, old' => [
337 SCHEMA_COMPAT_OLD, 'rev_user', $genericUser, true, [
338 'tables' => [],
339 'orconds' => [ 'userid' => "rev_user = '1'" ],
340 'joins' => [],
341 ],
342 ],
343 'Revision, read-old' => [
344 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rev_user', $genericUser, true, [
345 'tables' => [],
346 'orconds' => [ 'userid' => "rev_user = '1'" ],
347 'joins' => [],
348 ],
349 ],
350 'Revision, read-new' => [
351 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rev_user', $genericUser, true, [
352 'tables' => [
353 'temp_rev_user' => 'revision_actor_temp',
354 ],
355 'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
356 'joins' => [
357 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
358 ],
359 ],
360 ],
361 'Revision, new' => [
362 SCHEMA_COMPAT_NEW, 'rev_user', $genericUser, true, [
363 'tables' => [
364 'temp_rev_user' => 'revision_actor_temp',
365 ],
366 'orconds' => [ 'actor' => "temp_rev_user.revactor_actor = '11'" ],
367 'joins' => [
368 'temp_rev_user' => [ 'JOIN', 'temp_rev_user.revactor_rev = rev_id' ],
369 ],
370 ],
371 ],
372
373 'Multiple users, old' => [
374 SCHEMA_COMPAT_OLD, 'rc_user', $complicatedUsers, true, [
375 'tables' => [],
376 'orconds' => [
377 'userid' => "rc_user IN ('1','2','3') ",
378 'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
379 ],
380 'joins' => [],
381 ],
382 ],
383 'Multiple users, read-old' => [
384 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $complicatedUsers, true, [
385 'tables' => [],
386 'orconds' => [
387 'userid' => "rc_user IN ('1','2','3') ",
388 'username' => "rc_user_text IN ('192.168.12.34','192.168.12.35') "
389 ],
390 'joins' => [],
391 ],
392 ],
393 'Multiple users, read-new' => [
394 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $complicatedUsers, true, [
395 'tables' => [],
396 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
397 'joins' => [],
398 ],
399 ],
400 'Multiple users, new' => [
401 SCHEMA_COMPAT_NEW, 'rc_user', $complicatedUsers, true, [
402 'tables' => [],
403 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
404 'joins' => [],
405 ],
406 ],
407
408 'Multiple users, no use ID, old' => [
409 SCHEMA_COMPAT_OLD, 'rc_user', $complicatedUsers, false, [
410 'tables' => [],
411 'orconds' => [
412 'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
413 ],
414 'joins' => [],
415 ],
416 ],
417 'Multiple users, read-old' => [
418 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'rc_user', $complicatedUsers, false, [
419 'tables' => [],
420 'orconds' => [
421 'username' => "rc_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
422 ],
423 'joins' => [],
424 ],
425 ],
426 'Multiple users, read-new' => [
427 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'rc_user', $complicatedUsers, false, [
428 'tables' => [],
429 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
430 'joins' => [],
431 ],
432 ],
433 'Multiple users, new' => [
434 SCHEMA_COMPAT_NEW, 'rc_user', $complicatedUsers, false, [
435 'tables' => [],
436 'orconds' => [ 'actor' => "rc_actor IN ('11','12','34') " ],
437 'joins' => [],
438 ],
439 ],
440 ];
441 }
442
443 /**
444 * @dataProvider provideInsertRoundTrip
445 * @param string $table
446 * @param string $key
447 * @param string $pk
448 * @param array $extraFields
449 */
450 public function testInsertRoundTrip( $table, $key, $pk, $extraFields ) {
451 $u = $this->getTestUser()->getUser();
452 $user = $this->getMock( UserIdentity::class );
453 $user->method( 'getId' )->willReturn( $u->getId() );
454 $user->method( 'getName' )->willReturn( $u->getName() );
455 if ( $u->getActorId( $this->db ) ) {
456 $user->method( 'getActorId' )->willReturn( $u->getActorId() );
457 } else {
458 $this->db->insert(
459 'actor',
460 [ 'actor_user' => $u->getId(), 'actor_name' => $u->getName() ],
461 __METHOD__
462 );
463 $user->method( 'getActorId' )->willReturn( $this->db->insertId() );
464 }
465
466 $stageNames = [
467 SCHEMA_COMPAT_OLD => 'old',
468 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD => 'write-both-read-old',
469 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW => 'write-both-read-new',
470 SCHEMA_COMPAT_NEW => 'new',
471 ];
472
473 $stages = [
474 SCHEMA_COMPAT_OLD => [
475 SCHEMA_COMPAT_OLD,
476 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
477 ],
478 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD => [
479 SCHEMA_COMPAT_OLD,
480 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
481 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
482 SCHEMA_COMPAT_NEW
483 ],
484 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW => [
485 SCHEMA_COMPAT_OLD,
486 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
487 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
488 SCHEMA_COMPAT_NEW
489 ],
490 SCHEMA_COMPAT_NEW => [
491 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
492 SCHEMA_COMPAT_NEW
493 ],
494 ];
495
496 $nameKey = $key . '_text';
497 $actorKey = $key === 'ipb_by' ? 'ipb_by_actor' : substr( $key, 0, -5 ) . '_actor';
498
499 foreach ( $stages as $writeStage => $possibleReadStages ) {
500 if ( $key === 'ipb_by' ) {
501 $extraFields['ipb_address'] = __CLASS__ . "#{$stageNames[$writeStage]}";
502 }
503
504 $w = new ActorMigration( $writeStage );
505 $usesTemp = $key === 'rev_user';
506
507 if ( $usesTemp ) {
508 list( $fields, $callback ) = $w->getInsertValuesWithTempTable( $this->db, $key, $user );
509 } else {
510 $fields = $w->getInsertValues( $this->db, $key, $user );
511 }
512
513 if ( $writeStage & SCHEMA_COMPAT_WRITE_OLD ) {
514 $this->assertSame( $user->getId(), $fields[$key],
515 "old field, stage={$stageNames[$writeStage]}" );
516 $this->assertSame( $user->getName(), $fields[$nameKey],
517 "old field, stage={$stageNames[$writeStage]}" );
518 } else {
519 $this->assertArrayNotHasKey( $key, $fields, "old field, stage={$stageNames[$writeStage]}" );
520 $this->assertArrayNotHasKey( $nameKey, $fields, "old field, stage={$stageNames[$writeStage]}" );
521 }
522 if ( ( $writeStage & SCHEMA_COMPAT_WRITE_NEW ) && !$usesTemp ) {
523 $this->assertSame( $user->getActorId(), $fields[$actorKey],
524 "new field, stage={$stageNames[$writeStage]}" );
525 } else {
526 $this->assertArrayNotHasKey( $actorKey, $fields,
527 "new field, stage={$stageNames[$writeStage]}" );
528 }
529
530 $this->db->insert( $table, $extraFields + $fields, __METHOD__ );
531 $id = $this->db->insertId();
532 if ( $usesTemp ) {
533 $callback( $id, $extraFields );
534 }
535
536 foreach ( $possibleReadStages as $readStage ) {
537 $r = new ActorMigration( $readStage );
538
539 $queryInfo = $r->getJoin( $key );
540 $row = $this->db->selectRow(
541 [ $table ] + $queryInfo['tables'],
542 $queryInfo['fields'],
543 [ $pk => $id ],
544 __METHOD__,
545 [],
546 $queryInfo['joins']
547 );
548
549 $this->assertSame( $user->getId(), (int)$row->$key,
550 "w={$stageNames[$writeStage]}, r={$stageNames[$readStage]}, id" );
551 $this->assertSame( $user->getName(), $row->$nameKey,
552 "w={$stageNames[$writeStage]}, r={$stageNames[$readStage]}, name" );
553 $this->assertSame(
554 ( $readStage & SCHEMA_COMPAT_READ_OLD ) ? 0 : $user->getActorId(),
555 (int)$row->$actorKey,
556 "w={$stageNames[$writeStage]}, r={$stageNames[$readStage]}, actor"
557 );
558 }
559 }
560 }
561
562 public static function provideInsertRoundTrip() {
563 $db = wfGetDB( DB_REPLICA ); // for timestamps
564
565 $comment = MediaWikiServices::getInstance()->getCommentStore()
566 ->createComment( wfGetDB( DB_MASTER ), '' );
567
568 return [
569 'recentchanges' => [ 'recentchanges', 'rc_user', 'rc_id', [
570 'rc_timestamp' => $db->timestamp(),
571 'rc_namespace' => 0,
572 'rc_title' => 'Test',
573 'rc_this_oldid' => 42,
574 'rc_last_oldid' => 41,
575 'rc_source' => 'test',
576 'rc_comment_id' => $comment->id,
577 ] ],
578 'ipblocks' => [ 'ipblocks', 'ipb_by', 'ipb_id', [
579 'ipb_range_start' => '',
580 'ipb_range_end' => '',
581 'ipb_timestamp' => $db->timestamp(),
582 'ipb_expiry' => $db->getInfinity(),
583 'ipb_reason_id' => $comment->id,
584 ] ],
585 'revision' => [ 'revision', 'rev_user', 'rev_id', [
586 'rev_page' => 42,
587 'rev_len' => 0,
588 'rev_timestamp' => $db->timestamp(),
589 ] ],
590 ];
591 }
592
593 public static function provideStages() {
594 return [
595 'old' => [ SCHEMA_COMPAT_OLD ],
596 'read-old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD ],
597 'read-new' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW ],
598 'new' => [ SCHEMA_COMPAT_NEW ],
599 ];
600 }
601
602 /**
603 * @dataProvider provideStages
604 * @param int $stage
605 * @expectedException InvalidArgumentException
606 * @expectedExceptionMessage Must use getInsertValuesWithTempTable() for rev_user
607 */
608 public function testInsertWrong( $stage ) {
609 $m = new ActorMigration( $stage );
610 $m->getInsertValues( $this->db, 'rev_user', $this->getTestUser()->getUser() );
611 }
612
613 /**
614 * @dataProvider provideStages
615 * @param int $stage
616 * @expectedException InvalidArgumentException
617 * @expectedExceptionMessage Must use getInsertValues() for rc_user
618 */
619 public function testInsertWithTempTableWrong( $stage ) {
620 $m = new ActorMigration( $stage );
621 $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
622 }
623
624 /**
625 * @dataProvider provideStages
626 * @param int $stage
627 */
628 public function testInsertWithTempTableDeprecated( $stage ) {
629 $wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
630 $wrap->formerTempTables += [ 'rc_user' => '1.30' ];
631
632 $this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for rc_user' );
633 $m = new ActorMigration( $stage );
634 list( $fields, $callback )
635 = $m->getInsertValuesWithTempTable( $this->db, 'rc_user', $this->getTestUser()->getUser() );
636 $this->assertTrue( is_callable( $callback ) );
637 }
638
639 /**
640 * @dataProvider provideStages
641 * @param int $stage
642 * @expectedException InvalidArgumentException
643 * @expectedExceptionMessage $extra[rev_timestamp] is not provided
644 */
645 public function testInsertWithTempTableCallbackMissingFields( $stage ) {
646 $m = new ActorMigration( $stage );
647 list( $fields, $callback )
648 = $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $this->getTestUser()->getUser() );
649 $callback( 1, [] );
650 }
651
652 /**
653 * @dataProvider provideStages
654 * @param int $stage
655 */
656 public function testInsertUserIdentity( $stage ) {
657 $this->setMwGlobals( [
658 // for User::getActorId()
659 'wgActorTableSchemaMigrationStage' => $stage
660 ] );
661 $this->overrideMwServices();
662
663 $user = $this->getMutableTestUser()->getUser();
664 $userIdentity = $this->getMock( UserIdentity::class );
665 $userIdentity->method( 'getId' )->willReturn( $user->getId() );
666 $userIdentity->method( 'getName' )->willReturn( $user->getName() );
667 $userIdentity->method( 'getActorId' )->willReturn( 0 );
668
669 list( $cFields, $cCallback ) = MediaWikiServices::getInstance()->getCommentStore()
670 ->insertWithTempTable( $this->db, 'rev_comment', '' );
671 $m = new ActorMigration( $stage );
672 list( $fields, $callback ) =
673 $m->getInsertValuesWithTempTable( $this->db, 'rev_user', $userIdentity );
674 $extraFields = [
675 'rev_page' => 42,
676 'rev_len' => 0,
677 'rev_timestamp' => $this->db->timestamp(),
678 ] + $cFields;
679 $this->db->insert( 'revision', $extraFields + $fields, __METHOD__ );
680 $id = $this->db->insertId();
681 $callback( $id, $extraFields );
682 $cCallback( $id );
683
684 $qi = $m->getJoin( 'rev_user' );
685 $row = $this->db->selectRow(
686 [ 'revision' ] + $qi['tables'], $qi['fields'], [ 'rev_id' => $id ], __METHOD__, [], $qi['joins']
687 );
688 $this->assertSame( $user->getId(), (int)$row->rev_user );
689 $this->assertSame( $user->getName(), $row->rev_user_text );
690 $this->assertSame(
691 ( $stage & SCHEMA_COMPAT_READ_NEW ) ? $user->getActorId() : 0,
692 (int)$row->rev_actor
693 );
694
695 $m = new ActorMigration( $stage );
696 $fields = $m->getInsertValues( $this->db, 'dummy_user', $userIdentity );
697 if ( $stage & SCHEMA_COMPAT_WRITE_OLD ) {
698 $this->assertSame( $user->getId(), $fields['dummy_user'] );
699 $this->assertSame( $user->getName(), $fields['dummy_user_text'] );
700 } else {
701 $this->assertArrayNotHasKey( 'dummy_user', $fields );
702 $this->assertArrayNotHasKey( 'dummy_user_text', $fields );
703 }
704 if ( $stage & SCHEMA_COMPAT_WRITE_NEW ) {
705 $this->assertSame( $user->getActorId(), $fields['dummy_actor'] );
706 } else {
707 $this->assertArrayNotHasKey( 'dummy_actor', $fields );
708 }
709 }
710
711 public function testNewMigration() {
712 $m = ActorMigration::newMigration();
713 $this->assertInstanceOf( ActorMigration::class, $m );
714 $this->assertSame( $m, ActorMigration::newMigration() );
715 }
716
717 /**
718 * @dataProvider provideIsAnon
719 * @param int $stage
720 * @param string $isAnon
721 * @param string $isNotAnon
722 */
723 public function testIsAnon( $stage, $isAnon, $isNotAnon ) {
724 $m = new ActorMigration( $stage );
725 $this->assertSame( $isAnon, $m->isAnon( 'foo' ) );
726 $this->assertSame( $isNotAnon, $m->isNotAnon( 'foo' ) );
727 }
728
729 public static function provideIsAnon() {
730 return [
731 'old' => [ SCHEMA_COMPAT_OLD, 'foo = 0', 'foo != 0' ],
732 'read-old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'foo = 0', 'foo != 0' ],
733 'read-new' => [
734 SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'foo IS NULL', 'foo IS NOT NULL'
735 ],
736 'new' => [ SCHEMA_COMPAT_NEW, 'foo IS NULL', 'foo IS NOT NULL' ],
737 ];
738 }
739
740 }