diff --git a/bin/cfg b/bin/cfg index 3c7c0ef..78e84c5 100755 --- a/bin/cfg +++ b/bin/cfg @@ -22,12 +22,7 @@ foreach ($autoloadFiles as $autoloadFile) { } } -function verboseLog(bool $enabled, string $message): void -{ - if ($enabled) fwrite(STDERR, "[verbose] $message".PHP_EOL); -} - -$version = '0.4'; +$version = '0.3'; $actions = [ 'show', 'write', 'help' ]; $settings = ['key' => '', 'value' => '']; @@ -40,55 +35,15 @@ $collection = (new Input\InputCollection()) ->description('Display help text') ) // }}} - ->add( Input\InputTypeFactory::build('LongOption')->name('verbose')->short('v') // {{{ - ->flags(AbstractInputType::FLAG_OPTIONAL) - ->description('Print debug details to STDERR') + ->add( Input\InputTypeFactory::build('LongOption')->name('in')->short('i') // {{{ + ->flags(AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED) + ->description('Path to a json data file to to read') ) // }}} - ->add( Input\InputTypeFactory::build('LongOption')->name('in')->short('i') // {{{ - ->flags(AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED) - ->description('Path to a JSON data file to read (for write)') - ->validator(new Input\Validator( - function (AbstractInputType $input, AbstractInputHandler $context) - { - $path = $context->find('in'); - if ($path === null || $path === '') { - return null; - } - - if (!is_readable($path)) - { - throw new \Exception("Input file is not readable: $path"); - } - - $content = file_get_contents($path); - if ($content === false) - { - throw new \Exception("Can not read input file: $path"); - } - - try - { - $data = json_decode($content, true, 512, JSON_THROW_ON_ERROR); - } - catch (\JsonException $ex) - { - throw new \Exception(sprintf( - 'Input JSON is invalid (%s): %s', - (string)$ex->getCode(), - $ex->getMessage() - )); - } - - if (!is_array($data)) - { - throw new \Exception('Input JSON must decode to an object/array'); - } - - return $data; - } - )) - ) // }}} + ->add( Input\InputTypeFactory::build('LongOption')->name('out')->short('o') // {{{ + ->flags(AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED) + ->description('Path to a json file to to write to') + ) // }}} ->add( Input\InputTypeFactory::build('LongOption')->name('mode')->short('m') // {{{ ->flags(AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED) @@ -105,60 +60,6 @@ $collection = (new Input\InputCollection()) ->description('Path where the config/ directory of the package conf files is located, defaults to the working dir') ) // }}} - ->add( Input\InputTypeFactory::build('LongOption')->name('siteDir')->short('s') // {{{ - ->flags(AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED) - ->description('Site/instance directory below config/. Accepts owner_xyz or config/owner_xyz') - ->validator(new Input\Validator( - function (AbstractInputType $input, AbstractInputHandler $context) - { - $siteInput = $context->find('siteDir'); - if ($siteInput === null || $siteInput === false) { - return null; - } - - $siteInput = trim((string)$siteInput); - $appPath = $context->find('appPath'); - if (!$appPath) { - $appPath = getcwd().'/'; - } - $appPath = rtrim($appPath, '/').'/'; - - // Accept both "owner_xyz" and "config/owner_xyz" and normalize to site key. - if (str_starts_with($siteInput, $appPath.'config/')) - { - $siteInput = substr($siteInput, strlen($appPath.'config/')); - } - elseif (str_starts_with($siteInput, 'config/')) - { - $siteInput = substr($siteInput, strlen('config/')); - } - - $siteInput = trim($siteInput, '/'); - if ($siteInput === '') - { - throw new \Exception('Option --siteDir is empty.'); - } - - // Block directory traversal and hidden-dot segments. - if (str_contains($siteInput, '..') || preg_match('~(^|/)\.(?:/|$)~', $siteInput)) - { - throw new \Exception("Invalid directory in --siteDir: '$siteInput'."); - } - - // Allow only predictable path segments for instance directories. - foreach (explode('/', $siteInput) as $part) - { - if ($part === '' || !preg_match('/^[A-Za-z0-9._-]+$/', $part)) - { - throw new \Exception("Invalid directory name segment '$part' in --siteDir."); - } - } - - return $siteInput; - } - )) - ) // }}} - ->add( Input\InputTypeFactory::build('Argument')->name('action') // {{{ ->flags(AbstractInputType::FLAG_REQUIRED) ->description( @@ -201,30 +102,18 @@ $collection = (new Input\InputCollection()) ->description( 'the settings you want to work on. With action "write" you can pass the value to set as JSON after an equal sign.' ) - ->validator(new Input\Validator( - function (AbstractInputType $input, AbstractInputHandler $context) - { - $setting = $context->find('setting'); - $action = $context->find('action'); - $inputPayload = $context->find('in'); - - if ($action === 'write') - { - if (($setting !== null && $setting !== '') && $inputPayload) { - throw new \Exception('Please use either SETTING or --in, not both.'); - } - } - - if ($setting === null || $setting === '') { - return ['key' => '', 'value' => null]; - } - + ->validator(new Input\Validator( + function (AbstractInputType $input, AbstractInputHandler $context) + { + $setting = $context->find('setting'); + $action = $context->find('action'); + $setting = explode('=', $setting); $settings['key'] = $setting[0]; if ($action === 'write') { - $value = $setting[1] ?? null; - if (! (isset($value) || $context->find('in'))) { + $value = $setting[1]; + if (! (isset($value) || $context->find('data'))) { throw new \Exception('You need a value to write'); } $specialValues = [ 'true', 'false', 'null' ]; @@ -250,50 +139,18 @@ $collection = (new Input\InputCollection()) $usage = Cli\manpage( basename(__FILE__), $version, 'read write settings', $collection, Colour::FG_GREEN, Colour::FG_WHITE, - [ - 'Examples' => - 'Basic usage:' - .PHP_EOL - .'cfg show VeruA db:host' - .PHP_EOL - .'cfg write VeruA \'db:host="newHost"\'' - .PHP_EOL - .PHP_EOL - .'Advance usage:' - .PHP_EOL - .'cfg write prefix \'module:enabled=true\'' - .PHP_EOL - .PHP_EOL - .'#Site specific write:' - .PHP_EOL - .'cfg write prefix \'module:enabled=true\' --siteDir=owner_xyz' - .PHP_EOL - .PHP_EOL - .'#Batch write from JSON file:' - .PHP_EOL - .'cfg write prefix --siteDir=owner_xyz -i /tmp/extension.json' - .PHP_EOL - .PHP_EOL - .'#Batch write from JSON string:' - .PHP_EOL - .'cfg write prefix \'module={"enabled":true,"timeout":30,"label":"example"}\' --siteDir=owner_xyz' - .PHP_EOL - .PHP_EOL - .'#Read merged value for a site:' - .PHP_EOL - .'cfg show prefix module:enabled --siteDir=owner_xyz' - .PHP_EOL - ] + [ + 'Examples' => + 'cfg show VeruA db:host'.PHP_EOL. + 'cfg write VeruA \'db:host="newHost"\''.PHP_EOL + ] ).PHP_EOL; // Get the supplied input. Passing the collection will make the handler bind values // and validate the input according to our collection -try -{ +try { $argv = Input\InputHandlerFactory::build('Argv', $collection); -} -catch (\Exception $ex) -{ +} catch (\Exception $ex) { echo $usage; if (isset($argv[1])) { @@ -315,41 +172,42 @@ if ($argv->find( 'help' ) || $argv->find('action') == 'help') $prefix = $argv->find('prefix'); -$verbose = (bool)$argv->find('verbose'); +/* +echo $argv->find('action').PHP_EOL; +echo ($prefix).PHP_EOL; +echo $argv->find('setting')['key'].PHP_EOL; +echo $argv->find('setting')['value'].PHP_EOL; + */ +// var_dump($cfg); $appPath = $argv->find('appPath'); if (!$appPath) $appPath = getcwd().'/'; -$appPath = rtrim($appPath, '/').'/'; +/* $it = new RecursiveDirectoryIterator($appPath); + +foreach(new RecursiveIteratorIterator($it) as $file) +{ + $configDir = $file->getPath(); + if ($file->isDir() && $file->getFilename() == '.' && basename($configDir) == 'config') { + echo "found config dir: $configDir\n"; + } +} + */ $mode = ($argv->find('mode') == '') ? null : $argv->find('mode'); $cfg = (new Settings([], $mode))->appPath($appPath)->prefix($prefix); -// pkgPath points to package defaults (e.g. .default.conf.php) -if ($pkgPath = $argv->find('pkgPath')) $cfg->pkgPath(rtrim($pkgPath, '/').'/'); +if ($pkgPath = $argv->find('pkgPath')) $cfg->pkgPath($pkgPath); -$site = null; -$siteFlag = 0x01; -$site = $argv->find('siteDir'); -if ($site !== null && $site !== false) -{ - // Reuse util-settings site resolution: config//.conf.php - $cfg->site($site); - verboseLog($verbose, "siteDir resolved to '$site'"); -} - -try -{ +try { if (is_readable($cfg->buildFileName('default'))) { $cfg->load(); } elseif (is_readable($cfgFile = $cfg->buildFileName())) { $cfg->load(require($cfgFile)); } -} -catch (\Throwable $e) -{ +} catch (Exception $e) { fwrite(STDERR, "Error: ".$e->getMessage().PHP_EOL); exit(1); } -verboseLog($verbose, 'config bootstrap loaded'); +//var_dump($cfg); $result = $cfg; $settings = $argv->find('setting') ?? $settings; @@ -374,63 +232,33 @@ if ($result instanceof Settings) $result = $result->toArray(); switch ($argv->find('action')) { case 'show': - verboseLog($verbose, "show action for prefix '$prefix'"); $out = (is_string($result)) ? $result : json_encode($result, JSON_PRETTY_PRINT); echo $out.PHP_EOL; break; case 'write': - $inputPayload = $argv->find('in'); - if (!$inputPayload && $settings['key'] === '') - { - fwrite(STDERR, 'Nothing to write: provide SETTING or --in.'.PHP_EOL); - exit(1); - } $path = ($settings['key'] !== '') ? explode(':', $settings['key']) : []; - verboseLog($verbose, 'write source: '.($inputPayload ? '--in' : 'SETTING')); + var_dump($path); + + $setting2write = $settings['value']; - if ($inputPayload) - { - $setting2write = $inputPayload; - } - else - { - $setting2write = $settings['value']; - } - verboseLog($verbose, 'write path: '.json_encode($path)); - while ( ! empty($path)) { $setting2write = [array_pop($path) => $setting2write]; } - - $writeType = ($site !== null && $site !== false) ? $siteFlag : null; - $file = $cfg->buildFileName($writeType); - verboseLog($verbose, "write target: $file"); - if (is_readable($file)) + if (is_readable($file = $cfg->buildFileName())) { $setting2write = array_replace_recursive(require($file), $setting2write); copy($file, "$file.bak"); - verboseLog($verbose, "existing config merged from: $file"); } - - $targetDir = dirname($file); - if (!is_dir($targetDir) && !mkdir($targetDir, 0775, true) && !is_dir($targetDir)) - { - fwrite(STDERR, "Can not create directory: $targetDir".PHP_EOL); - exit(1); - } - $writeCfg = $cfg->create($setting2write); - verboseLog($verbose, 'payload prepared for writer'); - try - { - (new SettingsWriter($writeCfg, '', $writeType))->write(); +// var_dump($writeCfg->toArray()); + try { + (new SettingsWriter($writeCfg))->write(); echo "Written modified settings to: $file".PHP_EOL; } catch (\Exception $e) { - fwrite(STDERR, $e->getMessage().PHP_EOL); - exit(1); + echo $e->getMessage().PHP_EOL; } break; diff --git a/src/Settings.php b/src/Settings.php index ebf7aeb..56e1b0f 100644 --- a/src/Settings.php +++ b/src/Settings.php @@ -159,18 +159,7 @@ class Settings implements \Iterator, \Countable // if a mode was set in the constructor do not overwrite it if (! isset($this->mode)) { // if a localConf Mode is set use it, or take the default conf mode - // if local/default config has no mode, fall back to the first known mode (normally "prod") - if (isset($localConf['mode']) && is_string($localConf['mode']) && $localConf['mode'] !== '') { - $this->mode = $localConf['mode']; - } - elseif (isset($this->settings['mode']) && is_string($this->settings['mode']) && $this->settings['mode'] !== '') { - $this->mode = $this->settings['mode']; - } - else { - // Backward-compatible fallback for configs without explicit mode. - $this->mode = (string)array_key_first($this->modes); - $this->settings['mode'] = $this->mode; - } + $this->mode = (isset($localConf['mode'])) ? $localConf['mode'] : $this->settings['mode']; } else { $this->settings['mode'] = $this->mode; } @@ -369,4 +358,4 @@ class Settings implements \Iterator, \Countable /* jEdit buffer local properties {{{ * :folding=explicit:collapseFolds=1: -}}}*/ +}}}*/ \ No newline at end of file diff --git a/src/SettingsWriter.php b/src/SettingsWriter.php index 6246a9f..d6fc41c 100644 --- a/src/SettingsWriter.php +++ b/src/SettingsWriter.php @@ -22,8 +22,6 @@ declare(strict_types=1); namespace rabe\Util; -use ErrorException; - /** * Write Settings into a File * @author Norbert.e.Wagner dev@norb.me @@ -47,16 +45,13 @@ class SettingsWriter * * @param Settings $settings an object of type settings * @param String $name The name midfix for the settings File - * @param String|int|null $type Optional type for Settings::buildFileName(), e.g. site flag - */ - public function __construct( Settings $settings, $name='', $type=null ) + */ + public function __construct( Settings $settings, $name='' ) { $this->settings = $settings; - - $file = ( $type === null ) - ? $settings->buildFileName( $name ) - : $settings->buildFileName( $type ); - + + $file = $settings->buildFileName( $name ); + if ( ! $this->handle = fopen( $file, 'w' ) ) { throw new ErrorException( "Can not open File: $file" ); diff --git a/tests/CfgTest.php b/tests/CfgTest.php deleted file mode 100644 index 646bcb2..0000000 --- a/tests/CfgTest.php +++ /dev/null @@ -1,457 +0,0 @@ -tmpDir = rtrim(sys_get_temp_dir(), '/').'/util-settings-cfg-'.bin2hex(random_bytes(8)); - mkdir($this->tmpDir, 0775, true); - mkdir($this->tmpDir.'/config', 0775, true); - - file_put_contents( - $this->tmpDir.'/config/Extension.default.conf.php', - " 'prod',\n\t'module' => [\n\t\t'code' => '',\n\t\t'label' => '',\n\t\t'flags' => [\n\t\t\t'enabled' => false,\n\t\t],\n\t],\n\t'feature' => [\n\t\t'endpoint' => '',\n\t],\n];\n" - ); - } - - protected function tearDown(): void - { - $this->removeDir($this->tmpDir); - } - /* }}} */ - - /* Write Flow {{{ */ - public function testWriteWithoutDirectoryNameUsesLocalConfigFile(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - ]); - - $this->assertCfgSuccess($result); - - $localFile = $this->tmpDir.'/config/Extension.conf.php'; - $this->assertFileExists($localFile); - - $cfg = require $localFile; - $this->assertSame('X100', $cfg['module']['code']); - } - - public function testWriteWithoutModeFallsBackToDefaultMode(): void - { - file_put_contents( - $this->tmpDir.'/config/Extension.default.conf.php', - " [\n\t\t'code' => '',\n\t],\n];\n" - ); - - $result = $this->runWrite([ - 'module:code="X100"', - ]); - - $this->assertCfgSuccess($result); - $this->assertFileExists($this->tmpDir.'/config/Extension.conf.php'); - } - - public function testWriteWithInputFileWritesMultipleSettings(): void - { - $inFile = $this->createJsonInputFile([ - 'module' => [ - 'code' => 'X100', - 'label' => 'demo-module', - ], - 'feature' => [ - 'endpoint' => 'https://example.invalid/v1/resource', - ], - ]); - - $result = $this->runWrite([ - '--siteDir=owner_xyz', - '-i', - $inFile, - ]); - - $this->assertCfgSuccess($result); - $cfg = $this->loadWrittenConfig('owner_xyz'); - $this->assertSame('X100', $cfg['module']['code']); - $this->assertSame('demo-module', $cfg['module']['label']); - $this->assertSame('https://example.invalid/v1/resource', $cfg['feature']['endpoint']); - } - - public function testWriteAcceptsJsonStringValueInSetting(): void - { - $result = $this->runWrite([ - 'module={"code":"X100","label":"demo-module"}', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgSuccess($result); - $cfg = $this->loadWrittenConfig('owner_xyz'); - $this->assertSame('X100', $cfg['module']['code']); - $this->assertSame('demo-module', $cfg['module']['label']); - } - - public function testWriteSupportsNestedPathWithColonNotation(): void - { - $result = $this->runWrite([ - 'module:flags:enabled=true', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgSuccess($result); - $cfg = $this->loadWrittenConfig('owner_xyz'); - $this->assertTrue($cfg['module']['flags']['enabled']); - } - - public function testWriteCoercesSimpleStringValueToJsonString(): void - { - $result = $this->runWrite([ - 'module:code=X100', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgSuccess($result); - $cfg = $this->loadWrittenConfig('owner_xyz'); - $this->assertSame('X100', $cfg['module']['code']); - } - - public function testWriteRejectsInvalidJsonStringValue(): void - { - $result = $this->runWrite([ - 'module={"code":', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgFailure($result, 'The value does not appear to be a valid JSON string.'); - } - - public function testWriteWithSiteDirCreatesAndWritesSiteConfig(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgSuccess($result); - - $siteFile = $this->tmpDir.'/config/owner_xyz/Extension.conf.php'; - $this->assertFileExists($siteFile); - $this->assertFileDoesNotExist($this->tmpDir.'/config/Extension.conf.php'); - - $cfg = require $siteFile; - $this->assertSame('X100', $cfg['module']['code']); - } - - public function testWriteWithSiteDirMergesIntoExistingSiteConfig(): void - { - $firstWrite = $this->runWrite([ - 'module:code="X100"', - '--siteDir=owner_xyz', - ]); - $this->assertCfgSuccess($firstWrite); - - $secondWrite = $this->runWrite([ - 'module:label="demo-module"', - '--siteDir=owner_xyz', - ]); - $this->assertCfgSuccess($secondWrite); - - $cfg = $this->loadWrittenConfig('owner_xyz'); - - $this->assertSame('X100', $cfg['module']['code']); - $this->assertSame('demo-module', $cfg['module']['label']); - } - - public function testWriteWithSiteDirUsingConfigPrefixIsNormalized(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir=config/owner_xyz', - ]); - - $this->assertCfgSuccess($result); - $this->assertFileExists($this->tmpDir.'/config/owner_xyz/Extension.conf.php'); - $this->assertFileDoesNotExist($this->tmpDir.'/config/config/owner_xyz/Extension.conf.php'); - } - - public function testWriteWithSiteDirUsingAbsoluteConfigPrefixIsNormalized(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir='.$this->tmpDir.'/config/owner_xyz', - ]); - - $this->assertCfgSuccess($result); - $this->assertFileExists($this->tmpDir.'/config/owner_xyz/Extension.conf.php'); - } - - public function testWriteWithNestedSiteDirWritesToNestedSiteConfig(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir=owner_xyz/sub_a', - ]); - - $this->assertCfgSuccess($result); - $this->assertFileExists($this->tmpDir.'/config/owner_xyz/sub_a/Extension.conf.php'); - } - - public function testWriteWithSiteDirWritesToSiteConfig(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgSuccess($result); - $this->assertFileExists($this->tmpDir.'/config/owner_xyz/Extension.conf.php'); - } - - public function testWriteToExistingSiteConfigCreatesBackup(): void - { - $firstWrite = $this->runWrite([ - 'module:code="first"', - '--siteDir=owner_xyz', - ]); - $this->assertCfgSuccess($firstWrite); - - $secondWrite = $this->runWrite([ - 'module:code="second"', - '--siteDir=owner_xyz', - ]); - $this->assertCfgSuccess($secondWrite); - - $siteFile = $this->tmpDir.'/config/owner_xyz/Extension.conf.php'; - $backupFile = $siteFile.'.bak'; - $this->assertFileExists($backupFile); - - $current = require $siteFile; - $backup = require $backupFile; - $this->assertSame('second', $current['module']['code']); - $this->assertSame('first', $backup['module']['code']); - } - /* }}} */ - - /* Show Flow {{{ */ - public function testShowWithSiteReturnsSiteSpecificValue(): void - { - $this->runWrite([ - 'module:code="X100"', - '--siteDir=owner_xyz', - ]); - - $show = $this->runShow([ - 'module:code', - '--siteDir=owner_xyz', - ]); - - $this->assertCfgSuccess($show); - $this->assertStringContainsString('X100', $show['output']); - } - /* }}} */ - - /* Validation {{{ */ - public function testInvalidActionReturnsError(): void - { - $result = $this->runCfg([ - '-a', - $this->tmpDir, - 'delete', - 'Extension', - ]); - - $this->assertCfgFailure($result, "'delete' not found"); - } - - public function testSiteDirRejectsEmptyValue(): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir=', - ]); - - $this->assertCfgFailure($result, 'a value is required for --siteDir'); - } - - /** - * @dataProvider siteDirValidationDataProvider - */ - public function testSiteDirValidation(string $siteDir, string $expectedMessage): void - { - $result = $this->runWrite([ - 'module:code="X100"', - '--siteDir='.$siteDir, - ]); - - $this->assertCfgFailure($result, $expectedMessage); - } - - /** - * @dataProvider inputFileValidationDataProvider - */ - public function testInputFileValidation(?string $fileContents, string $pathSuffix, string $expectedMessage): void - { - $inFile = $this->tmpDir.'/'.$pathSuffix; - if ($fileContents !== null) { - file_put_contents($inFile, $fileContents); - } - - $result = $this->runWrite([ - '-i', - $inFile, - ]); - - $this->assertCfgFailure($result, $expectedMessage); - } - - /** - * @dataProvider writePayloadSourceValidationDataProvider - */ - public function testWritePayloadSourceValidation(array $extraArgs, string $expectedMessage): void - { - $inFile = $this->createJsonInputFile(['module' => ['code' => 'X100']]); - - $args = array_merge( - [ - '-a', - $this->tmpDir, - 'write', - 'Extension', - ], - array_map( - fn (string $arg): string => $arg === '__INPUT_FILE__' ? $inFile : $arg, - $extraArgs - ) - ); - - $result = $this->runCfg($args); - - $this->assertCfgFailure($result, $expectedMessage); - } - - public function siteDirValidationDataProvider(): array - { - return [ - 'traversal' => ['../owner_xyz', 'Invalid directory in --siteDir'], - 'invalid segment' => ['owner xyz', "Invalid directory name segment 'owner xyz' in --siteDir."], - 'hidden dot segment' => ['owner_xyz/./secret', "Invalid directory in --siteDir: 'owner_xyz/./secret'."], - 'config root only' => ['config/', 'Option --siteDir is empty.'], - ]; - } - - public function inputFileValidationDataProvider(): array - { - return [ - 'missing file' => [null, 'missing.json', 'Input file is not readable'], - 'invalid json' => ['{"module":', 'invalid.json', 'Input JSON is invalid'], - 'scalar json' => ['true', 'scalar.json', 'Input JSON must decode to an object/array'], - ]; - } - - public function writePayloadSourceValidationDataProvider(): array - { - return [ - 'missing payload source' => [[], 'Nothing to write: provide SETTING or --in.'], - 'both payload sources' => [['module:code="x"', '-i', '__INPUT_FILE__'], 'Please use either SETTING or --in, not both.'], - ]; - } - /* }}} */ - - /* Helpers {{{ */ - private function runWrite(array $args): array - { - return $this->runCfg(array_merge([ - '-a', - $this->tmpDir, - 'write', - 'Extension', - ], $args)); - } - - private function runShow(array $args): array - { - return $this->runCfg(array_merge([ - '-a', - $this->tmpDir, - 'show', - 'Extension', - ], $args)); - } - - private function createJsonInputFile(array $contents, string $name = 'extension-in.json'): string - { - $path = $this->tmpDir.'/'.$name; - file_put_contents($path, json_encode($contents, JSON_PRETTY_PRINT)); - - return $path; - } - - private function loadWrittenConfig(?string $siteDir = null): array - { - $path = $siteDir === null - ? $this->tmpDir.'/config/Extension.conf.php' - : $this->tmpDir.'/config/'.$siteDir.'/Extension.conf.php'; - - return require $path; - } - - private function assertCfgSuccess(array $result): void - { - $this->assertSame(0, $result['code'], $result['output']); - } - - private function assertCfgFailure(array $result, string $message): void - { - $this->assertSame(1, $result['code'], $result['output']); - $this->assertStringContainsString($message, $result['output']); - } - - private function runCfg(array $args): array - { - $script = realpath(__DIR__.'/../bin/cfg'); - $this->assertNotFalse($script); - - $command = escapeshellarg(PHP_BINARY).' '.escapeshellarg($script); - foreach ($args as $arg) { - $command .= ' '.escapeshellarg($arg); - } - $command .= ' 2>&1'; - - $outputLines = []; - $exitCode = 0; - exec($command, $outputLines, $exitCode); - - return [ - 'code' => $exitCode, - 'output' => implode(PHP_EOL, $outputLines), - ]; - } - - private function removeDir(string $dir): void - { - if (!is_dir($dir)) { - return; - } - - foreach (scandir($dir) ?: [] as $entry) { - if ($entry === '.' || $entry === '..') { - continue; - } - - $path = $dir.'/'.$entry; - if (is_dir($path)) { - $this->removeDir($path); - } else { - unlink($path); - } - } - - rmdir($dir); - } - /* }}} */ -} diff --git a/tests/SettingsTest.php b/tests/SettingsTest.php index 09f311c..64ed411 100644 --- a/tests/SettingsTest.php +++ b/tests/SettingsTest.php @@ -192,15 +192,6 @@ class SettingsTest extends TestCase $cfg = $this->appPath($cfg, 'localOverride')->load(); $this->assertEquals(42, $cfg->answer); } - - public function testLoadWithoutModeFallsBackToFirstKnownMode(): void - { - $cfg = new Settings(); - $cfg = $this->appPath($cfg, 'noMode')->load(); - - $this->assertEquals('prod', $cfg->mode); - $this->assertEquals('default', $cfg->testFiles); - } /** * @dataProvider fileNameData @@ -220,22 +211,6 @@ class SettingsTest extends TestCase $this->assertEquals($path.$expected, $cfg->buildFileName($type)); } - - public function testBuildFileNameWithPrefixAndSite(): void - { - $cfg = new Settings(); - $cfg->appPath('./')->prefix('Extension')->site('owner_xyz'); - - $this->assertEquals('./config/owner_xyz/Extension.conf.php', $cfg->buildFileName(0x01)); - } - - public function testBuildFileNameWithPrefixAndNestedSite(): void - { - $cfg = new Settings(); - $cfg->appPath('./')->prefix('Extension')->site('owner_xyz/sub_a'); - - $this->assertEquals('./config/owner_xyz/sub_a/Extension.conf.php', $cfg->buildFileName(0x01)); - } public function fileNameData() { diff --git a/tests/cfg/noMode/config/default.conf.php b/tests/cfg/noMode/config/default.conf.php deleted file mode 100644 index c0a0d9b..0000000 --- a/tests/cfg/noMode/config/default.conf.php +++ /dev/null @@ -1,3 +0,0 @@ - 'default', -];