Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
116 lines
4.0 KiB
TypeScript
116 lines
4.0 KiB
TypeScript
import { createMockTmdb, type MockTmdb, SPLIT_COUR_FIXTURE, TMDB_FIXTURES } from "@umt/testkit";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { createTmdbClient, fetchEpisodeGroup, fetchEpisodeGroups, groupSeasonAsRichSeason } from "./index.ts";
|
|
|
|
let mock: MockTmdb | null = null;
|
|
afterEach(async () => {
|
|
await mock?.close();
|
|
mock = null;
|
|
});
|
|
|
|
async function setup() {
|
|
mock = await createMockTmdb({ fixtures: [...TMDB_FIXTURES, SPLIT_COUR_FIXTURE] });
|
|
const tmdb = createTmdbClient({ baseUrl: `${mock.url}/3`, apiKey: "test-tmdb-key", retries: 0, timeoutMs: 1000 });
|
|
return { mock, tmdb };
|
|
}
|
|
|
|
describe("TMDB 剧集组", () => {
|
|
it("列表:id、名称、类型、组数、集数、播出平台", async () => {
|
|
const { tmdb } = await setup();
|
|
const groups = await fetchEpisodeGroups(tmdb, 990001, "zh-CN");
|
|
expect(groups).toEqual([
|
|
{
|
|
id: "grp-production",
|
|
name: "制作季",
|
|
type: 6,
|
|
description: null,
|
|
groupCount: 2,
|
|
episodeCount: 25,
|
|
network: "TOKYO MX",
|
|
},
|
|
{
|
|
id: "grp-aired",
|
|
name: "播出顺序",
|
|
type: 1,
|
|
description: null,
|
|
groupCount: 1,
|
|
episodeCount: 25,
|
|
network: null,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("详情:组序号 = 季号,组内顺序从 1 编集号,保留 TMDB 默认季集号、标题、播出日期", async () => {
|
|
const { tmdb } = await setup();
|
|
const g = await fetchEpisodeGroup(tmdb, "grp-production", "zh-CN");
|
|
expect(g.name).toBe("制作季");
|
|
expect(g.seasons.map((s) => [s.seasonNumber, s.name, s.episodes.length, s.airDate])).toEqual([
|
|
[1, "第一部", 12, "2024-01-07"],
|
|
[2, "第二部", 13, "2024-07-07"],
|
|
]);
|
|
const s2e1 = g.seasons[1]!.episodes[0]!;
|
|
expect(s2e1).toMatchObject({
|
|
seasonNumber: 2,
|
|
episodeNumber: 1,
|
|
tmdbSeason: 1,
|
|
tmdbEpisode: 13,
|
|
name: "下篇第1话",
|
|
nameLanguage: "zh-CN",
|
|
airDate: "2024-07-07",
|
|
});
|
|
expect(g.seasons[1]!.episodes.at(-1)).toMatchObject({ episodeNumber: 13, tmdbEpisode: 25 });
|
|
});
|
|
|
|
it("组内按 order 排序;重复的组序号顺延;占位名按无标题处理", async () => {
|
|
const calls: string[] = [];
|
|
const client = {
|
|
getJson: async <T>(path: string) => {
|
|
calls.push(path);
|
|
return {
|
|
id: "x",
|
|
name: "X",
|
|
type: 3,
|
|
groups: [
|
|
{
|
|
id: "b",
|
|
name: "B",
|
|
order: 1,
|
|
episodes: [{ order: 0, season_number: 2, episode_number: 1, name: "Opening", air_date: "2020-01-01" }],
|
|
},
|
|
{
|
|
id: "a",
|
|
name: "A",
|
|
order: 1,
|
|
episodes: [
|
|
{ order: 1, season_number: 1, episode_number: 5, name: "第 5 集", air_date: "2019-02-01" },
|
|
{ order: 0, season_number: 1, episode_number: 9, name: "Nine", air_date: "2019-01-01" },
|
|
],
|
|
},
|
|
],
|
|
} as T;
|
|
},
|
|
};
|
|
const g = await fetchEpisodeGroup(client, "x", "zh-CN");
|
|
expect(calls).toEqual(["/tv/episode_group/x"]);
|
|
expect(g.seasons.map((s) => [s.seasonNumber, s.name])).toEqual([
|
|
[1, "B"],
|
|
[2, "A"],
|
|
]);
|
|
expect(g.seasons[1]!.episodes.map((e) => [e.episodeNumber, e.tmdbSeason, e.tmdbEpisode, e.name])).toEqual([
|
|
[1, 1, 9, "Nine"],
|
|
[2, 1, 5, null],
|
|
]);
|
|
const rich = groupSeasonAsRichSeason(42, g.seasons[1]!, "zh-CN", "/p.jpg");
|
|
expect(rich).toMatchObject({ tvId: "42", seasonNumber: 2, name: "A", posterPath: "/p.jpg", airDate: "2019-01-01" });
|
|
});
|
|
|
|
it("剧集组不存在抛 notfound;请求走缓存", async () => {
|
|
const { tmdb, mock } = await setup();
|
|
await expect(fetchEpisodeGroup(tmdb, "nope")).rejects.toMatchObject({ kind: "notfound" });
|
|
await fetchEpisodeGroups(tmdb, 990001, "zh-CN");
|
|
const before = mock.calls.length;
|
|
await fetchEpisodeGroups(tmdb, 990001, "zh-CN");
|
|
expect(mock.calls.length).toBe(before);
|
|
});
|
|
});
|