Files
proxdeploy/tests/verlauf.test.ts
T
florian.wildeandClaude Opus 5.5 7f6cf00a0e 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>
2026-10-12 16:39:58 +02:00

138 lines
5.4 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import * as liste from "@/app/api/v1/auftraege/route";
import * as aufraeumenRoute from "@/app/api/v1/auftraege/[id]/aufraeumen/route";
import { verschluesseln } from "@/lib/geheimnisse";
import { auftragHolen, verlaufFilterAus, VERLAUF_PRO_SEITE } from "@/server/services/auftraege";
import { katalogSynchronisieren } from "@/server/services/katalog";
import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db";
import { angemeldet, anfrage, kontext } from "./helpers/anfragen";
describe("Verlaufsfilter aus der URL", () => {
it("übernimmt Gültiges und lässt Unsinn still fallen", () => {
expect(verlaufFilterAus({ typ: "lxc", status: "FEHLGESCHLAGEN", seite: "3", server: "rz1" })).toEqual({
typ: "lxc",
status: "FEHLGESCHLAGEN",
seite: 3,
server: "rz1",
});
expect(verlaufFilterAus({ typ: "quatsch", status: "x", seite: "-4", distribution: "" })).toEqual({
seite: 1,
});
});
});
describe.runIf(datenbankVerfuegbar)("Verlauf und Aufräumen", () => {
let cookie: string;
let benutzerId: string;
let serverIds: Record<string, string>;
beforeEach(async () => {
await datenbankLeeren();
await katalogSynchronisieren();
const op = await angemeldet("OPERATOR");
cookie = op.cookie;
benutzerId = op.benutzer.id;
serverIds = {};
for (const name of ["rz1", "rz2"]) {
serverIds[name] = (
await testPrisma.server.create({
data: {
name,
titel: name,
url: "https://127.0.0.1:8006",
tokenId: "a@pve!b",
tokenGeheimnis: verschluesseln("x"),
fingerabdruck: "AA",
},
})
).id;
}
});
let nr = 0;
const auftrag = async (daten: Record<string, unknown>) =>
await testPrisma.auftrag.create({
data: {
kurzId: `d-t${String(nr++).padStart(4, "0")}`,
art: "AUSROLLEN",
status: "ERFOLGREICH",
quelle: "WEB",
serverId: serverIds["rz1"]!,
erstelltVonId: benutzerId,
anfrage: {},
...daten,
} as never,
});
it("filtert nach Server, Typ, Distribution und Status und blättert", async () => {
await auftrag({ gastTyp: "VM", distributionId: "debian-13" });
await auftrag({ gastTyp: "LXC", distributionId: "ubuntu-24.04", status: "FEHLGESCHLAGEN" });
await auftrag({ gastTyp: "LXC", distributionId: "debian-13", serverId: serverIds["rz2"] });
await auftrag({ art: "TEMPLATE_BAUEN", gastTyp: "VM", distributionId: "debian-13" });
const holen = async (abfrage: string) =>
(await (await liste.GET(anfrage("GET", `/api/v1/auftraege?${abfrage}`, cookie))).json()) as {
eintraege: Array<{ kurzId: string; art: string; gastTyp: string }>;
gesamt: number;
seiten: number;
};
expect((await holen("")).gesamt).toBe(4);
expect((await holen("typ=lxc")).gesamt).toBe(2);
expect((await holen("typ=vm")).gesamt).toBe(1);
expect((await holen("typ=template")).eintraege[0]!.art).toBe("TEMPLATE_BAUEN");
expect((await holen("server=rz2")).gesamt).toBe(1);
expect((await holen("distribution=debian-13&typ=lxc")).gesamt).toBe(1);
expect((await holen("status=FEHLGESCHLAGEN")).gesamt).toBe(1);
for (let i = 0; i < VERLAUF_PRO_SEITE; i++) await auftrag({ gastTyp: "VM" });
const erste = await holen("");
const zweite = await holen("seite=2");
expect(erste.seiten).toBe(2);
expect(erste.eintraege).toHaveLength(VERLAUF_PRO_SEITE);
expect(zweite.eintraege).toHaveLength(4);
expect(new Set([...erste.eintraege, ...zweite.eintraege].map((e) => e.kurzId)).size).toBe(
VERLAUF_PRO_SEITE + 4,
);
});
const starten = async (kurzId: string) =>
await aufraeumenRoute.POST(
anfrage("POST", `/api/v1/auftraege/${kurzId}/aufraeumen`, cookie),
kontext({ id: kurzId }),
);
it("Aufräumen nur bei Fehlschlag/Abbruch mit angelegtem Gast, nie doppelt", async () => {
const ok = await auftrag({ gastTyp: "VM", angelegt: { vmid: 120, knoten: "pve01", typ: "VM" } });
const nichts = await auftrag({ gastTyp: "VM", status: "FEHLGESCHLAGEN", angelegt: { knoten: "pve01" } });
const kaputt = await auftrag({
gastTyp: "LXC",
name: "ct01",
status: "ABGEBROCHEN",
angelegt: { vmid: 121, knoten: "pve01", typ: "LXC" },
});
expect((await auftragHolen(ok.kurzId)).aufraeumbar).toBeNull();
expect((await auftragHolen(nichts.kurzId)).aufraeumbar).toBeNull();
expect((await auftragHolen(kaputt.kurzId)).aufraeumbar).toEqual({
typ: "LXC",
vmid: 121,
knoten: "pve01",
});
expect((await starten(ok.kurzId)).status).toBe(409);
expect((await starten(nichts.kurzId)).status).toBe(409);
const gestartet = await starten(kaputt.kurzId);
expect(gestartet.status).toBe(202);
const { kurzId } = (await gestartet.json()) as { kurzId: string };
expect(kurzId).toMatch(/^c-/);
const neu = await testPrisma.auftrag.findUniqueOrThrow({ where: { kurzId } });
expect(neu).toMatchObject({ art: "AUFRAEUMEN", bezugId: kaputt.id, gastTyp: "LXC", name: "ct01" });
expect(neu.anfrage).toEqual({ auftrag: kaputt.kurzId, typ: "LXC", vmid: 121, knoten: "pve01" });
const doppelt = await starten(kaputt.kurzId);
expect(doppelt.status).toBe(409);
expect((await doppelt.json()).fehler.nachricht).toContain(kurzId);
expect(await testPrisma.protokoll.count({ where: { aktion: "Aufräumen gestartet" } })).toBe(1);
});
});