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]>
This commit is contained in:
1 parent
387f7931e0
commit
03de030371
11 files changed
+12280
No files matched your search
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 身份匹配差分回归(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([]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user