reference-first-v3-contracts.test.mjs 3.6 KB

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