Files
ThothII/frontend/src/viewers/SchemaLinkingViewer.test.tsx
T
marcopanandClaude Opus 4.8 e4ada07dab feat(viewer): erDiagram shows real column types when present; ArtifactView accepts a {content} sql wrapper
erDiagram reads data_type/type from the candidate (top-level or signals),
falling back to the generic 'col' token Mermaid needs — real types render as
soon as schema_linking.json carries them. ArtifactView gains a defensive
{content} fallback for sql-kind artifacts, verified against real session shapes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:13:02 +02:00

113 lines
3.8 KiB
TypeScript

import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { SchemaLinkingViewer, buildErDiagram } from "./SchemaLinkingViewer";
vi.mock("./mermaid", () => ({
renderMermaid: vi.fn().mockResolvedValue('<svg data-testid="mm"></svg>'),
}));
const smallLinking = {
question: "quante ordini?",
candidates: [
{ kind: "table" as const, name: "orders", decision: "promoted" as const },
{ kind: "column" as const, name: "orders.id", decision: "promoted" as const },
{ kind: "table" as const, name: "users", decision: "excluded" as const },
],
joins: [{ from: "orders", to: "orders.id" }],
excluded: [{ kind: "table" as const, name: "users" }],
open_questions: [],
};
const bigLinking = {
question: "grossa query",
candidates: Array.from({ length: 46 }, (_, i) => ({
kind: "table" as const,
name: `table_${i}`,
decision: "promoted" as const,
})),
joins: [],
excluded: [],
open_questions: [],
};
test("(a) default renders the mermaid container (mocked svg appears)", async () => {
render(<SchemaLinkingViewer linking={smallLinking} />);
await waitFor(() => {
expect(screen.getByTestId("mm")).toBeInTheDocument();
});
});
test("(b) clicking toggle switches to table listing a promoted table name", async () => {
render(<SchemaLinkingViewer linking={smallLinking} />);
// wait for mermaid to load
await waitFor(() => expect(screen.getByTestId("mm")).toBeInTheDocument());
const toggle = screen.getByRole("button", { name: /table/i });
await userEvent.click(toggle);
expect(screen.getByText("orders")).toBeInTheDocument();
expect(screen.queryByTestId("mm")).not.toBeInTheDocument();
});
test("(c) oversized linking (>45) shows cap notice", () => {
render(<SchemaLinkingViewer linking={bigLinking} />);
expect(
screen.getByText(/too many elements for the graph/i)
).toBeInTheDocument();
});
test("(d) ~10 promoted + ~50 excluded does NOT cap; graph is available", async () => {
const mixedLinking = {
question: "caso misto",
candidates: [
...Array.from({ length: 10 }, (_, i) => ({
kind: "table" as const,
name: `kept_${i}`,
decision: "promoted" as const,
})),
...Array.from({ length: 50 }, (_, i) => ({
kind: "table" as const,
name: `dropped_${i}`,
decision: "excluded" as const,
})),
],
joins: [],
excluded: [],
open_questions: [],
};
render(<SchemaLinkingViewer linking={mixedLinking} />);
expect(
screen.queryByText(/troppi elementi per il grafico/i)
).not.toBeInTheDocument();
// Grafico (mermaid) view is the default and renders the mocked svg.
await waitFor(() => expect(screen.getByTestId("mm")).toBeInTheDocument());
});
test("(e) buildErDiagram emits entities, attributes and a relation", () => {
const def = buildErDiagram(
[
{ kind: "table", name: "orders", decision: "promoted" },
{ kind: "column", name: "orders.id", decision: "promoted" },
{ kind: "table", name: "customers", decision: "promoted" },
],
[{ from: "orders.customer_id", to: "customers.id" }],
);
expect(def.startsWith("erDiagram")).toBe(true);
expect(def).toContain("orders {");
expect(def).toContain("col id");
expect(def).toContain("orders }o--o{ customers : join");
});
test("(f) buildErDiagram uses a column's real type when the data carries one", () => {
const def = buildErDiagram(
[
{ kind: "table", name: "orders", decision: "promoted" },
{ kind: "column", name: "orders.total", decision: "promoted", signals: { data_type: "numeric" } },
{ kind: "column", name: "orders.id", decision: "promoted" }, // no type -> generic "col"
],
[],
);
expect(def).toContain("numeric total");
expect(def).toContain("col id");
});