Files
Jyotisha/frontend/tests/report-interpretation-packs.test.ts
T
2026-09-03 23:57:27 +08:00

93 lines
3.9 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import {
PACK_CHARACTER_LIMIT,
allInterpretationPacks,
cachedInterpretationPreamble,
interpretationPackForTheme,
} from "../src/lib/report-interpretation-packs/index.ts";
import { REPORT_THEME_IDS } from "../src/lib/report-theme-evidence-plan.ts";
import { buildWriterSystemContents } from "../src/mastra/personal-report.ts";
test("every report theme resolves an interpretation pack", () => {
for (const theme of REPORT_THEME_IDS) {
const pack = interpretationPackForTheme(theme);
assert.ok(pack && pack.length > 200, `theme ${theme} has no usable pack`);
}
assert.equal(interpretationPackForTheme("not_a_theme"), null);
assert.equal(interpretationPackForTheme(null), null);
assert.ok(cachedInterpretationPreamble().length > 500);
});
test("packs stay inside the character budget", () => {
for (const pack of allInterpretationPacks()) {
assert.ok(
pack.text.length <= PACK_CHARACTER_LIMIT,
`${pack.id} is ${pack.text.length} characters, over the ${PACK_CHARACTER_LIMIT} budget`,
);
}
});
// The packs travel into a model prompt, so they must not leak repository
// structure, internal module names, vendor names or artefact names.
const FORBIDDEN_PACK_SUBSTRINGS = [
"references/", "scripts/", "frontend/", "src/", "node_modules",
"SKILL.md", ".md", ".ts", ".py", ".json",
"vedastro", "swisseph", "swiss ephemeris", "jyotish_engine", "mastra",
"anthropic", "openai", "claude", "gpt-",
"prompt", "system message", "schema", "bundle", "claimcard", "evidenceref",
"mevg", "workflow", "endpoint", "http://", "https://", "sha256",
];
test("packs contain no paths, module names, vendor names or artefact names", () => {
for (const pack of allInterpretationPacks()) {
const lowered = pack.text.toLowerCase();
for (const token of FORBIDDEN_PACK_SUBSTRINGS) {
assert.ok(!lowered.includes(token), `${pack.id} leaks "${token}"`);
}
}
});
test("packs restate the hard truth boundaries instead of relaxing them", () => {
const general = cachedInterpretationPreamble();
for (const banned of ["必定", "一定", "保证", "百分之百"]) {
assert.ok(general.includes(banned), `general pack must name "${banned}" as forbidden phrasing`);
}
assert.ok(general.includes("不做医疗诊断"));
const health = interpretationPackForTheme("health_pressure")!;
assert.ok(health.startsWith("非医疗边界:"));
assert.ok(health.includes("不做任何诊断"));
const wealth = interpretationPackForTheme("wealth")!;
assert.ok(wealth.includes("不推荐任何具体投资标的"));
const timing = interpretationPackForTheme("timing")!;
assert.ok(timing.includes("不写年份、月份与日期"));
});
test("writer system content caches the general pack and appends only the chapter pack", () => {
const summaryContents = buildWriterSystemContents(null);
assert.equal(summaryContents.length, 1, "summary calls carry the general pack only");
assert.ok(summaryContents[0].includes(cachedInterpretationPreamble()));
assert.ok(summaryContents[0].includes("【上下文缓存边界】"));
const previousCached = summaryContents[0];
for (const theme of REPORT_THEME_IDS) {
const contents = buildWriterSystemContents(theme);
assert.equal(contents.length, 2, `${theme} must add its own pack`);
assert.equal(
contents[0],
previousCached,
"the cached prefix must stay byte-identical across sections or the cache never hits",
);
assert.equal(contents[1], interpretationPackForTheme(theme));
}
});
test("writer system content never carries chart, subject or evidence data", () => {
for (const theme of [null, ...REPORT_THEME_IDS]) {
for (const content of buildWriterSystemContents(theme)) {
assert.ok(!/ev-[a-z0-9_-]+/.test(content), "no evidence ids in the static packs");
assert.ok(!/\d{4}-\d{2}-\d{2}/.test(content), "no dates in the static packs");
}
}
});