feat: add extensive test suite
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { describe, expect, mock, test } from "bun:test";
|
||||
import { createLiveHandler } from "./handler";
|
||||
|
||||
const liveRequest = (query = "") =>
|
||||
new Request(`https://example.com/api/live${query}`);
|
||||
const youtubeResult = (
|
||||
thumbnails: Record<string, unknown> = {
|
||||
high: { url: "https://img/high.jpg", width: 480, height: 360 },
|
||||
},
|
||||
) => ({
|
||||
items: [
|
||||
{
|
||||
id: { videoId: "video-1" },
|
||||
snippet: { title: "Live title", thumbnails },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
describe("live handler", () => {
|
||||
test("returns none without calling YouTube when configuration is missing", async () => {
|
||||
const fetch = mock(() => Promise.resolve(Response.json({})));
|
||||
const handler = createLiveHandler({
|
||||
fetch,
|
||||
publish: mock(() => {}),
|
||||
getConfig: () => ({ apiKey: "", channelId: "channel" }),
|
||||
});
|
||||
expect(await (await handler(liveRequest())).json()).toBe("none");
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("builds an encoded YouTube request and publishes a live result", async () => {
|
||||
const fetch = mock((_input: RequestInfo | URL, _init?: RequestInit) =>
|
||||
Promise.resolve(Response.json(youtubeResult())),
|
||||
);
|
||||
const publish = mock(() => {});
|
||||
const handler = createLiveHandler({
|
||||
fetch,
|
||||
publish,
|
||||
getConfig: () => ({ apiKey: "key +", channelId: "channel/id" }),
|
||||
});
|
||||
const response = await handler(liveRequest());
|
||||
const result = await response.json();
|
||||
|
||||
expect(fetch.mock.calls[0]?.[0]).toContain("channelId=channel%2Fid");
|
||||
expect(fetch.mock.calls[0]?.[0]).toContain("key=key%20%2B");
|
||||
expect(fetch.mock.calls[0]?.[1]).toEqual({ cache: "no-store" });
|
||||
expect(result).toEqual({
|
||||
url: "https://www.youtube.com/watch?v=video-1",
|
||||
thumbnails: {
|
||||
url: "https://img/high.jpg",
|
||||
width: 480,
|
||||
height: 360,
|
||||
},
|
||||
title: "Live title",
|
||||
});
|
||||
expect(publish).toHaveBeenCalledWith(result);
|
||||
});
|
||||
|
||||
test("returns none and logs an upstream error", async () => {
|
||||
const logError = mock(() => {});
|
||||
const handler = createLiveHandler({
|
||||
fetch: mock(() =>
|
||||
Promise.resolve(
|
||||
new Response(null, { status: 403, statusText: "Nope" }),
|
||||
),
|
||||
),
|
||||
publish: mock(() => {}),
|
||||
getConfig: () => ({ apiKey: "key", channelId: "channel" }),
|
||||
logError,
|
||||
});
|
||||
expect(await (await handler(liveRequest())).json()).toBe("none");
|
||||
expect(logError).toHaveBeenCalledWith("YouTube API error: Nope");
|
||||
});
|
||||
|
||||
test("returns none for an empty search", async () => {
|
||||
const publish = mock(() => {});
|
||||
const handler = createLiveHandler({
|
||||
fetch: mock(() => Promise.resolve(Response.json({ items: [] }))),
|
||||
publish,
|
||||
getConfig: () => ({ apiKey: "key", channelId: "channel" }),
|
||||
});
|
||||
expect(await (await handler(liveRequest())).json()).toBe("none");
|
||||
expect(publish).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("returns none when YouTube supplies no thumbnail", async () => {
|
||||
const handler = createLiveHandler({
|
||||
fetch: mock(() => Promise.resolve(Response.json(youtubeResult({})))),
|
||||
publish: mock(() => {}),
|
||||
getConfig: () => ({ apiKey: "key", channelId: "channel" }),
|
||||
});
|
||||
expect(await (await handler(liveRequest())).json()).toBe("none");
|
||||
});
|
||||
|
||||
test("last returns a clone of the last successful local response", async () => {
|
||||
let configured = true;
|
||||
const fetch = mock(() => Promise.resolve(Response.json(youtubeResult())));
|
||||
const handler = createLiveHandler({
|
||||
fetch,
|
||||
publish: mock(() => {}),
|
||||
getConfig: () =>
|
||||
configured
|
||||
? { apiKey: "key", channelId: "channel" }
|
||||
: { apiKey: undefined, channelId: undefined },
|
||||
});
|
||||
const first = await handler(liveRequest());
|
||||
configured = false;
|
||||
const last = await handler(liveRequest("?last=true"));
|
||||
|
||||
expect(last).not.toBe(first);
|
||||
expect(await last.json()).toEqual(await first.json());
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("last performs a normal lookup before any successful response", async () => {
|
||||
const fetch = mock(() => Promise.resolve(Response.json({ items: [] })));
|
||||
const handler = createLiveHandler({
|
||||
fetch,
|
||||
publish: mock(() => {}),
|
||||
getConfig: () => ({ apiKey: "key", channelId: "channel" }),
|
||||
});
|
||||
expect(await (await handler(liveRequest("?last"))).json()).toBe("none");
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("an unsuccessful refresh does not replace the last response", async () => {
|
||||
const fetch = mock()
|
||||
.mockResolvedValueOnce(Response.json(youtubeResult()))
|
||||
.mockResolvedValueOnce(Response.json({ items: [] }));
|
||||
const handler = createLiveHandler({
|
||||
fetch,
|
||||
publish: mock(() => {}),
|
||||
getConfig: () => ({ apiKey: "key", channelId: "channel" }),
|
||||
});
|
||||
await handler(liveRequest());
|
||||
expect(await (await handler(liveRequest())).json()).toBe("none");
|
||||
expect(await (await handler(liveRequest("?last"))).json()).toMatchObject({
|
||||
title: "Live title",
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user