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>
138 lines
5.4 KiB
TypeScript
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);
|
|
});
|
|
});
|