Fassung 1.7.0: verlässliche Installation, übersichtliche Verwaltung und bedienbare Bewegung
This commit is contained in:
1 parent
b164c4edfe
commit
495b26896e
55 files changed
+1632
-423
No files matched your search
@@ -0,0 +1,122 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user