- 订阅已建 / 已改 / 已完成 / 已取消,下载完成 / 失败 / 移除,整理结束(统一 organization.changed 终态), 刮削完成 / 失败,作品进入关注,元数据已刷新(任务调用方),季集结构切换,站点不可达 / 恢复: 都在调用方事务内写 outbox,带幂等键 - 钩子挂点:name.recognize(resolveRelease / identifyParsed)、organize.rename 与 organize.intercept(planImport)、 resource.selection(订阅重评)、subscription.completion.check(completeSubscription) - 测试:各节点事件 payload 与去重、整理改名 / 拦截 / 出错不影响整理、名称识别改写、资源选择、订阅完成拦截 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
46 lines
2.1 KiB
TypeScript
46 lines
2.1 KiB
TypeScript
import type { SourceReleaseRow } from "@umt/db";
|
|
import { describe, expect, it } from "vitest";
|
|
import { HookRegistry } from "../../src/hooks/index.ts";
|
|
import { selectResources } from "../../src/modules/acquisition-organize/services/acquisition.ts";
|
|
|
|
// 链式钩子 resource.selection(ADR-006,对照 MP ChainEventType.ResourceSelection):订阅重评时调整候选顺序 / 过滤,
|
|
// 只能重排与删减,不能加入新候选。
|
|
|
|
const rel = (id: string, title: string) => ({ id, title, parsed: null }) as unknown as SourceReleaseRow;
|
|
const rels = [
|
|
rel("r1", "Show.S01E01.1080p.WEB-DL"),
|
|
rel("r2", "Show.S01E01.2160p.WEB-DL"),
|
|
rel("r3", "Show.S01E01.720p"),
|
|
];
|
|
const context = { mediaEntityId: "e", subscriptionId: "s", slotId: null };
|
|
const ctxOf = (hooks?: HookRegistry) => ({ d: { ctx: { hooks } } }) as never;
|
|
|
|
describe("resource.selection 钩子", () => {
|
|
it("没有钩子时原样返回同一个数组", async () => {
|
|
expect(await selectResources(ctxOf(), rels, context)).toBe(rels);
|
|
expect(await selectResources(ctxOf(new HookRegistry()), rels, context)).toBe(rels);
|
|
});
|
|
|
|
it("重排与过滤生效;不认识的 ID 忽略、重复的只取一次", async () => {
|
|
const hooks = new HookRegistry();
|
|
const seen: unknown[] = [];
|
|
hooks.forModule("test").rewrite("resource.selection", (cands, c) => {
|
|
seen.push({ ids: cands.map((x) => x.sourceReleaseId), c });
|
|
const by = new Map(cands.map((x) => [x.sourceReleaseId, x]));
|
|
// 2160p 优先、去掉 720p,夹带一个不存在的候选和一个重复
|
|
return [by.get("r2")!, { ...by.get("r1")!, sourceReleaseId: "intruder" }, by.get("r1")!, by.get("r2")!];
|
|
});
|
|
const out = await selectResources(ctxOf(hooks), rels, context);
|
|
expect(out.map((r) => r.id)).toEqual(["r2", "r1"]);
|
|
expect(seen).toEqual([{ ids: ["r1", "r2", "r3"], c: context }]);
|
|
});
|
|
|
|
it("钩子出错时沿用原顺序", async () => {
|
|
const hooks = new HookRegistry();
|
|
hooks.forModule("test").rewrite("resource.selection", () => {
|
|
throw new Error("坏了");
|
|
});
|
|
expect((await selectResources(ctxOf(hooks), rels, context)).map((r) => r.id)).toEqual(["r1", "r2", "r3"]);
|
|
});
|
|
});
|