A service for read-only mode

Introduce a service to represent wfReadOnly() and friends.

It's necessary to have two service instances, one for wfReadOnly() and
one for wfConfiguredReadOnlyReason(), to avoid a circular dependency,
since LoadBalancer needs the configured reason during construction, but
wfReadOnly() needs to query the currently active load balancer.

Not having a cache of the configuration makes it possible to dynamically
change the configuration. Ideally things would not change the
configuration, and I removed such instances in core, but to support
extensions, I added a test ensuring that the configuration can be changed.

Change-Id: I9bbee946c10742526d3423208efd68cb3cc5a7ee
This commit is contained in:
Tim Starling 2017-04-10 15:34:30 +10:00 committed by Timo Tijhof
parent 0a873ed202
commit 820f46964f
13 changed files with 553 additions and 115 deletions

View file

@ -285,6 +285,7 @@ $wgAutoloadLocalClasses = [
'Config' => __DIR__ . '/includes/config/Config.php',
'ConfigException' => __DIR__ . '/includes/config/ConfigException.php',
'ConfigFactory' => __DIR__ . '/includes/config/ConfigFactory.php',
'ConfiguredReadOnlyMode' => __DIR__ . '/includes/ReadOnlyMode.php',
'ConstantDependency' => __DIR__ . '/includes/cache/CacheDependency.php',
'Content' => __DIR__ . '/includes/content/Content.php',
'ContentHandler' => __DIR__ . '/includes/content/ContentHandler.php',
@ -1160,6 +1161,7 @@ $wgAutoloadLocalClasses = [
'RawAction' => __DIR__ . '/includes/actions/RawAction.php',
'RawMessage' => __DIR__ . '/includes/Message.php',
'ReadOnlyError' => __DIR__ . '/includes/exception/ReadOnlyError.php',
'ReadOnlyMode' => __DIR__ . '/includes/ReadOnlyMode.php',
'ReassignEdits' => __DIR__ . '/maintenance/reassignEdits.php',
'RebuildAll' => __DIR__ . '/maintenance/rebuildall.php',
'RebuildFileCache' => __DIR__ . '/maintenance/rebuildFileCache.php',

View file

@ -1273,7 +1273,8 @@ function wfIncrStats( $key, $count = 1 ) {
* @return bool
*/
function wfReadOnly() {
return wfReadOnlyReason() !== false;
return \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
->isReadOnly();
}
/**
@ -1285,19 +1286,8 @@ function wfReadOnly() {
* @return string|bool String when in read-only mode; false otherwise
*/
function wfReadOnlyReason() {
$readOnly = wfConfiguredReadOnlyReason();
if ( $readOnly !== false ) {
return $readOnly;
}
static $lbReadOnly = null;
if ( $lbReadOnly === null ) {
// Callers use this method to be aware that data presented to a user
// may be very stale and thus allowing submissions can be problematic.
$lbReadOnly = wfGetLB()->getReadOnlyReason();
}
return $lbReadOnly;
return \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
->getReason();
}
/**
@ -1307,18 +1297,8 @@ function wfReadOnlyReason() {
* @since 1.27
*/
function wfConfiguredReadOnlyReason() {
global $wgReadOnly, $wgReadOnlyFile;
if ( $wgReadOnly === null ) {
// Set $wgReadOnly for faster access next time
if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
$wgReadOnly = file_get_contents( $wgReadOnlyFile );
} else {
$wgReadOnly = false;
}
}
return $wgReadOnly;
return \MediaWiki\MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
->getReason();
}
/**

View file

@ -656,6 +656,22 @@ class MediaWikiServices extends ServiceContainer {
return $this->getService( 'VirtualRESTServiceClient' );
}
/**
* @since 1.29
* @return \ReadOnlyMode
*/
public function getConfiguredReadOnlyMode() {
return $this->getService( 'ConfiguredReadOnlyMode' );
}
/**
* @since 1.29
* @return \ReadOnlyMode
*/
public function getReadOnlyMode() {
return $this->getService( 'ReadOnlyMode' );
}
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service getter here, don't forget to add a test
// case for it in MediaWikiServicesTest::provideGetters() and in

128
includes/ReadOnlyMode.php Normal file
View file

@ -0,0 +1,128 @@
<?php
use Wikimedia\Rdbms\LoadBalancer;
/**
* A service class for fetching the wiki's current read-only mode.
* To obtain an instance, use MediaWikiServices::getReadOnlyMode().
*
* @since 1.29
*/
class ReadOnlyMode {
private $configuredReadOnly;
private $loadBalancer;
public function __construct( ConfiguredReadOnlyMode $cro, LoadBalancer $loadBalancer ) {
$this->configuredReadOnly = $cro;
$this->loadBalancer = $loadBalancer;
}
/**
* Check whether the wiki is in read-only mode.
*
* @return bool
*/
public function isReadOnly() {
return $this->getReason() !== false;
}
/**
* Check if the site is in read-only mode and return the message if so
*
* This checks the configuration and registered DB load balancers for
* read-only mode. This may result in DB connection being made.
*
* @return string|bool String when in read-only mode; false otherwise
*/
public function getReason() {
$reason = $this->configuredReadOnly->getReason();
if ( $reason !== false ) {
return $reason;
}
$reason = $this->loadBalancer->getReadOnlyReason();
if ( $reason !== false && $reason !== null ) {
return $reason;
}
return false;
}
/**
* Set the read-only mode, which will apply for the remainder of the
* request or until a service reset.
*/
public function setReason( $msg ) {
$this->configuredReadOnly->setReason( $msg );
}
/**
* Clear the cache of the read only file
*/
public function clearCache() {
$this->configuredReadOnly->clearCache();
}
}
/**
* A read-only mode service which does not depend on LoadBalancer.
* To obtain an instance, use MediaWikiServices::getConfiguredReadOnlyMode().
*
* @since 1.29
*/
class ConfiguredReadOnlyMode {
private $config;
private $fileReason;
private $overrideReason;
public function __construct( Config $config ) {
$this->config = $config;
}
/**
* Check whether the wiki is in read-only mode.
*
* @return bool
*/
public function isReadOnly() {
return $this->getReason() !== false;
}
/**
* Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
*
* @return string|bool String when in read-only mode; false otherwise
*/
public function getReason() {
if ( $this->overrideReason !== null ) {
return $this->overrideReason;
}
$confReason = $this->config->get( 'ReadOnly' );
if ( $confReason !== null ) {
return $confReason;
}
if ( $this->fileReason === null ) {
// Cache for faster access next time
$readOnlyFile = $this->config->get( 'ReadOnlyFile' );
if ( is_file( $readOnlyFile ) && filesize( $readOnlyFile ) > 0 ) {
$this->fileReason = file_get_contents( $readOnlyFile );
} else {
$this->fileReason = false;
}
}
return $this->fileReason;
}
/**
* Set the read-only mode, which will apply for the remainder of the
* request or until a service reset.
*/
public function setReason( $msg ) {
$this->overrideReason = $msg;
}
/**
* Clear the cache of the read only file
*/
public function clearCache() {
$this->fileReason = null;
}
}

View file

@ -48,7 +48,8 @@ return [
$lbConf = MWLBFactory::applyDefaultConfig(
$mainConfig->get( 'LBFactoryConf' ),
$mainConfig
$mainConfig,
$services->getConfiguredReadOnlyMode()
);
$class = MWLBFactory::getLBFactoryClass( $lbConf );
@ -155,7 +156,8 @@ return [
'WatchedItemStore' => function( MediaWikiServices $services ) {
$store = new WatchedItemStore(
$services->getDBLoadBalancer(),
new HashBagOStuff( [ 'maxKeys' => 100 ] )
new HashBagOStuff( [ 'maxKeys' => 100 ] ),
$services->getReadOnlyMode()
);
$store->setStatsdDataFactory( $services->getStatsdDataFactory() );
return $store;
@ -404,6 +406,17 @@ return [
return $vrsClient;
},
'ConfiguredReadOnlyMode' => function( MediaWikiServices $services ) {
return new ConfiguredReadOnlyMode( $services->getMainConfig() );
},
'ReadOnlyMode' => function( MediaWikiServices $services ) {
return new ReadOnlyMode(
$services->getConfiguredReadOnlyMode(),
$services->getDBLoadBalancer()
);
},
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service here, don't forget to add a getter function
// in the MediaWikiServices class. The convenience getter should just call

View file

@ -30,6 +30,11 @@ class WatchedItemStore implements StatsdAwareInterface {
*/
private $loadBalancer;
/**
* @var ReadOnlyMode
*/
private $readOnlyMode;
/**
* @var HashBagOStuff
*/
@ -61,13 +66,16 @@ class WatchedItemStore implements StatsdAwareInterface {
/**
* @param LoadBalancer $loadBalancer
* @param HashBagOStuff $cache
* @param ReadOnlyMode $readOnlyMode
*/
public function __construct(
LoadBalancer $loadBalancer,
HashBagOStuff $cache
HashBagOStuff $cache,
ReadOnlyMode $readOnlyMode
) {
$this->loadBalancer = $loadBalancer;
$this->cache = $cache;
$this->readOnlyMode = $readOnlyMode;
$this->stats = new NullStatsdDataFactory();
$this->deferredUpdatesAddCallableUpdateCallback = [ 'DeferredUpdates', 'addCallableUpdate' ];
$this->revisionGetTimestampFromIdCallback = [ 'Revision', 'getTimestampFromId' ];
@ -596,7 +604,7 @@ class WatchedItemStore implements StatsdAwareInterface {
* @return bool success
*/
public function addWatchBatchForUser( User $user, array $targets ) {
if ( $this->loadBalancer->getReadOnlyReason() !== false ) {
if ( $this->readOnlyMode->isReadOnly() ) {
return false;
}
// Only loggedin user can have a watchlist
@ -654,7 +662,7 @@ class WatchedItemStore implements StatsdAwareInterface {
*/
public function removeWatch( User $user, LinkTarget $target ) {
// Only logged in user can have a watchlist
if ( $this->loadBalancer->getReadOnlyReason() !== false || $user->isAnon() ) {
if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) {
return false;
}
@ -785,7 +793,7 @@ class WatchedItemStore implements StatsdAwareInterface {
*/
public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 ) {
// Only loggedin user can have a watchlist
if ( $this->loadBalancer->getReadOnlyReason() !== false || $user->isAnon() ) {
if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) {
return false;
}

View file

@ -33,9 +33,12 @@ abstract class MWLBFactory {
/**
* @param array $lbConf Config for LBFactory::__construct()
* @param Config $mainConfig Main config object from MediaWikiServices
* @param ConfiguredReadOnlyMode $readOnlyMode
* @return array
*/
public static function applyDefaultConfig( array $lbConf, Config $mainConfig ) {
public static function applyDefaultConfig( array $lbConf, Config $mainConfig,
ConfiguredReadOnlyMode $readOnlyMode
) {
global $wgCommandLineMode;
static $typesWithSchema = [ 'postgres', 'msssql' ];
@ -55,8 +58,7 @@ abstract class MWLBFactory {
'errorLogger' => [ MWExceptionHandler::class, 'logException' ],
'cliMode' => $wgCommandLineMode,
'hostname' => wfHostname(),
// TODO: replace the global wfConfiguredReadOnlyReason() with a service.
'readOnlyReason' => wfConfiguredReadOnlyReason(),
'readOnlyReason' => $readOnlyMode->getReason(),
];
// When making changes here, remember to also specify MediaWiki-specific options

View file

@ -41,7 +41,7 @@ class RebuildFileCache extends Maintenance {
}
public function finalSetup() {
global $wgDebugToolbar, $wgUseFileCache, $wgReadOnly;
global $wgDebugToolbar, $wgUseFileCache;
$this->enabled = $wgUseFileCache;
// Script will handle capturing output and saving it itself
@ -50,7 +50,8 @@ class RebuildFileCache extends Maintenance {
// Has to be done before Setup.php initialize MWDebug
$wgDebugToolbar = false;
// Avoid DB writes (like enotif/counters)
$wgReadOnly = 'Building cache'; // avoid DB writes (like enotif/counters)
MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
->setReason( 'Building cache' );
parent::finalSetup();
}

View file

@ -63,7 +63,8 @@ class ImageBuilder extends Maintenance {
$this->dbw = $this->getDB( DB_MASTER );
$this->dryrun = $this->hasOption( 'dry-run' );
if ( $this->dryrun ) {
$GLOBALS['wgReadOnly'] = 'Dry run mode, image upgrades are suppressed';
MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
->setReason( 'Dry run mode, image upgrades are suppressed' );
}
if ( $this->hasOption( 'missing' ) ) {

View file

@ -102,22 +102,28 @@ class GlobalTest extends MediaWikiTestCase {
}
/**
* Intended to cover the relevant bits of ServiceWiring.php, as well as GlobalFunctions.php
* @covers ::wfReadOnly
*/
public function testReadOnlyEmpty() {
global $wgReadOnly;
$wgReadOnly = null;
MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()->clearCache();
$this->assertFalse( wfReadOnly() );
$this->assertFalse( wfReadOnly() );
}
/**
* Intended to cover the relevant bits of ServiceWiring.php, as well as GlobalFunctions.php
* @covers ::wfReadOnly
*/
public function testReadOnlySet() {
global $wgReadOnly, $wgReadOnlyFile;
$readOnlyMode = MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->clearCache();
$f = fopen( $wgReadOnlyFile, "wt" );
fwrite( $f, 'Message' );
fclose( $f );
@ -127,10 +133,21 @@ class GlobalTest extends MediaWikiTestCase {
$this->assertTrue( wfReadOnly() ); # Check cached
unlink( $wgReadOnlyFile );
$wgReadOnly = null; # Clean cache
$readOnlyMode->clearCache();
$this->assertFalse( wfReadOnly() );
$this->assertFalse( wfReadOnly() );
}
$this->assertFalse( wfReadOnly() );
$this->assertFalse( wfReadOnly() );
/**
* This behaviour could probably be deprecated. Several extensions rely on it as of 1.29.
* @covers ::wfReadOnlyReason
*/
public function testReadOnlyGlobalChange() {
$this->assertFalse( wfReadOnlyReason() );
$this->setMwGlobals( [
'wgReadOnly' => 'reason'
] );
$this->assertSame( 'reason', wfReadOnlyReason() );
}
public static function provideArrayToCGI() {

View file

@ -0,0 +1,196 @@
<?php
use MediaWiki\MediaWikiServices;
/**
* @group Database
*
* @covers ReadOnlyMode
* @covers ConfiguredReadOnlyMode
*/
class ReadOnlyModeTest extends MediaWikiTestCase {
public function provider() {
$rawTests = [
'None of anything' => [
'confMessage' => null,
'hasFileName' => false,
'fileContents' => false,
'lbMessage' => false,
'expectedState' => false,
'expectedMessage' => false,
'expectedConfState' => false,
'expectedConfMessage' => false
],
'File missing' => [
'confMessage' => null,
'hasFileName' => true,
'fileContents' => false,
'lbMessage' => false,
'expectedState' => false,
'expectedMessage' => false,
'expectedConfState' => false,
'expectedConfMessage' => false
],
'File empty' => [
'confMessage' => null,
'hasFileName' => true,
'fileContents' => '',
'lbMessage' => false,
'expectedState' => false,
'expectedMessage' => false,
'expectedConfState' => false,
'expectedConfMessage' => false
],
'File has message' => [
'confMessage' => null,
'hasFileName' => true,
'fileContents' => 'Message',
'lbMessage' => false,
'expectedState' => true,
'expectedMessage' => 'Message',
'expectedConfState' => true,
'expectedConfMessage' => 'Message',
],
'Conf has message' => [
'confMessage' => 'Message',
'hasFileName' => false,
'fileContents' => false,
'lbMessage' => false,
'expectedState' => true,
'expectedMessage' => 'Message',
'expectedConfState' => true,
'expectedConfMessage' => 'Message'
],
"Conf=false means don't check the file" => [
'confMessage' => false,
'hasFileName' => true,
'fileContents' => 'Message',
'lbMessage' => false,
'expectedState' => false,
'expectedMessage' => false,
'expectedConfState' => false,
'expectedConfMessage' => false,
],
'LB has message' => [
'confMessage' => null,
'hasFileName' => false,
'fileContents' => false,
'lbMessage' => 'Message',
'expectedState' => true,
'expectedMessage' => 'Message',
'expectedConfState' => false,
'expectedConfMessage' => false
],
'All three have a message: conf wins' => [
'confMessage' => 'conf',
'hasFileName' => true,
'fileContents' => 'file',
'lbMessage' => 'lb',
'expectedState' => true,
'expectedMessage' => 'conf',
'expectedConfState' => true,
'expectedConfMessage' => 'conf'
]
];
$cookedTests = [];
foreach ( $rawTests as $desc => $test ) {
$cookedTests[$desc] = [ $test ];
}
return $cookedTests;
}
private function createMode( $params, $makeLB ) {
$config = new HashConfig( [
'ReadOnly' => $params['confMessage'],
'ReadOnlyFile' => $this->createFile( $params ),
] );
$rom = new ConfiguredReadOnlyMode( $config );
if ( $makeLB ) {
$lb = $this->createLB( $params );
$rom = new ReadOnlyMode( $rom, $lb );
}
return $rom;
}
private function createLB( $params ) {
$lb = $this->getMockBuilder( \Wikimedia\Rdbms\LoadBalancer::class )
->disableOriginalConstructor()
->getMock();
$lb->expects( $this->any() )->method( 'getReadOnlyReason' )
->willReturn( $params['lbMessage'] );
return $lb;
}
private function createFile( $params ) {
if ( $params['hasFileName'] ) {
$fileName = $this->getNewTempFile();
if ( $params['fileContents'] === false ) {
unlink( $fileName );
} else {
file_put_contents( $fileName, $params['fileContents'] );
}
} else {
$fileName = null;
}
return $fileName;
}
/**
* @dataProvider provider
*/
public function testWithLB( $params ) {
$rom = $this->createMode( $params, true );
$this->assertSame( $params['expectedMessage'], $rom->getReason() );
$this->assertSame( $params['expectedState'], $rom->isReadOnly() );
}
/**
* @dataProvider provider
*/
public function testWithoutLB( $params ) {
$cro = $this->createMode( $params, false );
$this->assertSame( $params['expectedConfMessage'], $cro->getReason() );
$this->assertSame( $params['expectedConfState'], $cro->isReadOnly() );
}
public function testSetReadOnlyReason() {
$rom = $this->createMode(
[
'confMessage' => 'conf',
'hasFileName' => false,
'fileContents' => false,
'lbMessage' => 'lb'
],
true );
$rom->setReason( 'override' );
$this->assertSame( 'override', $rom->getReason() );
}
/**
* @covers ReadOnlyMode::clearCache
* @covers ConfiguredReadOnlyMode::clearCache
*/
public function testClearCache() {
$fileName = $this->getNewTempFile();
unlink( $fileName );
$config = new HashConfig( [
'ReadOnly' => null,
'ReadOnlyFile' => $fileName,
] );
$cro = new ConfiguredReadOnlyMode( $config );
$lb = $this->createLB( [ 'lbMessage' => false ] );
$rom = new ReadOnlyMode( $cro, $lb );
$this->assertSame( false, $rom->getReason(), 'initial' );
file_put_contents( $fileName, 'file' );
$this->assertSame( false, $rom->getReason(), 'stale' );
$rom->clearCache();
$this->assertSame( 'file', $rom->getReason(), 'fresh' );
}
}

View file

@ -21,8 +21,7 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
*/
private function getMockLoadBalancer(
$mockDb,
$expectedConnectionType = null,
$readOnlyReason = false
$expectedConnectionType = null
) {
$mock = $this->getMockBuilder( LoadBalancer::class )
->disableOriginalConstructor()
@ -37,9 +36,6 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
->method( 'getConnectionRef' )
->will( $this->returnValue( $mockDb ) );
}
$mock->expects( $this->any() )
->method( 'getReadOnlyReason' )
->will( $this->returnValue( $readOnlyReason ) );
return $mock;
}
@ -58,6 +54,19 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
return $mock;
}
/**
* @return PHPUnit_Framework_MockObject_MockObject|ReadOnlyMode
*/
private function getMockReadOnlyMode( $readOnly = false ) {
$mock = $this->getMockBuilder( ReadOnlyMode::class )
->disableOriginalConstructor()
->getMock();
$mock->expects( $this->any() )
->method( 'isReadOnly' )
->will( $this->returnValue( $readOnly ) );
return $mock;
}
/**
* @param int $id
* @return PHPUnit_Framework_MockObject_MockObject|User
@ -88,10 +97,13 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
return $fakeRow;
}
private function newWatchedItemStore( LoadBalancer $loadBalancer, HashBagOStuff $cache ) {
private function newWatchedItemStore( LoadBalancer $loadBalancer, HashBagOStuff $cache,
ReadOnlyMode $readOnlyMode
) {
return new WatchedItemStore(
$loadBalancer,
$cache
$cache,
$readOnlyMode
);
}
@ -118,7 +130,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals( 12, $store->countWatchedItems( $user ) );
@ -148,7 +161,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals( 7, $store->countWatchers( $titleValue ) );
@ -199,7 +213,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$expected = [
@ -265,7 +280,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$expected = [
@ -313,7 +329,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals( 7, $store->countVisitingWatchers( $titleValue, '111' ) );
@ -392,7 +409,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$expected = [
@ -494,7 +512,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$expected = [
@ -547,7 +566,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$expected = [
@ -584,7 +604,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals( 9, $store->countUnreadNotifications( $user ) );
@ -618,7 +639,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertSame(
@ -655,7 +677,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -685,7 +708,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
$store->duplicateEntry(
@ -743,7 +767,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$store->duplicateEntry(
@ -789,7 +814,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$store->duplicateAllAssociatedEntries(
@ -882,7 +908,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$store->duplicateAllAssociatedEntries(
@ -914,7 +941,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$store->addWatch(
@ -934,7 +962,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$store->addWatch(
@ -945,8 +974,9 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
public function testAddWatchBatchForUser_readOnlyDBReturnsFalse() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb(), null, 'Some Reason' ),
$this->getMockCache()
$this->getMockLoadBalancer( $this->getMockDb() ),
$this->getMockCache(),
$this->getMockReadOnlyMode( true )
);
$this->assertFalse(
@ -991,7 +1021,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$mockUser = $this->getMockNonAnonUserWithId( 1 );
@ -1015,7 +1046,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1038,7 +1070,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertTrue(
@ -1072,7 +1105,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$watchedItem = $store->loadWatchedItem(
@ -1106,7 +1140,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1128,7 +1163,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1163,7 +1199,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertTrue(
@ -1198,7 +1235,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1221,7 +1259,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1265,7 +1304,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$watchedItem = $store->getWatchedItem(
@ -1299,7 +1339,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -1336,7 +1377,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1359,7 +1401,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1399,7 +1442,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$user = $this->getMockNonAnonUserWithId( 1 );
@ -1449,7 +1493,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$mockLoadBalancer,
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$watchedItems = $store->getWatchedItemsForUser(
@ -1462,7 +1507,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
public function testGetWatchedItemsForUser_badSortOptionThrowsException() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
$this->getMockCache()
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
$this->setExpectedException( 'InvalidArgumentException' );
@ -1503,7 +1549,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertTrue(
@ -1539,7 +1586,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1562,7 +1610,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1629,7 +1678,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -1679,7 +1729,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -1740,7 +1791,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -1780,7 +1832,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -1806,7 +1859,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -1830,7 +1884,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1863,7 +1918,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertFalse(
@ -1908,7 +1964,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
// Note: This does not actually assert the job is correct
@ -1948,7 +2005,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
// Note: This does not actually assert the job is correct
@ -2041,7 +2099,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$callableCallCounter = 0;
@ -2107,7 +2166,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$addUpdateCallCounter = 0;
@ -2182,7 +2242,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$callableCallCounter = 0;
@ -2248,7 +2309,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$addUpdateCallCounter = 0;
@ -2325,7 +2387,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$addUpdateCallCounter = 0;
@ -2370,7 +2433,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
public function testSetNotificationTimestampsForUser_anonUser() {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $this->getMockDb() ),
$this->getMockCache()
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
$this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) );
}
@ -2396,7 +2460,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
$this->assertTrue(
@ -2425,7 +2490,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
$this->assertTrue(
@ -2463,7 +2529,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$this->getMockCache()
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
$this->assertTrue(
@ -2505,7 +2572,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$this->assertEquals(
@ -2545,7 +2613,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
$watchers = $store->updateNotificationTimestamp(
@ -2588,7 +2657,8 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase {
$store = $this->newWatchedItemStore(
$this->getMockLoadBalancer( $mockDb ),
$mockCache
$mockCache,
$this->getMockReadOnlyMode()
);
// This will add the item to the cache

View file

@ -1404,12 +1404,13 @@ class AuthManagerTest extends \MediaWikiTestCase {
$this->manager->checkAccountCreatePermissions( new \User )
);
$this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
$this->assertEquals(
\Status::newFatal( 'readonlytext', 'Because' ),
$this->manager->checkAccountCreatePermissions( new \User )
);
$this->setMwGlobals( [ 'wgReadOnly' => false ] );
$readOnlyMode->setReason( false );
$wgGroupPermissions['*']['createaccount'] = false;
$status = $this->manager->checkAccountCreatePermissions( new \User );
@ -1597,7 +1598,8 @@ class AuthManagerTest extends \MediaWikiTestCase {
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'noname', $ret->message->getKey() );
$this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
$this->hook( 'LocalUserCreated', $this->never() );
$userReq->username = self::usernameForCreation();
$ret = $this->manager->beginAccountCreation( $creator, [ $userReq ], 'http://localhost/' );
@ -1605,7 +1607,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'readonlytext', $ret->message->getKey() );
$this->assertSame( [ 'Because' ], $ret->message->getParams() );
$this->setMwGlobals( [ 'wgReadOnly' => false ] );
$readOnlyMode->setReason( false );
$this->hook( 'LocalUserCreated', $this->never() );
$userReq->username = self::usernameForCreation();
@ -1770,14 +1772,15 @@ class AuthManagerTest extends \MediaWikiTestCase {
$this->request->getSession()->setSecret( 'AuthManager::accountCreationState',
[ 'username' => $creator->getName() ] + $session );
$this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
$this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->continueAccountCreation( [] );
$this->unhook( 'LocalUserCreated' );
$this->assertSame( AuthenticationResponse::FAIL, $ret->status );
$this->assertSame( 'readonlytext', $ret->message->getKey() );
$this->assertSame( [ 'Because' ], $ret->message->getParams() );
$this->setMwGlobals( [ 'wgReadOnly' => false ] );
$readOnlyMode->setReason( false );
$this->request->getSession()->setSecret( 'AuthManager::accountCreationState',
[ 'username' => $creator->getName() ] + $session );
@ -2468,7 +2471,8 @@ class AuthManagerTest extends \MediaWikiTestCase {
// Wiki is read-only
$session->clear();
$this->setMwGlobals( [ 'wgReadOnly' => 'Because' ] );
$readOnlyMode = \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode();
$readOnlyMode->setReason( 'Because' );
$user = \User::newFromName( $username );
$this->hook( 'LocalUserCreated', $this->never() );
$ret = $this->manager->autoCreateUser( $user, AuthManager::AUTOCREATE_SOURCE_SESSION, true );
@ -2481,7 +2485,7 @@ class AuthManagerTest extends \MediaWikiTestCase {
[ LogLevel::DEBUG, 'denied by wfReadOnly(): {reason}' ],
], $logger->getBuffer() );
$logger->clearBuffer();
$this->setMwGlobals( [ 'wgReadOnly' => false ] );
$readOnlyMode->setReason( false );
// Session blacklisted
$session->clear();