Files
ThothII/frontend/src/shell/AppShell.database-management.test.tsx
T

188 lines
6.6 KiB
TypeScript

import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { http, HttpResponse } from "msw";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { server } from "../test/msw";
import { FakeEventSource } from "../test/fakeEventSource";
import { useSessionStore } from "../store/sessionStore";
import { clearAuthState, setAuthState } from "../auth/authState";
import { AppShell } from "./AppShell";
function renderShell() {
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
return render(
<QueryClientProvider client={client}>
<AppShell canLogout={false} />
</QueryClientProvider>,
);
}
beforeEach(() => {
clearAuthState();
setAuthState({
issuer: "test",
subject: "test",
roles: ["admin"],
permissions: ["session.use", "workspace.manage", "workspace.secrets.manage", "database.manage", "pi.manage"],
isAdmin: true,
csrfToken: null,
session: null,
});
localStorage.clear();
FakeEventSource.instances = [];
(globalThis as any).EventSource = FakeEventSource;
useSessionStore.getState().resetSession();
server.use(
http.get("/api/me", () => HttpResponse.json({
issuer: "test",
subject: "test",
displayName: "Test",
isAdmin: true,
})),
http.get("/api/sessions", () => HttpResponse.json([])),
http.get("/api/settings", () => HttpResponse.json({
workspace: "default",
provider: "test",
model: "test",
thinking: "low",
})),
http.get("/api/workspaces", () => HttpResponse.json([])),
http.get("/api/models", () => HttpResponse.json({ models: [] })),
http.post("/api/runtime/prewarm", () => new HttpResponse(null, { status: 202 })),
);
});
test("opens the database management surface and returns to the core", async () => {
renderShell();
const composer = screen.getByRole("textbox", { name: /new question/i });
await userEvent.type(composer, "Unsent draft");
const databaseManagement = screen.getByRole("button", { name: "Database management" });
await userEvent.click(databaseManagement);
expect(screen.getByRole("main", { name: "Database management" })).toBeVisible();
expect(databaseManagement).toHaveAttribute("aria-current", "page");
expect(databaseManagement).toHaveClass("w-full");
expect(databaseManagement).not.toHaveClass("bg-sidebar-accent");
expect(screen.queryByRole("textbox", { name: /new question/i })).not.toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: "New session" }));
await waitFor(() => {
expect(screen.queryByRole("main", { name: "Database management" })).not.toBeInTheDocument();
});
expect(screen.getByRole("textbox", { name: /new question/i })).toHaveValue("Unsent draft");
});
test("keeps a live core session connected and returns when that session is opened", async () => {
server.use(
http.get("/api/sessions", () => HttpResponse.json([{
id: "s1",
status: "open",
question: "Active question",
summary: null,
created_at: "2026-01-02T00:00:00Z",
updated_at: null,
author: "test",
name: null,
group: null,
archived: false,
active: true,
}])),
http.post("/api/sessions/:id/resume", ({ params }) => HttpResponse.json({
id: params.id,
alreadyActive: true,
})),
http.get("/api/sessions/:id", ({ params }) => HttpResponse.json({
id: params.id,
status: "open",
phase: 1,
})),
);
renderShell();
const session = await screen.findByTestId("session-item-s1");
await userEvent.click(session);
await waitFor(() => expect(FakeEventSource.instances).toHaveLength(1));
const source = FakeEventSource.instances[0];
await userEvent.click(screen.getByRole("button", { name: "Database management" }));
expect(screen.getByRole("main", { name: "Database management" })).toBeVisible();
expect(source.closed).toBe(false);
expect(FakeEventSource.instances).toHaveLength(1);
await userEvent.click(session);
await waitFor(() => {
expect(screen.queryByRole("main", { name: "Database management" })).not.toBeInTheDocument();
});
expect(source.closed).toBe(false);
expect(FakeEventSource.instances).toHaveLength(1);
});
test("keeps read-safe workspace access but hides privileged management entries", () => {
clearAuthState();
setAuthState({
issuer: "test",
subject: "test",
roles: ["user"],
permissions: ["session.use"],
isAdmin: false,
csrfToken: null,
session: null,
});
renderShell();
expect(screen.getByRole("button", { name: "Workspace management" })).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Database management" })).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Pi management" })).not.toBeInTheDocument();
});
test("does not leave database management without confirming a dirty form", async () => {
server.use(http.get("/api/catalog/databases", () => HttpResponse.json([{
id: "11111111-1111-4111-8111-111111111111",
workspaceId: "psd-clinical",
workspaceName: "Policlinico San Donato",
workspaceAvailable: true,
configured: true,
engine: "postgres",
databaseName: "warehouse",
schema: "datawarehouse",
version: 3,
createdAt: "2026-08-27T08:00:00Z",
updatedAt: "2026-08-27T09:00:00Z",
binding: { transport: "postgres_direct", host: "db.internal", port: 5432, username: "reader" },
connectionStatus: "untested",
secrets: {
password: false,
apiKey: false,
sshPrivateKey: false,
sshPrivateKeyPassphrase: false,
sshKnownHosts: false,
tlsCa: false,
},
}])));
const confirm = vi.spyOn(window, "confirm").mockReturnValue(false);
renderShell();
await userEvent.click(screen.getByRole("button", { name: "Database management" }));
await userEvent.click(await screen.findByRole("button", { name: "Edit Policlinico San Donato" }));
const schema = screen.getByLabelText("Schema");
await userEvent.clear(schema);
await userEvent.type(schema, "reporting");
await userEvent.click(screen.getByRole("button", { name: "New session" }));
expect(confirm).toHaveBeenCalledWith("Discard unsaved database changes and leave database management?");
expect(screen.getByRole("main", { name: "Database management" })).toBeVisible();
expect(schema).toHaveValue("reporting");
confirm.mockReturnValue(true);
await userEvent.click(screen.getByRole("button", { name: "New session" }));
await waitFor(() => expect(screen.queryByRole("main", { name: "Database management" })).not.toBeInTheDocument());
confirm.mockRestore();
});