prompt-template.test.js
4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Checks the Stage2 prompt assembler (dev-plan.md T7) against the
// mechanical items from MVCP M.15.1, plus the truncation behavior and the
// footwear fix (S1 spike finding). Run: node --test prompt-template.test.js
const test = require("node:test");
const assert = require("node:assert/strict");
const { buildStage2Prompt, truncateWords, wordCount, extractSection, MAX_DESCRIPTOR_WORDS } = require("./prompt-template.js");
const SAMPLE = {
character: { appearanceDescriptor: "shoulder-length straight black hair, a slender build, natural fair skin, and a friendly neutral expression" },
garment: { appearanceDescriptor: "a fitted red qipao-style dress with gold trim embroidery and a side slit" },
danceRef: { motionDescriptor: "a lively hip-hop inspired routine with hip sways and raised-arm turns" },
};
test("truncateWords/wordCount behave as expected", () => {
assert.equal(wordCount("one two three"), 3);
assert.equal(truncateWords("one two three", 5), "one two three");
assert.equal(truncateWords("one two three four five six", 3), "one two three");
});
test("M.15.1 H6: detailed_description is 350-500 words for a representative fill", () => {
const { prompt } = buildStage2Prompt(SAMPLE);
const n = wordCount(extractSection(prompt, "detailed_description", "overall_soundscape"));
assert.ok(n >= 350 && n <= 500, `detailed_description word count ${n} outside 350-500`);
});
test("M.15.1 H1: total prompt stays well under the 7000-character cap", () => {
const { prompt } = buildStage2Prompt(SAMPLE);
assert.ok(prompt.length < 7000, `prompt is ${prompt.length} chars`);
});
test("M.15.1 H9: retention_analysis only uses whitelisted relation markers", () => {
const { prompt } = buildStage2Prompt(SAMPLE);
const analysis = extractSection(prompt, "retention_analysis", "detailed_description");
const WHITELIST = ["fully_preserved", "weak_reference", "fully_copy", "attribute_transfer"];
const found = analysis.match(/- ([a-z_]+) -|: ([a-z_]+) -/g) || [];
assert.ok(found.length > 0, "expected to find relation markers in retention_analysis");
for (const marker of WHITELIST) assert.ok(analysis.includes(marker), `expected marker "${marker}" to appear`);
});
test("M.15.1 H5: single-shot clip (every [Shot N] reference is shot 1), no MM:SS timestamps", () => {
const { prompt } = buildStage2Prompt(SAMPLE);
const shotRefs = prompt.match(/\[Shot (\d+)\]/g) || [];
assert.ok(shotRefs.length > 0, "expected at least one [Shot N] reference");
assert.ok(shotRefs.every((s) => s === "[Shot 1]"), `expected every shot reference to be [Shot 1], got: ${shotRefs}`);
assert.doesNotMatch(prompt, /At \d{2}:\d{2}\.\d{3}/);
});
test("footwear fix (S1 spike finding): prompt pins footwear to <Picture 1> and says <Picture 2> doesn't affect it", () => {
const { prompt } = buildStage2Prompt(SAMPLE);
assert.match(prompt, /footwear.*retained unchanged/);
assert.match(prompt, /does not affect her footwear/);
});
test("appearanceDescriptor/garmentDescriptor over the word budget get truncated", () => {
const longDescriptor = Array.from({ length: 100 }, (_, i) => `word${i}`).join(" ");
const { prompt, appearanceDescriptor, garmentDescriptor } = buildStage2Prompt({
character: { appearanceDescriptor: longDescriptor },
garment: { appearanceDescriptor: longDescriptor },
danceRef: SAMPLE.danceRef,
});
assert.equal(wordCount(appearanceDescriptor), MAX_DESCRIPTOR_WORDS);
assert.equal(wordCount(garmentDescriptor), MAX_DESCRIPTOR_WORDS);
assert.ok(!prompt.includes("word99"), "expected the tail of the long descriptor to be gone");
});
test("an oversized motionDescriptor is truncated so detailed_description stays within the 500-word H6 cap", () => {
const longMotion = Array.from({ length: 300 }, (_, i) => `move${i}`).join(" ");
const { prompt, motionDescriptor } = buildStage2Prompt({ ...SAMPLE, danceRef: { motionDescriptor: longMotion } });
const n = wordCount(extractSection(prompt, "detailed_description", "overall_soundscape"));
assert.ok(n <= 500, `detailed_description word count ${n} exceeds 500 after truncation`);
assert.ok(wordCount(motionDescriptor) < 300, "expected motionDescriptor itself to have been shortened");
});