map: tickets 06+07 resolved; 05 partial (live spawn pending gitea token)
This commit is contained in:
+85
-58
@@ -4,70 +4,97 @@ import { saveSettings } from "./settings";
|
||||
import { jsonResponse } from "./test/setup";
|
||||
|
||||
describe("api", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
saveSettings({ serverUrl: "http://srv", token: "sekret" });
|
||||
});
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
saveSettings({ serverUrl: "http://srv", token: "sekret" });
|
||||
});
|
||||
|
||||
it("throws 401 when not configured", async () => {
|
||||
localStorage.clear();
|
||||
await expect(fetchJson("/api/sessions")).rejects.toMatchObject({ status: 401 });
|
||||
});
|
||||
it("throws 401 when not configured", async () => {
|
||||
localStorage.clear();
|
||||
await expect(fetchJson("/api/sessions")).rejects.toMatchObject({
|
||||
status: 401,
|
||||
});
|
||||
});
|
||||
|
||||
it("GET sends bearer header and parses JSON", async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse([{ id: "s1" }]));
|
||||
const out = await fetchJson<Array<{ id: string }>>("/api/sessions");
|
||||
expect(out).toEqual([{ id: "s1" }]);
|
||||
const [input, init] = fetchMock.mock.calls[0] as unknown as [string, RequestInit];
|
||||
expect(input).toBe("http://srv/api/sessions");
|
||||
expect(init.headers).toEqual({ Authorization: "Bearer sekret" });
|
||||
expect(init.body).toBeUndefined();
|
||||
});
|
||||
it("GET sends bearer header and parses JSON", async () => {
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, "fetch")
|
||||
.mockResolvedValue(jsonResponse([{ id: "s1" }]));
|
||||
const out = await fetchJson<Array<{ id: string }>>("/api/sessions");
|
||||
expect(out).toEqual([{ id: "s1" }]);
|
||||
const [input, init] = fetchMock.mock.calls[0] as unknown as [
|
||||
string,
|
||||
RequestInit,
|
||||
];
|
||||
expect(input).toBe("http://srv/api/sessions");
|
||||
expect(init.headers).toEqual({ Authorization: "Bearer sekret" });
|
||||
expect(init.body).toBeUndefined();
|
||||
});
|
||||
|
||||
it("POST sends JSON content-type with body", async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse({ ok: true }));
|
||||
await fetchJson("/api/sessions/s1/prompt", { method: "POST", body: JSON.stringify({ message: "hi" }) });
|
||||
const init = fetchMock.mock.calls[0]?.[1] as RequestInit;
|
||||
expect(init.method).toBe("POST");
|
||||
expect(init.headers).toEqual({ Authorization: "Bearer sekret", "Content-Type": "application/json" });
|
||||
});
|
||||
it("POST sends JSON content-type with body", async () => {
|
||||
const fetchMock = vi
|
||||
.spyOn(globalThis, "fetch")
|
||||
.mockResolvedValue(jsonResponse({ ok: true }));
|
||||
await fetchJson("/api/sessions/s1/prompt", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ message: "hi" }),
|
||||
});
|
||||
const init = fetchMock.mock.calls[0]?.[1] as RequestInit;
|
||||
expect(init.method).toBe("POST");
|
||||
expect(init.headers).toEqual({
|
||||
Authorization: "Bearer sekret",
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
});
|
||||
|
||||
it("throws ApiError with server error message", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse({ error: "boom" }, 500));
|
||||
const err: unknown = await fetchJson("/api/sessions").catch((e: unknown) => e);
|
||||
expect(err).toBeInstanceOf(ApiError);
|
||||
expect((err as ApiError).message).toBe("boom");
|
||||
expect((err as ApiError).status).toBe(500);
|
||||
});
|
||||
it("throws ApiError with server error message", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(
|
||||
jsonResponse({ error: "boom" }, 500),
|
||||
);
|
||||
const err: unknown = await fetchJson("/api/sessions").catch(
|
||||
(e: unknown) => e,
|
||||
);
|
||||
expect(err).toBeInstanceOf(ApiError);
|
||||
expect((err as ApiError).message).toBe("boom");
|
||||
expect((err as ApiError).status).toBe(500);
|
||||
});
|
||||
|
||||
it("falls back to status text when body has no error string", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse({ other: 1 }, 404));
|
||||
const err: unknown = await fetchJson("/api/sessions").catch((e: unknown) => e);
|
||||
expect((err as ApiError).message).toBe("404 StatusText");
|
||||
});
|
||||
it("falls back to status text when body has no error string", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(
|
||||
jsonResponse({ other: 1 }, 404),
|
||||
);
|
||||
const err: unknown = await fetchJson("/api/sessions").catch(
|
||||
(e: unknown) => e,
|
||||
);
|
||||
expect((err as ApiError).message).toBe("404 StatusText");
|
||||
});
|
||||
|
||||
it("falls back to status text when body is not JSON", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue({
|
||||
ok: false,
|
||||
status: 502,
|
||||
statusText: "Bad Gateway",
|
||||
json: async () => {
|
||||
throw new SyntaxError("bad json");
|
||||
},
|
||||
} as unknown as Response);
|
||||
const err: unknown = await fetchJson("/api/sessions").catch((e: unknown) => e);
|
||||
expect((err as ApiError).message).toBe("502 Bad Gateway");
|
||||
});
|
||||
it("falls back to status text when body is not JSON", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue({
|
||||
ok: false,
|
||||
status: 502,
|
||||
statusText: "Bad Gateway",
|
||||
json: async () => {
|
||||
throw new SyntaxError("bad json");
|
||||
},
|
||||
} as unknown as Response);
|
||||
const err: unknown = await fetchJson("/api/sessions").catch(
|
||||
(e: unknown) => e,
|
||||
);
|
||||
expect((err as ApiError).message).toBe("502 Bad Gateway");
|
||||
});
|
||||
|
||||
it("rejects null JSON bodies gracefully in error path", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse(null, 409));
|
||||
const err: unknown = await fetchJson("/api/sessions").catch((e: unknown) => e);
|
||||
expect((err as ApiError).message).toBe("409 StatusText");
|
||||
});
|
||||
it("rejects null JSON bodies gracefully in error path", async () => {
|
||||
vi.spyOn(globalThis, "fetch").mockResolvedValue(jsonResponse(null, 409));
|
||||
const err: unknown = await fetchJson("/api/sessions").catch(
|
||||
(e: unknown) => e,
|
||||
);
|
||||
expect((err as ApiError).message).toBe("409 StatusText");
|
||||
});
|
||||
|
||||
it("errMessage maps Error and non-Error values", () => {
|
||||
expect(errMessage(new Error("oops"))).toBe("oops");
|
||||
expect(errMessage(42)).toBe("42");
|
||||
expect(errMessage(null)).toBe("null");
|
||||
});
|
||||
it("errMessage maps Error and non-Error values", () => {
|
||||
expect(errMessage(new Error("oops"))).toBe("oops");
|
||||
expect(errMessage(42)).toBe("42");
|
||||
expect(errMessage(null)).toBe("null");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user