import { expect, test } from "@playwright/test"; import { anmelden, archivieren, aufZiel, hostEinrichten, kernAufbauen, PORT, zieleZuruecksetzen, } from "./hilfen"; /** * Gefuehrte Rotation im Browser (#023), gebauter Stand + Worker: Ausgangslage ist der * Kern-Anwendungsfall. Neuer Schluessel → Zuweisungen uebernehmen → ausrollen → Verbindung * testen → alten widerrufen. `ssh zu-b` von A funktioniert vorher und nachher. */ test.setTimeout(300_000); test.beforeAll(() => zieleZuruecksetzen()); test("Rotation mit Schrittanzeige", async ({ page }) => { const kennung = Date.now().toString(36); const hostA = `zz-e2e-a-${kennung}`; const hostB = `zz-e2e-b-${kennung}`; const zweck = `zz-e2e Rotation ${kennung}`; await anmelden(page); await hostEinrichten(page, hostA, PORT.a); await hostEinrichten(page, hostB, PORT.b); await kernAufbauen(page, zweck, hostA, hostB); const altDateien = aufZiel("ziel-a", "ls /root/.ssh"); await page.reload(); await page.getByRole("link", { name: "Rotieren" }).click(); await expect(page.getByRole("heading", { name: `„${zweck}“ rotieren` })).toBeVisible(); await page.getByRole("button", { name: "Neuen Schlüssel erzeugen und Zuweisungen übernehmen" }).click(); // Schritt 3: neuen Schluessel ausrollen. await expect(page.getByRole("button", { name: "Dry-Run starten" })).toBeVisible({ timeout: 60_000 }); await page.getByRole("button", { name: "Dry-Run starten" }).click(); // Derselbe Alias, neue Datei: nur die IdentityFile-Zeile aendert sich. await expect(page.getByText(/IdentityFile ~\/\.ssh\/.*-r\d{8}$/)).toBeVisible({ timeout: 30_000 }); await page.screenshot({ path: "docs/anleitung/bilder/rotation-desktop.png", fullPage: true }); await page.getByRole("button", { name: /Änderung anwenden/ }).click(); // Schritt 4: Verbindungstest mit dem neuen Schluessel. await page.getByRole("button", { name: "Verbindungen testen" }).click({ timeout: 60_000 }); // Schritt 5: alten Schluessel widerrufen. await page.getByRole("button", { name: "Alten Schlüssel widerrufen" }).click({ timeout: 60_000 }); await page.getByRole("button", { name: "Dry-Run starten" }).click({ timeout: 30_000 }); await page.getByRole("button", { name: /Änderung anwenden/ }).click({ timeout: 30_000 }); await expect(page.getByRole("heading", { name: "Rotation abgeschlossen" })).toBeVisible({ timeout: 60_000, }); expect(aufZiel("ziel-a", "ssh -o BatchMode=yes zu-b id -un").trim()).toBe("root"); const neueDateien = aufZiel("ziel-a", "ls /root/.ssh"); const alterKey = altDateien.split("\n").find((d) => d.startsWith("id_ed25519") && !d.endsWith(".pub"))!; expect(neueDateien.split("\n")).not.toContain(alterKey); expect(neueDateien).toMatch(/-r\d{8}\n/); // Kette auf der Seite des neuen Schluessels. await page.getByRole("link", { name: /\(rotiert / }).click(); const kette = page.getByRole("list", { name: "Rotationskette" }); await expect(kette.getByRole("link", { name: zweck })).toBeVisible(); await expect(kette.getByText("rotiert", { exact: true })).toBeVisible(); await archivieren(page, hostA); await archivieren(page, hostB); });