import { act, cleanup, fireEvent, render, screen } from "@testing-library/react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { HardwareWorkspace } from "./HardwareWorkspace"; import { api, clearOperatorCredential, controlPlaneOriginForAgent, setOperatorCredential } from "../lib/api"; import type { HardwareState, NodeState, ObservedValue } from "../types"; const known = (value: T): ObservedValue => ({ value, availability: "known" }); function node(id: string, hostname: string): NodeState { return { id, generation: 1, identity_key: `identity-${id}`, identity_source: "persisted_uuid", hostname, display_name: `Display ${hostname}`, status: "active", os_name: "Linux", os_version: known("test"), architecture: "x86_64", kernel_version: known("6.6"), cpu_model: known("Test CPU"), logical_cpu_count: known(16), physical_core_count: known(8), total_ram_bytes: known(32 * 1024 ** 3), available_ram_bytes: known(20 * 1024 ** 3), agent_version: "0.1.0", first_seen_at: "2026-09-01T00:00:00Z", last_seen_at: "2026-09-01T00:00:00Z", inventory_at: "2026-09-01T00:00:00Z", hardware_fingerprint: "a".repeat(64), enabled: true, liveness: "online", agent_health: "healthy", observation_source: "remote_agent", protocol_version: 1, supported_capabilities: ["hardware.inventory"], last_heartbeat_at: "2026-09-01T00:00:00Z", labels: {}, production_eligible: false, lab_eligible: true, benchmark_eligible: false, storage: [], accelerators: [], telemetry_age_seconds: null, inventory_age_seconds: null, role: "worker", }; } const hardware: HardwareState = { overview: { status: "active", inventory_state: "active", node_count: 2, accelerator_count: 0 }, nodes: [node("node-a", "host-a"), node("node-b", "host-b")], }; function lifecyclePreview(overrides: Partial<{ current_enabled: boolean; current_liveness: NodeState["liveness"]; target_enabled: boolean; node_generation: number; can_apply: boolean; blockers: { code: string; message: string; record_type: string; count: number; resource_ids: string[] }[]; agent_evidence_after_enable: "unchanged" | "awaiting_fresh_authenticated_heartbeat"; }> = {}) { return { node_id: "node-a", persisted_identity: "identity-node-a", hostname: "host-a", display_name: "Display host-a", current_enabled: true, current_liveness: "online" as const, target_enabled: false, node_generation: 1, dependency_digest: "d".repeat(64), can_apply: true, blockers: [], agent_evidence_after_enable: "unchanged" as const, ...overrides, }; } const candidateImage = "modelforge-node-agent:1.3.0-rc.1"; const candidateRevision = "a".repeat(40); const candidateLayers = [`sha256:${"1".repeat(64)}`, `sha256:${"2".repeat(64)}`]; const candidateBuildHostImageId = `sha256:${"b".repeat(64)}`; const candidateManifestEntry = JSON.stringify({ tag: candidateImage, image_id: candidateBuildHostImageId, image_digest: null, repo_digest: null, architecture: "amd64", labels: { "org.opencontainers.image.title": "ITWorx ModelForge node agent", "org.opencontainers.image.description": "Outbound-only compute node agent for ITWorx ModelForge", "org.opencontainers.image.version": "1.3.0-rc.1", "org.opencontainers.image.revision": candidateRevision, "org.opencontainers.image.created": "2026-09-24T00:00:00Z", "org.opencontainers.image.source": "ssh://git@example.invalid/model-forge.git", "org.opencontainers.image.vendor": "ITWorx", "org.opencontainers.image.licenses": "AGPL-3.0-or-later", }, rootfs_layers: candidateLayers, }); function selectCandidateBundleImage(): void { fireEvent.change(screen.getByLabelText(/Node Agent image entry from the release manifest/i), { target: { value: candidateManifestEntry } }); } afterEach(() => { cleanup(); clearOperatorCredential(); vi.restoreAllMocks(); vi.useRealTimers(); }); describe("HardwareWorkspace Quick Jump target", () => { it("tracks target changes in the same mount and never falls back for a missing ID", () => { const props = { hardware, role: "admin" as const, onNavigate: vi.fn(), onChanged: vi.fn(), onError: vi.fn() }; const view = render(); expect(screen.getByRole("heading", { name: "host-a" })).toBeInTheDocument(); view.rerender(); expect(screen.getByRole("heading", { name: "host-b" })).toBeInTheDocument(); expect(screen.queryByRole("heading", { name: "host-a" })).not.toBeInTheDocument(); view.rerender(); expect(screen.getByRole("status")).toHaveTextContent(/Node missing-node is not in the loaded hardware index/i); expect(screen.queryByRole("heading", { name: "host-a" })).not.toBeInTheDocument(); expect(screen.queryByRole("heading", { name: "host-b" })).not.toBeInTheDocument(); view.rerender(); expect(screen.getByRole("heading", { name: "host-a" })).toBeInTheDocument(); }); it.each(["viewer", "operator"] as const)("hides node lifecycle and credential recovery actions from %s", (role) => { render(); expect(screen.getByText(/Node lifecycle actions are restricted to administrators/i)).toBeInTheDocument(); expect(screen.getByText(/Credential recovery is restricted to administrators/i)).toBeInTheDocument(); expect(screen.getByText(/Node decommission is restricted to administrators/i)).toBeInTheDocument(); expect(screen.queryByRole("button", { name: /Rotate node credential/i })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /Impact controleren/i })).not.toBeInTheDocument(); }); it("requires an impact review, operator, reason and exact identity before disabling a node", async () => { const updatedHardware: HardwareState = { ...hardware, nodes: hardware.nodes.map((item) => item.id === "node-a" ? { ...item, enabled: false, liveness: "disabled" } : item), }; const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => { const url = String(input); if (url.endsWith("/api/v1/admin/hardware/nodes/node-a/lifecycle/preview")) { return new Response(JSON.stringify(lifecyclePreview()), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/admin/hardware/nodes/node-a/lifecycle")) { return new Response(JSON.stringify({ node_id: "node-a", enabled: false, generation: 2, liveness: "disabled", agent_evidence_state: "unchanged" }), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) { return new Response(JSON.stringify(updatedHardware), { status: 200, headers: { "Content-Type": "application/json" } }); } return new Response("not found", { status: 404 }); }); const onChanged = vi.fn(); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Review disable impact/i })); expect(await screen.findByText(/No blocking dependencies found/i)).toBeInTheDocument(); expect(fetchMock).toHaveBeenCalledTimes(1); const confirmButton = screen.getByRole("button", { name: /Confirm disable/i }); expect(confirmButton).toBeDisabled(); fireEvent.change(screen.getByLabelText(/Operator identity/i), { target: { value: "Jens" } }); fireEvent.change(screen.getByLabelText(/Reason/i), { target: { value: "Planned hardware maintenance after draining workloads." } }); fireEvent.change(screen.getByLabelText(/Type the persisted node identity/i), { target: { value: "wrong-node" } }); expect(confirmButton).toBeDisabled(); fireEvent.change(screen.getByLabelText(/Type the persisted node identity/i), { target: { value: "identity-node-a" } }); expect(confirmButton).toBeEnabled(); fireEvent.click(confirmButton); expect(await screen.findByRole("status")).toHaveTextContent(/Node disabled/i); expect(screen.getByRole("status")).toHaveTextContent(/reject agent heartbeats/i); const lifecycleCalls = fetchMock.mock.calls.filter(([input]) => String(input).endsWith("/lifecycle")); expect(lifecycleCalls).toHaveLength(1); expect(JSON.parse(String(lifecycleCalls[0][1]?.body))).toEqual({ target_enabled: false, expected_generation: 1, dependency_digest: "d".repeat(64), operator: "Jens", reason: "Planned hardware maintenance after draining workloads.", confirmation: "identity-node-a", }); expect(new Headers(lifecycleCalls[0][1]?.headers).get("X-ModelForge-Admin-Token")).toBe("admin-session"); expect(onChanged).toHaveBeenCalledWith(updatedHardware); }); it("keeps active workload blockers visible and offers no disable action", async () => { const blocked = lifecyclePreview({ can_apply: false, blockers: [{ code: "active_gpu_lease", message: "GPU leases must be released first", record_type: "gpu_lease", count: 1, resource_ids: ["lease-1"] }], }); const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue( new Response(JSON.stringify(blocked), { status: 200, headers: { "Content-Type": "application/json" } }), ); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Review disable impact/i })); expect(await screen.findByText(/active gpu lease ยท 1/i)).toBeInTheDocument(); expect(screen.getByText(/GPU leases must be released first/i)).toBeInTheDocument(); expect(screen.getByText(/Resolve each blocker/i)).toBeInTheDocument(); expect(screen.queryByRole("button", { name: /Confirm disable/i })).not.toBeInTheDocument(); expect(fetchMock).toHaveBeenCalledTimes(1); }); it("keeps a re-enabled remote node offline and unknown until a fresh heartbeat", async () => { const disabledHardware: HardwareState = { ...hardware, nodes: hardware.nodes.map((item) => item.id === "node-a" ? { ...item, enabled: false, liveness: "disabled", generation: 2 } : item), }; const freshHardware: HardwareState = { ...disabledHardware, nodes: disabledHardware.nodes.map((item) => item.id === "node-a" ? { ...item, enabled: true, liveness: "offline", agent_health: "unknown", protocol_version: null, supported_capabilities: [], last_heartbeat_at: null } : item), }; const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => { const url = String(input); if (url.endsWith("/lifecycle/preview")) { return new Response(JSON.stringify(lifecyclePreview({ current_enabled: false, current_liveness: "disabled", target_enabled: true, node_generation: 2, agent_evidence_after_enable: "awaiting_fresh_authenticated_heartbeat", })), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/lifecycle")) { return new Response(JSON.stringify({ node_id: "node-a", enabled: true, generation: 3, liveness: "offline", agent_evidence_state: "awaiting_fresh_authenticated_heartbeat" }), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) return new Response(JSON.stringify(freshHardware), { status: 200, headers: { "Content-Type": "application/json" } }); return new Response("not found", { status: 404 }); }); const onChanged = vi.fn(); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Review enable impact/i })); expect(await screen.findByText(/Agent credential, protocol and health readiness remain unknown/i)).toBeInTheDocument(); fireEvent.change(screen.getByLabelText(/Operator identity/i), { target: { value: "Jens" } }); fireEvent.change(screen.getByLabelText(/Reason/i), { target: { value: "Return the node after maintenance." } }); fireEvent.change(screen.getByLabelText(/Type the persisted node identity/i), { target: { value: "identity-node-a" } }); fireEvent.click(screen.getByRole("button", { name: /Confirm enable/i })); expect(await screen.findByRole("status")).toHaveTextContent(/remains offline until a fresh authenticated heartbeat/i); expect(onChanged).toHaveBeenCalledWith(freshHardware); expect(fetchMock).toHaveBeenCalledTimes(3); }); it("invalidates stale lifecycle preview and does not retry a raced transition", async () => { const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => { const url = String(input); if (url.endsWith("/lifecycle/preview")) { return new Response(JSON.stringify(lifecyclePreview()), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/lifecycle")) { return new Response(JSON.stringify({ error: { code: "node_lifecycle_preview_stale", message: "Dependencies changed", details: {} } }), { status: 409, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) return new Response(JSON.stringify(hardware), { status: 200, headers: { "Content-Type": "application/json" } }); return new Response("not found", { status: 404 }); }); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Review disable impact/i })); await screen.findByText(/No blocking dependencies found/i); fireEvent.change(screen.getByLabelText(/Operator identity/i), { target: { value: "Jens" } }); fireEvent.change(screen.getByLabelText(/Reason/i), { target: { value: "Planned hardware maintenance after draining workloads." } }); fireEvent.change(screen.getByLabelText(/Type the persisted node identity/i), { target: { value: "identity-node-a" } }); fireEvent.click(screen.getByRole("button", { name: /Confirm disable/i })); expect(await screen.findByRole("alert")).toHaveTextContent(/changed after review.*not retried/i); expect(screen.queryByRole("button", { name: /Confirm disable/i })).not.toBeInTheDocument(); expect(fetchMock.mock.calls.filter(([input]) => String(input).endsWith("/lifecycle"))).toHaveLength(1); expect(fetchMock).toHaveBeenCalledTimes(3); }); it("rotates an admin-selected node credential only after identity confirmation and clears the one-time reveal", async () => { const credential = "mfnode_one_time_rotated_secret"; const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input, init) => { const url = String(input); if (url.endsWith("/api/v1/admin/hardware/nodes/node-a/credential/rotate")) { return new Response(JSON.stringify({ node_id: "node-a", credential_id: "credential-2", node_credential: credential }), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) { return new Response(JSON.stringify(hardware), { status: 200, headers: { "Content-Type": "application/json" } }); } return new Response("not found", { status: 404 }); }); const onChanged = vi.fn(); setOperatorCredential("admin-session", Date.now() + 60_000); render(); expect(screen.getByText(/The current credential is revoked immediately/i)).toBeInTheDocument(); const confirmation = screen.getByLabelText(/Confirm by entering the node ID/i); const rotateButton = screen.getByRole("button", { name: /Rotate node credential/i }); expect(rotateButton).toBeDisabled(); fireEvent.change(confirmation, { target: { value: "node-b" } }); expect(rotateButton).toBeDisabled(); fireEvent.change(confirmation, { target: { value: "node-a" } }); expect(rotateButton).toBeEnabled(); fireEvent.click(rotateButton); expect(await screen.findByLabelText(/One-time node credential/i)).toHaveValue(credential); expect(fetchMock).toHaveBeenCalledTimes(1); expect(onChanged).not.toHaveBeenCalled(); const [rotationUrl, rotationInit] = fetchMock.mock.calls[0]; expect(String(rotationUrl)).toContain("/api/v1/admin/hardware/nodes/node-a/credential/rotate"); expect(rotationInit?.method).toBe("POST"); expect(rotationInit?.cache).toBe("no-store"); expect(new Headers(rotationInit?.headers).get("X-ModelForge-Admin-Token")).toBe("admin-session"); expect(window.localStorage.getItem("node_credential")).toBeNull(); expect(window.sessionStorage.getItem("node_credential")).toBeNull(); await act(async () => { fireEvent.click(screen.getByRole("button", { name: /I installed the replacement.*refresh status/i })); }); expect(fetchMock).toHaveBeenCalledTimes(2); expect(onChanged).toHaveBeenCalledWith(hardware); expect(screen.queryByLabelText(/One-time node credential/i)).not.toBeInTheDocument(); expect(screen.queryByText(credential)).not.toBeInTheDocument(); }); it("blocks an automatic retry when the rotation response is lost and gives the audit recovery step", async () => { vi.spyOn(globalThis, "fetch").mockRejectedValue(new TypeError("Failed to fetch")); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.change(screen.getByLabelText(/Confirm by entering the node ID/i), { target: { value: "node-a" } }); fireEvent.click(screen.getByRole("button", { name: /Rotate node credential/i })); expect(await screen.findByRole("alert")).toHaveTextContent(/may have completed the rotation before the response was lost/i); expect(screen.getByRole("alert")).toHaveTextContent(/check the admin audit timeline.*node status first/i); expect(screen.getByRole("alert")).toHaveTextContent(/only start a new confirmed rotation when an administrator can install its replacement/i); expect(screen.queryByRole("button", { name: /Rotate node credential/i })).not.toBeInTheDocument(); expect(screen.queryByLabelText(/One-time node credential/i)).not.toBeInTheDocument(); expect(fetch).toHaveBeenCalledTimes(1); }); it("clears a rotated credential if the administrator role is downgraded", async () => { const credential = "mfnode_demotion_secret"; vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => { const url = String(input); if (url.endsWith("/api/v1/admin/hardware/nodes/node-a/credential/rotate")) { return new Response(JSON.stringify({ node_id: "node-a", credential_id: "credential-2", node_credential: credential }), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) return new Response(JSON.stringify(hardware), { status: 200, headers: { "Content-Type": "application/json" } }); return new Response("not found", { status: 404 }); }); setOperatorCredential("admin-session", Date.now() + 60_000); const props = { hardware, targetNodeId: "node-a", onNavigate: vi.fn(), onChanged: vi.fn(), onError: vi.fn() }; const view = render(); fireEvent.change(screen.getByLabelText(/Confirm by entering the node ID/i), { target: { value: "node-a" } }); fireEvent.click(screen.getByRole("button", { name: /Rotate node credential/i })); expect(await screen.findByLabelText(/One-time node credential/i)).toHaveValue(credential); view.rerender(); expect(screen.queryByLabelText(/One-time node credential/i)).not.toBeInTheDocument(); expect(screen.queryByText(credential)).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /Impact controleren/i })).not.toBeInTheDocument(); }); it("does not carry a one-time credential into a different node detail", async () => { const credential = "mfnode_node_a_secret"; vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => { const url = String(input); if (url.endsWith("/api/v1/admin/hardware/nodes/node-a/credential/rotate")) { return new Response(JSON.stringify({ node_id: "node-a", credential_id: "credential-2", node_credential: credential }), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) return new Response(JSON.stringify(hardware), { status: 200, headers: { "Content-Type": "application/json" } }); return new Response("not found", { status: 404 }); }); setOperatorCredential("admin-session", Date.now() + 60_000); const props = { hardware, role: "admin" as const, onNavigate: vi.fn(), onChanged: vi.fn(), onError: vi.fn() }; const view = render(); fireEvent.change(screen.getByLabelText(/Confirm by entering the node ID/i), { target: { value: "node-a" } }); fireEvent.click(screen.getByRole("button", { name: /Rotate node credential/i })); expect(await screen.findByLabelText(/One-time node credential/i)).toHaveValue(credential); view.rerender(); expect(screen.getByRole("heading", { name: "host-b" })).toBeInTheDocument(); expect(screen.queryByLabelText(/One-time node credential/i)).not.toBeInTheDocument(); expect(screen.queryByText(credential)).not.toBeInTheDocument(); }); it.each(["viewer", "operator"] as const)("does not offer enrollment to the %s role", (role) => { render(); expect(screen.queryByRole("button", { name: /Enroll node/i })).not.toBeInTheDocument(); expect(screen.getByText(/only administrators can enroll compute nodes/i)).toBeInTheDocument(); }); it("clears an open one-time token if the operator role is downgraded", async () => { const token = "mfenroll_demotion_secret"; vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-demotion", enrollment_token: token, expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), setup_environment: {}, }); setOperatorCredential("admin-session", Date.now() + 60_000); const props = { hardware, onNavigate: vi.fn(), onChanged: vi.fn(), onError: vi.fn() }; const view = render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); selectCandidateBundleImage(); fireEvent.click(screen.getByRole("button", { name: /Create enrollment token/i })); expect(await screen.findByLabelText(/One-time enrollment token/i)).toHaveTextContent(token); view.rerender(); expect(screen.queryByLabelText(/One-time enrollment token/i)).not.toBeInTheDocument(); expect(screen.queryByRole("heading", { name: /Configure the node agent/i })).not.toBeInTheDocument(); view.rerender(); expect(screen.queryByRole("heading", { name: /Configure the node agent/i })).not.toBeInTheDocument(); }); it("starts an admin enrollment with an explicit expiry choice", () => { render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); expect(screen.getByLabelText(/Enrollment token expires in/i)).toBeInTheDocument(); expect(screen.getByText(/Production eligibility is locked off for RC enrollment/i)).toBeInTheDocument(); }); it("locks node setup to the current API origin", () => { setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); const originInput = screen.getByLabelText(/Control plane URL/i); expect(originInput).toHaveValue(controlPlaneOriginForAgent()); expect(originInput).toHaveAttribute("readonly"); expect(screen.getByText(/locked to the Console's current API origin/i)).toBeInTheDocument(); }); it("requires a verified RC bundle manifest entry and rejects missing or mismatched identities", () => { const create = vi.spyOn(api, "createEnrollment"); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); const createButton = screen.getByRole("button", { name: /Create enrollment token/i }); const imageInput = screen.getByLabelText(/Node Agent image entry from the release manifest/i); expect(createButton).toBeDisabled(); expect(screen.getByRole("alert")).toHaveTextContent(/release manifest/i); fireEvent.change(imageInput, { target: { value: JSON.stringify({ ...JSON.parse(candidateManifestEntry), tag: "modelforge-node-agent:latest" }) } }); expect(createButton).toBeDisabled(); expect(screen.getByRole("alert")).toHaveTextContent(/exact 1.3.0-rc.1.*latest/i); fireEvent.change(imageInput, { target: { value: JSON.stringify({ ...JSON.parse(candidateManifestEntry), rootfs_layers: [] }) } }); expect(createButton).toBeDisabled(); expect(screen.getByRole("alert")).toHaveTextContent(/RootFS layer digests/i); fireEvent.change(imageInput, { target: { value: candidateManifestEntry } }); expect(createButton).toBeEnabled(); expect(screen.queryByRole("alert")).not.toBeInTheDocument(); expect(create).not.toHaveBeenCalled(); }); it("shell-quotes manifest label values before placing them in the generated command", async () => { const token = "mfenroll_manifest_quote_secret"; vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-quoted-manifest", enrollment_token: token, expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), setup_environment: {}, }); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); const manifestEntry = JSON.parse(candidateManifestEntry) as { labels: Record }; manifestEntry.labels["org.opencontainers.image.description"] = "Node agent's reviewed candidate"; fireEvent.change(screen.getByLabelText(/Node Agent image entry from the release manifest/i), { target: { value: JSON.stringify(manifestEntry) } }); fireEvent.click(screen.getByRole("button", { name: /Create enrollment token/i })); expect(await screen.findByLabelText(/One-time enrollment token/i)).toHaveTextContent(token); const command = screen.getByLabelText(/Safe start command/i).textContent ?? ""; expect(command).toContain("'Node agent'\\''s reviewed candidate'"); expect(command).not.toContain("; touch"); expect(command).not.toContain(token); }); it("supports registry images only when pinned to a digest and verifies the pull", async () => { const token = "mfenroll_registry_test_secret"; vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-registry", enrollment_token: token, expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), setup_environment: {}, }); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); fireEvent.click(screen.getByLabelText(/Registry image pinned by digest/i)); const createButton = screen.getByRole("button", { name: /Create enrollment token/i }); const imageInput = screen.getByLabelText(/Exact Node Agent registry image digest/i); fireEvent.change(imageInput, { target: { value: "registry.example.net/team/modelforge-node-agent:latest" } }); expect(createButton).toBeDisabled(); expect(screen.getByRole("alert")).toHaveTextContent(/64 lowercase hex.*floating tags/i); const digestReference = `registry.example.net/team/modelforge-node-agent@sha256:${"c".repeat(64)}`; fireEvent.change(imageInput, { target: { value: digestReference } }); expect(createButton).toBeEnabled(); expect(screen.queryByRole("alert")).not.toBeInTheDocument(); fireEvent.click(createButton); expect(await screen.findByLabelText(/One-time enrollment token/i)).toHaveTextContent(token); const command = screen.getByLabelText(/Safe start command/i).textContent ?? ""; expect(command).toContain(`docker pull '${digestReference}'`); expect(command).toContain("RepoDigests"); expect(command).toContain("config --images"); expect(command).toContain("--no-build --pull never node-agent"); expect(command).toContain("container_image_id=$(docker inspect --format '{{.Image}}'"); expect(command).not.toContain(token); }); it("creates an RC-safe enrollment and keeps the secret out of generated commands", async () => { const token = "mfenroll_one_time_test_secret"; const create = vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-1", enrollment_token: token, expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), setup_environment: { MODELFORGE_AGENT_CONTROL_PLANE_URL: "", MODELFORGE_AGENT_ENROLLMENT_TOKEN: token, }, }); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); selectCandidateBundleImage(); fireEvent.change(screen.getByLabelText(/Display name/i), { target: { value: "RC canary node" } }); fireEvent.change(screen.getByLabelText(/Enrollment token expires in/i), { target: { value: "3600" } }); fireEvent.click(screen.getByRole("button", { name: /Create enrollment token/i })); expect(await screen.findByLabelText(/One-time enrollment token/i)).toHaveTextContent(token); expect(create).toHaveBeenCalledWith(expect.objectContaining({ display_name: "RC canary node", expires_in_seconds: 3600, production_eligible: false, lab_eligible: true, }), "modelforge:in-memory-operator-session"); const command = screen.getByLabelText(/Safe start command/i).textContent ?? ""; expect(command).toContain(controlPlaneOriginForAgent()); expect(command).toContain(candidateImage); expect(command).toContain(JSON.stringify(candidateLayers)); expect(command).toContain(candidateRevision); expect(command).toContain("{{.Architecture}}"); expect(command).toContain("{{.Image}}"); expect(command).toContain("--no-build --pull never"); expect(command).toContain("config --images"); expect(command).toContain("docker image inspect --format '{{json .RootFS.Layers}}' 'modelforge-node-agent:1.3.0-rc.1'"); expect(command).toContain("test \"$compose_image\" = 'modelforge-node-agent:1.3.0-rc.1'"); for (const [labelKey, labelValue] of Object.entries((JSON.parse(candidateManifestEntry) as { labels: Record }).labels)) { expect(command).toContain(labelKey); expect(command).toContain(labelValue); } expect(command).not.toContain("docker pull"); expect(command).not.toContain(candidateBuildHostImageId); expect(command).not.toContain("--build"); expect(screen.getByLabelText(/Non-secret node configuration/i).textContent).toContain(controlPlaneOriginForAgent()); expect(screen.getByLabelText(/Non-secret node configuration/i).textContent).toContain(`MODELFORGE_NODE_AGENT_IMAGE=${candidateImage}`); expect(command).toContain("set +x"); expect(command).toContain("set -eu"); expect(command).toContain("chmod 600"); expect(command).toContain("trap"); expect(command).toContain("grep -Eq"); for (const key of ["MODELFORGE_AGENT_CONTROL_PLANE_URL", "MODELFORGE_NODE_AGENT_IMAGE"]) { expect(command).toContain(`${key}_ASSIGNMENT_COUNT=$(grep -Ec`); expect(command).toContain(`test "$${key}_ASSIGNMENT_COUNT" -eq 1`); expect(command).toContain(`grep -Fqx -- '${key}=`); } expect(command).toContain("env -u MODELFORGE_AGENT_ENROLLMENT_TOKEN -u MODELFORGE_AGENT_CONTROL_PLANE_URL -u MODELFORGE_NODE_AGENT_IMAGE"); expect(command).not.toContain(token); expect(window.localStorage.getItem("enrollment_token")).toBeNull(); expect(window.sessionStorage.getItem("enrollment_token")).toBeNull(); }); it("clears the revealed token when the unused enrollment is revoked", async () => { const token = "mfenroll_revoke_secret"; vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-revoke", enrollment_token: token, expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), setup_environment: {}, }); const revoke = vi.spyOn(api, "revokeEnrollment").mockResolvedValue({}); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); selectCandidateBundleImage(); fireEvent.click(screen.getByRole("button", { name: /Create enrollment token/i })); expect(await screen.findByLabelText(/One-time enrollment token/i)).toHaveTextContent(token); fireEvent.click(screen.getByRole("button", { name: /Revoke unused token/i })); expect(await screen.findByText(/This token was revoked/i)).toBeInTheDocument(); expect(screen.queryByLabelText(/One-time enrollment token/i)).not.toBeInTheDocument(); expect(revoke).toHaveBeenCalledWith("enrollment-revoke", "modelforge:in-memory-operator-session"); }); it("clears the revealed token automatically when the one-time expiry elapses", async () => { vi.useFakeTimers(); const token = "mfenroll_expiry_secret"; vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-expiry", enrollment_token: token, expires_at: new Date(Date.now() + 1_000).toISOString(), setup_environment: {}, }); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); selectCandidateBundleImage(); await act(async () => { fireEvent.click(screen.getByRole("button", { name: /Create enrollment token/i })); }); expect(screen.getByLabelText(/One-time enrollment token/i)).toHaveTextContent(token); await act(async () => { vi.advanceTimersByTime(1_001); }); expect(screen.queryByLabelText(/One-time enrollment token/i)).not.toBeInTheDocument(); expect(screen.getByText(/Token expired/i)).toBeInTheDocument(); vi.useRealTimers(); }); it("does not call a node ready before the matching enrollment, inventory, protocol, and health are proven", async () => { const token = "mfenroll_readiness_secret"; vi.spyOn(api, "createEnrollment").mockResolvedValue({ id: "enrollment-2", enrollment_token: token, expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), setup_environment: {}, }); let summaryRead = 0; const unrelatedNode = node("unrelated", "unrelated-host"); const incompatibleNode = { ...node("node-b", "enrolled-host"), liveness: "offline" as const, agent_health: "incompatible" as const, protocol_version: 99, last_heartbeat_at: null, last_inventory_received_at: new Date(Date.now() - 601_000).toISOString(), inventory_age_seconds: 601, last_telemetry_received_at: new Date(Date.now() - 91_000).toISOString(), telemetry_age_seconds: 91, last_connection_error: "protocol mismatch", }; const readyNode = { ...node("node-b", "enrolled-host"), liveness: "online" as const, agent_health: "healthy" as const, protocol_version: 1, last_heartbeat_at: new Date().toISOString(), last_inventory_received_at: new Date().toISOString(), inventory_age_seconds: 2, last_telemetry_received_at: new Date().toISOString(), telemetry_age_seconds: 2, }; const connectedOnlyNode = { ...node("node-b", "enrolled-host"), liveness: "online" as const, agent_health: "healthy" as const, protocol_version: 1, last_heartbeat_at: new Date().toISOString(), last_inventory_received_at: null, inventory_age_seconds: null, last_telemetry_received_at: null, telemetry_age_seconds: null, }; const disabledNode = { ...readyNode, enabled: false }; vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => { const url = String(input); if (url.endsWith("/api/v1/admin/node-enrollments")) { summaryRead += 1; const linked = summaryRead >= 2; return new Response(JSON.stringify([{ id: "enrollment-2", created_at: new Date().toISOString(), expires_at: new Date(Date.now() + 15 * 60_000).toISOString(), used_at: linked ? new Date().toISOString() : null, revoked_at: null, enrolled_node_id: linked ? "node-b" : null, }]), { status: 200, headers: { "Content-Type": "application/json" } }); } if (url.endsWith("/api/v1/hardware")) { const nodeState = summaryRead <= 2 ? [unrelatedNode] : summaryRead === 3 ? [unrelatedNode, connectedOnlyNode] : summaryRead === 4 ? [unrelatedNode, incompatibleNode] : summaryRead === 5 ? [unrelatedNode, readyNode] : [unrelatedNode, disabledNode]; return new Response(JSON.stringify({ ...hardware, nodes: nodeState }), { status: 200, headers: { "Content-Type": "application/json" } }); } return new Response("not found", { status: 404 }); }); setOperatorCredential("admin-session", Date.now() + 60_000); render(); fireEvent.click(screen.getByRole("button", { name: /Enroll node/i })); selectCandidateBundleImage(); fireEvent.click(screen.getByRole("button", { name: /Create enrollment token/i })); expect(await screen.findByLabelText(/One-time enrollment token/i)).toBeInTheDocument(); expect(screen.getAllByText(/UNKNOWN.*enrolled node ID/i)).toHaveLength(5); fireEvent.click(screen.getByRole("button", { name: /Check node readiness/i })); expect(await screen.findByText(/Waiting for the agent to use this token/i)).toBeInTheDocument(); expect(screen.getAllByText(/UNKNOWN.*enrolled node ID/i)).toHaveLength(5); fireEvent.click(screen.getByRole("button", { name: /Check node readiness/i })); expect(await screen.findByText(/Node node-b is not in the hardware inventory yet/i)).toBeInTheDocument(); expect(screen.queryByText("Node is ready", { exact: true })).not.toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: /Check node readiness/i })); expect(await screen.findByText(/Heartbeat is online/i)).toBeInTheDocument(); expect(screen.getByText("Node is not ready", { exact: true })).toBeInTheDocument(); expect(screen.getByText(/No node inventory report has been received yet/i)).toBeInTheDocument(); expect(screen.getByText(/No telemetry report has been received yet/i)).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: /Check node readiness/i })); expect(await screen.findByText(/protocol 99 is unsupported/i)).toBeInTheDocument(); expect(screen.getByText("Node is not ready", { exact: true })).toBeInTheDocument(); expect(screen.getByText(/Heartbeat age unknown.*protocol mismatch/i)).toBeInTheDocument(); expect(screen.getByText(/Inventory is stale: age 10m 1s/i)).toBeInTheDocument(); expect(screen.getByText(/Agent health is incompatible/i)).toBeInTheDocument(); expect(screen.getByText(/Telemetry is stale: telemetry age 1m 31s/i)).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: /Check node readiness/i })); expect(await screen.findByText("Node is ready", { exact: true })).toBeInTheDocument(); expect(screen.getByText(/telemetry age: 2s/i)).toBeInTheDocument(); const cleanupCommand = screen.getByLabelText(/Clear enrollment secret from node container/i).textContent ?? ""; expect(cleanupCommand).toContain(controlPlaneOriginForAgent()); expect(cleanupCommand).toContain("grep -Eq"); expect(cleanupCommand).toContain(candidateImage); expect(cleanupCommand).toContain(JSON.stringify(candidateLayers)); expect(cleanupCommand).toContain(candidateRevision); expect(cleanupCommand).toContain("{{.Architecture}}"); expect(cleanupCommand).toContain("{{.Image}}"); expect(cleanupCommand).not.toContain(candidateBuildHostImageId); for (const key of ["MODELFORGE_AGENT_CONTROL_PLANE_URL", "MODELFORGE_NODE_AGENT_IMAGE"]) { expect(cleanupCommand).toContain(`${key}_ASSIGNMENT_COUNT=$(grep -Ec`); expect(cleanupCommand).toContain(`test "$${key}_ASSIGNMENT_COUNT" -eq 1`); expect(cleanupCommand).toContain(`grep -Fqx -- '${key}=`); } expect(cleanupCommand).toContain("env -u MODELFORGE_AGENT_ENROLLMENT_TOKEN -u MODELFORGE_AGENT_CONTROL_PLANE_URL -u MODELFORGE_NODE_AGENT_IMAGE"); expect(cleanupCommand).toContain("--no-build --pull never --force-recreate node-agent"); expect(cleanupCommand).not.toContain("--build"); fireEvent.click(screen.getByRole("button", { name: /Check node readiness/i })); expect(await screen.findByText("Node is not ready", { exact: true })).toBeInTheDocument(); expect(screen.getByText(/The node is disabled/i)).toBeInTheDocument(); }); });