huskies: merge 949

This commit is contained in:
dave
2026-05-13 07:10:00 +00:00
parent d87722f6c8
commit 4a0fbcaa95
15 changed files with 1454 additions and 231 deletions
+84 -18
View File
@@ -45,31 +45,88 @@ describe("api client", () => {
});
describe("openProject", () => {
it("sends POST with path", async () => {
mockFetch.mockResolvedValueOnce(okResponse("/home/user/project"));
it("dispatches project.open RPC with path and returns the canonical path", async () => {
const rpc = installRpcMock();
rpc.respond("project.open", { path: "/home/user/project" });
await api.openProject("/home/user/project");
const result = await api.openProject("/home/user/project");
expect(mockFetch).toHaveBeenCalledWith(
"/api/project",
expect.objectContaining({
method: "POST",
body: JSON.stringify({ path: "/home/user/project" }),
}),
);
expect(rpc.calls).toEqual([
{
method: "project.open",
params: { path: "/home/user/project" },
},
]);
expect(result).toBe("/home/user/project");
});
});
describe("closeProject", () => {
it("sends DELETE to /project", async () => {
mockFetch.mockResolvedValueOnce(okResponse(true));
it("dispatches project.close RPC and returns ok", async () => {
const rpc = installRpcMock();
rpc.respond("project.close", { ok: true });
await api.closeProject();
const result = await api.closeProject();
expect(mockFetch).toHaveBeenCalledWith(
"/api/project",
expect.objectContaining({ method: "DELETE" }),
);
expect(rpc.calls).toEqual([{ method: "project.close", params: {} }]);
expect(result).toBe(true);
});
});
describe("forgetKnownProject", () => {
it("dispatches project.forget RPC with path", async () => {
const rpc = installRpcMock();
rpc.respond("project.forget", { ok: true });
const result = await api.forgetKnownProject("/some/path");
expect(rpc.calls).toEqual([
{ method: "project.forget", params: { path: "/some/path" } },
]);
expect(result).toBe(true);
});
});
describe("setModelPreference", () => {
it("dispatches model.set_preference RPC", async () => {
const rpc = installRpcMock();
rpc.respond("model.set_preference", { ok: true });
await api.setModelPreference("claude-sonnet-4-6");
expect(rpc.calls).toEqual([
{
method: "model.set_preference",
params: { model: "claude-sonnet-4-6" },
},
]);
});
});
describe("setAnthropicApiKey", () => {
it("dispatches anthropic.set_api_key RPC", async () => {
const rpc = installRpcMock();
rpc.respond("anthropic.set_api_key", { ok: true });
await api.setAnthropicApiKey("sk-ant-xxx");
expect(rpc.calls).toEqual([
{
method: "anthropic.set_api_key",
params: { api_key: "sk-ant-xxx" },
},
]);
});
});
describe("cancelChat", () => {
it("dispatches chat.cancel RPC", async () => {
const rpc = installRpcMock();
rpc.respond("chat.cancel", { ok: true });
await api.cancelChat();
expect(rpc.calls).toEqual([{ method: "chat.cancel", params: {} }]);
});
});
@@ -92,10 +149,19 @@ describe("api client", () => {
await expect(api.getCurrentProject()).rejects.toThrow("store offline");
});
it("surfaces RPC errors visibly for write methods", async () => {
const rpc = installRpcMock();
rpc.respondError("project.open", "No such directory", "INTERNAL");
await expect(api.openProject("/some/path")).rejects.toThrow(
"No such directory",
);
});
it("throws on non-ok HTTP response for legacy POST endpoints", async () => {
mockFetch.mockResolvedValueOnce(errorResponse(500, ""));
await expect(api.openProject("/some/path")).rejects.toThrow(
await expect(api.searchFiles("query")).rejects.toThrow(
"Request failed (500)",
);
});