Merge branch 'phase/019-vorlagen'

This commit is contained in:
2026-10-12 10:54:31 +02:00
4 changed files with 418 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
import { describe, expect, it } from "vitest";
import { EINGEBAUTE_VORLAGEN } from "../prisma/vorlagen";
import {
VorlagenFehler,
istIpAdresse,
platzhalterErsetzen,
vorlageAufloesen,
vorlagePruefen,
type VorlageEingabe,
} from "@/lib/vorlagen";
/** Vorlagen und Platzhalter (#019). */
const BASIS: VorlageEingabe = {
name: "Test",
schluesselTyp: "ED25519",
kategorie: "DIENST",
zielbenutzer: "deploy",
zielbenutzerAnlegen: true,
sudoModus: "KEINER",
sudoBefehle: [],
knownHostsEintragen: true,
privatNachAusrollenLoeschen: false,
};
describe("Platzhalter", () => {
it("ersetzt bekannte Platzhalter", () => {
expect(platzhalterErsetzen('from="{{quellHostIp}}"', { quellHostIp: "10.0.0.5" })).toBe(
'from="10.0.0.5"',
);
expect(platzhalterErsetzen("deploy-{{ zielHost }}", { zielHost: "srv-app01" })).toBe("deploy-srv-app01");
});
it("verweigert unbekannte, fehlende und gefaehrliche Werte", () => {
expect(() => platzhalterErsetzen("{{irgendwas}}", {})).toThrow(VorlagenFehler);
expect(() => platzhalterErsetzen("{{quellHostIp}}", {})).toThrow(/fehlt ein Wert/);
expect(() =>
platzhalterErsetzen('from="{{quellHostIp}}"', { quellHostIp: '1.2.3.4",command="id' }),
).toThrow(VorlagenFehler);
});
it("erkennt IP-Adressen", () => {
expect(istIpAdresse("10.0.0.5")).toBe(true);
expect(istIpAdresse("10.0.0.0/24")).toBe(true);
expect(istIpAdresse("fe80::1")).toBe(true);
expect(istIpAdresse("srv-app01")).toBe(false);
});
});
describe("Vorlage pruefen", () => {
it("nimmt alle eingebauten Vorlagen an", () => {
for (const vorlage of EINGEBAUTE_VORLAGEN) {
expect(() =>
vorlagePruefen({
...BASIS,
...vorlage,
beschreibung: vorlage.beschreibung ?? null,
kategorie: vorlage.kategorie as "PERSON" | "DIENST",
schluesselTyp: (vorlage.schluesselTyp ?? "ED25519") as "ED25519",
sudoBefehle: [],
zielbenutzerAnlegen: vorlage.zielbenutzerAnlegen ?? false,
sudoModus: (vorlage.sudoModus ?? "KEINER") as "KEINER",
knownHostsEintragen: vorlage.knownHostsEintragen ?? true,
privatNachAusrollenLoeschen: false,
}),
).not.toThrow();
}
});
it.each([
[{ zielbenutzer: "Root" }, "zielbenutzer"],
[{ authorizedKeysOptionen: 'from="{{unbekannt}}"' }, "authorizedKeysOptionen"],
[{ authorizedKeysOptionen: "restriktiv" }, "authorizedKeysOptionen"],
[{ sudoModus: "BEFEHLE" as const, sudoBefehle: [] }, "sudoBefehle"],
[{ sudoModus: "BEFEHLE" as const, sudoBefehle: ["systemctl restart x"] }, "sudoBefehle"],
[{ zielbenutzerAnlegen: false, sudoModus: "VOLL" as const }, "sudoModus"],
[{ sshConfigAlias: "a b {{zielHost}}" }, "sshConfigAlias"],
[{ kategorie: "PERSON" as const }, "kategorie"],
])("weist ab: %o", (fehler, feld) => {
try {
vorlagePruefen({ ...BASIS, ...fehler });
throw new Error("kein Fehler");
} catch (fehlerObjekt) {
expect(fehlerObjekt).toBeInstanceOf(VorlagenFehler);
expect((fehlerObjekt as VorlagenFehler).feld).toBe(feld);
}
});
});
describe("Vorlage aufloesen", () => {
it("liefert Optionen, Alias und Ablaufdatum", () => {
const jetzt = new Date("2026-10-12T10:00:00Z");
expect(
vorlageAufloesen(
{
zielbenutzer: "deploy",
authorizedKeysOptionen: 'from="{{quellHostIp}}"',
sshConfigAlias: "deploy-{{zielHost}}",
standardLaufzeitTage: 90,
},
{ quellHostIp: "10.0.0.5", zielHost: "srv-app01" },
jetzt,
),
).toEqual({
zielbenutzer: "deploy",
authorizedKeysOptionen: 'from="10.0.0.5"',
sshConfigAlias: "deploy-srv-app01",
ablaufAm: new Date("2027-01-10T10:00:00Z"),
});
});
});