Files
sshkeyvault/tests/vorlagen-service.test.ts
florian.wildeandClaude Opus 5.5 458f62289a test(vorlagen): Service-, E2E-Tests und Screenshots (#019)
Der Service war nur ueber die reinen Pruefregeln abgedeckt. Die
DB-Tests halten fest, was die Oberflaeche voraussetzt: doppelte Namen
melden sich am Feld name, Aenderungen protokollieren genau die
geaenderten Felder, eingebaute Vorlagen lassen sich nicht archivieren.
Der E2E-Ablauf prueft Feldfehler, Vorschau und Rechte im gebauten
Stand; die Screenshots dienen der Layout-Abnahme bei 390 px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:16:42 +02:00

134 lines
5.6 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import { prisma } from "@/lib/db";
import { VorlagenFehler, type VorlageEingabe } from "@/lib/vorlagen";
import { vorlagenListe } from "@/server/queries/vorlagen";
import { vorlageAnlegen, vorlageArchivieren, vorlageBearbeiten } from "@/server/services/vorlagen";
import { datenbankLeeren } from "./hilfen/db";
/** Vorlagen-Service (#019) gegen die Testdatenbank: Eindeutigkeit, Protokoll, Schutz der eingebauten. */
let akteur: { id: string; name: string };
beforeEach(async () => {
await datenbankLeeren();
const benutzer = await prisma.benutzer.create({ data: { name: "Ada Admin", rolle: "ADMIN" } });
akteur = { id: benutzer.id, name: benutzer.name };
});
const BASIS: VorlageEingabe = {
name: "Monitoring",
beschreibung: " Zugang für Prometheus ",
schluesselTyp: "ED25519",
kategorie: "DIENST",
standardLaufzeitTage: 30,
zielbenutzer: "monitor",
zielbenutzerAnlegen: true,
sudoModus: "BEFEHLE",
sudoBefehle: ["/usr/bin/systemctl status nginx"],
authorizedKeysOptionen: 'from="{{quellHostIp}}",no-pty',
sshConfigAlias: "mon-{{zielHost}}",
knownHostsEintragen: true,
privatNachAusrollenLoeschen: false,
};
async function fehlerVon(versuch: Promise<unknown>): Promise<VorlagenFehler> {
const fehler = await versuch.then(
() => undefined,
(f: unknown) => f,
);
expect(fehler).toBeInstanceOf(VorlagenFehler);
return fehler as VorlagenFehler;
}
describe("Vorlage anlegen", () => {
it("speichert bereinigt, laesst Platzhalter stehen und protokolliert", async () => {
const id = await vorlageAnlegen(BASIS, akteur);
const v = await prisma.vorlage.findUniqueOrThrow({ where: { id } });
expect(v.eingebaut).toBe(false);
expect(v.beschreibung).toBe("Zugang für Prometheus");
expect(v.sudoBefehle).toEqual(["/usr/bin/systemctl status nginx"]);
expect(v.authorizedKeysOptionen).toBe('from="{{quellHostIp}}",no-pty');
expect(v.sshConfigAlias).toBe("mon-{{zielHost}}");
const eintrag = await prisma.protokoll.findFirstOrThrow({ where: { aktion: "vorlage.angelegt" } });
expect(eintrag.akteurId).toBe(akteur.id);
expect(eintrag.details).toMatchObject({ name: "Monitoring" });
expect((await vorlagenListe()).map((l) => l.name)).toEqual(["Monitoring"]);
});
it("verwirft sudo-Befehle, wenn der Modus nicht BEFEHLE ist", async () => {
const id = await vorlageAnlegen({ ...BASIS, sudoModus: "KEINER" }, akteur);
expect((await prisma.vorlage.findUniqueOrThrow({ where: { id } })).sudoBefehle).toEqual([]);
});
it("meldet einen doppelten Namen am Feld name", async () => {
await vorlageAnlegen(BASIS, akteur);
const fehler = await fehlerVon(vorlageAnlegen({ ...BASIS, zielbenutzer: "anders" }, akteur));
expect(fehler.feld).toBe("name");
expect(await prisma.vorlage.count()).toBe(1);
});
it("meldet einen unbekannten Platzhalter am Feld der Optionen", async () => {
const fehler = await fehlerVon(
vorlageAnlegen({ ...BASIS, authorizedKeysOptionen: 'from="{{quellIp}}"' }, akteur),
);
expect(fehler.feld).toBe("authorizedKeysOptionen");
expect(fehler.message).toContain("{{quellIp}}");
expect(await prisma.vorlage.count()).toBe(0);
});
});
describe("Vorlage bearbeiten", () => {
it("protokolliert genau die geaenderten Felder", async () => {
const id = await vorlageAnlegen(BASIS, akteur);
await vorlageBearbeiten(
id,
{ ...BASIS, standardLaufzeitTage: 60, sudoBefehle: ["/usr/bin/systemctl status nginx", "/usr/bin/id"] },
akteur,
);
const v = await prisma.vorlage.findUniqueOrThrow({ where: { id } });
expect(v.standardLaufzeitTage).toBe(60);
const eintrag = await prisma.protokoll.findFirstOrThrow({ where: { aktion: "vorlage.geaendert" } });
const details = eintrag.details as { name: string; geaendert: string[] };
expect(details.name).toBe("Monitoring");
expect(details.geaendert.sort()).toEqual(["standardLaufzeitTage", "sudoBefehle"]);
});
it("meldet beim Umbenennen auf einen vergebenen Namen das Feld name", async () => {
await vorlageAnlegen(BASIS, akteur);
const id = await vorlageAnlegen({ ...BASIS, name: "Zweite" }, akteur);
const fehler = await fehlerVon(vorlageBearbeiten(id, BASIS, akteur));
expect(fehler.feld).toBe("name");
});
it("verweigert archivierte Vorlagen", async () => {
const id = await vorlageAnlegen(BASIS, akteur);
await vorlageArchivieren(id, akteur);
await fehlerVon(vorlageBearbeiten(id, BASIS, akteur));
});
});
describe("Vorlage archivieren", () => {
it("archiviert eigene Vorlagen und blendet sie aus der Liste aus", async () => {
const id = await vorlageAnlegen(BASIS, akteur);
await vorlageArchivieren(id, akteur);
expect((await prisma.vorlage.findUniqueOrThrow({ where: { id } })).archiviertAm).not.toBeNull();
expect(await vorlagenListe()).toEqual([]);
expect(await prisma.protokoll.count({ where: { aktion: "vorlage.archiviert" } })).toBe(1);
});
it("laesst eingebaute Vorlagen nicht archivieren, aber bearbeiten", async () => {
const eingebaut = await prisma.vorlage.create({
data: { name: "Deployment-Zugang", eingebaut: true, zielbenutzer: "deploy" },
});
await fehlerVon(vorlageArchivieren(eingebaut.id, akteur));
expect((await prisma.vorlage.findUniqueOrThrow({ where: { id: eingebaut.id } })).archiviertAm).toBeNull();
await vorlageBearbeiten(eingebaut.id, { ...BASIS, name: "Deployment-Zugang" }, akteur);
const v = await prisma.vorlage.findUniqueOrThrow({ where: { id: eingebaut.id } });
expect(v.eingebaut).toBe(true);
expect(v.zielbenutzer).toBe("monitor");
});
});