CluPilotCloud/tests/Feature/Readiness/OnboardingChecksTest.php

113 lines
4.5 KiB
PHP

<?php
use App\Models\Datacenter;
use App\Models\Operator;
use App\Services\Secrets\SecretVault;
use App\Support\OperatingMode;
use App\Support\Readiness;
use App\Support\Readiness\Check;
use App\Support\Settings;
/**
* Die Bereitschaftsprüfung berichtet, was fehlt — und sagt dazu, was
* kaputtgeht. Siehe BillingChecksTest für dieselbe Begründung.
*/
function onboardingCheck(string $key): ?Check
{
return collect(Readiness::all())->firstWhere('key', $key);
}
it('needs at least one datacenter', function () {
// create_datacenters_table selbst legt 'fsn'/'hel' als Vorgabe an — auch
// in dieser Suite, da es sich um eine Migration und keinen Seeder
// handelt. Erst geleert, damit dieser Test den echten leeren Zustand
// prüft statt der Werkseinstellung dieser Installation.
Datacenter::query()->delete();
// ValidateHostInput ist der erste Schritt der Host-Kette und bricht ohne
// Rechenzentrum ab — bevor überhaupt eine SSH-Verbindung versucht wird.
expect(onboardingCheck('onboarding.datacenter')->satisfied)->toBeFalse();
Datacenter::factory()->create();
expect(onboardingCheck('onboarding.datacenter')->satisfied)->toBeTrue();
});
it('needs an ssh private key', function () {
// Mode selbst gesetzt (Testisolation: CACHE_STORE=array wird zwischen
// Testdateien nicht geleert), damit dieser Test nicht vom Rest der Zeile
// abhängt, in der die Suite läuft.
OperatingMode::set(OperatingMode::Live);
// Diese Installation hat in .env tatsächlich einen SSH-Schlüssel
// konfiguriert (CLUPILOT_SSH_PRIVATE_KEY_PATH), auf den SecretVault::get()
// zurückfällt, solange kein Tresor-Eintrag existiert — direkt
// zurückgesetzt, damit der Test nicht vom Entwicklerrechner abhängt.
config()->set('provisioning.ssh.private_key', '');
expect(onboardingCheck('onboarding.ssh_private_key')->satisfied)->toBeFalse();
app(SecretVault::class)->put(
'ssh.private_key',
'-----BEGIN OPENSSH PRIVATE KEY-----',
Operator::factory()->create(),
OperatingMode::Live,
);
expect(onboardingCheck('onboarding.ssh_private_key')->satisfied)->toBeTrue();
});
it('needs a usable vault key', function () {
// isUsable() statt eines bloßen filled()-Checks: SecretCipher warnt in
// seinem eigenen Kommentar genau davor, eine zweite, schwächere Regel zu
// bauen, die einen falsch geformten Schlüssel als gültig durchwinkt.
config()->set('admin_access.secrets_key', '');
expect(onboardingCheck('onboarding.secrets_key')->satisfied)->toBeFalse();
config()->set('admin_access.secrets_key', base64_encode(str_repeat('a', 32)));
expect(onboardingCheck('onboarding.secrets_key')->satisfied)->toBeTrue();
});
it('needs a usable vpn config key', function () {
config()->set('admin_access.vpn_config_key', '');
expect(onboardingCheck('onboarding.vpn_config_key')->satisfied)->toBeFalse();
config()->set('admin_access.vpn_config_key', base64_encode(str_repeat('b', 32)));
expect(onboardingCheck('onboarding.vpn_config_key')->satisfied)->toBeTrue();
});
it('needs all three wireguard values, not just some', function () {
// Zwei von drei ist kein halber Tunnel, sondern gar keiner. Subnet wird
// bewusst direkt über config() geprüft statt über Settings/
// ProvisioningSettings (siehe deren eigener Kopfkommentar: der Compose-
// Stack liest denselben Schlüssel beim Containerstart) — und hier im Test
// ebenso direkt zurückgesetzt, damit das Ergebnis nicht von den echten
// WireGuard-Werten dieser Installation abhängt.
config()->set('provisioning.wireguard.hub_public_key', '');
config()->set('provisioning.wireguard.endpoint', '');
config()->set('provisioning.wireguard.subnet', '');
expect(onboardingCheck('onboarding.wg_hub')->satisfied)->toBeFalse();
Settings::set('provisioning.wg_hub_pubkey', 'abc=');
Settings::set('provisioning.wg_endpoint', '198.51.45.9:51820');
expect(onboardingCheck('onboarding.wg_hub')->satisfied)->toBeFalse();
config()->set('provisioning.wireguard.subnet', '10.66.0.0/24');
expect(onboardingCheck('onboarding.wg_hub')->satisfied)->toBeTrue();
});
it('names what breaks for every onboarding check, not just what is missing', function () {
foreach ([
'onboarding.ssh_private_key',
'onboarding.secrets_key',
'onboarding.vpn_config_key',
'onboarding.wg_hub',
'onboarding.datacenter',
] as $key) {
expect(onboardingCheck($key)->breaks)->not->toBe('');
}
});