Task: #53687 Support instance-specific cfg write targets and add coverage

This commit is contained in:
Alejandro Sosa 2026-03-25 16:39:28 +01:00
commit 832b37e613
4 changed files with 484 additions and 15 deletions

307
tests/CfgTest.php Normal file
View file

@ -0,0 +1,307 @@
<?php
declare(strict_types=1);
namespace rabe\Util\tests;
use PHPUnit\Framework\TestCase;
class CfgTest extends TestCase
{
private string $tmpDir;
protected function setUp(): void
{
$this->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/myCEESV.default.conf.php',
"<?php return [\n\t'mode' => 'prod',\n\t'auth' => [\n\t\t'projectId' => '',\n\t\t'clientId' => '',\n\t],\n];\n"
);
}
protected function tearDown(): void
{
$this->removeDir($this->tmpDir);
}
public function testWriteWithoutDirectoryNameUsesLocalConfigFile(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
]);
$this->assertSame(0, $result['code'], $result['output']);
$localFile = $this->tmpDir.'/config/myCEESV.conf.php';
$this->assertFileExists($localFile);
$cfg = require $localFile;
$this->assertSame('218523', $cfg['auth']['projectId']);
}
public function testWriteWithDirectoryNameCreatesAndWritesSiteConfig(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name',
'owner_xyz',
]);
$this->assertSame(0, $result['code'], $result['output']);
$siteFile = $this->tmpDir.'/config/owner_xyz/myCEESV.conf.php';
$this->assertFileExists($siteFile);
$this->assertFileDoesNotExist($this->tmpDir.'/config/myCEESV.conf.php');
$cfg = require $siteFile;
$this->assertSame('218523', $cfg['auth']['projectId']);
}
public function testWriteWithDirectoryNameMergesIntoExistingSiteConfig(): void
{
$firstWrite = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name',
'owner_xyz',
]);
$this->assertSame(0, $firstWrite['code'], $firstWrite['output']);
$secondWrite = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:clientId="service-9999-qual@myceesv.ch"',
'--directory-name',
'owner_xyz',
]);
$this->assertSame(0, $secondWrite['code'], $secondWrite['output']);
$siteFile = $this->tmpDir.'/config/owner_xyz/myCEESV.conf.php';
$cfg = require $siteFile;
$this->assertSame('218523', $cfg['auth']['projectId']);
$this->assertSame('service-9999-qual@myceesv.ch', $cfg['auth']['clientId']);
}
public function testWriteWithDirectoryNameUsingConfigPrefixIsNormalized(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name=config/owner_xyz',
]);
$this->assertSame(0, $result['code'], $result['output']);
$this->assertFileExists($this->tmpDir.'/config/owner_xyz/myCEESV.conf.php');
$this->assertFileDoesNotExist($this->tmpDir.'/config/config/owner_xyz/myCEESV.conf.php');
}
public function testWriteWithSiteOptionWritesToSiteConfig(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--site=owner_xyz',
]);
$this->assertSame(0, $result['code'], $result['output']);
$this->assertFileExists($this->tmpDir.'/config/owner_xyz/myCEESV.conf.php');
}
public function testWriteWithDirectoryNameCamelCaseOptionWritesToSiteConfig(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directoryName=owner_xyz',
]);
$this->assertSame(0, $result['code'], $result['output']);
$this->assertFileExists($this->tmpDir.'/config/owner_xyz/myCEESV.conf.php');
}
public function testWriteToExistingSiteConfigCreatesBackup(): void
{
$firstWrite = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="first"',
'--directory-name=owner_xyz',
]);
$this->assertSame(0, $firstWrite['code'], $firstWrite['output']);
$secondWrite = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="second"',
'--directory-name=owner_xyz',
]);
$this->assertSame(0, $secondWrite['code'], $secondWrite['output']);
$siteFile = $this->tmpDir.'/config/owner_xyz/myCEESV.conf.php';
$backupFile = $siteFile.'.bak';
$this->assertFileExists($backupFile);
$current = require $siteFile;
$backup = require $backupFile;
$this->assertSame('second', $current['auth']['projectId']);
$this->assertSame('first', $backup['auth']['projectId']);
}
public function testShowWithSiteReturnsSiteSpecificValue(): void
{
$this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name=owner_xyz',
]);
$show = $this->runCfg([
'-a',
$this->tmpDir,
'show',
'myCEESV',
'auth:projectId',
'--site=owner_xyz',
]);
$this->assertSame(0, $show['code'], $show['output']);
$this->assertStringContainsString('218523', $show['output']);
}
public function testDirectoryNameRejectsTraversal(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name=../owner_xyz',
]);
$this->assertSame(1, $result['code']);
$this->assertStringContainsString('Invalid directory in --directoryName', $result['output']);
}
public function testDirectoryNameRejectsInvalidSegment(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name=owner xyz',
]);
$this->assertSame(1, $result['code']);
$this->assertStringContainsString("Invalid directory name segment 'owner xyz' in --directoryName.", $result['output']);
}
public function testDirectoryNameRejectsEmptyValue(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--directory-name=',
]);
$this->assertSame(1, $result['code']);
$this->assertStringContainsString('Option --directoryName is empty.', $result['output']);
}
public function testUsingSiteAndDirectoryNameTogetherReturnsError(): void
{
$result = $this->runCfg([
'-a',
$this->tmpDir,
'write',
'myCEESV',
'auth:projectId="218523"',
'--site',
'owner_xyz',
'--directory-name',
'owner_xyz',
]);
$this->assertSame(1, $result['code']);
$this->assertStringContainsString('Please use only one of --site or --directoryName (alias: --directory-name).', $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);
}
}

View file

@ -211,6 +211,22 @@ class SettingsTest extends TestCase
$this->assertEquals($path.$expected, $cfg->buildFileName($type));
}
public function testBuildFileNameWithPrefixAndSite(): void
{
$cfg = new Settings();
$cfg->appPath('./')->prefix('myCEESV')->site('owner_xyz');
$this->assertEquals('./config/owner_xyz/myCEESV.conf.php', $cfg->buildFileName(0x01));
}
public function testBuildFileNameWithPrefixAndNestedSite(): void
{
$cfg = new Settings();
$cfg->appPath('./')->prefix('myCEESV')->site('owner_xyz/sub_a');
$this->assertEquals('./config/owner_xyz/sub_a/myCEESV.conf.php', $cfg->buildFileName(0x01));
}
public function fileNameData()
{