feat(verlauf): Filter, Wiederholen und Aufräumen
Verlauf filterbar (URL, API, proxdeploy history); Wiederholen füllt den Assistenten aus einem alten Auftrag; Aufräumen löscht nach Fehlschlag oder Abbruch den halb angelegten Gast – nur, wenn dessen Beschreibung auf den Auftrag verweist. Deshalb setzt das Klonen die Beschreibung jetzt sofort. Auf pve-demo geprüft (CT 109). Closes #014 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var aktualisieren = flag.Bool("update", false, "Golden-Dateien neu schreiben")
|
||||
@@ -78,6 +79,8 @@ type fakeAPI struct {
|
||||
plan map[string]any
|
||||
status int
|
||||
antwort any
|
||||
verlauf map[string]any
|
||||
einzeln map[string]any
|
||||
}
|
||||
|
||||
func (f *fakeAPI) handler(t *testing.T) http.Handler {
|
||||
@@ -88,6 +91,11 @@ func (f *fakeAPI) handler(t *testing.T) http.Handler {
|
||||
fmt.Fprint(w, `{"fehler":{"code":"nicht_angemeldet","nachricht":"Nicht angemeldet."}}`)
|
||||
return
|
||||
}
|
||||
f.mu.Lock()
|
||||
if f.anfragen == nil {
|
||||
f.anfragen = map[string][]map[string]any{}
|
||||
}
|
||||
f.mu.Unlock()
|
||||
if r.Body != nil && r.Method == http.MethodPost {
|
||||
var rumpf map[string]any
|
||||
_ = json.NewDecoder(r.Body).Decode(&rumpf)
|
||||
@@ -112,6 +120,15 @@ func (f *fakeAPI) handler(t *testing.T) http.Handler {
|
||||
schreiben(200, map[string]any{"benutzername": "florian", "anzeigename": "Florian", "rolle": "OPERATOR", "rechte": []string{}, "version": "0.4.0"})
|
||||
case r.URL.Path == "/api/v1/auftraege" && r.Method == http.MethodPost:
|
||||
schreiben(202, map[string]any{"id": "c1", "kurzId": "a-7f3k"})
|
||||
case r.URL.Path == "/api/v1/auftraege" && r.Method == http.MethodGet:
|
||||
f.mu.Lock()
|
||||
f.anfragen["GET "+r.URL.RawQuery] = nil
|
||||
f.mu.Unlock()
|
||||
schreiben(200, f.verlauf)
|
||||
case strings.HasSuffix(r.URL.Path, "/aufraeumen"):
|
||||
schreiben(202, map[string]any{"id": "c2", "kurzId": "c-9x9x9"})
|
||||
case r.URL.Path == "/api/v1/auftraege/d-kaputt":
|
||||
schreiben(200, f.einzeln)
|
||||
case r.URL.Path == "/api/v1/auftraege/plan":
|
||||
schreiben(200, f.plan)
|
||||
case strings.HasSuffix(r.URL.Path, "/ereignisse"):
|
||||
@@ -323,3 +340,48 @@ func TestLoginMitToken(t *testing.T) {
|
||||
t.Error("falsches Token hat die Konfiguration verändert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHistory(t *testing.T) {
|
||||
ok := auftrag("ERFOLGREICH", alle, nil)
|
||||
ok["erstelltAm"] = "2026-10-12T08:05:00.000Z"
|
||||
ct := auftrag("FEHLGESCHLAGEN", alle, nil)
|
||||
ct["kurzId"], ct["name"], ct["gastTyp"], ct["vmid"], ct["distributionId"] = "d-ct001", "dns01", "LXC", 121, "ubuntu-24.04"
|
||||
ct["erstelltAm"] = "2026-10-12T08:00:00.000Z"
|
||||
f := &fakeAPI{verlauf: map[string]any{"eintraege": []any{ok, ct}, "gesamt": 30, "seite": 1, "seiten": 2}}
|
||||
alt := time.Local
|
||||
time.Local = time.UTC
|
||||
t.Cleanup(func() { time.Local = alt })
|
||||
aus, fehler, code := lauf(t, f, "history", "--type", "lxc", "--status", "fehlgeschlagen")
|
||||
if code != 0 {
|
||||
t.Fatalf("Exit %d: %s", code, fehler)
|
||||
}
|
||||
golden(t, "history.golden", aus)
|
||||
if _, gesehen := f.anfragen["GET status=FEHLGESCHLAGEN&typ=lxc"]; !gesehen {
|
||||
t.Errorf("Filter nicht gesendet: %v", f.anfragen)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanup(t *testing.T) {
|
||||
kaputt := auftrag("FEHLGESCHLAGEN", alle, nil)
|
||||
kaputt["kurzId"] = "d-kaputt"
|
||||
kaputt["aufraeumbar"] = map[string]any{"typ": "VM", "vmid": 120, "knoten": "pve01"}
|
||||
f := &fakeAPI{einzeln: kaputt}
|
||||
// Ohne Terminal keine Rückfrage möglich: ohne --yes wird nichts gelöscht.
|
||||
_, fehler, code := lauf(t, f, "cleanup", "d-kaputt")
|
||||
if code != ExitBedienung || !strings.Contains(fehler, "VM 120 auf pve01 löschen?") {
|
||||
t.Errorf("ohne --yes: Exit %d, %s", code, fehler)
|
||||
}
|
||||
if len(f.anfragen["/api/v1/auftraege/d-kaputt/aufraeumen"]) != 0 {
|
||||
t.Error("ohne Bestätigung aufgeräumt")
|
||||
}
|
||||
aus, _, code := lauf(t, f, "cleanup", "d-kaputt", "--yes", "--no-wait")
|
||||
if code != 0 || strings.TrimSpace(aus) != "c-9x9x9" || len(f.anfragen["/api/v1/auftraege/d-kaputt/aufraeumen"]) != 1 {
|
||||
t.Errorf("mit --yes: Exit %d, %q", code, aus)
|
||||
}
|
||||
|
||||
kaputt["aufraeumbar"] = nil
|
||||
_, fehler, code = lauf(t, f, "cleanup", "d-kaputt", "--yes")
|
||||
if code != ExitBedienung || !strings.Contains(fehler, "nichts aufzuräumen") {
|
||||
t.Errorf("nicht aufräumbar: Exit %d, %s", code, fehler)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user