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', " '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 testWriteWithoutModeFallsBackToDefaultMode(): void { file_put_contents( $this->tmpDir.'/config/myCEESV.default.conf.php', " [\n\t\t'projectId' => '',\n\t],\n];\n" ); $result = $this->runCfg([ '-a', $this->tmpDir, 'write', 'myCEESV', 'auth:projectId="218523"', ]); $this->assertSame(0, $result['code'], $result['output']); $this->assertFileExists($this->tmpDir.'/config/myCEESV.conf.php'); } 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); } }