98 lines
2.9 KiB
TypeScript
98 lines
2.9 KiB
TypeScript
import test, { afterEach } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { AdminApiError, fetchAdminJson, getAdminAccessState, getAdminErrorMessage } from "./api";
|
|
|
|
type MockFetch = typeof fetch;
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
function installFetchMock(mock: MockFetch) {
|
|
Object.defineProperty(globalThis, "fetch", {
|
|
configurable: true,
|
|
value: mock,
|
|
});
|
|
}
|
|
|
|
afterEach(() => {
|
|
installFetchMock(originalFetch);
|
|
});
|
|
|
|
test("fetchAdminJson includes cookie credentials", async () => {
|
|
let seenCredentials: RequestCredentials | undefined;
|
|
|
|
installFetchMock(async (_input, init) => {
|
|
seenCredentials = init?.credentials;
|
|
return new Response(JSON.stringify({ ok: true }), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
});
|
|
});
|
|
|
|
const response = await fetchAdminJson<{ ok: boolean }>("admin/access");
|
|
|
|
assert.equal(seenCredentials, "include");
|
|
assert.deepEqual(response, { ok: true });
|
|
});
|
|
|
|
test("fetchAdminJson classifies 401 as unauthorized", async () => {
|
|
installFetchMock(async () => new Response(JSON.stringify({ detail: "Not authenticated" }), {
|
|
status: 401,
|
|
headers: { "Content-Type": "application/json" },
|
|
}));
|
|
|
|
await assert.rejects(
|
|
() => fetchAdminJson("admin/access"),
|
|
(error: unknown) =>
|
|
error instanceof AdminApiError &&
|
|
error.kind === "unauthorized" &&
|
|
getAdminAccessState(error) === "unauthenticated" &&
|
|
getAdminErrorMessage(error) === "Session expired. Log in again.",
|
|
);
|
|
});
|
|
|
|
test("fetchAdminJson classifies 403 as forbidden", async () => {
|
|
installFetchMock(async () => new Response(JSON.stringify({ detail: "Admin access required" }), {
|
|
status: 403,
|
|
headers: { "Content-Type": "application/json" },
|
|
}));
|
|
|
|
await assert.rejects(
|
|
() => fetchAdminJson("admin/overview"),
|
|
(error: unknown) =>
|
|
error instanceof AdminApiError &&
|
|
error.kind === "forbidden" &&
|
|
getAdminAccessState(error) === "forbidden" &&
|
|
getAdminErrorMessage(error) === "Admin access required.",
|
|
);
|
|
});
|
|
|
|
test("fetchAdminJson classifies 5xx html as unavailable", async () => {
|
|
installFetchMock(async () => new Response("<html><body>bad gateway</body></html>", {
|
|
status: 502,
|
|
headers: { "Content-Type": "text/html" },
|
|
}));
|
|
|
|
await assert.rejects(
|
|
() => fetchAdminJson("admin/overview"),
|
|
(error: unknown) =>
|
|
error instanceof AdminApiError &&
|
|
error.kind === "unavailable" &&
|
|
getAdminErrorMessage(error) === "Admin service unavailable.",
|
|
);
|
|
});
|
|
|
|
test("fetchAdminJson classifies network errors distinctly", async () => {
|
|
installFetchMock(async () => {
|
|
throw new TypeError("Failed to fetch");
|
|
});
|
|
|
|
await assert.rejects(
|
|
() => fetchAdminJson("admin/overview"),
|
|
(error: unknown) =>
|
|
error instanceof AdminApiError &&
|
|
error.kind === "network" &&
|
|
getAdminAccessState(error) === "unavailable" &&
|
|
getAdminErrorMessage(error) === "Cannot reach server.",
|
|
);
|
|
});
|