/** * 身份匹配差分回归(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; 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([]); }); });