This removes most of the pre-actor user and user_text columns, and the $wgActorTableSchemaMigrationStage setting that used to determine whether the columns were used. rev_user and rev_user_text remain in the code, as on Wikimedia wikis the revision table is too large to alter at this time. A future change will combine that with the removal of rev_comment, rev_content_model, and rev_content_format (and the addition of rev_comment_id and rev_actor). ActorMigration's constructor continues to take a $stage parameter, and continues to have the logic for handling it, for the benefit of extensions that might need their own migration process. Code using ActorMigration for accessing the core fields should be updated to use the new actor fields directly. That will be done for in a followup. Bug: T188327 Change-Id: Id35544b879af1cd708f3efd303fce8d9a1b9eb02
771 lines
23 KiB
PHP
771 lines
23 KiB
PHP
<?php
|
|
|
|
use MediaWiki\User\UserIdentity;
|
|
use Wikimedia\ScopedCallback;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
/**
|
|
* @group Database
|
|
* @covers ActorMigration
|
|
*/
|
|
class ActorMigrationTest extends MediaWikiLangTestCase {
|
|
|
|
protected $resetActorMigration = null;
|
|
protected static $amId = 0;
|
|
|
|
protected $tablesUsed = [
|
|
'actor',
|
|
];
|
|
|
|
protected function setUp() {
|
|
parent::setUp();
|
|
|
|
$w = TestingAccessWrapper::newFromClass( ActorMigration::class );
|
|
$data = [
|
|
'tempTables' => $w->tempTables,
|
|
'formerTempTables' => $w->formerTempTables,
|
|
'deprecated' => $w->deprecated,
|
|
'removed' => $w->removed,
|
|
'specialFields' => $w->specialFields,
|
|
];
|
|
$this->resetActorMigration = new ScopedCallback( function ( $w, $data ) {
|
|
foreach ( $data as $k => $v ) {
|
|
$w->$k = $v;
|
|
}
|
|
}, [ $w, $data ] );
|
|
|
|
$w->tempTables = [
|
|
'am2_user' => [
|
|
'table' => 'actormigration2_temp',
|
|
'pk' => 'am2t_id',
|
|
'field' => 'am2t_actor',
|
|
'joinPK' => 'am2_id',
|
|
'extra' => [],
|
|
]
|
|
];
|
|
$w->specialFields = [
|
|
'am3_xxx' => [ 'am3_xxx_text', 'am3_xxx_actor' ],
|
|
];
|
|
}
|
|
|
|
protected function tearDown() {
|
|
parent::tearDown();
|
|
ScopedCallback::consume( $this->resetActorMigration );
|
|
}
|
|
|
|
protected function getSchemaOverrides( IMaintainableDatabase $db ) {
|
|
return [
|
|
'scripts' => [
|
|
__DIR__ . '/ActorMigrationTest.sql',
|
|
],
|
|
'drop' => [],
|
|
'create' => [ 'actormigration1', 'actormigration2', 'actormigration2_temp', 'actormigration3' ],
|
|
'alter' => [],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideConstructor
|
|
* @param int $stage
|
|
* @param string|null $exceptionMsg
|
|
*/
|
|
public function testConstructor( $stage, $exceptionMsg ) {
|
|
try {
|
|
$m = new ActorMigration( $stage );
|
|
if ( $exceptionMsg !== null ) {
|
|
$this->fail( 'Expected exception not thrown' );
|
|
}
|
|
$this->assertInstanceOf( ActorMigration::class, $m );
|
|
} catch ( InvalidArgumentException $ex ) {
|
|
$this->assertSame( $exceptionMsg, $ex->getMessage() );
|
|
}
|
|
}
|
|
|
|
public static function provideConstructor() {
|
|
return [
|
|
[ 0, '$stage must include a write mode' ],
|
|
[ SCHEMA_COMPAT_READ_OLD, '$stage must include a write mode' ],
|
|
[ SCHEMA_COMPAT_READ_NEW, '$stage must include a write mode' ],
|
|
[ SCHEMA_COMPAT_READ_BOTH, '$stage must include a write mode' ],
|
|
|
|
[ SCHEMA_COMPAT_WRITE_OLD, '$stage must include a read mode' ],
|
|
[ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_OLD, null ],
|
|
[
|
|
SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_NEW,
|
|
'Cannot read the new schema without also writing it'
|
|
],
|
|
[ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_BOTH, 'Cannot read both schemas' ],
|
|
|
|
[ SCHEMA_COMPAT_WRITE_NEW, '$stage must include a read mode' ],
|
|
[
|
|
SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_OLD,
|
|
'Cannot read the old schema without also writing it'
|
|
],
|
|
[ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_NEW, null ],
|
|
[ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_BOTH, 'Cannot read both schemas' ],
|
|
|
|
[ SCHEMA_COMPAT_WRITE_BOTH, '$stage must include a read mode' ],
|
|
[ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, null ],
|
|
[ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, null ],
|
|
[ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_BOTH, 'Cannot read both schemas' ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetJoin
|
|
* @param int $stage
|
|
* @param string $key
|
|
* @param array $expect
|
|
*/
|
|
public function testGetJoin( $stage, $key, $expect ) {
|
|
$m = new ActorMigration( $stage );
|
|
$result = $m->getJoin( $key );
|
|
$this->assertEquals( $expect, $result );
|
|
}
|
|
|
|
public static function provideGetJoin() {
|
|
return [
|
|
'Simple table, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am1_user', [
|
|
'tables' => [],
|
|
'fields' => [
|
|
'am1_user' => 'am1_user',
|
|
'am1_user_text' => 'am1_user_text',
|
|
'am1_actor' => 'NULL',
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Simple table, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', [
|
|
'tables' => [],
|
|
'fields' => [
|
|
'am1_user' => 'am1_user',
|
|
'am1_user_text' => 'am1_user_text',
|
|
'am1_actor' => 'NULL',
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Simple table, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', [
|
|
'tables' => [ 'actor_am1_user' => 'actor' ],
|
|
'fields' => [
|
|
'am1_user' => 'actor_am1_user.actor_user',
|
|
'am1_user_text' => 'actor_am1_user.actor_name',
|
|
'am1_actor' => 'am1_actor',
|
|
],
|
|
'joins' => [
|
|
'actor_am1_user' => [ 'JOIN', 'actor_am1_user.actor_id = am1_actor' ],
|
|
],
|
|
],
|
|
],
|
|
'Simple table, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am1_user', [
|
|
'tables' => [ 'actor_am1_user' => 'actor' ],
|
|
'fields' => [
|
|
'am1_user' => 'actor_am1_user.actor_user',
|
|
'am1_user_text' => 'actor_am1_user.actor_name',
|
|
'am1_actor' => 'am1_actor',
|
|
],
|
|
'joins' => [
|
|
'actor_am1_user' => [ 'JOIN', 'actor_am1_user.actor_id = am1_actor' ],
|
|
],
|
|
],
|
|
],
|
|
|
|
'Special name, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am3_xxx', [
|
|
'tables' => [],
|
|
'fields' => [
|
|
'am3_xxx' => 'am3_xxx',
|
|
'am3_xxx_text' => 'am3_xxx_text',
|
|
'am3_xxx_actor' => 'NULL',
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Special name, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am3_xxx', [
|
|
'tables' => [],
|
|
'fields' => [
|
|
'am3_xxx' => 'am3_xxx',
|
|
'am3_xxx_text' => 'am3_xxx_text',
|
|
'am3_xxx_actor' => 'NULL',
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Special name, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am3_xxx', [
|
|
'tables' => [ 'actor_am3_xxx' => 'actor' ],
|
|
'fields' => [
|
|
'am3_xxx' => 'actor_am3_xxx.actor_user',
|
|
'am3_xxx_text' => 'actor_am3_xxx.actor_name',
|
|
'am3_xxx_actor' => 'am3_xxx_actor',
|
|
],
|
|
'joins' => [
|
|
'actor_am3_xxx' => [ 'JOIN', 'actor_am3_xxx.actor_id = am3_xxx_actor' ],
|
|
],
|
|
],
|
|
],
|
|
'Special name, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am3_xxx', [
|
|
'tables' => [ 'actor_am3_xxx' => 'actor' ],
|
|
'fields' => [
|
|
'am3_xxx' => 'actor_am3_xxx.actor_user',
|
|
'am3_xxx_text' => 'actor_am3_xxx.actor_name',
|
|
'am3_xxx_actor' => 'am3_xxx_actor',
|
|
],
|
|
'joins' => [
|
|
'actor_am3_xxx' => [ 'JOIN', 'actor_am3_xxx.actor_id = am3_xxx_actor' ],
|
|
],
|
|
],
|
|
],
|
|
|
|
'Temp table, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am2_user', [
|
|
'tables' => [],
|
|
'fields' => [
|
|
'am2_user' => 'am2_user',
|
|
'am2_user_text' => 'am2_user_text',
|
|
'am2_actor' => 'NULL',
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Temp table, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am2_user', [
|
|
'tables' => [],
|
|
'fields' => [
|
|
'am2_user' => 'am2_user',
|
|
'am2_user_text' => 'am2_user_text',
|
|
'am2_actor' => 'NULL',
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Temp table, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am2_user', [
|
|
'tables' => [
|
|
'temp_am2_user' => 'actormigration2_temp',
|
|
'actor_am2_user' => 'actor',
|
|
],
|
|
'fields' => [
|
|
'am2_user' => 'actor_am2_user.actor_user',
|
|
'am2_user_text' => 'actor_am2_user.actor_name',
|
|
'am2_actor' => 'temp_am2_user.am2t_actor',
|
|
],
|
|
'joins' => [
|
|
'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
|
|
'actor_am2_user' => [ 'JOIN', 'actor_am2_user.actor_id = temp_am2_user.am2t_actor' ],
|
|
],
|
|
],
|
|
],
|
|
'Temp table, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am2_user', [
|
|
'tables' => [
|
|
'temp_am2_user' => 'actormigration2_temp',
|
|
'actor_am2_user' => 'actor',
|
|
],
|
|
'fields' => [
|
|
'am2_user' => 'actor_am2_user.actor_user',
|
|
'am2_user_text' => 'actor_am2_user.actor_name',
|
|
'am2_actor' => 'temp_am2_user.am2t_actor',
|
|
],
|
|
'joins' => [
|
|
'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
|
|
'actor_am2_user' => [ 'JOIN', 'actor_am2_user.actor_id = temp_am2_user.am2t_actor' ],
|
|
],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetWhere
|
|
* @param int $stage
|
|
* @param string $key
|
|
* @param UserIdentity[] $users
|
|
* @param bool $useId
|
|
* @param array $expect
|
|
*/
|
|
public function testGetWhere( $stage, $key, $users, $useId, $expect ) {
|
|
$expect['conds'] = '(' . implode( ') OR (', $expect['orconds'] ) . ')';
|
|
|
|
if ( count( $users ) === 1 ) {
|
|
$users = reset( $users );
|
|
}
|
|
|
|
$m = new ActorMigration( $stage );
|
|
$result = $m->getWhere( $this->db, $key, $users, $useId );
|
|
$this->assertEquals( $expect, $result );
|
|
}
|
|
|
|
public function provideGetWhere() {
|
|
$makeUserIdentity = function ( $id, $name, $actor ) {
|
|
$u = $this->getMock( UserIdentity::class );
|
|
$u->method( 'getId' )->willReturn( $id );
|
|
$u->method( 'getName' )->willReturn( $name );
|
|
$u->method( 'getActorId' )->willReturn( $actor );
|
|
return $u;
|
|
};
|
|
|
|
$genericUser = [ $makeUserIdentity( 1, 'User1', 11 ) ];
|
|
$complicatedUsers = [
|
|
$makeUserIdentity( 1, 'User1', 11 ),
|
|
$makeUserIdentity( 2, 'User2', 12 ),
|
|
$makeUserIdentity( 3, 'User3', 0 ),
|
|
$makeUserIdentity( 0, '192.168.12.34', 34 ),
|
|
$makeUserIdentity( 0, '192.168.12.35', 0 ),
|
|
];
|
|
|
|
return [
|
|
'Simple table, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am1_user', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'userid' => "am1_user = '1'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Simple table, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'userid' => "am1_user = '1'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Simple table, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am1_actor = '11'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Simple table, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am1_user', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am1_actor = '11'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
|
|
'Special name, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am3_xxx', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'userid' => "am3_xxx = '1'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Special name, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am3_xxx', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'userid' => "am3_xxx = '1'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Special name, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am3_xxx', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am3_xxx_actor = '11'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Special name, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am3_xxx', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am3_xxx_actor = '11'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
|
|
'Temp table, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am2_user', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'userid' => "am2_user = '1'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Temp table, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am2_user', $genericUser, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'userid' => "am2_user = '1'" ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Temp table, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am2_user', $genericUser, true, [
|
|
'tables' => [
|
|
'temp_am2_user' => 'actormigration2_temp',
|
|
],
|
|
'orconds' => [ 'actor' => "temp_am2_user.am2t_actor = '11'" ],
|
|
'joins' => [
|
|
'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
|
|
],
|
|
],
|
|
],
|
|
'Temp table, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am2_user', $genericUser, true, [
|
|
'tables' => [
|
|
'temp_am2_user' => 'actormigration2_temp',
|
|
],
|
|
'orconds' => [ 'actor' => "temp_am2_user.am2t_actor = '11'" ],
|
|
'joins' => [
|
|
'temp_am2_user' => [ 'JOIN', 'temp_am2_user.am2t_id = am2_id' ],
|
|
],
|
|
],
|
|
],
|
|
|
|
'Multiple users, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am1_user', $complicatedUsers, true, [
|
|
'tables' => [],
|
|
'orconds' => [
|
|
'userid' => "am1_user IN ('1','2','3') ",
|
|
'username' => "am1_user_text IN ('192.168.12.34','192.168.12.35') "
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Multiple users, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $complicatedUsers, true, [
|
|
'tables' => [],
|
|
'orconds' => [
|
|
'userid' => "am1_user IN ('1','2','3') ",
|
|
'username' => "am1_user_text IN ('192.168.12.34','192.168.12.35') "
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Multiple users, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $complicatedUsers, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Multiple users, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am1_user', $complicatedUsers, true, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
|
|
'Multiple users, no use ID, old' => [
|
|
SCHEMA_COMPAT_OLD, 'am1_user', $complicatedUsers, false, [
|
|
'tables' => [],
|
|
'orconds' => [
|
|
'username' => "am1_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Multiple users, read-old' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'am1_user', $complicatedUsers, false, [
|
|
'tables' => [],
|
|
'orconds' => [
|
|
'username' => "am1_user_text IN ('User1','User2','User3','192.168.12.34','192.168.12.35') "
|
|
],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Multiple users, read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'am1_user', $complicatedUsers, false, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
'Multiple users, new' => [
|
|
SCHEMA_COMPAT_NEW, 'am1_user', $complicatedUsers, false, [
|
|
'tables' => [],
|
|
'orconds' => [ 'actor' => "am1_actor IN ('11','12','34') " ],
|
|
'joins' => [],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideInsertRoundTrip
|
|
* @param string $table
|
|
* @param string $key
|
|
* @param string $pk
|
|
* @param bool $usesTemp
|
|
*/
|
|
public function testInsertRoundTrip( $table, $key, $pk, $usesTemp ) {
|
|
$u = $this->getTestUser()->getUser();
|
|
$user = $this->getMock( UserIdentity::class );
|
|
$user->method( 'getId' )->willReturn( $u->getId() );
|
|
$user->method( 'getName' )->willReturn( $u->getName() );
|
|
$user->method( 'getActorId' )->willReturn( $u->getActorId( $this->db ) );
|
|
|
|
$stageNames = [
|
|
SCHEMA_COMPAT_OLD => 'old',
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD => 'write-both-read-old',
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW => 'write-both-read-new',
|
|
SCHEMA_COMPAT_NEW => 'new',
|
|
];
|
|
|
|
$stages = [
|
|
SCHEMA_COMPAT_OLD => [
|
|
SCHEMA_COMPAT_OLD,
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
|
|
],
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD => [
|
|
SCHEMA_COMPAT_OLD,
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
|
|
SCHEMA_COMPAT_NEW
|
|
],
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW => [
|
|
SCHEMA_COMPAT_OLD,
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
|
|
SCHEMA_COMPAT_NEW
|
|
],
|
|
SCHEMA_COMPAT_NEW => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW,
|
|
SCHEMA_COMPAT_NEW
|
|
],
|
|
];
|
|
|
|
$nameKey = $key . '_text';
|
|
$actorKey = ( $key === 'am3_xxx' ? $key : substr( $key, 0, -5 ) ) . '_actor';
|
|
|
|
foreach ( $stages as $writeStage => $possibleReadStages ) {
|
|
$w = new ActorMigration( $writeStage );
|
|
|
|
if ( $usesTemp ) {
|
|
list( $fields, $callback ) = $w->getInsertValuesWithTempTable( $this->db, $key, $user );
|
|
} else {
|
|
$fields = $w->getInsertValues( $this->db, $key, $user );
|
|
}
|
|
|
|
if ( $writeStage & SCHEMA_COMPAT_WRITE_OLD ) {
|
|
$this->assertSame( $user->getId(), $fields[$key],
|
|
"old field, stage={$stageNames[$writeStage]}" );
|
|
$this->assertSame( $user->getName(), $fields[$nameKey],
|
|
"old field, stage={$stageNames[$writeStage]}" );
|
|
} else {
|
|
$this->assertArrayNotHasKey( $key, $fields, "old field, stage={$stageNames[$writeStage]}" );
|
|
$this->assertArrayNotHasKey( $nameKey, $fields, "old field, stage={$stageNames[$writeStage]}" );
|
|
}
|
|
if ( ( $writeStage & SCHEMA_COMPAT_WRITE_NEW ) && !$usesTemp ) {
|
|
$this->assertSame( $user->getActorId(), $fields[$actorKey],
|
|
"new field, stage={$stageNames[$writeStage]}" );
|
|
} else {
|
|
$this->assertArrayNotHasKey( $actorKey, $fields,
|
|
"new field, stage={$stageNames[$writeStage]}" );
|
|
}
|
|
|
|
$id = ++self::$amId;
|
|
$this->db->insert( $table, [ $pk => $id ] + $fields, __METHOD__ );
|
|
if ( $usesTemp ) {
|
|
$callback( $id, [] );
|
|
}
|
|
|
|
foreach ( $possibleReadStages as $readStage ) {
|
|
$r = new ActorMigration( $readStage );
|
|
|
|
$queryInfo = $r->getJoin( $key );
|
|
$row = $this->db->selectRow(
|
|
[ $table ] + $queryInfo['tables'],
|
|
$queryInfo['fields'],
|
|
[ $pk => $id ],
|
|
__METHOD__,
|
|
[],
|
|
$queryInfo['joins']
|
|
);
|
|
|
|
$this->assertSame( $user->getId(), (int)$row->$key,
|
|
"w={$stageNames[$writeStage]}, r={$stageNames[$readStage]}, id" );
|
|
$this->assertSame( $user->getName(), $row->$nameKey,
|
|
"w={$stageNames[$writeStage]}, r={$stageNames[$readStage]}, name" );
|
|
$this->assertSame(
|
|
( $readStage & SCHEMA_COMPAT_READ_OLD ) ? 0 : $user->getActorId(),
|
|
(int)$row->$actorKey,
|
|
"w={$stageNames[$writeStage]}, r={$stageNames[$readStage]}, actor"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function provideInsertRoundTrip() {
|
|
return [
|
|
'normal' => [ 'actormigration1', 'am1_user', 'am1_id', false ],
|
|
'temp table' => [ 'actormigration2', 'am2_user', 'am2_id', true ],
|
|
'special name' => [ 'actormigration3', 'am3_xxx', 'am3_id', false ],
|
|
];
|
|
}
|
|
|
|
public static function provideStages() {
|
|
return [
|
|
'old' => [ SCHEMA_COMPAT_OLD ],
|
|
'read-old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD ],
|
|
'read-new' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW ],
|
|
'new' => [ SCHEMA_COMPAT_NEW ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStages
|
|
* @param int $stage
|
|
* @expectedException InvalidArgumentException
|
|
* @expectedExceptionMessage Must use getInsertValuesWithTempTable() for am2_user
|
|
*/
|
|
public function testInsertWrong( $stage ) {
|
|
$m = new ActorMigration( $stage );
|
|
$m->getInsertValues( $this->db, 'am2_user', $this->getTestUser()->getUser() );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStages
|
|
* @param int $stage
|
|
* @expectedException InvalidArgumentException
|
|
* @expectedExceptionMessage Must use getInsertValues() for am1_user
|
|
*/
|
|
public function testInsertWithTempTableWrong( $stage ) {
|
|
$m = new ActorMigration( $stage );
|
|
$m->getInsertValuesWithTempTable( $this->db, 'am1_user', $this->getTestUser()->getUser() );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStages
|
|
* @param int $stage
|
|
*/
|
|
public function testInsertWithTempTableDeprecated( $stage ) {
|
|
$wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
|
|
$wrap->formerTempTables += [ 'am1_user' => '1.30' ];
|
|
|
|
$this->hideDeprecated( 'ActorMigration::getInsertValuesWithTempTable for am1_user' );
|
|
$m = new ActorMigration( $stage );
|
|
list( $fields, $callback )
|
|
= $m->getInsertValuesWithTempTable( $this->db, 'am1_user', $this->getTestUser()->getUser() );
|
|
$this->assertTrue( is_callable( $callback ) );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStages
|
|
* @param int $stage
|
|
* @expectedException InvalidArgumentException
|
|
* @expectedExceptionMessage $extra[foo_timestamp] is not provided
|
|
*/
|
|
public function testInsertWithTempTableCallbackMissingFields( $stage ) {
|
|
$w = TestingAccessWrapper::newFromClass( ActorMigration::class );
|
|
$w->tempTables = [
|
|
'foo_user' => [
|
|
'table' => 'foo_temp',
|
|
'pk' => 'footmp_id',
|
|
'field' => 'footmp_actor',
|
|
'joinPK' => 'foo_id',
|
|
'extra' => [ 'footmp_timestamp' => 'foo_timestamp' ],
|
|
],
|
|
];
|
|
|
|
$m = new ActorMigration( $stage );
|
|
list( $fields, $callback )
|
|
= $m->getInsertValuesWithTempTable( $this->db, 'foo_user', $this->getTestUser()->getUser() );
|
|
$callback( 1, [] );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideStages
|
|
* @param int $stage
|
|
*/
|
|
public function testInsertUserIdentity( $stage ) {
|
|
$user = $this->getMutableTestUser()->getUser();
|
|
$userIdentity = $this->getMock( UserIdentity::class );
|
|
$userIdentity->method( 'getId' )->willReturn( $user->getId() );
|
|
$userIdentity->method( 'getName' )->willReturn( $user->getName() );
|
|
$userIdentity->method( 'getActorId' )->willReturn( 0 );
|
|
|
|
$m = new ActorMigration( $stage );
|
|
list( $fields, $callback ) =
|
|
$m->getInsertValuesWithTempTable( $this->db, 'am2_user', $userIdentity );
|
|
$id = ++self::$amId;
|
|
$this->db->insert( 'actormigration2', [ 'am2_id' => $id ] + $fields, __METHOD__ );
|
|
$callback( $id, [] );
|
|
|
|
$qi = $m->getJoin( 'am2_user' );
|
|
$row = $this->db->selectRow(
|
|
[ 'actormigration2' ] + $qi['tables'],
|
|
$qi['fields'],
|
|
[ 'am2_id' => $id ],
|
|
__METHOD__,
|
|
[],
|
|
$qi['joins']
|
|
);
|
|
$this->assertSame( $user->getId(), (int)$row->am2_user );
|
|
$this->assertSame( $user->getName(), $row->am2_user_text );
|
|
$this->assertSame(
|
|
( $stage & SCHEMA_COMPAT_READ_NEW ) ? $user->getActorId() : 0,
|
|
(int)$row->am2_actor
|
|
);
|
|
|
|
$m = new ActorMigration( $stage );
|
|
$fields = $m->getInsertValues( $this->db, 'dummy_user', $userIdentity );
|
|
if ( $stage & SCHEMA_COMPAT_WRITE_OLD ) {
|
|
$this->assertSame( $user->getId(), $fields['dummy_user'] );
|
|
$this->assertSame( $user->getName(), $fields['dummy_user_text'] );
|
|
} else {
|
|
$this->assertArrayNotHasKey( 'dummy_user', $fields );
|
|
$this->assertArrayNotHasKey( 'dummy_user_text', $fields );
|
|
}
|
|
if ( $stage & SCHEMA_COMPAT_WRITE_NEW ) {
|
|
$this->assertSame( $user->getActorId(), $fields['dummy_actor'] );
|
|
} else {
|
|
$this->assertArrayNotHasKey( 'dummy_actor', $fields );
|
|
}
|
|
}
|
|
|
|
public function testNewMigration() {
|
|
$m = ActorMigration::newMigration();
|
|
$this->assertInstanceOf( ActorMigration::class, $m );
|
|
$this->assertSame( $m, ActorMigration::newMigration() );
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideIsAnon
|
|
* @param int $stage
|
|
* @param string $isAnon
|
|
* @param string $isNotAnon
|
|
*/
|
|
public function testIsAnon( $stage, $isAnon, $isNotAnon ) {
|
|
$m = new ActorMigration( $stage );
|
|
$this->assertSame( $isAnon, $m->isAnon( 'foo' ) );
|
|
$this->assertSame( $isNotAnon, $m->isNotAnon( 'foo' ) );
|
|
}
|
|
|
|
public static function provideIsAnon() {
|
|
return [
|
|
'old' => [ SCHEMA_COMPAT_OLD, 'foo = 0', 'foo != 0' ],
|
|
'read-old' => [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, 'foo = 0', 'foo != 0' ],
|
|
'read-new' => [
|
|
SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, 'foo IS NULL', 'foo IS NOT NULL'
|
|
],
|
|
'new' => [ SCHEMA_COMPAT_NEW, 'foo IS NULL', 'foo IS NOT NULL' ],
|
|
];
|
|
}
|
|
|
|
public function testCheckDeprecation() {
|
|
$wrap = TestingAccessWrapper::newFromClass( ActorMigration::class );
|
|
$wrap->deprecated += [ 'soft' => null, 'hard' => '1.34' ];
|
|
$wrap->removed += [ 'gone' => '1.34' ];
|
|
|
|
$this->hideDeprecated( 'ActorMigration for \'hard\'' );
|
|
|
|
$wrap->checkDeprecation( 'valid' );
|
|
$wrap->checkDeprecation( 'soft' );
|
|
$wrap->checkDeprecation( 'hard' );
|
|
try {
|
|
$wrap->checkDeprecation( 'gone' );
|
|
} catch ( InvalidArgumentException $ex ) {
|
|
$this->assertSame(
|
|
'Use of ActorMigration for \'gone\' was removed in MediaWiki 1.34',
|
|
$ex->getMessage()
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|