Files
ThothII/frontend/src/shell/WorkspacePreprocessingControl.test.tsx
T
Codex d8a29bfbdd Add full shell, replaceable Omics adapter and bilingual interaction
Implement approved specification #32 and tickets #33-#37. Keep host authentication server-verified and pin session interaction language. Compile scoped base selectors for browser compatibility and retain full gutters during CSS pruning.
2026-09-13 14:26:39 +02:00

186 lines
6.6 KiB
TypeScript

import { act, render, screen, waitFor } from "@testing-library/react";
import { setLocale } from "../i18n";
import userEvent from "@testing-library/user-event";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { http, HttpResponse } from "msw";
import { server } from "../test/msw";
import type { WorkspacePreprocessingStatus } from "../api/workspace-preprocessing";
import { WorkspacePreprocessingControl } from "./WorkspacePreprocessingControl";
afterEach(() => act(() => setLocale("en")));
test("translates a live readiness diagnostic while preserving its catalog revision", () => {
renderControl({ schemaVersion: 1, workspaceId: "catalog-workspace", state: "ready",
actionable: true, clearable: true, detail: "Catalog revision 18 is indexed." });
act(() => setLocale("it"));
expect(screen.getByText("La revisione 18 del Catalogo è indicizzata.")).toBeVisible();
expect(screen.getByRole("button", { name: "Esegui di nuovo" })).toBeEnabled();
});
function renderControl(status: WorkspacePreprocessingStatus) {
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
return render(
<QueryClientProvider client={client}>
<WorkspacePreprocessingControl
workspaceId="catalog-workspace"
status={status}
loading={false}
unavailable={false}
canManage
/>
</QueryClientProvider>,
);
}
test("failed state shows the reason and only the latest run diagnostic", async () => {
renderControl({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "failed",
actionable: true,
clearable: true,
detail: "The schema index could not be rebuilt.",
reason: "The schema indexing worker stopped before publication.",
nextStep: "Check the core service log, then retry.",
lastFailure: {
stage: "schema_index",
errorCode: "schema_index_failed",
finishedAt: "2026-09-05T10:04:00.000Z",
},
});
expect(screen.getByText("The schema indexing worker stopped before publication.")).toBeVisible();
await userEvent.click(screen.getByText("Last run details"));
expect(screen.getByText("schema_index", { selector: "dd" })).toBeVisible();
expect(screen.getByText("schema_index_failed", { selector: "dd" })).toBeVisible();
expect(screen.getByText(/docker compose logs core/)).toBeVisible();
expect(screen.queryByText(/history/i)).not.toBeInTheDocument();
});
test("blocked state gives the current prerequisite and no run details", () => {
renderControl({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "blocked",
actionable: false,
clearable: true,
detail: "Catalog synchronization is required.",
reason: "Database configuration v4 is newer than Catalog synchronization v3.",
nextStep: "Open Database management and run Synchronize schema.",
});
expect(screen.getByText("Database configuration v4 is newer than Catalog synchronization v3.")).toBeVisible();
expect(screen.getByText("No preprocessing log is available because a run did not start.")).toBeVisible();
expect(screen.queryByText("Last run details")).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "Run" })).toBeDisabled();
});
test("running state shows the current durable phase instead of only a spinner", () => {
renderControl({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "running",
actionable: false,
clearable: false,
detail: "Indexing Evidence.",
progress: { stage: "evidence", step: 3, totalSteps: 4 },
});
expect(screen.getByText("Indexing Evidence")).toBeVisible();
expect(screen.getByText("3 / 4")).toBeVisible();
expect(screen.getByRole("progressbar", { name: "Preprocessing progress" })).toHaveAttribute(
"aria-valuetext",
"Indexing Evidence, stage 3 of 4",
);
expect(screen.getByRole("button", { name: "Running" })).toBeDisabled();
});
test("ready state offers an explicit rerun from the GUI", async () => {
let calls = 0;
server.use(http.post("/api/workspaces/catalog-workspace/preprocessing", () => {
calls += 1;
return HttpResponse.json({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "ready",
actionable: true,
clearable: true,
detail: "Catalog revision 18 is indexed.",
});
}));
renderControl({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "ready",
actionable: true,
clearable: true,
detail: "Catalog revision 18 is indexed.",
});
await userEvent.click(screen.getByRole("button", { name: "Run again" }));
await waitFor(() => expect(calls).toBe(1));
});
test("Retry invokes the single preprocessing endpoint", async () => {
let calls = 0;
server.use(http.post("/api/workspaces/catalog-workspace/preprocessing", () => {
calls += 1;
return HttpResponse.json({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "ready",
actionable: true,
clearable: true,
detail: "Catalog revision 18 is indexed.",
});
}));
renderControl({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "failed",
actionable: true,
clearable: true,
detail: "Preprocessing did not complete.",
reason: "The worker stopped.",
nextStep: "Retry.",
lastFailure: {
stage: "preprocessing",
errorCode: "preprocessing_failed",
finishedAt: "2026-09-05T10:04:00.000Z",
},
});
await userEvent.click(screen.getByRole("button", { name: "Retry" }));
await waitFor(() => expect(calls).toBe(1));
});
test("Clear uses the approved inline review and preserves memory in its scope copy", async () => {
let calls = 0;
server.use(http.delete("/api/workspaces/catalog-workspace/preprocessing", () => {
calls += 1;
return HttpResponse.json({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "required",
actionable: true,
clearable: false,
detail: "Reference vectors and LSH are empty. Memory is preserved.",
});
}));
renderControl({
schemaVersion: 1,
workspaceId: "catalog-workspace",
state: "ready",
actionable: true,
clearable: true,
detail: "Catalog revision 18 is indexed.",
});
await userEvent.click(screen.getByRole("button", { name: "Clear" }));
expect(screen.getByText("Clear derived data?")).toBeVisible();
expect(screen.getByText("Schema/Evidence vectors, LSH and derived checkpoints")).toBeVisible();
expect(screen.getByText("Memory and solved questions")).toBeVisible();
await userEvent.click(screen.getByRole("button", { name: "Clear derived data" }));
await waitFor(() => expect(calls).toBe(1));
});