Files
KantSites/tests/EnvironmentTest.php
T
kantarci 495b26896e
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Fassung 1.7.0: verlässliche Installation, übersichtliche Verwaltung und bedienbare Bewegung
2026-09-14 10:49:54 +02:00

123 lines
6.0 KiB
PHP

<?php
namespace App\Tests;
use App\Ops\Environment;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Dotenv\Dotenv;
final class EnvironmentTest extends TestCase
{
private string $root;
protected function setUp(): void
{
$this->root = sys_get_temp_dir().'/kant-environment-'.bin2hex(random_bytes(6));
mkdir($this->root);
file_put_contents($this->root.'/.env.example', "APP_ENV=prod\nDATABASE_URL=sqlite:///default.db\nMAILER_DSN=null://null\n");
}
protected function tearDown(): void
{
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->root, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $file) {
$file->isDir() ? rmdir($file->getPathname()) : unlink($file->getPathname());
}
rmdir($this->root);
}
public function testNewInstallationUsesOnlyEnvAndKeepsSecretAcrossRequests(): void
{
Environment::prepare($this->root);
$secret = Environment::ensureSecret($this->root);
Environment::write($this->root, ['DATABASE_URL' => 'sqlite:///customer.db']);
Environment::prepare($this->root);
self::assertSame($secret, Environment::ensureSecret($this->root));
self::assertSame('sqlite:///customer.db', $this->values()['DATABASE_URL']);
self::assertFileDoesNotExist($this->root.'/.env.local');
}
public function testLegacySettingsAreConsolidatedOnceWithBothOriginalsBackedUp(): void
{
Environment::prepare($this->root);
$legacy = "DATABASE_URL=sqlite:///live.db\nAPP_SECRET=existing-secret\nCUSTOM_OPTION=kept\n";
file_put_contents($this->root.'/.env.local', $legacy);
Environment::prepare($this->root);
self::assertSame('sqlite:///live.db', $this->values()['DATABASE_URL']);
self::assertSame('existing-secret', Environment::ensureSecret($this->root));
self::assertSame('kept', $this->values()['CUSTOM_OPTION']);
self::assertFileDoesNotExist($this->root.'/.env.local');
$backups = glob($this->root.'/var/backups/environment-*/.env.local');
self::assertCount(1, $backups);
self::assertSame($legacy, file_get_contents($backups[0]));
Environment::write($this->root, ['DATABASE_URL' => 'sqlite:///chosen.db']);
Environment::prepare($this->root);
self::assertSame('sqlite:///chosen.db', $this->values()['DATABASE_URL'], 'No legacy override after editing .env.');
self::assertCount(1, glob($this->root.'/var/backups/environment-*'));
}
public function testSecretsAndCustomSettingsRoundTripWithoutInterpolation(): void
{
Environment::prepare($this->root);
$value = 'a$NOT_AN_ENV_VALUE"b\\c';
Environment::write($this->root, ['APP_SECRET' => $value, 'CUSTOM_OPTION' => "one\ntwo"]);
Environment::write($this->root, ['SITE_ORIGIN' => 'https://example.org']);
self::assertSame($value, $this->values()['APP_SECRET']);
self::assertSame("one\ntwo", $this->values()['CUSTOM_OPTION']);
self::assertSame('null://null', $this->values()['MAILER_DSN']);
}
public function testFailedMigrationPreservesBothOriginalFiles(): void
{
Environment::prepare($this->root);
$before = file_get_contents($this->root.'/.env');
$legacy = "APP_SECRET=preserved\nDATABASE_URL=sqlite:///live.db\n";
file_put_contents($this->root.'/.env.local', $legacy);
unlink($this->root.'/.env.lock');
mkdir($this->root.'/.env.lock'); // An unwritable lock target, reproducible on Windows/Linux.
try {
Environment::prepare($this->root);
self::fail('Migration must report failure.');
} catch (\RuntimeException $error) {
self::assertStringContainsString('Schreibrechte', $error->getMessage());
}
self::assertSame($before, file_get_contents($this->root.'/.env'));
self::assertSame($legacy, file_get_contents($this->root.'/.env.local'));
}
public function testBootstrapIgnoresOtherOverlaysButHonorsExplicitServerVariables(): void
{
mkdir($this->root.'/config');
mkdir($this->root.'/vendor');
copy(dirname(__DIR__).'/config/bootstrap.php', $this->root.'/config/bootstrap.php');
file_put_contents($this->root.'/vendor/autoload.php', '<?php require '.var_export(dirname(__DIR__).'/vendor/autoload.php', true).';');
file_put_contents($this->root.'/.env', "APP_ENV=prod\nAPP_DEBUG=0\nAPP_SECRET=fixture-secret\nDATABASE_URL=sqlite:///chosen.db\n");
file_put_contents($this->root.'/.env.prod', "DATABASE_URL=sqlite:///wrong.db\n");
file_put_contents($this->root.'/.env.prod.local', "DATABASE_URL=sqlite:///also-wrong.db\n");
file_put_contents($this->root.'/.env.local.php', '<?php return ["DATABASE_URL" => "sqlite:///cached-wrong.db"];');
file_put_contents($this->root.'/boot.php', '<?php require __DIR__."/config/bootstrap.php"; echo $_SERVER["DATABASE_URL"];');
foreach ([null, 'sqlite:///server.db'] as $override) {
$environment = getenv();
unset($environment['DATABASE_URL']);
$environment['APP_ENV'] = 'prod';
if (null !== $override) {
$environment['DATABASE_URL'] = $override;
}
$process = proc_open([PHP_BINARY, $this->root.'/boot.php'], [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, $this->root, $environment);
self::assertIsResource($process);
$output = stream_get_contents($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
self::assertSame(0, proc_close($process), $error);
self::assertSame($override ?? 'sqlite:///chosen.db', $output);
self::assertFileDoesNotExist($this->root.'/.env.local');
}
}
private function values(): array
{
return (new Dotenv())->parse((string) file_get_contents($this->root.'/.env'));
}
}