import { render, screen, waitFor, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { http, HttpResponse } from "msw"; import { useState } from "react"; import type { SensitiveDataSuggestionRun } from "../../api/catalog-databases"; import { server } from "../../test/msw"; import { SensitiveDataSuggestionHistoryDrawer } from "./SensitiveDataSuggestionHistoryDrawer"; const runningRun: SensitiveDataSuggestionRun = { id: "99999999-9999-4999-8999-999999999999", databaseId: "11111111-1111-4111-8111-111111111111", scope: "selected_columns", modelId: "local-qwen", status: "running", total: 4, suggestedSensitive: 1, suggestedNonSensitive: 1, createdAt: "2026-08-28T09:00:00Z", startedAt: "2026-08-28T09:00:00Z", updatedAt: "2026-08-28T09:00:01Z", finishedAt: null, errorSummary: null, }; function renderDrawer(initialRun: SensitiveDataSuggestionRun | null) { const client = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: Infinity } }, }); function Harness() { const [run, setRun] = useState(initialRun); return ( undefined} onRunUpdate={setRun} /> ); } return render( , ); } test("shows an explicit empty state before any sensitive suggestion runs exist", async () => { server.use(http.get( "/api/catalog/sensitive-data-suggestion-runs", () => HttpResponse.json([]), )); renderDrawer(null); const drawer = await screen.findByRole("dialog", { name: "Sensitive suggestion history" }); expect(within(drawer).getByRole("heading", { name: "Suggestion run history" })).toBeVisible(); expect(await within(drawer).findByText("No sensitive suggestion runs yet.")).toBeVisible(); }); test("shows sensitive suggestion results, safe events, and lets operators inspect an older run", async () => { const completedRun: SensitiveDataSuggestionRun = { ...runningRun, id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", scope: "all", status: "completed", total: 6, suggestedSensitive: 2, suggestedNonSensitive: 4, createdAt: "2026-08-28T08:00:00Z", startedAt: "2026-08-28T08:00:00Z", updatedAt: "2026-08-28T08:00:01Z", finishedAt: "2026-08-28T08:00:01Z", }; server.use( http.get("/api/catalog/sensitive-data-suggestion-runs", () => ( HttpResponse.json([runningRun, completedRun]) )), http.get("/api/catalog/sensitive-data-suggestion-runs/:runId", ({ params }) => ( HttpResponse.json(params.runId === completedRun.id ? completedRun : runningRun) )), http.get("/api/catalog/sensitive-data-suggestion-runs/:runId/events-list", ({ params }) => ( HttpResponse.json([{ runId: params.runId, sequence: 1, level: "info", message: params.runId === completedRun.id ? "Classified 6 columns" : "Classifying columns", createdAt: "2026-08-28T09:00:01Z", }]) )), ); const user = userEvent.setup(); renderDrawer(runningRun); const drawer = await screen.findByRole("dialog", { name: "Sensitive suggestion history" }); expect(within(drawer).getByRole("heading", { name: "Running" })).toBeVisible(); const progress = within(drawer).getByRole("table", { name: "Sensitive suggestion progress" }); expect(within(progress).getAllByRole("columnheader")).toHaveLength(3); expect(within(drawer).getByRole("log", { name: "Sensitive suggestion events" })).toHaveClass( "thot-catalog-drawer__event-log", ); expect(await within(drawer).findByText("Classifying columns")).toBeVisible(); expect(within(drawer).queryByRole("button", { name: "Stop" })).not.toBeInTheDocument(); expect(within(drawer).queryByRole("button", { name: "Unlock stale run" })).not.toBeInTheDocument(); const history = within(drawer).getByRole("region", { name: "Sensitive suggestion run history" }); await user.click(within(history).getByRole("button", { name: /Completed.*all/i })); expect(await within(drawer).findByRole("heading", { name: "Completed" })).toBeVisible(); expect(within(drawer).getByText("2", { selector: "[data-count='suggestedSensitive']" })).toBeVisible(); expect(within(drawer).getByText("4", { selector: "[data-count='suggestedNonSensitive']" })).toBeVisible(); await waitFor(() => expect(within(drawer).getByText("Classified 6 columns")).toBeVisible()); }); test("does not expose unsafe provider details from a final error summary", async () => { const failedRun: SensitiveDataSuggestionRun = { ...runningRun, status: "failed", finishedAt: "2026-08-28T09:00:02Z", errorSummary: "stack trace at provider.ts:42 private source sample sk-secret", }; server.use( http.get("/api/catalog/sensitive-data-suggestion-runs", () => HttpResponse.json([failedRun])), http.get("/api/catalog/sensitive-data-suggestion-runs/:runId", () => HttpResponse.json(failedRun)), http.get("/api/catalog/sensitive-data-suggestion-runs/:runId/events-list", () => HttpResponse.json([])), ); renderDrawer(failedRun); expect(await screen.findByText( "The run ended before all columns were classified. Review the event log for safe details.", )).toBeVisible(); expect(screen.queryByText(/sk-secret/i)).not.toBeInTheDocument(); });