108 lines
3.5 KiB
TypeScript
108 lines
3.5 KiB
TypeScript
import { describe, expect, mock, test } from "bun:test";
|
|
import { checkSlip } from "./payment";
|
|
|
|
describe("checkSlip", () => {
|
|
test("posts the image, amount, and authorization to the configured API", async () => {
|
|
const payload = { success: true, data: { amount: 125.5 } };
|
|
const fetcher = mock(async () => Response.json(payload));
|
|
|
|
const result = await checkSlip(Buffer.from([0, 1, 2]), "image/png", 125.5, {
|
|
fetch: fetcher as unknown as typeof fetch,
|
|
apiUrl: "https://slip.test/check",
|
|
apiKey: "secret",
|
|
});
|
|
|
|
expect(result as unknown).toEqual(payload);
|
|
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
const [url, init] = fetcher.mock.calls[0] as unknown as [
|
|
string,
|
|
RequestInit,
|
|
];
|
|
expect(url).toBe("https://slip.test/check");
|
|
expect(init.method).toBe("POST");
|
|
expect(init.headers).toEqual({ "x-authorization": "secret" });
|
|
expect(init.body).toBeInstanceOf(FormData);
|
|
|
|
const body = init.body as FormData;
|
|
expect(body.get("amount")).toBe("125.5");
|
|
const image = body.get("files");
|
|
expect(image).toBeInstanceOf(File);
|
|
expect(image).toMatchObject({ name: "image", type: "image/png", size: 3 });
|
|
expect([...new Uint8Array(await (image as File).arrayBuffer())]).toEqual([
|
|
0, 1, 2,
|
|
]);
|
|
});
|
|
|
|
test.each([
|
|
[0, "0"],
|
|
[-10, "-10"],
|
|
[Number.NaN, "NaN"],
|
|
])("serializes amount %p as %s", async (amount, expected) => {
|
|
const fetcher = mock(async (_url: unknown, init?: RequestInit) => {
|
|
expect((init?.body as FormData).get("amount")).toBe(expected);
|
|
return Response.json({});
|
|
});
|
|
await checkSlip(Buffer.from([]), "image/jpeg", amount, {
|
|
fetch: fetcher as unknown as typeof fetch,
|
|
apiUrl: "https://slip.test",
|
|
apiKey: "key",
|
|
});
|
|
});
|
|
|
|
test("returns JSON even when the HTTP response is unsuccessful", async () => {
|
|
const payload = {
|
|
success: false,
|
|
code: 1001,
|
|
message: "invalid slip",
|
|
} as const;
|
|
const fetcher = mock(async () => Response.json(payload, { status: 422 }));
|
|
expect(
|
|
await checkSlip(Buffer.from([]), "image/jpeg", 1, {
|
|
fetch: fetcher as unknown as typeof fetch,
|
|
apiUrl: "https://slip.test",
|
|
}),
|
|
).toEqual(payload);
|
|
});
|
|
|
|
test("propagates fetch failures", async () => {
|
|
const failure = new Error("offline");
|
|
const fetcher = mock(async () => {
|
|
throw failure;
|
|
});
|
|
expect(
|
|
checkSlip(Buffer.from([]), "image/jpeg", 1, {
|
|
fetch: fetcher as unknown as typeof fetch,
|
|
apiUrl: "https://slip.test",
|
|
}),
|
|
).rejects.toBe(failure);
|
|
});
|
|
|
|
test("propagates invalid JSON responses", async () => {
|
|
const fetcher = mock(async () => new Response("not json"));
|
|
expect(
|
|
checkSlip(Buffer.from([]), "image/jpeg", 1, {
|
|
fetch: fetcher as unknown as typeof fetch,
|
|
apiUrl: "https://slip.test",
|
|
}),
|
|
).rejects.toBeInstanceOf(SyntaxError);
|
|
});
|
|
|
|
test("fails before fetching when no API URL is configured", async () => {
|
|
const original = process.env.SLIPOK_API_URL;
|
|
const fetcher = mock(async () => Response.json({}));
|
|
delete process.env.SLIPOK_API_URL;
|
|
|
|
try {
|
|
expect(
|
|
checkSlip(Buffer.from([]), "image/jpeg", 1, {
|
|
fetch: fetcher as unknown as typeof fetch,
|
|
}),
|
|
).rejects.toThrow("SLIPOK_API_URL is not configured");
|
|
expect(fetcher).not.toHaveBeenCalled();
|
|
} finally {
|
|
if (original === undefined) delete process.env.SLIPOK_API_URL;
|
|
else process.env.SLIPOK_API_URL = original;
|
|
}
|
|
});
|
|
});
|