Probably good to start testing this in CI sometime soon.
This also updates a bunch of tests that were forcing an older stage to
force SCHEMA_COMPAT_NEW instead, or to test both ways (until a future
patch removes the _OLD version).
Bug: T188327
Change-Id: Icb9b55cb9d754f2d30d6883005658b9670834756
* @since 1.32 changed allowed flags
* @var int An appropriate combination of SCHEMA_COMPAT_XXX flags.
*/
-$wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW;
+$wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_NEW;
/**
* Flag to enable Partial Blocks. This allows an admin to prevent a user from editing specific pages
$callback( 1, [] );
}
- public function testInsertUserIdentity() {
+ /**
+ * @dataProvider provideStages
+ * @param int $stage
+ */
+ public function testInsertUserIdentity( $stage ) {
$this->setMwGlobals( [
// for User::getActorId()
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
+ 'wgActorTableSchemaMigrationStage' => $stage
] );
$this->overrideMwServices();
- $user = $this->getTestUser()->getUser();
+ $user = $this->getMutableTestUser()->getUser();
$userIdentity = $this->getMock( UserIdentity::class );
$userIdentity->method( 'getId' )->willReturn( $user->getId() );
$userIdentity->method( 'getName' )->willReturn( $user->getName() );
list( $cFields, $cCallback ) = MediaWikiServices::getInstance()->getCommentStore()
->insertWithTempTable( $this->db, 'rev_comment', '' );
- $m = $this->makeMigration( SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW );
+ $m = $this->makeMigration( $stage );
list( $fields, $callback ) =
$m->getInsertValuesWithTempTable( $this->db, 'rev_user', $userIdentity );
$extraFields = [
);
$this->assertSame( $user->getId(), (int)$row->rev_user );
$this->assertSame( $user->getName(), $row->rev_user_text );
- $this->assertSame( $user->getActorId(), (int)$row->rev_actor );
+ $this->assertSame(
+ ( $stage & SCHEMA_COMPAT_READ_NEW ) ? $user->getActorId() : 0,
+ (int)$row->rev_actor
+ );
- $m = $this->makeMigration( SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW );
+ $m = $this->makeMigration( $stage );
$fields = $m->getInsertValues( $this->db, 'dummy_user', $userIdentity );
- $this->assertSame( $user->getId(), $fields['dummy_user'] );
- $this->assertSame( $user->getName(), $fields['dummy_user_text'] );
- $this->assertSame( $user->getActorId(), $fields['dummy_actor'] );
+ 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() {
$this->setMwGlobals( [
'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
+ 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
$this->overrideMwServices();
$queryInfo = $store->getQueryInfo( [ 'user' ] );
$row = get_object_vars( $row );
+
+ // Use aliased fields from $queryInfo, e.g. rev_user
+ $keys = array_keys( $row );
+ $keys = array_combine( $keys, $keys );
+ $fields = array_intersect_key( $queryInfo['fields'], $keys ) + $keys;
+
+ // assertSelect() fails unless the orders match.
+ ksort( $fields );
+ ksort( $row );
+
$this->assertSelect(
$queryInfo['tables'],
- array_keys( $row ),
+ $fields,
[ 'rev_id' => $rev->getId() ],
[ array_values( $row ) ],
[],
'rev_page' => (string)$rev->getPage(),
'rev_timestamp' => $this->db->timestamp( $rev->getTimestamp() ),
'rev_user_text' => (string)$rev->getUserText(),
- 'rev_user' => (string)$rev->getUser(),
+ 'rev_user' => (string)$rev->getUser() ?: null,
'rev_minor_edit' => $rev->isMinor() ? '1' : '0',
'rev_deleted' => (string)$rev->getVisibility(),
'rev_len' => (string)$rev->getSize(),
/** @var Revision $rev */
$rev = $page->doEditContent(
new WikitextContent( $text ),
- __METHOD__
+ __METHOD__,
+ 0,
+ false,
+ $this->getMutableTestUser()->getUser()
)->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
$this->setMwGlobals( [
'wgMultiContentRevisionSchemaMigrationStage' => $this->getMcrMigrationStage(),
'wgContentHandlerUseDB' => $this->getContentHandlerUseDB(),
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_OLD,
+ 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
$this->overrideMwServices();
* @covers Revision::loadFromTitle
*/
public function testLoadFromTitle() {
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_OLD );
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
$this->overrideMwServices();
$title = $this->getMockTitle();
$this->equalTo( [
'revision', 'page', 'user',
'temp_rev_comment' => 'revision_comment_temp', 'comment_rev_comment' => 'comment',
+ 'temp_rev_user' => 'revision_actor_temp', 'actor_rev_user' => 'actor',
] ),
// We don't really care about the fields are they come from the selectField methods
$this->isType( 'array' ),
$wgActorTableSchemaMigrationStage = $v;
$this->overrideMwServices();
}, [ $wgActorTableSchemaMigrationStage ] );
- $wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD;
+ // Needs to WRITE_BOTH so READ_OLD tests below work. READ mode here doesn't really matter.
+ $wgActorTableSchemaMigrationStage = SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW;
$this->overrideMwServices();
$users = [
return [
[
'ar_minor_edit' => '0',
- 'ar_user' => '0',
+ 'ar_user' => null,
'ar_user_text' => $this->ipEditor,
- 'ar_actor' => null,
+ 'ar_actor' => (string)User::newFromName( $this->ipEditor, false )->getActorId( $this->db ),
'ar_len' => '11',
'ar_deleted' => '0',
'ar_rev_id' => strval( $this->ipRev->getId() ),
'ar_minor_edit' => '0',
'ar_user' => (string)$this->getTestUser()->getUser()->getId(),
'ar_user_text' => $this->getTestUser()->getUser()->getName(),
- 'ar_actor' => null,
+ 'ar_actor' => (string)$this->getTestUser()->getUser()->getActorId(),
'ar_len' => '7',
'ar_deleted' => '0',
'ar_rev_id' => strval( $this->firstRev->getId() ),
return [
[
'ar_minor_edit' => '0',
- 'ar_user' => '0',
+ 'ar_user' => null,
'ar_user_text' => $this->ipEditor,
- 'ar_actor' => null,
+ 'ar_actor' => (string)User::newFromName( $this->ipEditor, false )->getActorId( $this->db ),
'ar_len' => '11',
'ar_deleted' => '0',
'ar_rev_id' => strval( $this->ipRev->getId() ),
'ar_minor_edit' => '0',
'ar_user' => (string)$this->getTestUser()->getUser()->getId(),
'ar_user_text' => $this->getTestUser()->getUser()->getName(),
- 'ar_actor' => null,
+ 'ar_actor' => (string)$this->getTestUser()->getUser()->getActorId(),
'ar_len' => '7',
'ar_deleted' => '0',
'ar_rev_id' => strval( $this->firstRev->getId() ),
$this->tablesUsed += $this->getMcrTablesToReset();
- $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_OLD );
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
$this->setMwGlobals( 'wgContentHandlerUseDB', $this->getContentHandlerUseDB() );
$this->setMwGlobals(
'wgMultiContentRevisionSchemaMigrationStage',
}
public function testRcHidemyselfFilter() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $user = $this->getTestUser()->getUser();
+ $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "NOT((rc_actor = '{$user->getActorId()}'))",
+ ],
+ [
+ 'hidemyself' => 1,
+ ],
+ "rc conditions: hidemyself=1 (logged in)",
+ $user
+ );
+
+ $user = User::newFromName( '10.11.12.13', false );
+ $id = $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "NOT((rc_actor = '{$user->getActorId()}'))",
+ ],
+ [
+ 'hidemyself' => 1,
+ ],
+ "rc conditions: hidemyself=1 (anon)",
+ $user
+ );
+ }
+
+ public function testRcHidemyselfFilter_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
}
public function testRcHidebyothersFilter() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $user = $this->getTestUser()->getUser();
+ $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "(rc_actor = '{$user->getActorId()}')",
+ ],
+ [
+ 'hidebyothers' => 1,
+ ],
+ "rc conditions: hidebyothers=1 (logged in)",
+ $user
+ );
+
+ $user = User::newFromName( '10.11.12.13', false );
+ $id = $user->getActorId( wfGetDB( DB_MASTER ) );
+ $this->assertConditions(
+ [ # expected
+ "(rc_actor = '{$user->getActorId()}')",
+ ],
+ [
+ 'hidebyothers' => 1,
+ ],
+ "rc conditions: hidebyothers=1 (anon)",
+ $user
+ );
+ }
+
+ public function testRcHidebyothersFilter_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
}
public function testFilterUserExpLevelAllExperienceLevels() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'actor_rc_user.actor_user IS NOT NULL',
+ ],
+ [
+ 'userExpLevel' => 'newcomer;learner;experienced',
+ ],
+ "rc conditions: userExpLevel=newcomer;learner;experienced"
+ );
+ }
+
+ public function testFilterUserExpLevelAllExperienceLevels_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
}
public function testFilterUserExpLevelRegistrered() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'actor_rc_user.actor_user IS NOT NULL',
+ ],
+ [
+ 'userExpLevel' => 'registered',
+ ],
+ "rc conditions: userExpLevel=registered"
+ );
+ }
+
+ public function testFilterUserExpLevelRegistrered_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
}
public function testFilterUserExpLevelUnregistrered() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'actor_rc_user.actor_user IS NULL',
+ ],
+ [
+ 'userExpLevel' => 'unregistered',
+ ],
+ "rc conditions: userExpLevel=unregistered"
+ );
+ }
+
+ public function testFilterUserExpLevelUnregistrered_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
}
public function testFilterUserExpLevelRegistreredOrLearner() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $this->assertConditions(
+ [
+ # expected
+ 'actor_rc_user.actor_user IS NOT NULL',
+ ],
+ [
+ 'userExpLevel' => 'registered;learner',
+ ],
+ "rc conditions: userExpLevel=registered;learner"
+ );
+ }
+
+ public function testFilterUserExpLevelRegistreredOrLearner_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
}
public function testFilterUserExpLevelUnregistreredOrExperienced() {
+ $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW );
+ $this->overrideMwServices();
+
+ $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
+
+ $this->assertRegExp(
+ '/\(actor_rc_user\.actor_user IS NULL\) OR '
+ . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
+ reset( $conds ),
+ "rc conditions: userExpLevel=unregistered;experienced"
+ );
+ }
+
+ public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
$this->setMwGlobals(
'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
);
$this->setMwGlobals( [
'wgGroupPermissions' => [],
'wgRevokePermissions' => [],
- 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
+ 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_NEW,
] );
$this->overrideMwServices();
$user = User::newFromId( $id );
$this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
+ $user2 = User::newFromActorId( $user->getActorId() );
+ $this->assertEquals( $user->getId(), $user2->getId(),
+ 'User::newFromActorId works for an existing user' );
+
+ $row = $this->db->selectRow( 'user', User::selectFields(), [ 'user_id' => $id ], __METHOD__ );
+ $user = User::newFromRow( $row );
+ $this->assertTrue( $user->getActorId() > 0,
+ 'Actor ID can be retrieved for user loaded with User::selectFields()' );
+
+ $user = User::newFromId( $id );
+ $user->setName( 'UserTestActorId4-renamed' );
+ $user->saveSettings();
+ $this->assertEquals(
+ $user->getName(),
+ $this->db->selectField(
+ 'actor', 'actor_name', [ 'actor_id' => $user->getActorId() ], __METHOD__
+ ),
+ 'User::saveSettings updates actor table for name change'
+ );
+
+ // For sanity
+ $ip = '192.168.12.34';
+ $this->db->delete( 'actor', [ 'actor_name' => $ip ], __METHOD__ );
+
+ $user = User::newFromName( $ip, false );
+ $this->assertFalse( $user->getActorId() > 0, 'Anonymous user has no actor ID by default' );
+ $this->assertTrue( $user->getActorId( $this->db ) > 0,
+ 'Actor ID can be created for an anonymous user' );
+
+ $user = User::newFromName( $ip, false );
+ $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be loaded for an anonymous user' );
+ $user2 = User::newFromActorId( $user->getActorId() );
+ $this->assertEquals( $user->getName(), $user2->getName(),
+ 'User::newFromActorId works for an anonymous user' );
+ }
+
+ /**
+ * Actor tests with SCHEMA_COMPAT_READ_OLD
+ *
+ * The only thing different from testActorId() is the behavior if the actor
+ * row doesn't exist in the DB, since with SCHEMA_COMPAT_READ_NEW that
+ * situation can't happen. But we copy all the other tests too just for good measure.
+ *
+ * @covers User::newFromActorId
+ */
+ public function testActorId_old() {
+ $this->setMwGlobals( [
+ 'wgActorTableSchemaMigrationStage' => SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD,
+ ] );
+ $this->overrideMwServices();
+
+ $domain = MediaWikiServices::getInstance()->getDBLoadBalancer()->getLocalDomainID();
+ $this->hideDeprecated( 'User::selectFields' );
+
+ // Newly-created user has an actor ID
+ $user = User::createNew( 'UserTestActorIdOld1' );
+ $id = $user->getId();
+ $this->assertTrue( $user->getActorId() > 0, 'User::createNew sets an actor ID' );
+
+ $user = User::newFromName( 'UserTestActorIdOld2' );
+ $user->addToDatabase();
+ $this->assertTrue( $user->getActorId() > 0, 'User::addToDatabase sets an actor ID' );
+
+ $user = User::newFromName( 'UserTestActorIdOld1' );
+ $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by name' );
+
+ $user = User::newFromId( $id );
+ $this->assertTrue( $user->getActorId() > 0, 'Actor ID can be retrieved for user loaded by ID' );
+
$user2 = User::newFromActorId( $user->getActorId() );
$this->assertEquals( $user->getId(), $user2->getId(),
'User::newFromActorId works for an existing user' );
$this->assertFalse( $user->getActorId() > 0, 'No Actor ID by default if none in database' );
$this->assertTrue( $user->getActorId( $this->db ) > 0, 'Actor ID can be created if none in db' );
- $user->setName( 'UserTestActorId4-renamed' );
+ $user->setName( 'UserTestActorIdOld4-renamed' );
$user->saveSettings();
$this->assertEquals(
$user->getName(),