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.
186 lines
6.6 KiB
TypeScript
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));
|
|
});
|