Files
Jyotisha/frontend/tests/personal-report-writer-table-guard.test.ts
T
jesse-uxandClaude Code a12f2c0d26
Independent Staging Quality Gate / validate (push) Failing after 11m4s
Independent Staging Quality Gate / publish (push) Skipped
fix(report): make density facts readable and printable
Unify reader cleanup rules, lock writer table guards, and register the exact fictional timestamp collision. Preserve existing ordinary-report safety contracts and source-data gaps.

Validation: report Node 165/165, final safety 29/29, Python 101/101, Chrome 28/28; both PDFs retain all 130 rows. Full Node 3704 tests with the same 91 baseline failures. Privacy test: 62 passed, 1 failed due to 17 protected-file READ_ERRORs; not a green gate. Build, DB, manual checklist and controlled-login gaps remain documented. User explicitly authorized staging push with these gaps disclosed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-23 15:26:25 +08:00

58 lines
3.2 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { Agent } from "@mastra/core/agent";
import { validatePersonalReportAgentOutputAgainstPlan } from "../src/lib/personal-report-generation.ts";
import type { PersonalReportSectionPlan } from "../src/lib/personal-report-plan.ts";
import type { ReportEvidenceBundleV2 } from "../src/lib/report-evidence-bundle-v2.ts";
import { createPersonalReportAgent, type PersonalReportAgentOutput } from "../src/mastra/personal-report.ts";
// Writer-boundary unit inputs contain no astrological facts or fabricated engine payload.
const plan: PersonalReportSectionPlan = { schemaVersion: "personal_report_section_plan.v1", depth: "standard", sections: [] };
const bundle = { claimCards: [] } as unknown as ReportEvidenceBundleV2;
function prose(): PersonalReportAgentOutput {
return {
executiveSummary: { headline: "先把已有安排理清", summary: "把要做的事写下来,再安排下一步。", priorities: ["核对现有安排"] },
thematicNarrative: [],
};
}
const forbiddenCases = {
"Markdown table": () => ({ ...prose(), executiveSummary: { ...prose().executiveSummary, summary: "| 项目 | 说明 |\n| --- | --- |\n| 安排 | 待核对 |" } }),
"HTML table in nested array": () => ({ ...prose(), executiveSummary: { ...prose().executiveSummary, priorities: ['<TABLE class="facts"><tr><td>待核对</td></tr></TABLE>'] } }),
"factTables top-level key": () => ({ ...prose(), factTables: [] }),
"factTables nested key": () => ({ ...prose(), executiveSummary: { ...prose().executiveSummary, factTables: [] } }),
};
for (const [name, output] of Object.entries(forbiddenCases)) {
test(`writer rejects ${name} with the exact fact-table error code`, () => {
assert.throws(() => validatePersonalReportAgentOutputAgainstPlan(output(), plan, bundle), {
name: "Error", message: "report_writer_fact_tables_forbidden",
});
});
}
test("writer accepts normal prose unchanged, including a non-table inline pipe", () => {
const output = { ...prose(), executiveSummary: {
...prose().executiveSummary,
summary: prose().executiveSummary.summary + "安排 A | 安排 B 只是两种写法,不是表格。",
} };
assert.strictEqual(validatePersonalReportAgentOutputAgainstPlan(output, plan, bundle), output);
});
test("actual writer agent instructions prohibit tables and assign them to the system", async (t) => {
let calls = 0;
t.mock.method(Agent.prototype, "generate", async function (this: Agent) {
calls++;
const instructions = await this.getInstructions();
assert.match(String(instructions), /不要输出表格,表格由系统提供/);
assert.match(String(instructions), /Markdown 表格、HTML table 或 factTables 字段/);
return { object: prose(), usage: { inputTokens: 1, outputTokens: 1 }, finishReason: "stop" };
});
const writer = createPersonalReportAgent({
id: "writer-table-contract", label: "Writer test", description: "Writer test", creditCost: 0,
isDefault: false, mode: "openai",
model: { specificationVersion: "v2", provider: "writer-test", modelId: "writer-test" } as never,
});
assert.deepEqual(await writer.generate(bundle, plan), prose());
assert.equal(calls, 1);
});