Files
UHD_Media_Tool/apps/server/test/sources-identity/identity-differential.test.ts
T
rootandClaude Opus 5.5 03de030371 test(identity): MoviePilot differential on 113 HHClub titles (ID-2)
MP MetaInfo + TheMovieDbModule name matching vs parse/recall/resolveIdentity,
TMDB responses replayed offline; includes the category-alias misbinding
regression from the user stack.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 17:41:22 +08:00

66 lines
2.9 KiB
TypeScript

/**
* 身份匹配差分回归(ID-2):憨憨真实标题,MP 期望(mp.json)vs 我们(parse → 召回 → resolveIdentity),
* TMDB 响应离线回放(tmdb-fixtures.json)。说明见 identity-differential.lib.ts 与
* docs/porting/moviepilot-identity-match.md。
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import {
DATA_DIR,
fixtureFetch,
loadData,
runDifferential,
runPoisoned,
summarize,
tmdbFor,
} from "./identity-differential.lib.ts";
const fixtures = JSON.parse(readFileSync(join(DATA_DIR, "tmdb-fixtures.json"), "utf8")) as Record<string, unknown>;
const tmdb = () => tmdbFor(fixtureFetch(fixtures));
describe("身份匹配差分(MP recognize ↔ resolveIdentity)", async () => {
const rows = await runDifferential(tmdb());
const byId = new Map(rows.map((r) => [r.id, r]));
const { samples, deviations } = loadData();
it("样本 ≥ 100 条,覆盖「英文 [中文]」RSS、动漫、续作、通用词、短剧", () => {
expect(samples.length).toBeGreaterThanOrEqual(100);
const kinds = new Set(samples.map((s) => s.kind));
for (const k of ["rss_bracket", "title_subtitle", "short_drama"]) expect(kinds.has(k), k).toBe(true);
});
it("非故意偏离项一致率 ≥ 95%", () => {
expect(summarize(rows).rate).toBeGreaterThanOrEqual(0.95);
});
it("登记的偏离都对应存在的样本,且 mp_defect 的结果与登记一致", () => {
for (const [id, d] of Object.entries(deviations)) {
const r = byId.get(id);
expect(r, id).toBeDefined();
if (d.kind === "mp_defect") expect(r!.ours, id).toBe(d.expect ?? null);
}
});
it("未登记的样本全部与 MP 一致(新增不一致需要归因后登记)", () => {
const unexplained = rows.filter((r) => r.verdict !== "consistent" && !r.deviation).map((r) => `${r.id} ${r.title}`);
expect(unexplained).toEqual([]);
});
it("brief 三类:方括号中文名(武神主宰)、续作章节(物怪第三章)、通用词 + 副标题中文名(档案)", () => {
for (const r of rows.filter((x) => x.title.includes("Martial Master"))) expect(r.ours, r.id).toBe("tv:110181");
for (const r of rows.filter((x) => x.title.startsWith("Mononoke The Movie Chapter III")))
expect(r.ours, r.id).toBe("movie:1322562");
for (const r of rows.filter((x) => x.title.startsWith("Archives S01 2009"))) expect(r.ours, r.id).toBe("tv:201401");
});
});
describe("真机误识别回归:分类词别名污染的本地作品", async () => {
const results = await runPoisoned(tmdb());
it("被误并入《东北有个周东北》的资源重新识别到各自的作品,只有英文名时也不并入", () => {
expect(results.length).toBeGreaterThanOrEqual(15);
const bad = results.filter((r) => !r.ok).map((r) => `${r.c.title} → ${r.status} ${r.best}`);
expect(bad).toEqual([]);
});
});