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