wiki.techinc.nl/tests/phpunit/includes/ActorMigrationTest.php
Tim Starling 673d496f2d Have Database::addQuotes() pass through bare integers without quoting
Quotes started being added to integers in r4984 (August 2004). Before
that, is_numeric() was used to determine whether to add quotes, so
quotes were omitted from numeric strings, which is obviously wrong.

The idea here is to use the type of the variable to hint to the database
as to whether quotes are needed. The results are somewhat inconsistent,
since some callers do not convert numeric strings obtained from user
input to integers. That makes it a more conservative change. Callers can
opt out of unquoted integers by casting them to string.

The reason for doing this is that quoting integers turns out to be not
as harmless as originally assumed. We found a case of it confusing the
MariaDB query planner, causing inappropriate indexes to be used.

I also made addQuotes() consistently return a string, instead of
returning an integer for boolean values. This was already the case for
MySQL, but it seems like a good idea everywhere.

Bug: T238378
Change-Id: I70473280f542ee5ecd79e187f580807410fbd548
2019-11-18 11:40:28 +11:00

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() : void {
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() : void {
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->createMock( 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->createMock( 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
*/
public function testInsertWrong( $stage ) {
$m = new ActorMigration( $stage );
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( "Must use getInsertValuesWithTempTable() for am2_user" );
$m->getInsertValues( $this->db, 'am2_user', $this->getTestUser()->getUser() );
}
/**
* @dataProvider provideStages
* @param int $stage
*/
public function testInsertWithTempTableWrong( $stage ) {
$m = new ActorMigration( $stage );
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( "Must use getInsertValues() for am1_user" );
$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
*/
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() );
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( '$extra[foo_timestamp] is not provided' );
$callback( 1, [] );
}
/**
* @dataProvider provideStages
* @param int $stage
*/
public function testInsertUserIdentity( $stage ) {
$user = $this->getMutableTestUser()->getUser();
$userIdentity = $this->createMock( 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()
);
}
}
}