Files
lda-wf/web/apps/console/src/presentation/usePresentationTargetStatus.test.tsx
T

139 lines
4.7 KiB
TypeScript

import { act, renderHook, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { callOperation } from "../connection/api.js";
import { initialDemoTimelineState } from "../demo/timeline/reducer.js";
import { usePresentationTargetStatus } from "./usePresentationTargetStatus.js";
vi.mock("../connection/api.js", () => ({ callOperation: vi.fn() }));
const mockedCallOperation = vi.mocked(callOperation);
const target = { mode: "live" as const, target: "http://127.0.0.1:8765/rpc", source: "default" as const };
const replayState = {
...initialDemoTimelineState,
mode: "replay" as const,
phase: "paused" as const,
};
beforeEach(() => mockedCallOperation.mockReset());
describe("usePresentationTargetStatus", () => {
it("marks live target ready after workflow health succeeds", async () => {
mockedCallOperation.mockResolvedValue({
ok: true as const,
operation: "workflow.health",
label: "Health",
interpreted: { status: "ok", storeRoot: "store" },
exchange: { request: {}, response: {} },
equivalentCli: "uv run wf status",
durationMs: 2,
});
const { result } = renderHook(() =>
usePresentationTargetStatus(
target,
initialDemoTimelineState,
),
);
await waitFor(() => expect(result.current.status.kind).toBe("ready"));
expect(result.current.liveTargetReady).toBe(true);
});
it("does not label a completed live run as active", async () => {
mockedCallOperation.mockResolvedValue({
ok: true as const,
operation: "workflow.health",
label: "Health",
interpreted: { status: "ok", storeRoot: "store" },
exchange: { request: {}, response: {} },
equivalentCli: "uv run wf status",
durationMs: 2,
});
const { result } = renderHook(() =>
usePresentationTargetStatus(target, {
...initialDemoTimelineState,
phase: "completed",
}),
);
await waitFor(() => expect(result.current.status.kind).toBe("ready"));
expect(result.current.status.label).toBe("Live target ready");
});
it("falls back to replay when health fails", async () => {
mockedCallOperation.mockResolvedValue({
ok: false as const,
error: { code: "upstream_unreachable", message: "connection refused" },
exchange: { request: {}, response: {} },
});
const { result } = renderHook(() =>
usePresentationTargetStatus(
target,
initialDemoTimelineState,
),
);
await waitFor(() => expect(result.current.status.kind).toBe("failed"));
expect(result.current.status.label).toBe("Replay fallback");
expect(result.current.liveTargetReady).toBe(false);
});
it("keeps live target readiness visible while direct replay is active", async () => {
mockedCallOperation.mockResolvedValue({
ok: true as const,
operation: "workflow.health",
label: "Health",
interpreted: { status: "ok", storeRoot: "store" },
exchange: { request: {}, response: {} },
equivalentCli: "uv run wf status",
durationMs: 2,
});
const { result } = renderHook(() =>
usePresentationTargetStatus(target, replayState),
);
await waitFor(() => expect(result.current.liveTargetReady).toBe(true));
expect(result.current.status.kind).toBe("ready");
expect(result.current.status.label).toBe("Live target ready");
});
it("does not probe when health probing is disabled", async () => {
const { result } = renderHook(() =>
usePresentationTargetStatus(target, replayState, false),
);
await waitFor(() => expect(result.current.status.kind).toBe("replay"));
expect(mockedCallOperation).not.toHaveBeenCalled();
});
it("retries health without changing replay playback", async () => {
mockedCallOperation
.mockResolvedValueOnce({
ok: false as const,
error: { code: "upstream_unreachable", message: "connection refused" },
exchange: { request: {}, response: {} },
})
.mockResolvedValueOnce({
ok: true as const,
operation: "workflow.health",
label: "Health",
interpreted: { status: "ok", storeRoot: "store" },
exchange: { request: {}, response: {} },
equivalentCli: "uv run wf status",
durationMs: 2,
});
const { result } = renderHook(() =>
usePresentationTargetStatus(target, initialDemoTimelineState),
);
await waitFor(() => expect(result.current.status.kind).toBe("failed"));
act(() => result.current.retryHealth());
await waitFor(() => expect(result.current.liveTargetReady).toBe(true));
expect(result.current.status.kind).toBe("ready");
expect(mockedCallOperation).toHaveBeenCalledTimes(2);
});
});