Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
78 lines
3.2 KiB
TypeScript
78 lines
3.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
Api,
|
|
EpisodeMappingPreview,
|
|
EpisodeMappingPreviewInput,
|
|
EpisodeMappingRuleCreate,
|
|
EpisodeMappingRuleUpdate,
|
|
EpisodeResolutionView,
|
|
listEndpoints,
|
|
} from "../src/index.ts";
|
|
|
|
// 集数映射接口(批次 4)的契约:端点登记、写入与预览的入参、解析结果结构。
|
|
|
|
describe("集数映射契约", () => {
|
|
it("端点登记在 Api 注册表:读用 read,写用 organize.execute", () => {
|
|
const keys = listEndpoints()
|
|
.filter((e) => e.path.includes("episode-mapping"))
|
|
.map((e) => `${e.method.toUpperCase()} ${e.path} ${e.scope}`)
|
|
.sort();
|
|
expect(keys).toEqual([
|
|
"DELETE /episode-mapping-rules/{id} organize.execute",
|
|
"GET /media-entities/{id}/episode-mapping-rules read",
|
|
"PATCH /episode-mapping-rules/{id} organize.execute",
|
|
"POST /media-entities/{id}/episode-mapping-rules organize.execute",
|
|
"POST /media-entities/{id}/episode-mapping/preview read",
|
|
]);
|
|
expect(Api.episodeMapping.preview.response).toBe(EpisodeMappingPreview);
|
|
});
|
|
|
|
it("新建:三种规则都接受;季号不能为负,发布组去首尾空白", () => {
|
|
expect(EpisodeMappingRuleCreate.parse({ kind: "expression", expression: "EP-12", targetSeason: 2 })).toEqual({
|
|
kind: "expression",
|
|
expression: "EP-12",
|
|
targetSeason: 2,
|
|
});
|
|
expect(
|
|
EpisodeMappingRuleCreate.safeParse({
|
|
season: 1,
|
|
releaseGroup: "ANi",
|
|
kind: "table",
|
|
tableEntries: [{ from: { start: 27, end: 28 }, to: { season: 2, episode: 2 } }],
|
|
}).success,
|
|
).toBe(true);
|
|
expect(
|
|
EpisodeMappingRuleCreate.parse({ kind: "special", special: "ignore", releaseGroup: " ANi " }).releaseGroup,
|
|
).toBe("ANi");
|
|
expect(EpisodeMappingRuleCreate.safeParse({ kind: "special", special: "skip" }).success).toBe(false);
|
|
expect(EpisodeMappingRuleCreate.safeParse({ kind: "expression", season: -1 }).success).toBe(false);
|
|
expect(EpisodeMappingRuleCreate.safeParse({ kind: "expression", expression: "E".repeat(65) }).success).toBe(false);
|
|
});
|
|
|
|
it("修改:全部可选,可只改状态", () => {
|
|
expect(EpisodeMappingRuleUpdate.parse({ status: "disabled" })).toEqual({ status: "disabled" });
|
|
expect(EpisodeMappingRuleUpdate.safeParse({ status: "deleted" }).success).toBe(false);
|
|
});
|
|
|
|
it("预览入参:草稿可空,条数上限 300", () => {
|
|
expect(EpisodeMappingPreviewInput.parse({})).toEqual({});
|
|
expect(EpisodeMappingPreviewInput.safeParse({ season: 2, releaseGroup: "ANi", draft: null }).success).toBe(true);
|
|
expect(EpisodeMappingPreviewInput.safeParse({ limit: 301 }).success).toBe(false);
|
|
});
|
|
|
|
it("解析结果:与 source_release.episode_resolution 同结构", () => {
|
|
const v = {
|
|
method: "absolute_cross_season",
|
|
source: { season: 1, episodes: { start: 25, end: 25 } },
|
|
canonical: { season: 2, episodes: { start: 13, end: 13 } },
|
|
evidence: ["conversion:cross_season"],
|
|
};
|
|
expect(EpisodeResolutionView.parse(v)).toEqual(v);
|
|
expect(EpisodeResolutionView.safeParse({ ...v, method: "guess" }).success).toBe(false);
|
|
expect(
|
|
EpisodeResolutionView.safeParse({ method: "rule", source: {}, canonical: null, special: "ignore", evidence: [] })
|
|
.success,
|
|
).toBe(true);
|
|
});
|
|
});
|