core/workbench-ui/src/app/StatusFooter.test.tsx
Shay 0bdc2eef6d feat(workbench): wire /health liveness probe into the status footer
The /health endpoint was the only W-026 API route with no UI consumer.
Surface it as a liveness indicator (colored dot + label) in the status
footer, isolated from /runtime/status so a live server still reads
"Healthy" even when runtime status is degraded.

- types: HealthStatus matching the server's {"status":"ok"} payload
- client: fetchHealth() with a short 3s timeout for fast failure
- queries: useHealth() polling every 15s, retry disabled
- footer: HealthIndicator rendered in both normal and degraded branches;
  any non-ok status fails safe to "Unhealthy"
- tests: healthy / errored / non-ok / checking / survives-runtime-failure
2026-06-14 17:16:43 -07:00

137 lines
4.6 KiB
TypeScript

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { QueryClientProvider } from "@tanstack/react-query";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createTestQueryClient } from "../test/createTestQueryClient";
import type { HealthStatus, RuntimeStatus } from "../types/api";
vi.mock("../api/queries", async (importOriginal) => {
const actual = await importOriginal<typeof import("../api/queries")>();
return { ...actual, useRuntimeStatus: vi.fn(), useHealth: vi.fn() };
});
import { useHealth, useRuntimeStatus } from "../api/queries";
import { StatusFooter } from "./StatusFooter";
const status: RuntimeStatus = {
backend: "numpy",
git_revision: "5474a152057d9999",
engine_state_present: false,
checkpoint_revision: "unknown",
revision_warning: false,
active_session_id: null,
mutation_mode: "read_only",
};
function mockHealth(
value: Partial<ReturnType<typeof useHealth>> & { data?: HealthStatus },
) {
vi.mocked(useHealth).mockReturnValue({
data: undefined,
isError: false,
isLoading: false,
...value,
} as unknown as ReturnType<typeof useHealth>);
}
function renderFooter() {
return render(
<QueryClientProvider client={createTestQueryClient()}>
<StatusFooter />
</QueryClientProvider>,
);
}
beforeEach(() => {
vi.mocked(useRuntimeStatus).mockReturnValue({
data: status,
isError: false,
} as unknown as ReturnType<typeof useRuntimeStatus>);
mockHealth({ data: { status: "ok" } });
});
afterEach(() => {
vi.restoreAllMocks();
});
describe("StatusFooter", () => {
it("labels Read Only as a non-interactive status, not a toggle", () => {
renderFooter();
const chip = screen.getByText("Read Only");
expect(chip).toHaveAttribute(
"title",
"Runtime mutation mode — read-only by design (status, not a toggle)",
);
expect(chip.tagName).toBe("SPAN"); // status display, never a button
});
it("copies the full SHA and shows a transient confirmation on click", async () => {
// userEvent.setup() installs its own clipboard stub; read it back to verify
// the full SHA was copied (not just the short display form).
const user = userEvent.setup();
renderFooter();
const sha = screen.getByTestId("git-revision");
expect(sha).toHaveAttribute("title", "Copy full git revision SHA");
expect(sha).toHaveTextContent("5474a152"); // short SHA before copy
await user.click(sha);
// The confirmation the silent-copy version lacked.
expect(await screen.findByText("Copied")).toBeInTheDocument();
expect(await navigator.clipboard.readText()).toBe("5474a152057d9999");
});
it("gives the checkpoint-revision toggle an explanatory tooltip", () => {
renderFooter();
const checkpoint = screen.getByTestId("checkpoint-revision");
expect(checkpoint.getAttribute("title")).toMatch(/checkpoint revision/i);
expect(checkpoint.getAttribute("title")).toMatch(/unknown/);
});
it("shows a healthy liveness indicator when GET /health reports ok", () => {
renderFooter();
const health = screen.getByTestId("health-indicator");
expect(health).toHaveAttribute("data-health", "healthy");
expect(health).toHaveTextContent("Healthy");
});
it("marks the server unhealthy when /health errors", () => {
mockHealth({ isError: true });
renderFooter();
const health = screen.getByTestId("health-indicator");
expect(health).toHaveAttribute("data-health", "unhealthy");
expect(health).toHaveTextContent("Unhealthy");
});
it("marks the server unhealthy when /health returns a non-ok status", () => {
mockHealth({ data: { status: "degraded" } });
renderFooter();
expect(screen.getByTestId("health-indicator")).toHaveAttribute(
"data-health",
"unhealthy",
);
});
it("shows a checking state before the first probe resolves", () => {
mockHealth({ isLoading: true });
renderFooter();
const health = screen.getByTestId("health-indicator");
expect(health).toHaveAttribute("data-health", "checking");
expect(health).toHaveTextContent("Checking");
});
it("still reports health when runtime status is unavailable", () => {
vi.mocked(useRuntimeStatus).mockReturnValue({
data: undefined,
isError: true,
} as unknown as ReturnType<typeof useRuntimeStatus>);
mockHealth({ data: { status: "ok" } });
renderFooter();
expect(screen.getByText("Status unavailable")).toBeInTheDocument();
expect(screen.getByTestId("health-indicator")).toHaveAttribute(
"data-health",
"healthy",
);
});
});