feat: unify installation model catalog
This commit is contained in:
@@ -13,111 +13,42 @@ const readyStatus = {
|
||||
checkedAt: "2026-08-05T10:00:00.000Z",
|
||||
};
|
||||
|
||||
const options = {
|
||||
providers: ["zai", "deepseek"],
|
||||
models: [
|
||||
{ provider: "zai", id: "glm-5.2" },
|
||||
{ provider: "deepseek", id: "deepseek-v4" },
|
||||
],
|
||||
reasoning: ["low", "medium", "high"],
|
||||
checkedAt: "2026-08-05T10:00:00.000Z",
|
||||
};
|
||||
|
||||
function renderManagement() {
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
return render(<QueryClientProvider client={client}><PiManagement open onClose={() => undefined} /></QueryClientProvider>);
|
||||
return render(
|
||||
<QueryClientProvider client={client}>
|
||||
<PiManagement open onClose={() => undefined} />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () => HttpResponse.json(readyStatus)),
|
||||
http.get("/api/pi-management/options", () => HttpResponse.json(options)),
|
||||
);
|
||||
server.use(http.get("/api/pi-management/status", () => HttpResponse.json(readyStatus)));
|
||||
});
|
||||
|
||||
test("loads Pi version and readiness as an accessible operational rail", async () => {
|
||||
test("shows the catalog default as read-only installation state", async () => {
|
||||
renderManagement();
|
||||
|
||||
expect(screen.getByText("Loading Pi management…")).toBeVisible();
|
||||
expect(await screen.findByRole("heading", { name: "Pi management" })).toBeVisible();
|
||||
await screen.findByLabelText("Provider");
|
||||
const dialog = screen.getByRole("dialog", { name: "Pi management" });
|
||||
expect(dialog).toHaveAttribute("aria-modal", "false");
|
||||
expect(dialog).toHaveAttribute("data-work-area-panel");
|
||||
expect(dialog.querySelectorAll(':scope > [data-work-area-panel-region="header"]')).toHaveLength(1);
|
||||
expect(dialog.querySelectorAll(':scope > [data-work-area-panel-region="body"]')).toHaveLength(1);
|
||||
expect(await screen.findByText("Pi 0.80.3")).toBeVisible();
|
||||
expect(screen.getByTestId("pi-readiness-rail")).toHaveTextContent("Runtime ready");
|
||||
expect(screen.getByRole("status", { name: "Pi readiness" })).toHaveTextContent("Ready");
|
||||
expect(screen.getByText("Pi 0.80.3")).toBeVisible();
|
||||
expect(screen.getByText("Defaults ready")).toBeVisible();
|
||||
expect(screen.getByRole("button", { name: "Test saved defaults" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("uses closed provider, model, and reasoning choices without a secret field or terminal", async () => {
|
||||
renderManagement();
|
||||
|
||||
const provider = await screen.findByLabelText("Provider");
|
||||
expect(provider).toHaveValue("zai");
|
||||
expect(screen.getByLabelText("Model")).toHaveValue("glm-5.2");
|
||||
expect(screen.getByLabelText("Reasoning level")).toHaveValue("medium");
|
||||
expect(within(provider).getAllByRole("option").map((option) => option.textContent)).toEqual(["zai", "deepseek"]);
|
||||
expect(screen.getByLabelText("Model")).toHaveTextContent("GLM 5.2");
|
||||
expect(screen.queryByRole("textbox", { name: /provider|model|reasoning|credential/i })).not.toBeInTheDocument();
|
||||
const catalog = screen.getByRole("region", { name: "Installation catalog default" });
|
||||
expect(catalog).toHaveTextContent("zai");
|
||||
expect(catalog).toHaveTextContent("glm-5.2");
|
||||
expect(catalog).toHaveTextContent("medium");
|
||||
expect(screen.getByRole("button", { name: "Test catalog default" })).toBeVisible();
|
||||
expect(screen.queryByRole("button", { name: /save defaults/i })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("combobox", { name: /provider|model|reasoning/i })).not.toBeInTheDocument();
|
||||
expect(document.querySelector('input[type="password"]')).toBeNull();
|
||||
expect(screen.queryByRole("button", { name: /terminal|shell access/i })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Update Pi" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("saves only a selected non-secret configuration", async () => {
|
||||
const user = userEvent.setup();
|
||||
let saved: unknown;
|
||||
server.use(http.put("/api/pi-management/config", async ({ request }) => {
|
||||
saved = await request.json();
|
||||
return HttpResponse.json({ ...saved as object, updatedAt: "2026-08-05T10:02:00.000Z" });
|
||||
}));
|
||||
renderManagement();
|
||||
|
||||
await user.selectOptions(await screen.findByLabelText("Provider"), "deepseek");
|
||||
await user.selectOptions(screen.getByLabelText("Reasoning level"), "high");
|
||||
await user.click(screen.getByRole("button", { name: "Save defaults" }));
|
||||
|
||||
await waitFor(() => expect(saved).toEqual({ provider: "deepseek", model: "deepseek-v4", reasoning: "high" }));
|
||||
expect(screen.getByRole("status", { name: "Pi management feedback" })).toHaveTextContent("Defaults saved");
|
||||
});
|
||||
|
||||
// Catches a provider switch updating only the saved config while leaving the credential rail
|
||||
// attached to the previously selected provider.
|
||||
test("shows authoritative credential presence after saving a different provider", async () => {
|
||||
const user = userEvent.setup();
|
||||
let saved = false;
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () => HttpResponse.json(saved ? {
|
||||
...readyStatus,
|
||||
credentials: "missing",
|
||||
config: { provider: "deepseek", model: "deepseek-v4", reasoning: "medium" },
|
||||
checkedAt: "2026-08-05T10:02:00.000Z",
|
||||
} : readyStatus)),
|
||||
http.put("/api/pi-management/config", async ({ request }) => {
|
||||
saved = true;
|
||||
return HttpResponse.json({
|
||||
...await request.json() as object,
|
||||
updatedAt: "2026-08-05T10:01:00.000Z",
|
||||
});
|
||||
}),
|
||||
);
|
||||
renderManagement();
|
||||
|
||||
expect(await screen.findByText("Credentials present")).toBeVisible();
|
||||
await user.selectOptions(screen.getByLabelText("Provider"), "deepseek");
|
||||
await user.click(screen.getByRole("button", { name: "Save defaults" }));
|
||||
|
||||
expect(await screen.findByText("Credentials missing")).toBeVisible();
|
||||
expect(screen.queryByText("Credentials present")).not.toBeInTheDocument();
|
||||
expect(screen.getByLabelText("Provider")).toHaveValue("deepseek");
|
||||
expect(screen.getByRole("status", { name: "Pi management feedback" })).toHaveTextContent("Defaults saved");
|
||||
});
|
||||
|
||||
test("runs the saved-configuration test without changing credential presence", async () => {
|
||||
test("tests the catalog default without mutating credential presence", async () => {
|
||||
const user = userEvent.setup();
|
||||
let tests = 0;
|
||||
server.use(http.post("/api/pi-management/test", () => {
|
||||
@@ -128,204 +59,95 @@ test("runs the saved-configuration test without changing credential presence", a
|
||||
}));
|
||||
renderManagement();
|
||||
|
||||
const testButton = await screen.findByRole("button", { name: "Test saved defaults" });
|
||||
expect(screen.getByText("Defaults ready")).toBeVisible();
|
||||
expect(screen.queryByText("Changes are not saved yet.")).not.toBeInTheDocument();
|
||||
expect(testButton).toBeEnabled();
|
||||
const testButton = await screen.findByRole("button", { name: "Test catalog default" });
|
||||
await user.click(testButton);
|
||||
await waitFor(() => expect(tests).toBe(1));
|
||||
expect(await screen.findByText("Saved configuration test passed")).toBeVisible();
|
||||
expect(await screen.findByText("Catalog default test passed.")).toBeVisible();
|
||||
expect(screen.getByText("Credentials present")).toBeVisible();
|
||||
await user.click(screen.getByRole("button", { name: "Test saved defaults" }));
|
||||
expect(await screen.findByText("Saved configuration test failed")).toBeVisible();
|
||||
await user.click(testButton);
|
||||
expect(await screen.findByText(/Catalog default test failed/)).toBeVisible();
|
||||
expect(screen.getByText("Credentials present")).toBeVisible();
|
||||
});
|
||||
|
||||
test("fetches and displays bounded sanitized diagnostic logs only on request", async () => {
|
||||
test("fetches bounded sanitized diagnostics only on request", async () => {
|
||||
const user = userEvent.setup();
|
||||
let logRequests = 0;
|
||||
server.use(http.get("/api/pi-management/logs", () => {
|
||||
logRequests += 1;
|
||||
return HttpResponse.json({ lines: ["Pi smoke check succeeded", "provider token=[REDACTED]"], checkedAt: "2026-08-05T10:04:00.000Z" });
|
||||
return HttpResponse.json({
|
||||
lines: ["Pi smoke check succeeded", "provider token=[REDACTED]"],
|
||||
checkedAt: "2026-08-05T10:04:00.000Z",
|
||||
});
|
||||
}));
|
||||
renderManagement();
|
||||
|
||||
await screen.findByLabelText("Provider");
|
||||
await screen.findByText("Pi 0.80.3");
|
||||
expect(logRequests).toBe(0);
|
||||
await user.click(screen.getByRole("button", { name: "Show sanitized logs" }));
|
||||
expect(await screen.findByLabelText("Sanitized Pi diagnostics")).toHaveTextContent("provider token=[REDACTED]");
|
||||
expect(logRequests).toBe(1);
|
||||
expect(screen.queryByText("raw-provider-token")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("explains forbidden management access without offering mutation controls", async () => {
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () =>
|
||||
HttpResponse.json({ code: "pi_management_forbidden", error: "Pi management is not permitted" }, { status: 403 })),
|
||||
);
|
||||
test("explains forbidden management access without offering controls", async () => {
|
||||
server.use(http.get("/api/pi-management/status", () =>
|
||||
HttpResponse.json(
|
||||
{ code: "pi_management_forbidden", error: "Pi management is not permitted" },
|
||||
{ status: 403 },
|
||||
)));
|
||||
renderManagement();
|
||||
|
||||
expect(await screen.findByRole("alert", { name: "Pi management unavailable" })).toHaveTextContent("Pi management is not permitted");
|
||||
expect(screen.queryByLabelText("Provider")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Save defaults" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Test saved defaults" })).not.toBeInTheDocument();
|
||||
expect(await screen.findByRole("alert", { name: "Pi management unavailable" }))
|
||||
.toHaveTextContent("Pi management is not permitted");
|
||||
expect(screen.queryByRole("button", { name: "Test catalog default" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("shows a seven-step host-terminal workflow in scrollable platform tabs", async () => {
|
||||
test("shows the single-source host workflow in accessible platform tabs", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderManagement();
|
||||
|
||||
const tablist = await screen.findByRole("tablist", { name: "Pi host platform" });
|
||||
const [linuxTab, macosTab, windowsTab] = within(tablist).getAllByRole("tab");
|
||||
expect([linuxTab, macosTab, windowsTab].map((tab) => tab.textContent)).toEqual(["Linux", "macOS", "Windows"]);
|
||||
expect(screen.getByRole("dialog", { name: "Pi management" })).toHaveAttribute("data-width", "standard");
|
||||
expect(screen.getByTestId("pi-platform-instructions-scroll")).toHaveClass("overflow-y-scroll");
|
||||
expect(screen.queryByRole("tabpanel")).not.toBeInTheDocument();
|
||||
expect([linuxTab, macosTab, windowsTab].every((tab) => tab.getAttribute("aria-selected") === "false")).toBe(true);
|
||||
expect(linuxTab).toHaveAttribute("tabindex", "0");
|
||||
expect(macosTab).toHaveAttribute("tabindex", "-1");
|
||||
expect(windowsTab).toHaveAttribute("tabindex", "-1");
|
||||
for (const tab of [linuxTab, macosTab, windowsTab]) {
|
||||
const panelId = tab.getAttribute("aria-controls");
|
||||
const panel = panelId ? document.getElementById(panelId) : null;
|
||||
expect(panel).toBeInTheDocument();
|
||||
expect(panel).toHaveAttribute("role", "tabpanel");
|
||||
expect(panel).toHaveAttribute("aria-labelledby", tab.id);
|
||||
expect(panel).toHaveAttribute("hidden");
|
||||
expect(panel).toHaveAttribute("tabindex", "-1");
|
||||
}
|
||||
expect(screen.getByText("Using the host terminal:")).toBeVisible();
|
||||
expect(screen.queryByText(/not this browser page/i)).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("region", { name: "Host update" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Copy update command" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/:5173/)).not.toBeInTheDocument();
|
||||
expect(await screen.findByText("Select the provider, model, and reasoning used for new Pi work. Credentials stay in protected host files.")).toBeVisible();
|
||||
expect(screen.getByText("Shows at most 200 recent lines with declared secret values removed.")).toBeVisible();
|
||||
|
||||
linuxTab.focus();
|
||||
await user.keyboard("{ArrowRight}");
|
||||
expect(macosTab).toHaveFocus();
|
||||
expect(macosTab).toHaveAttribute("aria-selected", "true");
|
||||
expect(macosTab).toHaveAttribute("tabindex", "0");
|
||||
expect(linuxTab).toHaveAttribute("tabindex", "-1");
|
||||
await user.keyboard("{ArrowRight}");
|
||||
expect(windowsTab).toHaveFocus();
|
||||
expect(windowsTab).toHaveAttribute("aria-selected", "true");
|
||||
expect(windowsTab).toHaveAttribute("tabindex", "0");
|
||||
expect(macosTab).toHaveAttribute("tabindex", "-1");
|
||||
await user.keyboard("{ArrowRight}");
|
||||
expect(linuxTab).toHaveFocus();
|
||||
expect(linuxTab).toHaveAttribute("aria-selected", "true");
|
||||
await user.keyboard("{ArrowLeft}");
|
||||
expect(windowsTab).toHaveFocus();
|
||||
await user.keyboard("{Home}");
|
||||
expect(linuxTab).toHaveFocus();
|
||||
await user.keyboard("{End}");
|
||||
expect(windowsTab).toHaveFocus();
|
||||
|
||||
await user.click(windowsTab);
|
||||
expect(windowsTab).toHaveAttribute("aria-selected", "false");
|
||||
expect(screen.queryByRole("tabpanel")).not.toBeInTheDocument();
|
||||
await user.keyboard(" ");
|
||||
expect(windowsTab).toHaveAttribute("aria-selected", "true");
|
||||
await user.keyboard(" ");
|
||||
expect(windowsTab).toHaveAttribute("aria-selected", "false");
|
||||
await user.keyboard("{Enter}");
|
||||
expect(windowsTab).toHaveAttribute("aria-selected", "true");
|
||||
await user.keyboard("{Enter}");
|
||||
expect(windowsTab).toHaveAttribute("aria-selected", "false");
|
||||
|
||||
await user.click(linuxTab);
|
||||
|
||||
const linux = screen.getByRole("tabpanel", { name: "Linux" });
|
||||
expect(within(linux).getAllByRole("listitem")).toHaveLength(7);
|
||||
expect(within(linux).getAllByRole("heading", { level: 4 }).map((heading) => heading.textContent)).toEqual([
|
||||
"Open the project root",
|
||||
"Edit the provider catalog",
|
||||
"Enable the model",
|
||||
"Edit the Installation Model Catalog",
|
||||
"Choose eligibility and defaults",
|
||||
"Complete the provider setup",
|
||||
"Reload Pi configuration",
|
||||
"Update the Pi version",
|
||||
"Diagnose and recover a failed operation",
|
||||
]);
|
||||
expect(linux).toHaveTextContent("cd /absolute/path/to/ThothII");
|
||||
expect(linux).toHaveTextContent("tht pi status");
|
||||
expect(linux).toHaveTextContent("tht pi doctor");
|
||||
expect(linux).toHaveTextContent("Invalid JSON");
|
||||
expect(linux).toHaveTextContent("Provider connectivity");
|
||||
expect(linux).toHaveTextContent("If maintenance is inactive");
|
||||
expect(linux).toHaveTextContent("active after an update");
|
||||
expect(linux).toHaveTextContent("active after a restart");
|
||||
expect(linux).toHaveTextContent("run every command in this list from that directory");
|
||||
expect(linux).toHaveTextContent("deploy/pi/models.json");
|
||||
expect(linux).toHaveTextContent("deploy/pi/settings.json");
|
||||
expect(linux).toHaveTextContent("baseUrl is the provider API endpoint");
|
||||
expect(linux).toHaveTextContent("enabledModels uses provider/model identifiers");
|
||||
expect(linux).toHaveTextContent("During the initial installation");
|
||||
expect(linux).toHaveTextContent("Pi credentials file location");
|
||||
expect(linux).toHaveTextContent("tht pi restart --yes --drain");
|
||||
expect(linux).toHaveTextContent("tht pi update");
|
||||
expect(linux).toHaveTextContent("tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain");
|
||||
expect(linux).toHaveTextContent("tht pi maintenance status");
|
||||
expect(linux).toHaveTextContent("tht pi logs");
|
||||
expect(linux).toHaveTextContent("tht pi rollback --yes");
|
||||
expect(linux).toHaveTextContent("tht pi maintenance recover --yes");
|
||||
expect(linux).not.toHaveTextContent("PI_AUTH_FILE");
|
||||
expect(linux).not.toHaveTextContent("thothctl");
|
||||
expect(linux).not.toHaveTextContent("~/.pi/agent/");
|
||||
for (const expected of [
|
||||
"deploy/<installation-id>/thothii-installation.yaml",
|
||||
"modelCatalog.defaults.session",
|
||||
"provider/model",
|
||||
"generated projections",
|
||||
"tht pi restart --yes --drain",
|
||||
"tht pi doctor",
|
||||
"Invalid YAML",
|
||||
]) expect(linux).toHaveTextContent(expected);
|
||||
for (const retired of ["deploy/pi/models.json", "deploy/pi/settings.json", "tht pi configure", "enabledModels"])
|
||||
expect(linux).not.toHaveTextContent(retired);
|
||||
|
||||
await user.click(macosTab);
|
||||
const macos = screen.getByRole("tabpanel", { name: "macOS" });
|
||||
expect(within(macos).getAllByRole("listitem")).toHaveLength(7);
|
||||
expect(within(macos).getAllByRole("heading", { level: 4 }).map((heading) => heading.textContent)).toEqual([
|
||||
"Open the project root",
|
||||
"Edit the provider catalog",
|
||||
"Enable the model",
|
||||
"Complete the provider setup",
|
||||
"Reload Pi configuration",
|
||||
"Update the Pi version",
|
||||
"Diagnose and recover a failed operation",
|
||||
]);
|
||||
expect(macos).toHaveTextContent("cd /absolute/path/to/ThothII");
|
||||
expect(macos).toHaveTextContent("run every command in this list from that directory");
|
||||
expect(macos).toHaveTextContent("deploy/pi/models.json");
|
||||
expect(macos).toHaveTextContent("deploy/pi/settings.json");
|
||||
expect(macos).toHaveTextContent("tht pi restart --yes --drain");
|
||||
expect(macos).toHaveTextContent("tht pi update");
|
||||
expect(macos).toHaveTextContent("tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain");
|
||||
expect(macos).toHaveTextContent("tht pi maintenance status");
|
||||
expect(macos).toHaveTextContent("tht pi logs");
|
||||
expect(macos).toHaveTextContent("tht pi rollback --yes");
|
||||
expect(macos).toHaveTextContent("tht pi maintenance recover --yes");
|
||||
|
||||
expect(screen.getByRole("tabpanel", { name: "macOS" }))
|
||||
.toHaveTextContent("deploy/<installation-id>/thothii-installation.yaml");
|
||||
await user.click(windowsTab);
|
||||
const windows = screen.getByRole("tabpanel", { name: "Windows" });
|
||||
expect(within(windows).getAllByRole("listitem")).toHaveLength(7);
|
||||
expect(within(windows).getAllByRole("heading", { level: 4 }).map((heading) => heading.textContent)).toEqual([
|
||||
"Open the project root",
|
||||
"Edit the provider catalog",
|
||||
"Enable the model",
|
||||
"Complete the provider setup",
|
||||
"Reload Pi configuration",
|
||||
"Update the Pi version",
|
||||
"Diagnose and recover a failed operation",
|
||||
]);
|
||||
expect(windows).toHaveTextContent("Set-Location C:\\absolute\\path\\to\\ThothII");
|
||||
expect(windows).toHaveTextContent("run every command in this list from that directory");
|
||||
expect(windows).toHaveTextContent("deploy\\pi\\models.json");
|
||||
expect(windows).toHaveTextContent("deploy\\pi\\settings.json");
|
||||
expect(windows).toHaveTextContent('tht pi restart --yes --drain');
|
||||
expect(windows).toHaveTextContent('tht pi update');
|
||||
expect(windows).toHaveTextContent('tht pi update --version <VERSION> --source pull --image <IMAGE>@sha256:<DIGEST> --yes --drain');
|
||||
expect(windows).toHaveTextContent('tht pi maintenance status');
|
||||
expect(windows).toHaveTextContent('tht pi logs');
|
||||
expect(windows).toHaveTextContent('tht pi rollback --yes');
|
||||
expect(windows).toHaveTextContent('tht pi maintenance recover --yes');
|
||||
expect(windows).not.toHaveTextContent("PI_AUTH_FILE");
|
||||
expect(windows).not.toHaveTextContent("thothctl");
|
||||
expect(screen.getByRole("tabpanel", { name: "Windows" }))
|
||||
.toHaveTextContent("deploy\\<installation-id>\\thothii-installation.yaml");
|
||||
});
|
||||
|
||||
|
||||
test("reloads installation defaults when the panel is reopened", async () => {
|
||||
test("reloads the catalog-derived default when reopened", async () => {
|
||||
let statusCalls = 0;
|
||||
server.use(http.get("/api/pi-management/status", () => {
|
||||
statusCalls += 1;
|
||||
@@ -334,70 +156,32 @@ test("reloads installation defaults when the panel is reopened", async () => {
|
||||
: { ...readyStatus, config: { provider: "deepseek", model: "deepseek-v4", reasoning: "high" } });
|
||||
}));
|
||||
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
const view = render(<QueryClientProvider client={client}><PiManagement open onClose={() => undefined} /></QueryClientProvider>);
|
||||
const view = render(
|
||||
<QueryClientProvider client={client}><PiManagement open onClose={() => undefined} /></QueryClientProvider>,
|
||||
);
|
||||
|
||||
expect(await screen.findByLabelText("Provider")).toHaveValue("zai");
|
||||
view.rerender(<QueryClientProvider client={client}><PiManagement open={false} onClose={() => undefined} /></QueryClientProvider>);
|
||||
expect(await screen.findByText("glm-5.2")).toBeVisible();
|
||||
view.rerender(
|
||||
<QueryClientProvider client={client}><PiManagement open={false} onClose={() => undefined} /></QueryClientProvider>,
|
||||
);
|
||||
await waitFor(() => expect(screen.queryByRole("dialog", { name: "Pi management" })).not.toBeInTheDocument());
|
||||
view.rerender(<QueryClientProvider client={client}><PiManagement open onClose={() => undefined} /></QueryClientProvider>);
|
||||
|
||||
await waitFor(() => expect(screen.getByLabelText("Provider")).toHaveValue("deepseek"));
|
||||
view.rerender(
|
||||
<QueryClientProvider client={client}><PiManagement open onClose={() => undefined} /></QueryClientProvider>,
|
||||
);
|
||||
expect(await screen.findByText("deepseek-v4")).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows an explicit recoverable incomplete state when no provider model is available", async () => {
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () => HttpResponse.json({ ...readyStatus, credentials: "present" })),
|
||||
http.get("/api/pi-management/options", () => HttpResponse.json({
|
||||
...options,
|
||||
providers: ["zai"],
|
||||
models: [],
|
||||
})),
|
||||
);
|
||||
test("shows an explicit incomplete state when the catalog default is unavailable", async () => {
|
||||
server.use(http.get("/api/pi-management/status", () => HttpResponse.json({
|
||||
...readyStatus,
|
||||
config: { reasoning: "medium" },
|
||||
})));
|
||||
renderManagement();
|
||||
|
||||
const incomplete = await screen.findByRole("alert", { name: "Pi configuration incomplete" });
|
||||
expect(incomplete).toHaveTextContent("No provider or model options are available");
|
||||
expect(incomplete).toHaveTextContent("Check the host-managed Pi model configuration");
|
||||
expect(screen.queryByText("Loading Pi management…")).not.toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Save defaults" })).toBeDisabled();
|
||||
expect(screen.getByRole("button", { name: "Test saved defaults" })).toBeDisabled();
|
||||
});
|
||||
|
||||
test("keeps suggested draft choices distinct from invalid persisted defaults until save succeeds", async () => {
|
||||
let configured = false;
|
||||
const persisted = {
|
||||
...readyStatus,
|
||||
credentials: "missing",
|
||||
config: { provider: "retired", model: "old-model", reasoning: "medium" },
|
||||
};
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () => HttpResponse.json(configured ? {
|
||||
...readyStatus,
|
||||
credentials: "missing",
|
||||
config: { provider: "zai", model: "glm-5.2", reasoning: "medium" },
|
||||
checkedAt: "2026-08-05T10:05:00.000Z",
|
||||
} : persisted)),
|
||||
http.put("/api/pi-management/config", () => {
|
||||
configured = true;
|
||||
return HttpResponse.json({
|
||||
provider: "zai",
|
||||
model: "glm-5.2",
|
||||
reasoning: "medium",
|
||||
updatedAt: "2026-08-05T10:05:00.000Z",
|
||||
});
|
||||
}),
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
renderManagement();
|
||||
|
||||
expect(await screen.findByLabelText("Provider")).toHaveValue("zai");
|
||||
expect(screen.getByText("Defaults incomplete")).toBeVisible();
|
||||
expect(screen.getByText("Suggested choices are not saved yet.")).toBeVisible();
|
||||
expect(screen.getByRole("button", { name: "Test saved defaults" })).toBeDisabled();
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Save defaults" }));
|
||||
expect(await screen.findByText("Defaults ready")).toBeVisible();
|
||||
expect(screen.getByRole("button", { name: "Test saved defaults" })).toBeEnabled();
|
||||
expect(incomplete).toHaveTextContent("The catalog default is unavailable");
|
||||
expect(incomplete).toHaveTextContent("modelCatalog.defaults.session");
|
||||
expect(screen.getByRole("button", { name: "Test catalog default" })).toBeDisabled();
|
||||
});
|
||||
|
||||
test.each(["present", "missing"] as const)(
|
||||
@@ -408,47 +192,6 @@ test.each(["present", "missing"] as const)(
|
||||
credentials,
|
||||
})));
|
||||
renderManagement();
|
||||
|
||||
expect(await screen.findByText(`Credentials ${credentials}`)).toBeVisible();
|
||||
},
|
||||
);
|
||||
|
||||
test("labels smoke only as a saved-configuration test and resets it when the draft changes", async () => {
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () => HttpResponse.json({ ...readyStatus, credentials: "present" })),
|
||||
http.post("/api/pi-management/test", () => HttpResponse.json({
|
||||
ready: true,
|
||||
checkedAt: "2026-08-05T10:06:00.000Z",
|
||||
})),
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
renderManagement();
|
||||
|
||||
await user.click(await screen.findByRole("button", { name: "Test saved defaults" }));
|
||||
expect(await screen.findByText("Saved configuration test passed")).toBeVisible();
|
||||
expect(screen.getByText("Credentials present")).toBeVisible();
|
||||
|
||||
await user.selectOptions(screen.getByLabelText("Provider"), "deepseek");
|
||||
expect(screen.getByText("Saved configuration test not run")).toBeVisible();
|
||||
expect(screen.getByText("Save these changes before testing. The test always uses saved defaults.")).toBeVisible();
|
||||
expect(screen.getByRole("button", { name: "Test saved defaults" })).toBeDisabled();
|
||||
expect(screen.getByText("Credentials present")).toBeVisible();
|
||||
});
|
||||
|
||||
test("a failed saved-configuration test does not change backend credential presence", async () => {
|
||||
server.use(
|
||||
http.get("/api/pi-management/status", () => HttpResponse.json({ ...readyStatus, credentials: "present" })),
|
||||
http.post("/api/pi-management/test", () => HttpResponse.json({
|
||||
ready: false,
|
||||
message: "Pi runtime is unavailable",
|
||||
checkedAt: "2026-08-05T10:07:00.000Z",
|
||||
})),
|
||||
);
|
||||
const user = userEvent.setup();
|
||||
renderManagement();
|
||||
|
||||
await user.click(await screen.findByRole("button", { name: "Test saved defaults" }));
|
||||
expect(await screen.findByText("Saved configuration test failed")).toBeVisible();
|
||||
expect(screen.getByText("Credentials present")).toBeVisible();
|
||||
expect(screen.queryByText("Credentials missing")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user