Files
UHD_Media_Tool/apps/web/app/components/library/slot-rules.test.ts
T

218 lines
7.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { draftFromJson, draftToJson, type SlotRulesDraft, type SlotRulesJson } from "./slot-rules.tsx";
const existingRules = (): SlotRulesJson => ({
hardConditions: {
resolutions: ["1080p", "2160p"],
sources: ["WEB-DL", "BluRay"],
requireEncode: true,
minSizeBytes: 1024 ** 3,
maxSizeBytes: 10 * 1024 ** 3,
requiredSubtitles: ["zh", "en"],
cuts: ["default", "IMAX"],
excludeGroups: ["LOW", "BAD"],
contentTypes: ["main"],
codecs: ["H.265"],
hdr: { anyOf: ["HDR10"], noneOf: ["DV"] },
},
softPreferences: {
hdr: ["DV", "HDR10"],
groups: ["CHD", "ADE"],
sources: ["BluRay", "WEB-DL"],
codecs: ["H.265", "H.264"],
preferredSizeBytes: { min: 2 * 1024 ** 3, max: 8 * 1024 ** 3 },
tiers: [
{ name: "优先蓝光", sources: ["BluRay"], codecs: ["H.265"] },
{ name: "追更", sources: ["WEB-DL"], resolutions: ["1080p"] },
],
tiersRequired: true,
},
fallback: {
unknownPolicy: "hold",
retryLimit: 3,
investigation: { enabled: true, fields: ["codec", "hdr"] },
},
upgradeThreshold: {
enabled: true,
ruleGroupIds: ["quality", "release-group"],
consistency: "season_release",
minTierSteps: 2,
allowResolutionUpgrade: true,
encodeGroupAllowlist: ["CHD", "ADE"],
ceilingTier: 4,
mode: "full_pack",
basis: "priority",
targetPri: 98,
sourceTiers: { BluRay: 3, "WEB-DL": 2 },
},
completion: { kind: "movie", cuts: ["default", "IMAX"] },
});
function save(draft: SlotRulesDraft): SlotRulesJson {
const result = draftToJson(draft);
if (!result.ok) throw new Error(JSON.stringify(result.errors));
return result.value;
}
describe("收录规则编辑回写", () => {
it("逐版本修改洗版方式,关闭再开启保留模式,明确选默认才清除模式", () => {
const original = existingRules();
const disabled = save({ ...draftFromJson(original), upgradeEnabled: false });
expect(disabled.upgradeThreshold).toEqual({ ...original.upgradeThreshold, enabled: false });
const reopened = save({ ...draftFromJson(disabled), upgradeEnabled: true });
expect(reopened).toEqual(original);
const perUnit = save({ ...draftFromJson(original), upgradeMode: "per_unit" });
expect(perUnit.upgradeThreshold).toEqual({ ...original.upgradeThreshold, mode: "per_unit" });
const { mode: _mode, ...rest } = original.upgradeThreshold;
expect(save({ ...draftFromJson(original), upgradeMode: "" }).upgradeThreshold).toEqual(rest);
expect(save(draftFromJson({})).upgradeThreshold).toEqual({ enabled: false });
});
it("未修改保存时保留表单没有展示的各层 JSON 字段", () => {
const original = existingRules();
expect(save(draftFromJson(original))).toEqual(original);
});
it.each([
{
name: "分辨率",
edit: { resolutions: ["2160p"] },
expected: (original: SlotRulesJson) => ({
...original,
hardConditions: { ...original.hardConditions, resolutions: ["2160p"] },
}),
},
{
name: "编码偏好",
edit: { softCodecs: ["AV1", "H.265"] },
expected: (original: SlotRulesJson) => ({
...original,
softPreferences: { ...original.softPreferences, codecs: ["AV1", "H.265"] },
}),
},
])("编辑$name只改写对应字段,不改硬编码限制或其他规则", ({ edit, expected }) => {
const original = existingRules();
const snapshot = structuredClone(original);
expect(save({ ...draftFromJson(original), ...edit })).toEqual(expected(original));
expect(original).toEqual(snapshot);
});
it("同时更新分辨率和编码偏好仍保留阶梯、洗版口径、完成版本及回退扩展", () => {
const original = existingRules();
const value = save({
...draftFromJson(original),
resolutions: ["720p", "1080p"],
softCodecs: ["AV1"],
});
expect(value).toEqual({
...original,
hardConditions: { ...original.hardConditions, resolutions: ["720p", "1080p"] },
softPreferences: { ...original.softPreferences, codecs: ["AV1"] },
});
});
it.each([false, true])("清空洗版规则和阈值不会从原 JSON 恢复,升级开关为 %s", (upgradeEnabled) => {
const original = existingRules();
const {
ruleGroupIds: _groups,
consistency: _consistency,
minTierSteps: _steps,
...remaining
} = original.upgradeThreshold;
const value = save({
...draftFromJson(original),
upgradeEnabled,
upgradeGroupIds: [],
seasonConsistency: false,
minTierSteps: "",
});
expect(value).toEqual({
...original,
upgradeThreshold: { ...remaining, enabled: upgradeEnabled },
});
expect(value.upgradeThreshold).not.toHaveProperty("ruleGroupIds");
expect(value.upgradeThreshold).not.toHaveProperty("consistency");
expect(value.upgradeThreshold).not.toHaveProperty("minTierSteps");
});
it("关闭升级并清空配置后,再加载并打开升级也不会复活旧规则", () => {
const disabled = save({
...draftFromJson(existingRules()),
upgradeEnabled: false,
upgradeGroupIds: [],
seasonConsistency: false,
minTierSteps: "",
});
const reopened = draftFromJson(disabled);
expect(reopened.upgradeGroupIds).toEqual([]);
expect(reopened.seasonConsistency).toBe(false);
expect(reopened.minTierSteps).toBe("");
const enabled = save({ ...reopened, upgradeEnabled: true });
expect(enabled.upgradeThreshold).toEqual({ ...disabled.upgradeThreshold, enabled: true });
expect(enabled.upgradeThreshold).not.toHaveProperty("ruleGroupIds");
expect(enabled.upgradeThreshold).not.toHaveProperty("consistency");
expect(enabled.upgradeThreshold).not.toHaveProperty("minTierSteps");
});
});
describe("按字段配置的未知值回退", () => {
const fieldFallback = () => ({
...existingRules(),
fallback: {
unknownPolicy: { codec: "hold", resolution: "reject", hdr: "accept" },
investigation: { enabled: true, fields: ["codec"] },
},
});
it("按字段对象回填为保留状态,未修改保存仍保留原配置", () => {
const original = fieldFallback();
const draft = draftFromJson(original);
expect(draft.unknownPolicy).toBe("fields");
expect(save(draft)).toEqual(original);
});
it("仅修改其他规则时也不会清除按字段回退对象及其扩展字段", () => {
const original = fieldFallback();
expect(save({ ...draftFromJson(original), resolutions: ["2160p"] })).toEqual({
...original,
hardConditions: { ...original.hardConditions, resolutions: ["2160p"] },
});
});
it("明确选择全局回退后替换原按字段对象,保留其他回退字段", () => {
const original = fieldFallback();
expect(save({ ...draftFromJson(original), unknownPolicy: "reject" })).toEqual({
...original,
fallback: { ...original.fallback, unknownPolicy: "reject" },
});
});
it("原按字段配置先改为全局拒绝,再明确选系统默认时清空回退,不恢复原对象", () => {
const original = fieldFallback();
const draft = draftFromJson(original);
const selectedGlobal = { ...draft, unknownPolicy: "reject" as const };
const cleared = { ...selectedGlobal, unknownPolicy: "" as const };
const { unknownPolicy: _policy, ...remaining } = original.fallback;
const value = save(cleared);
expect(value).toEqual({ ...original, fallback: remaining });
expect(value.fallback).not.toHaveProperty("unknownPolicy");
});
it("已有全局回退可以清空,保存后不会恢复原选择", () => {
const original = existingRules();
const { unknownPolicy: _policy, ...remaining } = original.fallback;
const value = save({ ...draftFromJson(original), unknownPolicy: "" });
expect(value).toEqual({ ...original, fallback: remaining });
expect(value.fallback).not.toHaveProperty("unknownPolicy");
});
});