114 lines
5.2 KiB
PHP
114 lines
5.2 KiB
PHP
<?php // tests/Feature/Cancellation/TeardownStateTest.php
|
|
|
|
use App\Models\Instance;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
it('eine frische Instanz weiss noch gar nichts über Archivierung oder Abbau', function () {
|
|
// Vier Felder, vier Zustände. Eine Instanz, die zum ersten Mal sichtbar wird, hat noch
|
|
// kein Archiv (archive_volid null), wurde nicht archiviert (archived_at null), wurde
|
|
// nicht abgebaut (torn_down_at null) und kein Fehler hat gehakt (teardown_error null).
|
|
// Das ist der gültige Normalzustand: noch am Leben.
|
|
$instance = Instance::factory()->create();
|
|
|
|
expect($instance->archive_volid)->toBeNull()
|
|
->and($instance->archived_at)->toBeNull()
|
|
->and($instance->torn_down_at)->toBeNull()
|
|
->and($instance->teardown_error)->toBeNull();
|
|
});
|
|
|
|
it('merkt sich, wo das Archiv liegt', function () {
|
|
$volid = '/mnt/archiv-2026-08-04/instance-abc123';
|
|
$instance = Instance::factory()->create(['archive_volid' => $volid]);
|
|
|
|
expect($instance->fresh()->archive_volid)->toEqual($volid);
|
|
});
|
|
|
|
it('speichert und gibt archived_at als Carbon zurück', function () {
|
|
// archived_at kommt als Datenbankzeichenkette an und muss als Carbon-Instanz wieder
|
|
// herauskommen — ein Objekt, nicht eine Zeichenkette, sonst kann ein Aufrufer
|
|
// Vergleiche, Arithmetik und Formatierungen nicht durchführen.
|
|
$now = now();
|
|
$instance = Instance::factory()->create(['archived_at' => $now]);
|
|
|
|
expect($instance->fresh()->archived_at)
|
|
->toBeInstanceOf(Carbon::class)
|
|
->and($instance->fresh()->archived_at->timestamp)->toBe($now->timestamp);
|
|
});
|
|
|
|
it('speichert und gibt torn_down_at als Carbon zurück', function () {
|
|
// torn_down_at kommt als Datenbankzeichenkette an und muss als Carbon-Instanz wieder
|
|
// herauskommen — ein Objekt, nicht eine Zeichenkette, sonst kann ein Aufrufer
|
|
// Vergleiche, Arithmetik und Formatierungen nicht durchführen.
|
|
$now = now();
|
|
$instance = Instance::factory()->create(['torn_down_at' => $now]);
|
|
|
|
expect($instance->fresh()->torn_down_at)
|
|
->toBeInstanceOf(Carbon::class)
|
|
->and($instance->fresh()->torn_down_at->timestamp)->toBe($now->timestamp);
|
|
});
|
|
|
|
it('speichert die Fehlermeldung beim Abbau', function () {
|
|
// teardown_error hält den Fehler fest, der beim Löschen gehakt hat — in Klartext,
|
|
// damit ein Betreiber ihn findet, ohne erst auf die Logs zu schauen.
|
|
$error = 'SSH-Verbindung zum Host timeout nach 30 Sekunden';
|
|
$instance = Instance::factory()->create(['teardown_error' => $error]);
|
|
|
|
expect($instance->fresh()->teardown_error)->toEqual($error);
|
|
});
|
|
|
|
it('ein gültiger Zwischenstand: archiviert aber nicht abgebaut', function () {
|
|
// Das ist der entscheidende Zwischenstand, der erklärt, warum es zwei Felder sind statt
|
|
// einem einzigen. Eine Instanz mit `archived_at` gesetzt und `torn_down_at` leer
|
|
// bedeutet: „Sicherung war erfolgreich, Löschung noch nicht versucht" oder „Sicherung
|
|
// erfolgreich, Löschung gescheitert — sieh teardown_error". Ein einziges Feld würde
|
|
// den Unterschied zwischen „noch nicht archiviert" und „archiviert aber noch nicht
|
|
// gelöscht" unsichtbar machen.
|
|
$archived = now()->subDays(5);
|
|
$instance = Instance::factory()->create([
|
|
'archive_volid' => '/mnt/archiv/instance-123',
|
|
'archived_at' => $archived,
|
|
'torn_down_at' => null,
|
|
'teardown_error' => null,
|
|
]);
|
|
|
|
expect($instance->fresh()->archived_at->timestamp)->toBe($archived->timestamp)
|
|
->and($instance->fresh()->torn_down_at)->toBeNull()
|
|
->and($instance->fresh()->teardown_error)->toBeNull();
|
|
});
|
|
|
|
it('ein fehlgeschlagener Abbau ist sichtbar', function () {
|
|
// Eine Instanz mit `archived_at` und `torn_down_at` beide gesetzt bedeutet: Archivierung
|
|
// UND Löschung waren erfolgreich. Eine mit `archived_at` gesetzt und `torn_down_at`
|
|
// leer aber `teardown_error` gefüllt bedeutet: Löschung hat gehakt. Der Fehler ist
|
|
// dokumentiert und der Betreiber kann reagieren.
|
|
$archived = now()->subDays(5);
|
|
$error = 'Löschen des Snapshots gescheitert: Volume ist noch in Verwendung';
|
|
$instance = Instance::factory()->create([
|
|
'archive_volid' => '/mnt/archiv/instance-123',
|
|
'archived_at' => $archived,
|
|
'torn_down_at' => null,
|
|
'teardown_error' => $error,
|
|
]);
|
|
|
|
expect($instance->fresh()->archived_at->timestamp)->toBe($archived->timestamp)
|
|
->and($instance->fresh()->torn_down_at)->toBeNull()
|
|
->and($instance->fresh()->teardown_error)->toEqual($error);
|
|
});
|
|
|
|
it('ein vollständiger Durchlauf: archiviert und abgebaut', function () {
|
|
// Die „glückliche" Sequenz: Archivierung erfolgreich, Löschung erfolgreich. Beide
|
|
// Zeitstempel sind gesetzt, kein Fehler.
|
|
$archived = now()->subDays(10);
|
|
$tornDown = now()->subDays(5);
|
|
$instance = Instance::factory()->create([
|
|
'archive_volid' => '/mnt/archiv/instance-123',
|
|
'archived_at' => $archived,
|
|
'torn_down_at' => $tornDown,
|
|
'teardown_error' => null,
|
|
]);
|
|
|
|
expect($instance->fresh()->archived_at->timestamp)->toBe($archived->timestamp)
|
|
->and($instance->fresh()->torn_down_at->timestamp)->toBe($tornDown->timestamp)
|
|
->and($instance->fresh()->teardown_error)->toBeNull();
|
|
});
|