From 2ccd59dec481b867a85d68e53ca33eb148e92873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Almeida?= Date: Sun, 17 May 2026 19:35:51 +0100 Subject: [PATCH] test(e2e): rerun ssh.test in hostkey-rotation afterEach to repin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hostkey-rotation specs mutate the worker-scoped sshd container and the seeded SSH executor's pinned fingerprint. Without a cleanup hook, the next spec in the same worker that resolves seedData.sshTarget. hostFingerprint observes a stale value and the UI's trusted-badge check (persistence R1) flaps. Add an afterEach that re-probes the live container via kandev, syncs the fingerprint into both seedData and the seeded executor's pinned config. The cleanup is a no-op when the probe fails (e.g. K2 left an intentionally bad fingerprint that no longer matches anything — the next test will re-establish state via its own setup). --- .../e2e/tests/ssh/hostkey-rotation.spec.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/apps/web/e2e/tests/ssh/hostkey-rotation.spec.ts b/apps/web/e2e/tests/ssh/hostkey-rotation.spec.ts index 8d831a41a7..d69f1ae1f2 100644 --- a/apps/web/e2e/tests/ssh/hostkey-rotation.spec.ts +++ b/apps/web/e2e/tests/ssh/hostkey-rotation.spec.ts @@ -11,6 +11,34 @@ import { regenerateHostKey } from "../../helpers/ssh"; * Covers e2e-plan.md group K (K1–K4). */ test.describe("ssh executor — host key rotation", () => { + // Each test mutates the worker-scoped sshd container's host key + the + // seeded executor's pinned fingerprint. After the test, refresh both so + // any later spec in the same worker that depends on seedData.sshTarget / + // seedData.sshExecutorId sees a coherent fingerprint. + test.afterEach(async ({ apiClient, seedData }) => { + const observed = await apiClient.testSSHConnection({ + name: "K cleanup observe", + host: seedData.sshTarget.host, + port: seedData.sshTarget.port, + user: seedData.sshTarget.user, + identity_source: "file", + identity_file: seedData.sshTarget.identityFile, + }); + if (observed.success && observed.fingerprint) { + seedData.sshTarget.hostFingerprint = observed.fingerprint; + await apiClient.updateExecutor(seedData.sshExecutorId, { + config: { + ssh_host: seedData.sshTarget.host, + ssh_port: String(seedData.sshTarget.port), + ssh_user: seedData.sshTarget.user, + ssh_identity_source: "file", + ssh_identity_file: seedData.sshTarget.identityFile, + ssh_host_fingerprint: observed.fingerprint, + }, + }); + } + }); + test("regenerating the host key surfaces a new fingerprint on the next test", async ({ apiClient, seedData,