Files
2026-09-16 10:38:31 +01:00

160 lines
5.1 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { ApiClient, removeRoutingProfileReferences } from "./api-client";
import { loadInterimSettingsInterlockToken } from "./interim-settings-interlock";
describe("ApiClient.createAgentProfile", () => {
afterEach(() => {
vi.unstubAllGlobals();
});
it("forwards auto_approve while preserving the profile request contract", async () => {
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url.endsWith("/api/v1/app-state?path=%2Fsettings%2Fagents")) {
return Response.json({ interimSettingsInterlockToken: "test-token" });
}
if (url.endsWith("/api/v1/agents/agent-1/profiles")) {
const body = JSON.parse(String(init?.body)) as Record<string, unknown>;
expect(body).toMatchObject({
name: "TUI profile",
model: "mock-fast",
auto_approve: true,
cli_passthrough: true,
});
expect(init?.headers).toMatchObject({
"Content-Type": "application/json",
"X-Kandev-Interim-Settings-Interlock": "test-token",
});
return Response.json({
id: "profile-1",
name: "TUI profile",
agent_id: "agent-1",
model: "mock-fast",
auto_approve: true,
cli_passthrough: true,
});
}
throw new Error(`unexpected request: ${url}`);
});
vi.stubGlobal("fetch", fetchMock);
const profile = await new ApiClient("http://backend.test").createAgentProfile(
"agent-1",
"TUI profile",
{ model: "mock-fast", auto_approve: true, cli_passthrough: true },
);
expect(profile.id).toBe("profile-1");
expect(fetchMock).toHaveBeenCalledTimes(2);
});
});
describe("ApiClient user settings", () => {
afterEach(() => {
vi.unstubAllGlobals();
});
// Contract coverage for the existing settings restore flow.
it("round-trips the startup choice and workspace scope", async () => {
const baseline = {
startup_page: "threads",
workspace_id: "workspace-1",
workflow_filter_id: "workflow-1",
};
let saved: unknown;
vi.stubGlobal(
"fetch",
vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url.endsWith("/api/v1/app-state?path=%2Fsettings%2Fagents")) {
return Response.json({ interimSettingsInterlockToken: "test-token" });
}
if (url.endsWith("/api/v1/user/settings")) {
if (init?.method === "PATCH") saved = JSON.parse(String(init.body));
return Response.json({ settings: baseline });
}
throw new Error(`unexpected request: ${url}`);
}),
);
const client = new ApiClient("http://backend.test");
const { settings } = await client.getUserSettings();
await client.saveUserSettings({
startup_page: settings.startup_page,
workspace_id: settings.workspace_id,
workflow_filter_id: settings.workflow_filter_id,
});
expect(saved).toEqual(baseline);
});
});
describe("loadInterimSettingsInterlockToken", () => {
afterEach(() => {
vi.unstubAllGlobals();
});
it("retries the backend startup response before reading the token", async () => {
let attempts = 0;
vi.stubGlobal(
"fetch",
vi.fn(async () => {
attempts += 1;
if (attempts < 3) return new Response(null, { status: 503 });
return Response.json({ interimSettingsInterlockToken: "ready-token" });
}),
);
await expect(loadInterimSettingsInterlockToken("http://backend.test")).resolves.toBe(
"ready-token",
);
expect(attempts).toBe(3);
});
});
describe("removeRoutingProfileReferences", () => {
it("removes role-tier overrides whose last execution profile was deleted", () => {
const updated = removeRoutingProfileReferences(
{
enabled: true,
provider_order: ["claude-acp", "codex-acp"],
default_tier: "balanced",
provider_profiles: {
"claude-acp": {
execution_profile_ids: { balanced: "profile-1", economy: "economy-1" },
tier_map: { balanced: "balanced-model", economy: "economy-model" },
},
"codex-acp": {
execution_profile_ids: { balanced: "profile-2" },
},
},
role_tiers: { ceo: "balanced", worker: "economy" },
},
"profile-1",
);
expect(updated?.role_tiers).toEqual({ ceo: "balanced", worker: "economy" });
expect(updated?.provider_profiles["claude-acp"]).toEqual({
execution_profile_ids: { economy: "economy-1" },
tier_map: { economy: "economy-model" },
});
});
it("drops a role-tier override when no provider still maps that tier", () => {
const updated = removeRoutingProfileReferences(
{
enabled: true,
provider_order: ["claude-acp"],
default_tier: "balanced",
provider_profiles: {
"claude-acp": {
execution_profile_ids: { balanced: "profile-1" },
},
},
role_tiers: { ceo: "balanced" },
},
"profile-1",
);
expect(updated?.role_tiers).toEqual({});
});
});