Zwei Wege in den Betrieb, beide so weit gefahren, wie es hier moeglich war. deploy/install.sh richtet Debian 13 in neun Schritten ein: PostgreSQL 17 hoert nur am Unix-Socket, der Dienstnutzer hat kein Login, die Geheimnisse entstehen im Zielsystem und liegen 0600 bei root. Die Umgebungsdatei wird nie ueberschrieben -- auch nicht beim Aktualisieren, sonst enden alle Sitzungen und die verschluesselten Bind-Passwoerter waeren unlesbar. deploy/proxmox/laufzettel.sh legt den Container an und ruft install.sh darin auf. Der pct-Teil ist ungetestet, weil kein PVE-Host zur Verfuegung stand; die Gegenprobe steht als Pruefliste in docs/betrieb/proxmox.md, das Folge-Issue ist #024. Die Abnahme gegen alle 61 Kriterien hat drei Zusagen ohne Deckung gefunden, alle behoben: der Healthcheck des Webdienstes konnte nie bestehen (busybox loest localhost nach ::1 auf, Next lauscht nur auf IPv4), die CI-Pipeline konnte in keinem Job gruen werden (kein LDAP-Dienst, kein gestarteter Server, kein shellcheck), und der README nannte fuer die Entwicklungsdatenbank den falschen Port. Closes #19
70 lines
2.8 KiB
TypeScript
70 lines
2.8 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { describe, expect, it } from "vitest";
|
|
import { parse } from "yaml";
|
|
|
|
/**
|
|
* Die Datenbank im Betriebsstand ist absichtlich von aussen nicht erreichbar.
|
|
* Diese Tests halten das fest, damit ein ports:-Mapping nicht versehentlich
|
|
* zurueckkommt. Siehe docs/betrieb/inbetriebnahme.md.
|
|
*/
|
|
function compose(datei: string): Record<string, never> {
|
|
return parse(readFileSync(datei, "utf8")) as Record<string, never>;
|
|
}
|
|
|
|
describe("Betriebsstand: docker-compose.yml", () => {
|
|
const stand = compose("docker-compose.yml") as unknown as {
|
|
services: Record<string, { ports?: unknown[]; networks?: string[] }>;
|
|
networks: Record<string, { internal?: boolean } | null>;
|
|
};
|
|
|
|
it("veroeffentlicht keinen Port der Datenbank", () => {
|
|
expect(stand.services["postgres"]?.ports).toBeUndefined();
|
|
});
|
|
|
|
it("haengt die Datenbank nur in das interne Netz", () => {
|
|
expect(stand.services["postgres"]?.networks).toEqual(["intern"]);
|
|
});
|
|
|
|
it("hat das interne Netz ohne Gateway", () => {
|
|
expect(stand.networks["intern"]?.internal).toBe(true);
|
|
});
|
|
|
|
it("laesst nur den Webdienst in das externe Netz", () => {
|
|
const aussen = Object.entries(stand.services)
|
|
.filter(([, dienst]) => dienst.networks?.includes("extern"))
|
|
.map(([name]) => name);
|
|
expect(aussen).toEqual(["web"]);
|
|
});
|
|
|
|
it("bindet den Webdienst standardmaessig nur auf localhost", () => {
|
|
expect(stand.services["web"]?.ports).toEqual(["${BIND_ADDR:-127.0.0.1}:${APP_PORT:-3000}:3000"]);
|
|
});
|
|
|
|
it("prueft die Gesundheit des Webdienstes ueber IPv4, nicht ueber localhost", () => {
|
|
// busybox-wget loest localhost zuerst nach ::1 auf, Next lauscht nur auf
|
|
// 0.0.0.0:3000. Mit localhost stand web dauerhaft auf (unhealthy), obwohl
|
|
// die App antwortete (#019).
|
|
const probe = (stand.services["web"] as unknown as { healthcheck?: { test?: string[] } }).healthcheck
|
|
?.test;
|
|
expect(probe).toEqual(["CMD", "wget", "-qO-", "http://127.0.0.1:3000/api/gesundheit"]);
|
|
});
|
|
|
|
it("bricht ohne Geheimnisse ab, statt mit Standardwerten zu starten", () => {
|
|
const roh = readFileSync("docker-compose.yml", "utf8");
|
|
expect(roh).toContain("${APP_SECRET:?");
|
|
expect(roh).toContain("${POSTGRES_PASSWORD:?");
|
|
expect(roh).toContain("${APP_URL:?");
|
|
});
|
|
});
|
|
|
|
describe("Betriebsstand: Entwicklungsdatenbank", () => {
|
|
it("bindet nur auf localhost und auf einen eigenen, umstellbaren Port", () => {
|
|
const stand = compose("docker-compose.dev.yml") as unknown as {
|
|
services: Record<string, { ports?: string[] }>;
|
|
};
|
|
// Der Standard ist 5434, weil 5432 lokal und 5433 von einem anderen Projekt
|
|
// belegt sein koennen. Umstellbar ueber DEV_DB_PORT.
|
|
expect(stand.services["postgres"]?.ports).toEqual(["127.0.0.1:${DEV_DB_PORT:-5434}:5432"]);
|
|
});
|
|
});
|