| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 |
- import assert from "node:assert/strict";
- import test from "node:test";
- import { assembleClaudeAuthoredPromptPlan, contractSha256, validateClaudeAuthoredSetPlan, validateProductSettingBoardSpec, V3_WORKFLOW } from "./reference-first-v3-contracts.mjs";
- import { boardSpec, setPlan } from "./reference-first-v3-fixtures.mjs";
- test("product setting board contract keeps reusable structure and requires Claude-authored dynamic content", () => {
- const spec = validateProductSettingBoardSpec(boardSpec());
- assert.equal(spec.authored_by, "claude");
- assert.equal(spec.multi_views.length, 4);
- assert.equal(spec.detail_breakdowns.length, 4);
- assert.equal(spec.scenes.length, 4);
- assert.throws(() => validateProductSettingBoardSpec({ ...boardSpec(), scenes: [] }), /SETTING_BOARD_FOUR_SCENES_REQUIRED/);
- });
- test("Claude-authored set plan owns all creative prompt modules and Node only assembles them", () => {
- const source = setPlan();
- const validated = validateClaudeAuthoredSetPlan(source, { runId: source.run_id, referenceObservationSha256: "refhash" });
- const promptPlan = assembleClaudeAuthoredPromptPlan({ plan: validated, generatedReferences: ["three", "board"], aspectRatio: "1:1" });
- assert.equal(promptPlan.workflow, V3_WORKFLOW);
- assert.equal(promptPlan.images.length, 7);
- assert.ok(promptPlan.images.every((image) => image.reference_images.join(",") === "three,board"));
- assert.ok(promptPlan.images[4].generation_prompt.includes(source.pages[4].prompt_modules.scene_relation));
- assert.ok(!promptPlan.images[4].generation_prompt.includes(source.pages[4].prompt_modules.negative_constraints));
- assert.equal(promptPlan.images[4].negative_prompt, source.pages[4].prompt_modules.negative_constraints);
- });
- test("V3 plan rejects Qwen-like or generic uncertain authorship", () => {
- assert.throws(() => validateClaudeAuthoredSetPlan({ ...setPlan(), authored_by: "qwen" }), /CLAUDE_SET_PLAN_CONTRACT_INVALID/);
- const invalid = setPlan();
- invalid.pages[4].prompt_modules.scene_relation = "场景自由发挥";
- assert.throws(() => validateClaudeAuthoredSetPlan(invalid), /UNCERTAIN_EXPRESSION_REJECTED/);
- });
- test("contract hashes are deterministic for cross-stage binding", () => {
- assert.equal(contractSha256(boardSpec()), contractSha256(boardSpec()));
- assert.notEqual(contractSha256(boardSpec()), contractSha256({ ...boardSpec(), product_story: { text: "不同故事", source_type: "creative_assumption", source_ref: "claude.creative_assumption" } }));
- });
- test("one semantic setting-board framework adapts across six product families without code branches", () => {
- const products = [
- ["精品咖啡豆", "包装正面", "居家手冲"], ["香氛洗衣液", "瓶身标签与泵头", "洗衣房取用"],
- ["运动鞋", "鞋面轮廓与鞋底", "真人行走"], ["机械键盘", "键位与机身边框", "桌面输入"],
- ["壁挂灯具", "灯体与安装连接", "墙面安装"], ["通勤背包", "软体轮廓与肩带", "真人背负"],
- ];
- const hashes = products.map(([title, focus, scene], index) => {
- const spec = boardSpec(`run-20260804210${String(index).padStart(3, "0")}-aabbcc`, `obs${index}`);
- spec.identity.main_title = { text: title, source_type: "visible_observation", source_ref: "original_visual_observation.product" };
- spec.hero.focus_area = focus;
- spec.scenes[0].content = { text: scene, source_type: "creative_assumption", source_ref: "claude.creative_assumption" };
- const result = validateProductSettingBoardSpec(spec); assert.equal(result.identity.main_title.text, title); assert.equal(result.scenes.length, 4);
- return contractSha256(result);
- });
- assert.equal(new Set(hashes).size, products.length);
- });
|