2021-11-09 17:29:14 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace phpunit\unit\includes\Settings;
|
|
|
|
|
|
2021-11-16 20:41:38 +00:00
|
|
|
use InvalidArgumentException;
|
2021-11-13 04:21:34 +00:00
|
|
|
use MediaWiki\Settings\Cache\CacheableSource;
|
2021-11-09 17:29:14 +00:00
|
|
|
use MediaWiki\Settings\Config\ArrayConfigBuilder;
|
2021-11-16 20:41:38 +00:00
|
|
|
use MediaWiki\Settings\Config\ConfigBuilder;
|
2021-11-12 19:56:33 +00:00
|
|
|
use MediaWiki\Settings\Config\MergeStrategy;
|
2021-11-24 16:20:29 +00:00
|
|
|
use MediaWiki\Settings\Config\PhpIniSink;
|
2021-11-09 17:29:14 +00:00
|
|
|
use MediaWiki\Settings\SettingsBuilder;
|
|
|
|
|
use MediaWiki\Settings\SettingsBuilderException;
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
2021-11-13 04:21:34 +00:00
|
|
|
use Psr\SimpleCache\CacheInterface;
|
2021-11-09 17:29:14 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers \MediaWiki\Settings\SettingsBuilder
|
|
|
|
|
*/
|
|
|
|
|
class SettingsBuilderTest extends TestCase {
|
|
|
|
|
|
|
|
|
|
/**
|
2021-11-16 20:41:38 +00:00
|
|
|
* @param ConfigBuilder|null $configBuilder
|
2021-11-24 16:20:29 +00:00
|
|
|
* @param PhpIniSink|null $phpIniSink
|
2021-11-13 04:21:34 +00:00
|
|
|
* @param CacheInterface|null $cache
|
2021-11-09 17:29:14 +00:00
|
|
|
* @return SettingsBuilder
|
|
|
|
|
*/
|
2021-11-24 16:20:29 +00:00
|
|
|
private function newSettingsBuilder(
|
2021-11-16 20:41:38 +00:00
|
|
|
ConfigBuilder $configBuilder = null,
|
2021-11-13 04:21:34 +00:00
|
|
|
PhpIniSink $phpIniSink = null,
|
|
|
|
|
CacheInterface $cache = null
|
2021-11-24 16:20:29 +00:00
|
|
|
): SettingsBuilder {
|
2021-11-09 17:29:14 +00:00
|
|
|
return new SettingsBuilder(
|
|
|
|
|
__DIR__,
|
2021-11-24 16:20:29 +00:00
|
|
|
$configBuilder ?? new ArrayConfigBuilder(),
|
2021-11-13 04:21:34 +00:00
|
|
|
$phpIniSink ?? new PhpIniSink(),
|
|
|
|
|
$cache
|
2021-11-09 17:29:14 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testLoadingFromFile() {
|
|
|
|
|
$configBuilder = new ArrayConfigBuilder();
|
2021-11-24 16:20:29 +00:00
|
|
|
|
|
|
|
|
$phpIniSinkMock = $this->createMock( PhpIniSink::class );
|
|
|
|
|
$phpIniSinkMock->expects( $this->once() )->method( 'set' )->with( 'foo', 'bar' );
|
|
|
|
|
|
|
|
|
|
$setting = $this->newSettingsBuilder( $configBuilder, $phpIniSinkMock );
|
2021-11-09 17:29:14 +00:00
|
|
|
$setting->loadFile( 'fixtures/settings.json' )->apply();
|
|
|
|
|
|
|
|
|
|
$config = $configBuilder->build();
|
|
|
|
|
$this->assertSame( 'TEST', $config->get( 'Something' ) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function provideConfigOverrides() {
|
|
|
|
|
yield 'sets a value from a single settings file' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[ 'config' => [ 'MySetting' => 'MyValue', ], ],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => 'MyValue',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'merges different values from multiple settings files' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[ 'config' => [ 'MySetting' => 'MyValue', ], ],
|
|
|
|
|
[ 'config' => [ 'MyOtherSetting' => 'MyOtherValue', ], ],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => 'MyValue',
|
|
|
|
|
'MyOtherSetting' => 'MyOtherValue',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'overrides value in config' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[ 'config' => [ 'MySetting' => 'MyValue', ], ],
|
|
|
|
|
[ 'config' => [ 'MySetting' => 'MyOtherValue', ], ],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => 'MyOtherValue',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'sets a default from schema' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[ 'config-schema' => [ 'MySetting' => [ 'default' => 'MyDefault', ], ], ],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => 'MyDefault',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'value in config overrides default from schema' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[
|
|
|
|
|
'config-schema' => [ 'MySetting' => [ 'default' => 'MyDefault', ], ],
|
|
|
|
|
'config' => [ 'MySetting' => 'MyValue', ],
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => 'MyValue',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'default null is applied' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[ 'config-schema' => [ 'MySetting' => [ 'default' => null, ], ], ],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => null,
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'null value can override default' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[
|
|
|
|
|
'config-schema' => [ 'MySetting' => [ 'default' => 'default', ], ],
|
|
|
|
|
'config' => [ 'MySetting' => null, ],
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => null,
|
|
|
|
|
],
|
|
|
|
|
];
|
2021-11-12 19:56:33 +00:00
|
|
|
yield 'merge strategy is applied when setting config' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[
|
|
|
|
|
'config-schema' => [ 'MySetting' => [
|
|
|
|
|
'mergeStrategy' => MergeStrategy::ARRAY_MERGE_RECURSIVE
|
|
|
|
|
], ],
|
|
|
|
|
'config' => [ 'MySetting' => [ 'a' => [ 'b' => 'c' ], ], ],
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'config' => [ 'MySetting' => [ 'a' => [ 'b' => 'd' ], ], ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => [ 'a' => [ 'b' => [ 'c', 'd' ], ], ],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'merge strategy is applied backwards setting schema default' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[
|
|
|
|
|
'config' => [ 'MySetting' => [ 'a' => [ 'b' => 'd' ], ], ],
|
|
|
|
|
'config-schema' => [ 'MySetting' => [
|
|
|
|
|
'mergeStrategy' => MergeStrategy::ARRAY_MERGE_RECURSIVE,
|
|
|
|
|
'default' => [ 'a' => [ 'b' => 'c' ], ],
|
|
|
|
|
], ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => [ 'a' => [ 'b' => [ 'c', 'd' ], ], ],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
yield 'merge strategy is applied backwards setting schema default in different batch' => [
|
|
|
|
|
'settingsBatches' => [
|
|
|
|
|
[
|
|
|
|
|
'config' => [ 'MySetting' => [ 'a' => [ 'b' => 'd' ], ], ],
|
|
|
|
|
], [
|
|
|
|
|
'config-schema' => [ 'MySetting' => [
|
|
|
|
|
'mergeStrategy' => MergeStrategy::ARRAY_MERGE_RECURSIVE,
|
|
|
|
|
'default' => [ 'a' => [ 'b' => 'c' ], ],
|
|
|
|
|
], ],
|
|
|
|
|
]
|
|
|
|
|
],
|
|
|
|
|
'expectedGlobals' => [
|
|
|
|
|
'MySetting' => [ 'a' => [ 'b' => [ 'c', 'd' ], ], ],
|
|
|
|
|
],
|
|
|
|
|
];
|
2021-11-09 17:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideConfigOverrides
|
|
|
|
|
*/
|
|
|
|
|
public function testConfigOverrides( array $settingsBatches, array $expectedGlobals ) {
|
|
|
|
|
$configBuilder = new ArrayConfigBuilder();
|
|
|
|
|
$setting = $this->newSettingsBuilder( $configBuilder );
|
|
|
|
|
foreach ( $settingsBatches as $batch ) {
|
|
|
|
|
$setting->loadArray( $batch );
|
|
|
|
|
}
|
|
|
|
|
$setting->apply();
|
2021-11-11 17:50:14 +00:00
|
|
|
foreach ( $expectedGlobals as $key => $value ) {
|
|
|
|
|
$this->assertSame( $value, $configBuilder->build()->get( $key ) );
|
|
|
|
|
}
|
2021-11-09 17:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testApplyPurgesState() {
|
|
|
|
|
$configBuilder = new ArrayConfigBuilder();
|
|
|
|
|
$setting = $this->newSettingsBuilder( $configBuilder );
|
2021-11-11 17:50:14 +00:00
|
|
|
$setting->loadArray( [ 'config' => [ 'MySetting' => 'MyValue', ], ] )
|
|
|
|
|
->apply();
|
|
|
|
|
$this->assertSame( 'MyValue', $configBuilder->build()->get( 'MySetting' ) );
|
2021-11-09 17:29:14 +00:00
|
|
|
$configBuilder->set( 'MySetting', 'MyOtherValue' );
|
|
|
|
|
// Calling apply a second time should not redefine the global
|
|
|
|
|
// since the state should be cleared
|
|
|
|
|
$setting->apply();
|
2021-11-11 17:50:14 +00:00
|
|
|
$this->assertSame( 'MyOtherValue', $configBuilder->build()->get( 'MySetting' ) );
|
2021-11-09 17:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testApplyDefaultDoesNotOverwriteExisting() {
|
2021-11-13 04:21:34 +00:00
|
|
|
$configBuilder = new ArrayConfigBuilder();
|
|
|
|
|
$configBuilder->set( 'MySetting', 'existing' );
|
2021-11-09 17:29:14 +00:00
|
|
|
$this->newSettingsBuilder( $configBuilder )
|
|
|
|
|
->loadArray( [ 'config-schema' => [ 'MySetting' => [ 'default' => 'default' ], ], ] )
|
|
|
|
|
->apply();
|
2021-11-11 17:50:14 +00:00
|
|
|
$this->assertSame( 'existing', $configBuilder->build()->get( 'MySetting' ) );
|
2021-11-09 17:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testConfigSchemaOverrideNotAllowed() {
|
|
|
|
|
$this->expectException( SettingsBuilderException::class );
|
|
|
|
|
$this->newSettingsBuilder()
|
|
|
|
|
->loadArray( [ 'config-schema' => [ 'MySetting' => [ 'default' => 'default' ], ], ] )
|
|
|
|
|
->loadArray( [ 'config-schema' => [ 'MySetting' => [ 'default' => 'override' ], ], ] )
|
|
|
|
|
->apply();
|
|
|
|
|
}
|
2021-11-13 04:21:34 +00:00
|
|
|
|
2021-11-16 20:41:38 +00:00
|
|
|
public function provideValidate() {
|
|
|
|
|
yield 'all good' => [
|
|
|
|
|
'settings' => [
|
|
|
|
|
'config-schema' => [ 'foo' => [ 'type' => 'string', ], ],
|
|
|
|
|
'config' => [ 'foo' => 'bar', ],
|
|
|
|
|
],
|
|
|
|
|
'valid' => true,
|
|
|
|
|
];
|
|
|
|
|
yield 'missing key' => [
|
|
|
|
|
'settings' => [
|
|
|
|
|
'config-schema' => [ 'foo' => [ 'type' => 'string', ], ],
|
|
|
|
|
'config' => [ 'bar' => 'bar' ],
|
|
|
|
|
],
|
|
|
|
|
'valid' => false,
|
|
|
|
|
];
|
|
|
|
|
yield 'invalid config' => [
|
|
|
|
|
'settings' => [
|
|
|
|
|
'config-schema' => [ 'foo' => [ 'type' => 'string', ], ],
|
|
|
|
|
'config' => [ 'foo' => 1 ],
|
|
|
|
|
],
|
|
|
|
|
'valid' => false,
|
|
|
|
|
];
|
|
|
|
|
yield 'no schema was added' => [
|
|
|
|
|
'settings' => [
|
|
|
|
|
'config-schema' => [],
|
|
|
|
|
'config' => [ 'foo' => 'bar', ],
|
|
|
|
|
],
|
|
|
|
|
'valid' => true,
|
|
|
|
|
];
|
|
|
|
|
yield 'key is in config but has no schema' => [
|
|
|
|
|
'settings' => [
|
|
|
|
|
'config-schema' => [ 'foo' => [ 'type' => 'array', 'mergeStrategy' => MergeStrategy::ARRAY_MERGE ], ],
|
|
|
|
|
'config' => [ 'foo' => [], 'baz' => false, ],
|
|
|
|
|
],
|
|
|
|
|
'valid' => true,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @dataProvider provideValidate
|
|
|
|
|
*/
|
|
|
|
|
public function testValidate( array $settings, bool $valid ) {
|
|
|
|
|
$status = $this->newSettingsBuilder()
|
|
|
|
|
->loadArray( $settings )
|
|
|
|
|
->apply()
|
|
|
|
|
->validate();
|
|
|
|
|
$this->assertSame( $valid, $status->isOK() );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function testValidateInvalidSchema() {
|
|
|
|
|
$this->expectException( InvalidArgumentException::class );
|
|
|
|
|
$this->newSettingsBuilder()
|
|
|
|
|
->loadArray( [
|
|
|
|
|
'config-schema' => [ 'foo' => [ 'type' => 1 ] ],
|
|
|
|
|
'config' => [ 'foo' => 'bar' ],
|
|
|
|
|
] )
|
|
|
|
|
->apply()
|
|
|
|
|
->validate();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-13 04:21:34 +00:00
|
|
|
public function testLoadsCacheableSource() {
|
|
|
|
|
$mockSource = $this->createMock( CacheableSource::class );
|
|
|
|
|
$mockCache = $this->createMock( CacheInterface::class );
|
|
|
|
|
$configBuilder = new ArrayConfigBuilder();
|
|
|
|
|
$builder = $this
|
|
|
|
|
->newSettingsBuilder( $configBuilder, null, $mockCache )
|
|
|
|
|
->load( $mockSource );
|
|
|
|
|
|
|
|
|
|
// Mock a cache miss
|
|
|
|
|
$mockSource
|
|
|
|
|
->expects( $this->once() )
|
|
|
|
|
->method( 'getHashKey' )
|
|
|
|
|
->willReturn( 'abc123' );
|
|
|
|
|
|
|
|
|
|
$mockCache
|
|
|
|
|
->expects( $this->once() )
|
|
|
|
|
->method( 'get' )
|
|
|
|
|
->with( 'abc123' )
|
|
|
|
|
->willReturn( null );
|
|
|
|
|
|
|
|
|
|
$mockSource
|
|
|
|
|
->expects( $this->once() )
|
|
|
|
|
->method( 'load' )
|
|
|
|
|
->willReturn( [ 'config' => [ 'MySetting' => 'BlaBla' ] ] );
|
|
|
|
|
|
|
|
|
|
$builder->apply();
|
|
|
|
|
|
|
|
|
|
$this->assertSame( 'BlaBla', $configBuilder->build()->get( 'MySetting' ) );
|
|
|
|
|
}
|
2021-11-09 17:29:14 +00:00
|
|
|
}
|