import { readFile } from "node:fs/promises"; import { expect, test } from "@playwright/test"; import { anmelden } from "./hilfen"; /** * Audit-Log (#027) gegen den gebauten Stand: Liste, Filter, Aufklappen, Kettenpruefung und * CSV-Export. Die Anmeldung selbst erzeugt den ersten Eintrag – ohne SSH-Testziele lauffaehig. */ test("Audit-Log zeigt, filtert, prüft und exportiert", async ({ page }) => { await anmelden(page); await page.goto("/protokoll"); await expect(page.getByRole("heading", { name: "Audit-Log" })).toBeVisible(); const tabelle = page.getByRole("table", { name: "Protokolleinträge" }); await expect(tabelle.getByRole("row").nth(1)).toBeVisible(); // Filter nach Aktionsgruppe: nur noch Anmeldungen. await page.getByLabel("Aktion").selectOption({ label: "Anmeldung" }); await page.getByRole("button", { name: "Filtern" }).click(); await expect(page).toHaveURL(/gruppe=anmeldung/); const zeilen = tabelle.locator("tbody > tr"); await expect(zeilen.first()).toBeVisible(); const aktionen = await zeilen.locator("td:nth-child(2)").allInnerTexts(); expect(aktionen.length).toBeGreaterThan(0); for (const aktion of aktionen) expect(aktion).toMatch(/Anmeld|TOTP/); // Zeile aufklappen: Hash und Vorgaenger-Hash erscheinen. await tabelle .getByRole("button", { name: /Einzelheiten zu Eintrag/ }) .first() .click(); await expect(tabelle.locator('[data-feld="hash"]').first()).toHaveText(/^[0-9a-f]{64}$/); // Kettenpruefung auf Knopfdruck. await page.getByRole("button", { name: "Kette prüfen" }).click(); await expect(page.getByText("Hash-Kette unversehrt")).toBeVisible({ timeout: 30_000 }); // CSV mit denselben Filtern; der Export landet selbst im Protokoll. const [download] = await Promise.all([ page.waitForEvent("download"), page.getByRole("link", { name: "CSV exportieren" }).click(), ]); expect(download.suggestedFilename()).toMatch(/^sshkv-protokoll-\d{4}-\d{2}-\d{2}\.csv$/); const text = (await readFile((await download.path())!, "utf8")).replace(/^/, ""); expect(text.split("\r\n")[0]).toBe( '"id";"zeitpunkt";"akteur";"aktion";"ergebnis";"host";"zielbenutzer";"fingerprint";"diff";"details";"vorgaengerHash";"hash"', ); expect(text).toContain("anmeldung."); await page.goto("/protokoll?gruppe=protokoll"); await expect(tabelle.getByText("Protokoll exportiert (CSV)").first()).toBeVisible(); });