Files
Jyotisha/frontend/tests/personal-report-writer-failure.test.ts
T
Jesse_Chen cf6405ed88
Independent Staging Quality Gate / validate (push) Successful in 8m9s
Independent Staging Quality Gate / publish (push) Successful in 1m48s
fix(report): say chapters wrote when assembly never finished
The 09:31 staging card had four ready sections but showed only structural
validation failure. Derive the list copy from section status so a written
pipeline stop is not described as an empty schema miss.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 09:58:06 +08:00

104 lines
4.6 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { classifySectionErrorCode } from "../src/lib/personal-report-generation.ts";
import { summarizePersonalReportFailure } from "../src/lib/personal-report-failure-summary.ts";
import {
PersonalReportAgentOutputError,
classifyWriterRepairCategory,
writerRepairPromptSuffix,
} from "../src/mastra/personal-report.ts";
test("refs mismatch is not classified as evidence insufficient", () => {
assert.equal(
classifySectionErrorCode(new Error("report_writer_evidence_refs_mismatch")),
"section_refs_mismatch",
);
assert.equal(
classifySectionErrorCode(new Error("report_writer_section_identity_mismatch")),
"section_identity_mismatch",
);
assert.equal(
classifySectionErrorCode(new Error("Report evidence insufficient: planets_missing")),
"section_evidence_insufficient",
);
assert.equal(
classifySectionErrorCode(new PersonalReportAgentOutputError("length")),
"section_output_truncated",
);
assert.equal(classifySectionErrorCode(new Error("section_output_invalid")), "section_output_invalid");
});
test("repair prompt names the failure category without carrying content", () => {
const refs = writerRepairPromptSuffix({ error: new Error("report_writer_evidence_refs_mismatch") });
assert.match(refs, /失败类别:refs mismatch/);
assert.match(refs, /evidenceRefs 必须逐字复制 plan\.evidenceRefs 数组,不得增删/);
assert.doesNotMatch(refs, /ev-|Sun|Moon|宫/);
const truncated = writerRepairPromptSuffix({ finishReason: "length", targetCharactersMin: 260 });
assert.equal(classifyWriterRepairCategory({ finishReason: "length" }), "output truncated");
assert.match(truncated, /失败类别:output truncated/);
assert.match(truncated, /压缩到约 260 字/);
const schema = writerRepairPromptSuffix({});
assert.match(schema, /失败类别:schema invalid/);
});
test("failure summary is readable and stays at error-code granularity", () => {
const truncated = summarizePersonalReportFailure({
themeCount: 4,
failureCode: "report_schema_invalid",
sections: [
{ status: "blocked", lastErrorCode: "section_output_truncated" },
{ status: "blocked", lastErrorCode: "section_output_truncated" },
{ status: "blocked", lastErrorCode: "section_output_truncated" },
{ status: "blocked", lastErrorCode: "section_output_truncated" },
],
});
assert.equal(truncated.summary, "4 个主题中 4 个写作失败:输出被截断");
assert.equal(truncated.innerReason, "all_sections_blocked");
const incomplete = summarizePersonalReportFailure({
themeCount: 4,
failureCode: "report_schema_invalid",
sections: [
{ status: "blocked", lastErrorCode: "section_refs_mismatch" },
{ status: "pending", lastErrorCode: null },
{ status: "pending", lastErrorCode: null },
{ status: "pending", lastErrorCode: null },
],
});
assert.equal(incomplete.summary, "4 个主题中 1 个写作失败:证据引用未对齐,3 个未开始");
assert.equal(incomplete.innerReason, "section_generation_incomplete");
assert.deepEqual(incomplete.lastErrorCodes, ["section_refs_mismatch"]);
const assembledChapters = summarizePersonalReportFailure({
themeCount: 4,
failureCode: "report_schema_invalid",
sections: [
{ status: "ready", lastErrorCode: null },
{ status: "ready", lastErrorCode: null },
{ status: "ready", lastErrorCode: null },
{ status: "ready", lastErrorCode: null },
],
});
assert.equal(assembledChapters.summary, "4 个主题已写成,整份报告未完成装配");
assert.equal(assembledChapters.innerReason, "section_generation_incomplete");
});
test("writer identity and evidenceRefs assertions keep set-equality semantics", () => {
const generationSource = readFileSync(
new URL("../src/lib/personal-report-generation.ts", import.meta.url),
"utf8",
);
const agentSource = readFileSync(
new URL("../src/mastra/personal-report.ts", import.meta.url),
"utf8",
);
assert.match(generationSource, /if \(candidate\.id !== entry\.id \|\| candidate\.theme !== entry\.theme\) throw new Error\("report_writer_section_identity_mismatch"\)/);
assert.match(generationSource, /equalStringSets\(candidate\.evidenceRefs, entry\.evidenceRefs\)/);
assert.match(generationSource, /throw new Error\("report_writer_evidence_refs_mismatch"\)/);
assert.match(agentSource, /evidenceRefs 必须逐字复制 plan\.evidenceRefs 数组,不得增删/);
assert.doesNotMatch(generationSource, /targetCharacters\.max\s*\/\s*2/);
});