Commit current project
This commit is contained in:
122
test/lib/twitch.followed.test.ts
Normal file
122
test/lib/twitch.followed.test.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { getFollowedChannels } from "../../src/lib/twitch";
|
||||
|
||||
describe("getFollowedChannels pagination + enrichment", () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("returns only online channels with profile images and viewer counts", async () => {
|
||||
// We'll simulate multiple fetch responses depending on the URL
|
||||
const fetchMock: any = vi.fn().mockImplementation(async (input: any) => {
|
||||
const url =
|
||||
typeof input === "string" ? input : input.url?.toString() || "";
|
||||
|
||||
if (url.includes("/channels/followed")) {
|
||||
// Serve two pages: first with cursor, second without
|
||||
if (!fetchMock.page1Served) {
|
||||
fetchMock.page1Served = true;
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: [
|
||||
{
|
||||
broadcaster_id: "10",
|
||||
broadcaster_login: "ch10",
|
||||
broadcaster_name: "Ch10",
|
||||
game_name: "Game1",
|
||||
thumbnail_url: "thumb1",
|
||||
},
|
||||
{
|
||||
broadcaster_id: "20",
|
||||
broadcaster_login: "ch20",
|
||||
broadcaster_name: "Ch20",
|
||||
game_name: "Game2",
|
||||
thumbnail_url: "thumb2",
|
||||
},
|
||||
],
|
||||
pagination: { cursor: "CURSOR1" },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// second page
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: [
|
||||
{
|
||||
broadcaster_id: "30",
|
||||
broadcaster_login: "ch30",
|
||||
broadcaster_name: "Ch30",
|
||||
game_name: "Game3",
|
||||
thumbnail_url: "thumb3",
|
||||
},
|
||||
],
|
||||
pagination: {},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (url.includes("/users?")) {
|
||||
// User profile enrichment
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: [
|
||||
{ id: "10", profile_image_url: "p10" },
|
||||
{ id: "20", profile_image_url: "p20" },
|
||||
{ id: "30", profile_image_url: "p30" },
|
||||
],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (url.includes("/streams?")) {
|
||||
// Streams: only user 20 is live
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: [
|
||||
{
|
||||
user_id: "20",
|
||||
viewer_count: 50,
|
||||
title: "Live Now",
|
||||
started_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// default
|
||||
return { ok: false, status: 404 };
|
||||
});
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock as any);
|
||||
|
||||
const result = await getFollowedChannels("token", "client", "me");
|
||||
|
||||
// Only channel with id '20' should be online
|
||||
expect(result).toHaveLength(1);
|
||||
const ch = result[0];
|
||||
expect(ch.id).toBe("20");
|
||||
expect(ch.profileImageUrl).toBe("p20");
|
||||
expect(ch.viewerCount).toBe(50);
|
||||
});
|
||||
|
||||
it("throws when channels endpoint returns 401 Unauthorized", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({ ok: false, status: 401 }),
|
||||
);
|
||||
|
||||
await expect(getFollowedChannels("token", "client", "me")).rejects.toThrow(
|
||||
"Twitch API error: 401 - Unauthorized. Token may be expired or invalid.",
|
||||
);
|
||||
});
|
||||
});
|
||||
72
test/lib/twitch.test.ts
Normal file
72
test/lib/twitch.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { getStreamStatuses, getRecentMessages } from "../../src/lib/twitch";
|
||||
|
||||
describe("twitch lib mocked HTTP tests", () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("getStreamStatuses returns correct live/offline mapping", async () => {
|
||||
// Stub fetch to return one live stream for user '1'
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
data: [
|
||||
{
|
||||
user_id: "1",
|
||||
title: "Live Stream",
|
||||
viewer_count: 123,
|
||||
started_at: "2024-01-01T00:00:00Z",
|
||||
},
|
||||
],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await getStreamStatuses("token", "client", ["1", "2"]);
|
||||
|
||||
expect(result["1"]).toBeDefined();
|
||||
expect(result["1"].isLive).toBe(true);
|
||||
expect(result["1"].title).toBe("Live Stream");
|
||||
expect(result["1"].viewerCount).toBe(123);
|
||||
|
||||
expect(result["2"]).toBeDefined();
|
||||
expect(result["2"].isLive).toBe(false);
|
||||
});
|
||||
|
||||
it("getRecentMessages parses raw IRC messages", async () => {
|
||||
const rawMsg =
|
||||
"@badge-info=;badges=moderator/1;color=#1E90FF;display-name=Mod;emotes=;tmi-sent-ts=1620000000000;user-id=456 :mod!mod@mod.tmi.twitch.tv PRIVMSG #channel :Hello there";
|
||||
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ messages: [rawMsg] }),
|
||||
}),
|
||||
);
|
||||
|
||||
const messages = await getRecentMessages("channel");
|
||||
expect(messages).toHaveLength(1);
|
||||
const m = messages[0];
|
||||
expect(m.author).toBe("mod");
|
||||
expect(m.content).toBe("Hello there");
|
||||
expect(m.userId).toBe("456");
|
||||
expect(m.isPreloaded).toBe(true);
|
||||
});
|
||||
|
||||
it("getRecentMessages returns empty array on non-ok response", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({ ok: false, status: 500 }),
|
||||
);
|
||||
const messages = await getRecentMessages("channel");
|
||||
expect(messages).toEqual([]);
|
||||
});
|
||||
});
|
||||
70
test/lib/youtube.test.ts
Normal file
70
test/lib/youtube.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { getYoutubeUser } from "../../src/lib/youtube";
|
||||
|
||||
describe("getYoutubeUser", () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("returns mapped user on success", async () => {
|
||||
const mockResponse = {
|
||||
items: [
|
||||
{
|
||||
id: "chan1",
|
||||
snippet: {
|
||||
title: "Channel Title",
|
||||
thumbnails: { default: { url: "http://example.com/avatar.png" } },
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => mockResponse,
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await getYoutubeUser("token-abc");
|
||||
|
||||
expect(result).toEqual({
|
||||
userId: "chan1",
|
||||
displayName: "Channel Title",
|
||||
profileImageUrl: "http://example.com/avatar.png",
|
||||
});
|
||||
});
|
||||
|
||||
it("throws when response not ok", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: false,
|
||||
statusText: "Unauthorized",
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(getYoutubeUser("bad-token")).rejects.toThrow(
|
||||
"Failed to get YouTube user: Unauthorized",
|
||||
);
|
||||
});
|
||||
|
||||
it("throws when no items returned", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ items: [] }),
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(getYoutubeUser("token-xyz")).rejects.toThrow(
|
||||
"No YouTube channel found",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user