Files
UHD_Media_Tool/packages/domain/test/collection-policy.test.ts
T

357 lines
16 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
evaluateCandidate,
type ParsedRelease,
parseRelease,
parseRuleString,
type QualityFacts,
type RuleGroupSpec,
rankForSlot,
type SlotSpec,
seasonReleaseKey,
selectAcquisitionPolicy,
selectSeasonRelease,
} from "../src/index.ts";
const release = (title: string, facts: Partial<ParsedRelease> = {}): ParsedRelease => ({
...parseRelease(title),
...facts,
});
const group = (name: string, rule: string): RuleGroupSpec => ({ name, layers: parseRuleString(rule).layers });
const current: QualityFacts = release("Show.S01E01.1080p.WEB-DL.H265-OTHER");
const target = release("Show.S01E02.1080p.WEB-DL.H265-CHD");
const consistent: SlotSpec = {
name: "正片",
hard: { resolutions: ["1080p", "2160p"] },
rules: { groups: [group("允许收录", "1080P | 4K")] },
upgrade: { enabled: true, consistency: "season_release" },
};
describe("按分类选择默认收录策略", () => {
const policies = [
{ id: "global", scope: {} },
{ id: "series", scope: { mediaKinds: ["series" as const] } },
{ id: "animation", scope: { categories: { tv: ["动漫剧集"] } } },
];
it("订阅显式指定的策略覆盖类型和分类范围", () => {
expect(
selectAcquisitionPolicy(policies, { explicitId: "series", mediaKind: "movie", category: "动画电影" })?.id,
).toBe("series");
});
it("显式指定的策略不存在时不悄悄回退", () => {
expect(
selectAcquisitionPolicy(policies, { explicitId: "missing", mediaKind: "series", category: "动漫剧集" }),
).toBeUndefined();
});
it("分类匹配优先于类型默认,类型默认优先于全局默认", () => {
expect(selectAcquisitionPolicy(policies, { mediaKind: "series", category: "动漫剧集" })?.id).toBe("animation");
expect(selectAcquisitionPolicy(policies, { mediaKind: "series", category: "华语剧集" })?.id).toBe("series");
expect(selectAcquisitionPolicy(policies, { mediaKind: "movie", category: "动画电影" })?.id).toBe("global");
});
it.each(["movie", "series", "variety"] as const)("%s 不串用另一类型的同名分类策略", (mediaKind) => {
const scoped = [
{ id: "variety", scope: { mediaKinds: ["variety" as const], categories: { tv: ["同名分类"] } } },
{ id: "movie", scope: { categories: { movie: ["同名分类"] } } },
{ id: "series", scope: { mediaKinds: ["series" as const], categories: { tv: ["同名分类"] } } },
];
expect(selectAcquisitionPolicy(scoped, { mediaKind, category: "同名分类" })?.id).toBe(mediaKind);
});
it("同一范围按传入的最新发布顺序取第一个", () => {
const latest = { id: "latest", scope: { categories: { tv: ["动漫剧集"] } } };
const old = { id: "old", scope: { categories: { tv: ["动漫剧集"] } } };
expect(selectAcquisitionPolicy([latest, old], { mediaKind: "series", category: "动漫剧集" })).toBe(latest);
expect(selectAcquisitionPolicy([old, latest], { mediaKind: "series", category: "动漫剧集" })).toBe(old);
});
it("分类未知时跳过分类限定策略,仍可使用类型默认", () => {
expect(selectAcquisitionPolicy(policies, { mediaKind: "series", category: null })?.id).toBe("series");
});
it("类型或分类没有匹配且无默认时返回 undefined", () => {
const scoped = policies.slice(1);
expect(selectAcquisitionPolicy(scoped, { mediaKind: "movie", category: "动漫剧集" })).toBeUndefined();
expect(selectAcquisitionPolicy([policies[2]!], { mediaKind: "series", category: null })).toBeUndefined();
expect(selectAcquisitionPolicy([], { mediaKind: "variety", category: "综艺" })).toBeUndefined();
});
it("空的分类列表仍按类型默认处理", () => {
const typeDefault = { id: "type", scope: { mediaKinds: ["series" as const], categories: { movie: [], tv: [] } } };
expect(selectAcquisitionPolicy([policies[0]!, typeDefault], { mediaKind: "series", category: null })).toBe(
typeDefault,
);
});
});
describe("整季同套的规格与目标选择", () => {
it("发布组大小写、联合组顺序、HDR 顺序不产生新的版本身份", () => {
const first = {
...target,
releaseGroup: { name: "CHD@FLUX", source: "suffix" as const },
hdr: ["DV", "HDR10"] as const,
};
const second = {
...target,
releaseGroup: { name: "flux@chd", source: "known" as const },
hdr: ["HDR10", "DV"] as const,
};
expect(seasonReleaseKey({ ...first, hdr: [...first.hdr] })).toBe(
seasonReleaseKey({ ...second, hdr: [...second.hdr] }),
);
});
it.each(["resolution", "source", "videoCodec", "releaseGroup"] as const)(
"缺少 %s 时不声称属于完整成套版本",
(field) => {
expect(seasonReleaseKey({ ...target, [field]: undefined })).toBeNull();
},
);
it("来源、分辨率、编码和剪辑不同分别是不同的成套版本", () => {
for (const change of [
{ source: "BluRay" as const },
{ resolution: "2160p" as const },
{ videoCodec: "H.264" as const },
{ cut: "Extended" as const },
{ hdr: ["DV" as const] },
]) {
expect(seasonReleaseKey({ ...target, ...change })).not.toBe(seasonReleaseKey(target));
}
});
const unrestricted: SlotSpec = { name: "主版本", hard: {} };
it.each([
{
name: "来源等级优先于更高分辨率和软偏好",
spec: { ...unrestricted, soft: { groups: ["CHD"] } },
weaker: release("Show.S01E01.2160p.WEB-DL.H265-CHD"),
better: release("Show.S01E02.1080p.BluRay.H264-OTHER"),
},
{
name: "同来源时分辨率优先于软偏好",
spec: { ...unrestricted, soft: { groups: ["CHD"] } },
weaker: target,
better: release("Show.S01E02.2160p.WEB-DL.H265-OTHER"),
},
{
name: "同档规格按发布组软偏好排序",
spec: { ...unrestricted, soft: { groups: ["CHD", "OTHER"] } },
weaker: release("Show.S01E01.1080p.WEB-DL.H265-OTHER"),
better: target,
},
{
name: "同档规格按 HDR 软偏好排序",
spec: { ...unrestricted, soft: { hdr: ["DV" as const, "HDR10" as const] } },
weaker: release("Show.S01E01.1080p.WEB-DL.HDR.H265-CHD"),
better: release("Show.S01E02.1080p.WEB-DL.DV.H265-CHD"),
},
{
name: "同档规格按编码软偏好排序",
spec: { ...unrestricted, soft: { codecs: ["H.265" as const, "H.264" as const] } },
weaker: release("Show.S01E01.1080p.WEB-DL.H264-CHD"),
better: target,
},
])("$name,成套选择与候选排序一致", ({ spec, weaker, better }) => {
expect(rankForSlot(spec, [weaker, better])[0]?.candidate).toBe(better);
expect(selectSeasonRelease(spec, [weaker, better])).toBe(better);
expect(selectSeasonRelease(spec, [better, weaker])).toBe(better);
});
it("组合优先级先于来源和分辨率等级", () => {
const spec: SlotSpec = { ...unrestricted, rules: { groups: [group("来源偏好", "WEBDL > BLURAY")] } };
const blue = release("Show.S01E01.2160p.BluRay.H265-OTHER");
expect(rankForSlot(spec, [blue, target])[0]?.candidate).toBe(target);
expect(selectSeasonRelease(spec, [blue, target])).toBe(target);
});
it("规格不完整的版本不参与成套选择", () => {
const incomplete = { ...target, videoCodec: undefined, pri: 1000 };
expect(selectSeasonRelease(unrestricted, [incomplete, target])).toBe(target);
expect(selectSeasonRelease(unrestricted, [incomplete])).toBeUndefined();
});
it("同档没有偏好时目标不随数据库返回顺序反复变化", () => {
const other = release("Show.S01E02.1080p.WEB-DL.H265-OTHER");
expect(selectSeasonRelease(unrestricted, [target, other])).toBe(selectSeasonRelease(unrestricted, [other, target]));
});
});
describe("追更和洗版分别使用规则", () => {
const phased: SlotSpec = {
name: "主版本",
hard: { resolutions: ["1080p", "2160p"] },
rules: { groups: [group("追更", "WEBDL")] },
upgradeRules: { groups: [group("洗版", "BLURAY > WEBDL")] },
upgrade: { enabled: true },
};
const blue = release("Show.S01E02.1080p.BluRay.H265-CHD");
it("补齐缺集按追更规则,洗版规则不提前过滤", () => {
expect(evaluateCandidate(phased, target)).toMatchObject({ decision: "accept", pri: 100 });
expect(evaluateCandidate(phased, blue)).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ field: "rule", expected: "追更" })],
});
});
it("已有当前资产时改用洗版规则,并按洗版规则重算当前档位", () => {
expect(evaluateCandidate(phased, blue, current)).toMatchObject({ decision: "upgrade", pri: 100, fromPri: 99 });
expect(evaluateCandidate(phased, target, current)).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ code: "not_higher_priority" })],
});
});
it("洗版专用硬条件不阻止追更,但在已有资产时生效", () => {
const spec: SlotSpec = { ...phased, upgradeHard: { resolutions: ["2160p"] } };
expect(evaluateCandidate(spec, target).decision).toBe("accept");
expect(evaluateCandidate(spec, blue, current)).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ field: "resolution", code: "not_allowed" })],
});
});
it.each(["1080p", "2160p"] as const)("允许分辨率 %s 在追更和洗版阶段均可使用", (resolution) => {
const spec: SlotSpec = { name: "主版本", hard: phased.hard, upgrade: { enabled: true } };
const candidate = release(`Show.S01E02.${resolution}.BluRay.H265-CHD`);
expect(evaluateCandidate(spec, candidate).decision).toBe("accept");
expect(evaluateCandidate(spec, candidate, current).decision).toBe("upgrade");
});
it.each(["720p", "4320p"] as const)("范围外的分辨率 %s 不因更优来源或整季对齐而放行", (resolution) => {
const candidate = release(`Show.S01E02.${resolution}.BluRay.H265-CHD`);
expect(evaluateCandidate(consistent, candidate).decision).toBe("reject");
expect(evaluateCandidate(consistent, candidate, current, { seasonReleaseTarget: candidate })).toMatchObject({
decision: "reject",
violations: expect.arrayContaining([expect.objectContaining({ field: "resolution", code: "not_allowed" })]),
});
});
it("分辨率未知时保持待补充,不把未知当作允许范围", () => {
expect(evaluateCandidate(phased, { ...target, resolution: undefined })).toMatchObject({
decision: "unknown_field",
missing: ["resolution"],
});
});
it("组合条件的编码未知时,不能因 NOT 自动命中低档", () => {
const spec: SlotSpec = { name: "主版本", hard: {}, rules: { groups: [group("编码", "H265 > !H265")] } };
expect(evaluateCandidate(spec, release("Show.S01E02.1080p.WEB-DL-CHD"))).toMatchObject({
decision: "unknown_field",
missing: ["codec"],
});
});
it("高档分辨率已明确不满足时,无关的未知编码不阻止已匹配的低档", () => {
const spec: SlotSpec = {
name: "主版本",
hard: {},
rules: { groups: [group("组合档位", "4K & H265 > 1080P")] },
};
expect(evaluateCandidate(spec, release("Show.S01E02.1080p.WEB-DL-CHD"))).toMatchObject({
decision: "accept",
pri: 99,
});
});
});
describe("整季同套的同档对齐不绕过升级限制", () => {
it("同档的不同发布组可以逐集对齐到选定的整季版本", () => {
expect(evaluateCandidate(consistent, target, current, { seasonReleaseTarget: target })).toMatchObject({
decision: "upgrade",
pri: 100,
fromPri: 100,
reasons: expect.arrayContaining(["season_release_aligned"]),
});
});
it.each([
{ name: "来源", source: "BluRay" as const, resolution: "1080p" as const },
{ name: "分辨率", source: "WEB-DL" as const, resolution: "2160p" as const },
])("同档对齐不能降低当前文件的$name", ({ source, resolution }) => {
expect(
evaluateCandidate(consistent, target, { ...current, source, resolution }, { seasonReleaseTarget: target }),
).toMatchObject({ decision: "reject", violations: [expect.objectContaining({ code: "lower_tier" })] });
});
it("当前文件已经同套时不重复整理或下载", () => {
expect(evaluateCandidate(consistent, target, target, { seasonReleaseTarget: target }).decision).toBe("reject");
const normalizedCurrent = { ...target, releaseGroup: { name: "chd", source: "known" as const } };
expect(evaluateCandidate(consistent, target, normalizedCurrent, { seasonReleaseTarget: target }).decision).toBe(
"reject",
);
});
it("另一套同档资源不能替换已选定的当季目标", () => {
const other = release("Show.S01E02.1080p.WEB-DL.H265-FLUX");
expect(evaluateCandidate(consistent, other, current, { seasonReleaseTarget: target })).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ code: "different_season_release" })],
});
});
it("关闭升级时,同档对齐也不能发起替换", () => {
const spec = { ...consistent, upgrade: { ...consistent.upgrade!, enabled: false } };
expect(evaluateCandidate(spec, target, current, { seasonReleaseTarget: target })).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ code: "upgrade_disabled" })],
});
});
it("全集洗版不能因同档对齐而接受单集或播出日期资源", () => {
const spec: SlotSpec = { ...consistent, upgrade: { ...consistent.upgrade!, mode: "full_pack" } };
for (const candidate of [target, { ...target, episodes: undefined, airDate: "20261013" }]) {
expect(evaluateCandidate(spec, candidate, current, { seasonReleaseTarget: candidate })).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ code: "not_full_pack" })],
});
}
const pack = release("Show.S01.1080p.WEB-DL.H265-CHD");
expect(pack.episodes).toBeUndefined();
expect(evaluateCandidate(spec, pack, current, { seasonReleaseTarget: pack }).decision).toBe("upgrade");
});
it("不合格蓝光压制组不能因同档对齐绕过名单", () => {
const spec: SlotSpec = {
...consistent,
upgrade: { ...consistent.upgrade!, encodeGroupAllowlist: ["CHD"] },
};
const old = release("Show.S01E01.1080p.BluRay.x265-OTHER");
const unqualified = release("Show.S01E02.1080p.BluRay.x265-BADGRP");
expect(unqualified.isEncode).toBe(true);
expect(evaluateCandidate(spec, unqualified, old, { seasonReleaseTarget: unqualified })).toMatchObject({
decision: "reject",
violations: [expect.objectContaining({ code: "encode_not_qualified" })],
});
const qualified = release("Show.S01E02.1080p.BluRay.x265-chd");
expect(evaluateCandidate(spec, qualified, old, { seasonReleaseTarget: qualified }).decision).toBe("upgrade");
});
it.each([
{ field: "resolution", missing: "resolution" },
{ field: "source", missing: "source" },
{ field: "videoCodec", missing: "codec" },
{ field: "releaseGroup", missing: "group" },
] as const)("候选缺少 $field 时不执行成套替换", ({ field, missing }) => {
const candidate = { ...target, [field]: undefined };
expect(evaluateCandidate(consistent, candidate, current, { seasonReleaseTarget: target })).toMatchObject({
decision: "unknown_field",
missing: expect.arrayContaining([missing]),
});
});
it("季目标缺失或规格不完整时等待,不自行认定当前候选是目标", () => {
expect(evaluateCandidate(consistent, target, current)).toMatchObject({
decision: "unknown_field",
missing: ["current_asset"],
});
expect(
evaluateCandidate(consistent, target, current, { seasonReleaseTarget: { ...target, videoCodec: undefined } }),
).toMatchObject({ decision: "unknown_field", missing: ["current_asset"] });
});
});