import { cpSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { beforeEach, describe, expect, it } from "vitest"; import { GET as distributionenGet } from "@/app/api/v1/distributionen/route"; import { PATCH as distributionPatch } from "@/app/api/v1/distributionen/[id]/route"; import { GET as profileGet } from "@/app/api/v1/profile/route"; import { katalogSynchronisieren } from "@/server/services/katalog"; import { datenbankLeeren, datenbankVerfuegbar, testPrisma } from "./helpers/db"; import { angemeldet, anfrage, kontext } from "./helpers/anfragen"; describe.runIf(datenbankVerfuegbar)("Katalog-Abgleich und API", () => { let wurzel: string; beforeEach(async () => { await datenbankLeeren(); wurzel = mkdtempSync(join(tmpdir(), "pd-sync-")); cpSync("catalog", wurzel, { recursive: true }); }); it("legt an, erkennt Änderungen über den Hash und ist idempotent", async () => { const erst = await katalogSynchronisieren(wurzel); expect(erst.distributionen).toEqual({ neu: 5, geaendert: 0, entfernt: 0 }); expect(erst.profile).toEqual({ neu: 3, geaendert: 0, entfernt: 0 }); const nochmal = await katalogSynchronisieren(wurzel); expect(nochmal.distributionen).toEqual({ neu: 0, geaendert: 0, entfernt: 0 }); writeFileSync( join(wurzel, "distros/debian-12.yaml"), (await import("node:fs")) .readFileSync(join(wurzel, "distros/debian-12.yaml"), "utf8") .replace("Bookworm", "Bookworm (alt)"), ); const geaendert = await katalogSynchronisieren(wurzel); expect(geaendert.distributionen.geaendert).toBe(1); expect((await testPrisma.distribution.findUniqueOrThrow({ where: { id: "debian-12" } })).name).toContain( "(alt)", ); }); it("behält „aktiv“ über Änderungen hinweg und markiert entfernte Dateien statt zu löschen", async () => { await katalogSynchronisieren(wurzel); await testPrisma.distribution.update({ where: { id: "ubuntu-22.04" }, data: { aktiv: false } }); rmSync(join(wurzel, "distros/ubuntu-22.04.yaml")); const ergebnis = await katalogSynchronisieren(wurzel); expect(ergebnis.distributionen.entfernt).toBe(1); const zeile = await testPrisma.distribution.findUniqueOrThrow({ where: { id: "ubuntu-22.04" } }); expect(zeile.entferntAm).not.toBeNull(); expect(zeile.aktiv).toBe(false); }); it("überschreibt kein eigenes Profil mit gleicher id", async () => { await testPrisma.profil.create({ data: { id: "docker", name: "Mein Docker", familien: ["debian"], herkunft: "EIGEN", definition: {}, quelltext: "x", }, }); const ergebnis = await katalogSynchronisieren(wurzel); expect(ergebnis.fehler.map((f) => f.meldung).join()).toMatch(/eigenes Profil „docker“/); expect((await testPrisma.profil.findUniqueOrThrow({ where: { id: "docker" } })).name).toBe("Mein Docker"); }); it("API: Operator sieht nur aktive; Admin schaltet ab und sieht mit ?alle=1 alles", async () => { await katalogSynchronisieren(wurzel); const admin = (await angemeldet("ADMIN")).cookie; const operator = (await angemeldet("OPERATOR")).cookie; expect( ( await distributionPatch( anfrage("PATCH", "/api/v1/distributionen/debian-12", operator, { aktiv: false }), kontext({ id: "debian-12" }), ) ).status, ).toBe(403); expect( ( await distributionPatch( anfrage("PATCH", "/api/v1/distributionen/debian-12", admin, { aktiv: false }), kontext({ id: "debian-12" }), ) ).status, ).toBe(200); const fuerOperator = await ( await distributionenGet(anfrage("GET", "/api/v1/distributionen?typ=vm&alle=1", operator)) ).json(); expect(fuerOperator.map((d: { id: string }) => d.id)).not.toContain("debian-12"); const fuerAdmin = await ( await distributionenGet(anfrage("GET", "/api/v1/distributionen?alle=1", admin)) ).json(); expect(fuerAdmin.find((d: { id: string }) => d.id === "debian-12").aktiv).toBe(false); const profile = await (await profileGet(anfrage("GET", "/api/v1/profile", operator))).json(); expect(profile.map((p: { id: string }) => p.id)).toEqual(["base", "docker", "zabbix-agent"]); expect(await testPrisma.protokoll.count({ where: { aktion: "Distribution deaktiviert" } })).toBe(1); }); });