133 lines
6.0 KiB
PHP
133 lines
6.0 KiB
PHP
<?php
|
|
|
|
use App\Models\Operator;
|
|
use App\Services\Secrets\SecretVault;
|
|
use App\Support\OperatingMode;
|
|
|
|
/**
|
|
* Zwei Plätze je Zugangsdatum, und eine Rückfallregel, die für vier von fünf
|
|
* Einträgen richtig ist: es gibt bei Hetzner, Uptime Kuma und SSH nur ein
|
|
* Konto, und zwei Felder mit zwingend gleichem Inhalt wären eine Attrappe.
|
|
*/
|
|
beforeEach(function () {
|
|
$this->by = Operator::factory()->create();
|
|
$this->vault = app(SecretVault::class);
|
|
|
|
// HETZNER_DNS_TOKEN is real in this operator's .env and, unlike
|
|
// STRIPE_WEBHOOK_SECRET, not blanked in phpunit.xml — left alone, the
|
|
// fallback-to-environment tests below would silently pass or fail
|
|
// depending on whose machine runs them instead of on the code.
|
|
config()->set('provisioning.dns.token', null);
|
|
});
|
|
|
|
it('reads the slot of the active mode', function () {
|
|
$this->vault->put('dns.token', 'live-token', $this->by, OperatingMode::Live);
|
|
$this->vault->put('dns.token', 'test-token', $this->by, OperatingMode::Test);
|
|
|
|
OperatingMode::set(OperatingMode::Test);
|
|
expect($this->vault->get('dns.token'))->toBe('test-token');
|
|
|
|
OperatingMode::set(OperatingMode::Live);
|
|
expect($this->vault->get('dns.token'))->toBe('live-token');
|
|
});
|
|
|
|
it('falls back to the live slot when the test slot is empty', function () {
|
|
// Die Regel des Inhabers: wo es keinen Testzugang gibt, wird der echte
|
|
// benutzt, damit der Testbetrieb überhaupt arbeiten kann.
|
|
$this->vault->put('dns.token', 'live-token', $this->by, OperatingMode::Live);
|
|
OperatingMode::set(OperatingMode::Test);
|
|
|
|
expect($this->vault->get('dns.token'))->toBe('live-token');
|
|
});
|
|
|
|
it('does not fall back from live to test', function () {
|
|
// Die andere Richtung wäre absurd: im Livebetrieb einen Testzugang zu
|
|
// benutzen, weil der echte fehlt.
|
|
$this->vault->put('dns.token', 'test-token', $this->by, OperatingMode::Test);
|
|
OperatingMode::set(OperatingMode::Live);
|
|
|
|
expect($this->vault->get('dns.token'))->toBeNull();
|
|
});
|
|
|
|
it('still falls back to the environment when neither slot is filled', function () {
|
|
// Der Weg für eine Installation, in der noch gar nichts gespeichert ist.
|
|
// Einwertig und modusfrei — den Anfangszustand gibt es nur einmal.
|
|
config()->set('provisioning.dns.token', 'from-env');
|
|
|
|
expect($this->vault->get('dns.token'))->toBe('from-env');
|
|
});
|
|
|
|
it('reports the source per slot', function () {
|
|
$this->vault->put('dns.token', 'live-token', $this->by, OperatingMode::Live);
|
|
|
|
expect($this->vault->source('dns.token', OperatingMode::Live))->toBe('stored');
|
|
// NICHT 'none': im Testbetrieb ist genau diese Live-Zeile in Kraft (der
|
|
// Rückfall zwei Tests weiter oben). 'none' hieße „nichts hinterlegt" über
|
|
// ein Zugangsdatum, mit dem gerade gearbeitet wird — siehe den Test
|
|
// „names the live slot as the source…" weiter unten.
|
|
expect($this->vault->source('dns.token', OperatingMode::Test))->toBe('stored_live');
|
|
});
|
|
|
|
it('forgets one slot without touching the other', function () {
|
|
$this->vault->put('dns.token', 'live-token', $this->by, OperatingMode::Live);
|
|
$this->vault->put('dns.token', 'test-token', $this->by, OperatingMode::Test);
|
|
|
|
$this->vault->forget('dns.token', OperatingMode::Test);
|
|
|
|
// 'stored_live' statt 'stored' ist der Beweis, dass die Testzeile weg ist:
|
|
// was jetzt im Testplatz gilt, kommt aus dem Live-Platz.
|
|
expect($this->vault->source('dns.token', OperatingMode::Test))->toBe('stored_live');
|
|
expect($this->vault->source('dns.token', OperatingMode::Live))->toBe('stored');
|
|
});
|
|
|
|
/**
|
|
* Der Widerspruch, den das Schluss-Review gemessen hat — und warum er zählt.
|
|
*
|
|
* Modus test, `dns.token` nur im `:live`-Platz, `HETZNER_DNS_TOKEN` in der
|
|
* `.env`: `get()` lieferte den gespeicherten Live-Wert (der Rückfall oben),
|
|
* `source()` meldete dazu „environment" und `outline()` null. Die Karte in der
|
|
* Konsole las sich damit als „Aus der Serverdatei" über ein Zugangsdatum, das
|
|
* gerade aus der Datenbank in Kraft ist — ein Betreiber, der den Token wechseln
|
|
* will, wird in die `.env` geschickt, wo eine Änderung wirkungslos bleibt.
|
|
* Genau die Falle, die im Kopfkommentar von OperatingMode als Anlass dieses
|
|
* ganzen Vorhabens steht, und R19s Attrappe wörtlich: eine Anzeige, die sich
|
|
* wie eine Auskunft liest, falsch ist, und den Nächsten vom Nachsehen abhält.
|
|
*
|
|
* Ein DRITTER Rückgabewert, nicht 'stored': 'stored' blendet den
|
|
* „Vergessen"-Knopf ein, und der zeigte dann auf den leeren Testplatz.
|
|
*/
|
|
it('names the live slot as the source when it is the one in force', function () {
|
|
config()->set('provisioning.dns.token', 'from-env');
|
|
$this->vault->put('dns.token', 'live-token-value', $this->by, OperatingMode::Live);
|
|
OperatingMode::set(OperatingMode::Test);
|
|
|
|
expect($this->vault->get('dns.token'))->toBe('live-token-value')
|
|
->and($this->vault->source('dns.token'))->toBe('stored_live');
|
|
});
|
|
|
|
it('shows the outline and the date of the value that is actually in force', function () {
|
|
// Die zweite Folge desselben Befunds: ohne Umriss fehlt der Block
|
|
// „Hinterlegt / Geändert" ganz, und die Karte behauptet durch Weglassen,
|
|
// es liege nichts vor.
|
|
$this->vault->put('dns.token', 'live-token-value', $this->by, OperatingMode::Live);
|
|
OperatingMode::set(OperatingMode::Test);
|
|
|
|
$outline = $this->vault->outline('dns.token');
|
|
|
|
expect($outline)->not->toBeNull()
|
|
->and($outline)->toContain('alue')
|
|
->and($this->vault->updatedAt('dns.token'))->not->toBeNull();
|
|
});
|
|
|
|
it('keeps the strict entry out of the fallback here too', function () {
|
|
// Stripe hat keinen Rückfall, in keine Richtung — source() darf ihn
|
|
// deshalb auch nicht als 'stored_live' anzeigen, sonst widerspräche die
|
|
// Karte wieder der Kasse.
|
|
$this->vault->put('stripe.secret', 'sk_live_real', $this->by, OperatingMode::Live);
|
|
OperatingMode::set(OperatingMode::Test);
|
|
|
|
expect($this->vault->get('stripe.secret'))->toBeNull()
|
|
->and($this->vault->source('stripe.secret'))->toBe('none')
|
|
->and($this->vault->outline('stripe.secret'))->toBeNull();
|
|
});
|