162 lines
6.4 KiB
PHP
162 lines
6.4 KiB
PHP
<?php
|
|
|
|
use App\Services\Env\EnvFileEditor;
|
|
|
|
beforeEach(function () {
|
|
// Eine eigene Datei je Test. Der Befehl schreibt sonst die .env dieser
|
|
// Entwicklungsmaschine, und ein Test, der das kann, wird irgendwann
|
|
// versehentlich scharf ausgeführt.
|
|
$this->envPath = sys_get_temp_dir().'/clupilot-env-'.bin2hex(random_bytes(6));
|
|
$this->app->instance(EnvFileEditor::class, new EnvFileEditor($this->envPath));
|
|
});
|
|
|
|
afterEach(function () {
|
|
foreach (glob($this->envPath.'*') ?: [] as $file) {
|
|
@unlink($file);
|
|
}
|
|
});
|
|
|
|
it('trägt die fehlenden Hostnamen nach', function () {
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\nADMIN_HOSTS=admin.example.test\n");
|
|
|
|
$this->artisan('clupilot:bind-hosts', [
|
|
'--site' => 'www.example.test,example.test',
|
|
'--status' => 'status.example.test',
|
|
'--files' => 'files.example.test',
|
|
'--force' => true,
|
|
])->assertSuccessful();
|
|
|
|
$written = file_get_contents($this->envPath);
|
|
|
|
// APP_HOST kommt aus dem Host von APP_URL — danach muss niemand gefragt
|
|
// werden, die Antwort steht schon in der Datei.
|
|
expect($written)->toContain('APP_HOST=app.example.test')
|
|
->and($written)->toContain('SITE_HOST=www.example.test,example.test')
|
|
->and($written)->toContain('STATUS_HOST=status.example.test')
|
|
->and($written)->toContain('FILES_HOST=files.example.test')
|
|
// Und nichts Vorhandenes geht verloren.
|
|
->and($written)->toContain('APP_URL=https://app.example.test')
|
|
->and($written)->toContain('ADMIN_HOSTS=admin.example.test');
|
|
});
|
|
|
|
it('überschreibt niemals einen Wert, den jemand von Hand gesetzt hat', function () {
|
|
file_put_contents(
|
|
$this->envPath,
|
|
"APP_URL=https://app.example.test\nAPP_HOST=eigener.example.test\nSITE_HOST=\n",
|
|
);
|
|
|
|
$this->artisan('clupilot:bind-hosts', [
|
|
'--site' => 'www.example.test',
|
|
'--force' => true,
|
|
])->assertSuccessful();
|
|
|
|
$written = file_get_contents($this->envPath);
|
|
|
|
// Gesetzt bleibt gesetzt …
|
|
expect($written)->toContain('APP_HOST=eigener.example.test')
|
|
->and($written)->not->toContain('APP_HOST=app.example.test')
|
|
// … und leer gilt als fehlend, denn genau so liefert .env.example aus.
|
|
->and($written)->toContain('SITE_HOST=www.example.test');
|
|
});
|
|
|
|
it('sichert die alte Datei, bevor es schreibt', function () {
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\n");
|
|
|
|
$this->artisan('clupilot:bind-hosts', ['--force' => true])->assertSuccessful();
|
|
|
|
// EnvFileEditor legt eine Kopie mit Zeitstempel daneben. Diese Datei hält
|
|
// jedes Geheimnis der Installation; ein Schreibfehler darf sie nicht
|
|
// ersatzlos ersetzen.
|
|
expect(glob($this->envPath.'.bak-*'))->not->toBeEmpty();
|
|
});
|
|
|
|
it('tut ohne Bestätigung nichts', function () {
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\n");
|
|
|
|
$this->artisan('clupilot:bind-hosts', ['--site' => 'www.example.test'])
|
|
->expectsConfirmation(
|
|
'Diese Namen jetzt binden?',
|
|
'no',
|
|
)
|
|
->assertFailed();
|
|
|
|
expect(file_get_contents($this->envPath))->not->toContain('SITE_HOST');
|
|
});
|
|
|
|
it('schreibt bei --dry-run nichts, sagt aber was geschähe', function () {
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\n");
|
|
|
|
$this->artisan('clupilot:bind-hosts', ['--site' => 'www.example.test', '--dry-run' => true])
|
|
->expectsOutputToContain('SITE_HOST=www.example.test')
|
|
->assertSuccessful();
|
|
|
|
expect(file_get_contents($this->envPath))->not->toContain('SITE_HOST')
|
|
->and(glob($this->envPath.'.bak-*'))->toBeEmpty();
|
|
});
|
|
|
|
it('sagt es, wenn schon alles steht, statt eine Sicherung anzulegen', function () {
|
|
file_put_contents($this->envPath, implode("\n", [
|
|
'APP_URL=https://app.example.test',
|
|
'APP_HOST=app.example.test',
|
|
'SITE_HOST=www.example.test',
|
|
'STATUS_HOST=status.example.test',
|
|
'FILES_HOST=files.example.test',
|
|
'',
|
|
]));
|
|
|
|
$this->artisan('clupilot:bind-hosts', ['--force' => true])
|
|
->expectsOutputToContain('Nichts nachzutragen')
|
|
->assertSuccessful();
|
|
|
|
expect(glob($this->envPath.'.bak-*'))->toBeEmpty();
|
|
});
|
|
|
|
it('lehnt einen Wert ab, der wie ein Rückverweis aussieht, statt ihn zu verschlucken', function () {
|
|
// SITE_HOST steht schon leer da — genau das, was .env.example wirklich
|
|
// ausliefert, und der Pfad, auf dem apply() eine vorhandene Zeile per
|
|
// preg_replace ersetzt statt sie nur anzuhängen.
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\nSITE_HOST=\n");
|
|
|
|
// `$1` und `\1` sind das, was preg_replace im ERSATZ als Rückverweis
|
|
// deutet und still verschluckt hätte — apply() schützt genau davor. Seit
|
|
// der Hostnamen-Prüfung kommt der Wert dort aber gar nicht mehr an: `$`
|
|
// und `\` sind in keinem Hostnamen erlaubt, also weist die Prüfung ihn
|
|
// vorher zurück, statt ihn (unbeschädigt oder nicht) zu schreiben.
|
|
$this->artisan('clupilot:bind-hosts', [
|
|
'--site' => 'www.example.test$1,foo\1bar',
|
|
'--force' => true,
|
|
])->assertFailed();
|
|
|
|
expect(file_get_contents($this->envPath))
|
|
->toBe("APP_URL=https://app.example.test\nSITE_HOST=\n")
|
|
->and(glob($this->envPath.'.bak-*'))->toBeEmpty();
|
|
});
|
|
|
|
it('lehnt ab, was kein Hostname sein kann, und schreibt nichts', function () {
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\n");
|
|
|
|
// Genau der Wert, der auf der Produktivmaschine landete. Er ist als
|
|
// Hostname offensichtlich Unsinn und wurde trotzdem geschrieben, weil
|
|
// EnvFileEditor nur prueft, ob die ZEILE die Form KEY=value hat.
|
|
$this->artisan('clupilot:bind-hosts', [
|
|
'--site' => '[www.example.test](https://www.example.test)',
|
|
'--force' => true,
|
|
])->assertFailed();
|
|
|
|
expect(file_get_contents($this->envPath))->not->toContain('SITE_HOST')
|
|
->and(glob($this->envPath.'.bak-*'))->toBeEmpty();
|
|
});
|
|
|
|
it('prüft jeden Namen einer Komma-Liste einzeln', function () {
|
|
file_put_contents($this->envPath, "APP_URL=https://app.example.test\n");
|
|
|
|
// Der erste Name ist gültig — geschrieben werden darf trotzdem nichts,
|
|
// sonst stünde die halbe Liste in der Datei.
|
|
$this->artisan('clupilot:bind-hosts', [
|
|
'--site' => 'www.example.test,nicht gültig',
|
|
'--force' => true,
|
|
])->assertFailed();
|
|
|
|
expect(file_get_contents($this->envPath))->not->toContain('SITE_HOST');
|
|
});
|