fix(organize): 同时设为作品季集结构改到执行前检查全部通过后再切换
计划一致、Move 授权、做种义务检查失败时作品结构不变;切换后按作品新结构重算一遍,前后路径与预览不一致按 plan_stale。 补测试:Move 授权不通过 / 做种义务未满足时作品结构不变。 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
f200ec695f
commit
6e21bda481
3 files changed
+67
-10
No files matched your search
@@ -6,7 +6,7 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import type { FrozenPlan } from "../../src/modules/acquisition-organize/services/organize.ts";
|
||||
import { executeRecord } from "../../src/modules/acquisition-organize/services/organize.ts";
|
||||
import { refreshMetadata } from "../../src/modules/sources-identity/service/metadata.ts";
|
||||
import { createFmHarness, type FmHarness, key, pgAvailable, seedEntity } from "./harness.ts";
|
||||
import { createFmHarness, EXEC_KEY, type FmHarness, key, pgAvailable, seedDownload, seedEntity } from "./harness.ts";
|
||||
|
||||
// 整理时指定季集结构(docs/runbook/organize.md「整理时指定剧集组」):作品是 TMDB 默认季(S1 = 25 集),
|
||||
// 剧集组「制作季」拆成 S1(1–12)、S2(13–25)。临时按组整理 S01E14 → 命名 S02E02,资产覆盖仍挂作品的 S01E14 单元;
|
||||
@@ -156,6 +156,59 @@ describe.skipIf(skip)("整理时指定季集结构", () => {
|
||||
expect(fx.episodeResolution?.evidence).toContain("structure:grp-production");
|
||||
});
|
||||
|
||||
it("勾「同时设为作品的季集结构」但 Move 授权不通过:不执行,作品结构不变", async () => {
|
||||
const body = {
|
||||
action: "move",
|
||||
items: [
|
||||
{
|
||||
root: h.dlRoot,
|
||||
path: "Split Cour S01E14 [2160p].mkv",
|
||||
mediaEntityId: show,
|
||||
targetDir: { root: h.libRoot, path: "alt" },
|
||||
},
|
||||
],
|
||||
episodeStructures: [{ mediaEntityId: show, groupId: "grp-production", applyToEntity: true }],
|
||||
};
|
||||
const dry = await h.api("POST", "/files/organize", { ...body, idempotencyKey: key(), dryRun: true }, EXEC_KEY);
|
||||
expect(dry.status, JSON.stringify(dry.body)).toBe(200);
|
||||
expect(dry.body.data.groups[0].error).toBeNull();
|
||||
const exec = await h.api(
|
||||
"POST",
|
||||
"/files/organize",
|
||||
{ ...body, idempotencyKey: key(), dryRun: false, planIds: [dry.body.data.groups[0].planId] },
|
||||
EXEC_KEY,
|
||||
);
|
||||
expect(exec.status).toBe(403);
|
||||
expect(exec.body.error.message).toMatch(/^approval_required:/);
|
||||
expect(await entityGroup()).toBeNull();
|
||||
expect(await unitAt(1, 14)).toBe(unitE14);
|
||||
});
|
||||
|
||||
it("勾「同时设为作品的季集结构」但做种义务未满足、不能移动:该组不执行,作品结构不变", async () => {
|
||||
const name = "Split Cour S01E14 [2160p].mkv";
|
||||
const task = await seedDownload(h.db, h, { name, status: "completed" });
|
||||
const body = {
|
||||
action: "move",
|
||||
items: [{ root: h.dlRoot, path: name, mediaEntityId: show, targetDir: { root: h.libRoot, path: "alt" } }],
|
||||
episodeStructures: [{ mediaEntityId: show, groupId: "grp-production", applyToEntity: true }],
|
||||
};
|
||||
const dry = await h.api("POST", "/files/organize", { ...body, idempotencyKey: key(), dryRun: true });
|
||||
expect(dry.status, JSON.stringify(dry.body)).toBe(200);
|
||||
expect(dry.body.data.groups[0].error).toBeNull();
|
||||
const exec = await h.api("POST", "/files/organize", {
|
||||
...body,
|
||||
idempotencyKey: key(),
|
||||
dryRun: false,
|
||||
planIds: [dry.body.data.groups[0].planId],
|
||||
});
|
||||
expect(exec.status).toBe(200);
|
||||
expect(exec.body.data.groups[0].error?.code).toBe("download_active");
|
||||
expect(exec.body.data.recordIds).toEqual([]);
|
||||
expect(await entityGroup()).toBeNull();
|
||||
expect(await unitAt(1, 14)).toBe(unitE14);
|
||||
await h.db.deleteFrom("download_task").where("id", "=", task).execute();
|
||||
});
|
||||
|
||||
it("勾「同时设为作品的季集结构」:预览不切换,执行时先切换作品、单元原地迁移,命名 S02E02,覆盖挂迁移后的单元", async () => {
|
||||
const r = await organize(
|
||||
"Split Cour S01E14 [2160p].mkv",
|
||||
|
||||
Reference in new issue
Block a user