shoc-frontend-new/src/test/api/api-error.test.ts
2026-09-22 23:41:17 -03:00

80 lines
2.7 KiB
TypeScript

import ky from "ky";
import { describe, expect, it, vi } from "vitest";
import { normalizeApiRequestError, REQUEST_UPLIFTS_ROLE_DENIAL_MESSAGE } from "@/api/api-error";
const RESOURCE_URL = "https://example.test/api/resource";
function createClient(fetchImpl: typeof fetch) {
return ky.create({
fetch: fetchImpl,
retry: 0,
timeout: 50,
hooks: { beforeError: [({ error }) => normalizeApiRequestError(error)] },
});
}
describe("normalizeApiRequestError", () => {
it("surfaces Ky's pre-parsed JSON body for HTTP errors via the hook", async () => {
const fetchImpl = vi.fn(() =>
Promise.resolve(
new Response(JSON.stringify({ message: "Validation failed" }), {
status: 422,
headers: { "Content-Type": "application/json" },
}),
),
) as unknown as typeof fetch;
await expect(createClient(fetchImpl).get(RESOURCE_URL).json()).rejects.toThrow(
"Validation failed",
);
});
it("preserves only the allowlisted public denial for 403 responses", async () => {
const allowedFetch = vi.fn(() =>
Promise.resolve(
new Response(JSON.stringify({ message: REQUEST_UPLIFTS_ROLE_DENIAL_MESSAGE }), {
status: 403,
headers: { "Content-Type": "application/json" },
}),
),
) as unknown as typeof fetch;
const unrelatedFetch = vi.fn(() =>
Promise.resolve(
new Response(JSON.stringify({ message: "accountId 42 is forbidden" }), {
status: 403,
headers: { "Content-Type": "application/json" },
}),
),
) as unknown as typeof fetch;
await expect(createClient(allowedFetch).get(RESOURCE_URL).json()).rejects.toThrow(
REQUEST_UPLIFTS_ROLE_DENIAL_MESSAGE,
);
await expect(createClient(unrelatedFetch).get(RESOURCE_URL).json()).rejects.toThrow(
"You do not have permission to perform this action.",
);
});
it("reports a connection failure for network errors", async () => {
const fetchImpl = vi.fn(() =>
Promise.reject(new TypeError("fetch failed")),
) as unknown as typeof fetch;
await expect(createClient(fetchImpl).get(RESOURCE_URL).json()).rejects.toThrow(
"No response from server. Please check your connection.",
);
});
it("returns a friendly timeout message", async () => {
const fetchImpl = vi.fn((_input: RequestInfo | URL, init?: RequestInit) => {
const signal = init?.signal;
return new Promise<Response>((_resolve, reject) => {
signal?.addEventListener("abort", () => reject(signal?.reason));
});
}) as unknown as typeof fetch;
await expect(createClient(fetchImpl).get(RESOURCE_URL).json()).rejects.toThrow(
"The request timed out. Please try again.",
);
});
});