wiki.techinc.nl/tests/phpunit/includes/user/UserOptionsManagerTest.php
Petr Pchelko ff50d815a5 UserOptionsManager: take into account $queryFlags when caching
- Don't care about $queryFlags for anons since nothing can be
 stored in the database for anons.
- For loading the options - discard the old cache in case higher
 query flags are used. This means that 'setOption' has to by default
 reload the options to ensure changing the options start from LATEST.
 This codepath shouldn't be executed in reality cause we should
 be already loading the user with READ_LATEST if we want to update
 the options. Where that was not done - it was probably a bug.

Also, expose optional $queryFlags parameter for UserOptionsLookup
methods. Otherwise there's no way to read from master using public API.

Bug: T248527
Change-Id: Id7b9868ecdfba89bfafd4618365fe520ec59fcfe
2020-06-01 09:42:45 -07:00

231 lines
7.7 KiB
PHP

<?php
use MediaWiki\Config\ServiceOptions;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserOptionsLookup;
use MediaWiki\User\UserOptionsManager;
use Psr\Log\NullLogger;
/**
* @group Database
* @covers MediaWiki\User\UserOptionsManager
*/
class UserOptionsManagerTest extends UserOptionsLookupTest {
private function getManager(
string $langCode = 'qqq',
array $defaultOptionsOverrides = []
) {
$services = MediaWikiServices::getInstance();
return new UserOptionsManager(
new ServiceOptions(
UserOptionsManager::CONSTRUCTOR_OPTIONS,
new HashConfig( [ 'HiddenPrefs' => [ 'hidden_user_option' ] ] )
),
$this->getDefaultManager( $langCode, $defaultOptionsOverrides ),
$services->getLanguageConverterFactory(),
$services->getDBLoadBalancer(),
new NullLogger(),
$services->getHookContainer()
);
}
protected function getLookup(
string $langCode = 'qqq',
array $defaultOptionsOverrides = []
) : UserOptionsLookup {
return $this->getManager( $langCode, $defaultOptionsOverrides );
}
/**
* @covers MediaWiki\User\UserOptionsManager::getOption
*/
public function testGetOptionsExcludeDefaults() {
$manager = $this->getManager();
$manager->setOption( $this->getAnon( __METHOD__ ), 'new_option', 'new_value' );
$this->assertSame( [
'language' => 'en',
'variant' => 'en',
'new_option' => 'new_value'
], $manager->getOptions( $this->getAnon( __METHOD__ ), UserOptionsManager::EXCLUDE_DEFAULTS ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::getOption
*/
public function testGetOptionHiddenPref() {
$user = $this->getAnon( __METHOD__ );
$manager = $this->getManager();
$manager->setOption( $user, 'hidden_user_option', 'hidden_value' );
$this->assertNull( $manager->getOption( $user, 'hidden_user_option' ) );
$this->assertSame( 'hidden_value',
$manager->getOption( $user, 'hidden_user_option', null, true ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::setOption
*/
public function testSetOptionNullIsDefault() {
$user = $this->getAnon( __METHOD__ );
$manager = $this->getManager();
$manager->setOption( $user, 'default_string_option', 'override_value' );
$this->assertSame( 'override_value', $manager->getOption( $user, 'default_string_option' ) );
$manager->setOption( $user, 'default_string_option', null );
$this->assertSame( 'string_value', $manager->getOption( $user, 'default_string_option' ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::getOption
* @covers MediaWiki\User\UserOptionsManager::setOption
* @covers MediaWiki\User\UserOptionsManager::saveOptions
*/
public function testGetSetSave() {
$user = $this->getTestUser()->getUser();
$manager = $this->getManager();
$this->assertSame( [], $manager->getOptions( $user, UserOptionsManager::EXCLUDE_DEFAULTS ) );
$manager->setOption( $user, 'string_option', 'user_value' );
$manager->setOption( $user, 'int_option', 42 );
$manager->setOption( $user, 'bool_option', true );
$this->assertSame( 'user_value', $manager->getOption( $user, 'string_option' ) );
$this->assertSame( 42, $manager->getIntOption( $user, 'int_option' ) );
$this->assertSame( true, $manager->getBoolOption( $user, 'bool_option' ) );
$manager->saveOptions( $user );
$manager = $this->getManager();
$this->assertSame( 'user_value', $manager->getOption( $user, 'string_option' ) );
$this->assertSame( 42, $manager->getIntOption( $user, 'int_option' ) );
$this->assertSame( true, $manager->getBoolOption( $user, 'bool_option' ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::loadUserOptions
*/
public function testLoadUserOptionsHook() {
$user = $this->getTestUser()->getUser();
$this->setTemporaryHook(
'UserLoadOptions',
function ( User $hookUser, &$options ) use ( $user ) {
if ( $hookUser->equals( $user ) ) {
$options['from_hook'] = 'value_from_hook';
}
}
);
$this->assertSame( 'value_from_hook', $this->getManager()->getOption( $user, 'from_hook' ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::saveOptions
*/
public function testSaveUserOptionsHookAbort() {
$user = $this->getTestUser()->getUser();
$this->setTemporaryHook(
'UserSaveOptions',
function () {
return false;
}
);
$manager = $this->getManager();
$manager->setOption( $user, 'will_be_aborted_by_hook', 'value' );
$manager->saveOptions( $user );
$this->assertNull( $this->getManager()->getOption( $user, 'will_be_aborted_by_hook' ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::saveOptions
*/
public function testSaveUserOptionsHookModify() {
$user = $this->getTestUser()->getUser();
$this->setTemporaryHook(
'UserSaveOptions',
function ( User $hookUser, &$options ) use ( $user ) {
if ( $hookUser->equals( $user ) ) {
$options['from_hook'] = 'value_from_hook';
}
return true;
}
);
$manager = $this->getManager();
$manager->saveOptions( $user );
$this->assertSame( 'value_from_hook', $manager->getOption( $user, 'from_hook' ) );
$this->assertSame( 'value_from_hook', $this->getManager()->getOption( $user, 'from_hook' ) );
}
/**
* @covers MediaWiki\User\UserOptionsManager::saveOptions
*/
public function testSaveUserOptionsHookOriginal() {
$user = $this->getTestUser()->getUser();
$manager = $this->getManager();
$originalLanguage = $manager->getOption( $user, 'language' );
$manager->setOption( $user, 'language', 'ru' );
$this->setTemporaryHook(
'UserSaveOptions',
function ( User $hookUser, &$options, $originalOptions ) use ( $user, $originalLanguage ) {
if ( $hookUser->equals( $user ) ) {
$this->assertSame( $originalLanguage, $originalOptions['language'] );
$this->assertSame( 'ru', $options['language'] );
$options['language'] = 'tr';
}
return true;
}
);
$manager->saveOptions( $user );
$this->assertSame( 'tr', $manager->getOption( $user, 'language' ) );
}
/**
* @covers \MediaWiki\User\UserOptionsManager::saveOptions
* @covers \MediaWiki\User\UserOptionsManager::loadUserOptions
*/
public function testLoadOptionsHookReflectsInOriginalOptions() {
$user = $this->getTestUser()->getUser();
$manager = $this->getManager();
$this->setTemporaryHook(
'UserLoadOptions',
function ( User $hookUser, &$options ) use ( $user ) {
if ( $hookUser->equals( $user ) ) {
$options['from_load_hook'] = 'from_load_hook';
}
}
);
$this->setTemporaryHook(
'UserSaveOptions',
function ( User $hookUser, &$options, $originalOptions ) use ( $user ) {
if ( $hookUser->equals( $user ) ) {
$this->assertSame( 'from_load_hook', $options['from_load_hook'] );
$this->assertSame( 'from_load_hook', $originalOptions['from_load_hook'] );
$options['from_save_hook'] = 'from_save_hook';
}
return true;
}
);
$manager->saveOptions( $user );
$this->assertSame( 'from_load_hook', $manager->getOption( $user, 'from_load_hook' ) );
$this->assertSame( 'from_save_hook', $manager->getOption( $user, 'from_save_hook' ) );
}
/**
* @covers \MediaWiki\User\UserOptionsManager::loadUserOptions
*/
public function testInfiniteRecursionOnUserLoadOptionsHook() {
$user = $this->getTestUser()->getUser();
$manager = $this->getManager();
$recursionCounter = 0;
$this->setTemporaryHook(
'UserLoadOptions',
function ( User $hookUser ) use ( $user, $manager, &$recursionCounter ) {
if ( $hookUser->equals( $user ) ) {
$recursionCounter += 1;
$this->assertSame( 1, $recursionCounter );
$manager->loadUserOptions( $hookUser );
}
}
);
$manager->loadUserOptions( $user, UserOptionsManager::READ_LATEST );
$this->assertSame( 1, $recursionCounter );
}
public function testSaveOptionsForAnonUser() {
$this->expectException( InvalidArgumentException::class );
$this->getManager()->saveOptions( $this->getAnon( __METHOD__ ) );
}
}