Files
Jyotisha/frontend/tests/reader-appendix-language-parity.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

85 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { readFileSync } from "node:fs";
import test from "node:test";
import { fileURLToPath } from "node:url";
import { cleanReaderAppendixMarkdown } from "../src/lib/reader-appendix-language.ts";
const golden = JSON.parse(readFileSync(new URL("./fixtures/report-density-fictional-reader.json", import.meta.url), "utf8"));
// Vocabulary-only cases, not invented calculation fixtures.
const pageCases = [
"PL9 第 42 页", "PL9第42页", "PL9.pdf 第 42 页", "PL9.pdf第42页后文",
"参见PL9 第 42 页。", "PL9 第 43–44 页", "PL9 第 40 / 48 页",
"PL9 pages 99-100", "PL9 p-42", "PL9第42页中的原始字段",
];
const chartFence = ' ```jyotish-chart\r\n{"title":"PL9 第 42 页 parameter_sensitive","id":"D1"}\r\n ```\r\n';
const copyCases = [
...pageCases,
"中文parameter_sensitive中文 PyJHora中文 cmd_full_reading后文",
"some_parameter_sensitive_field PL99 XPL9 PL9_suffix",
"The blocked / executed / available / computed result is not a claim.",
"| blocked | `executed` | available | computed | sign_cn | 12.50 |\n",
`parameter_sensitive\r\n${chartFence}| parameter_sensitive |\r\n`,
];
function pythonClean(inputs: string[]): string[] {
const script = [
"import json, sys",
"from scripts.reader_appendix_language import clean_reader_appendix_markdown",
"sys.stdout.write(json.dumps([clean_reader_appendix_markdown(value) for value in json.load(sys.stdin)], ensure_ascii=False))",
].join("\n");
const result = spawnSync(process.env.PYTHON ?? (process.platform === "win32" ? "python" : "python3"), ["-c", script], {
cwd: fileURLToPath(new URL("../../", import.meta.url)),
encoding: "utf8", input: JSON.stringify(inputs), maxBuffer: 16 * 1024 * 1024,
env: { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONDONTWRITEBYTECODE: "1" },
});
assert.equal(result.status, 0, `${result.error ?? ""}\n${result.stderr}`);
return JSON.parse(result.stdout);
}
test("appendix Python and TS share byte-identical golden and Chinese-boundary output", () => {
assert.equal(golden.fixtureProvenance.fictional, true);
const inputs = [golden.markdown, golden.reader_dasha_applicability, ...copyCases] as string[];
const python = pythonClean(inputs);
for (const [index, input] of inputs.entries()) {
const ts = cleanReaderAppendixMarkdown(input);
assert.deepEqual(Buffer.from(python[index], "utf8"), Buffer.from(ts, "utf8"), `case ${index}`);
assert.equal(cleanReaderAppendixMarkdown(ts), ts, `idempotent case ${index}`);
}
assert.doesNotMatch(python[0], /第\s*\d+(?:\s*[–—/-]\s*\d+)*\s*页/);
assert.doesNotMatch(cleanReaderAppendixMarkdown(golden.markdown), /第\s*\d+(?:\s*[–—/-]\s*\d+)*\s*页/);
});
test("appendix page references disappear without swallowing adjacent Chinese prose", () => {
for (const source of pageCases) {
const output = cleanReaderAppendixMarkdown(source);
assert.doesNotMatch(output, /PL9|第\s*\d+|pages?\s*\d+|p-42/);
}
assert.equal(cleanReaderAppendixMarkdown("参见PL9第42页中的原始字段"), "参见外部参照资料中的原始字段");
assert.equal(cleanReaderAppendixMarkdown("PL9.pdf第42页后文"), "外部参照资料后文");
assert.equal(cleanReaderAppendixMarkdown("some_parameter_sensitive_field PL99 XPL9"), "some_parameter_sensitive_field PL99 外部参照资料");
});
test("appendix golden preserves line and table shape, field names, numeric cells and chart bytes", () => {
const raw = golden.markdown as string;
const clean = cleanReaderAppendixMarkdown(raw);
const sourceLines = raw.split("\n");
const cleanLines = clean.split("\n");
assert.equal(cleanLines.length, sourceLines.length);
for (const [index, line] of sourceLines.entries()) {
if (!line.startsWith("|")) continue;
const cells = line.split("|");
const outputCells = cleanLines[index].split("|");
assert.equal(outputCells.length, cells.length, `table columns at line ${index}`);
cells.forEach((cell, column) => {
if (/^\s*-?\d+(?:\.\d+)?\s*$/.test(cell)) assert.equal(outputCells[column], cell);
});
}
for (const field of ["sign_cn", "degree_in_sign", "nakshatra_lord"]) {
assert.equal(clean.split(field).length, raw.split(field).length);
}
const fences = (text: string) => [...text.matchAll(/^[ \t]*```jyotish-chart[^\n]*\n[\s\S]*?^[ \t]*```[^\n]*(?:\n|$)/gm)].map(match => Buffer.from(match[0]));
assert.deepEqual(fences(clean), fences(raw));
assert.deepEqual(fences(cleanReaderAppendixMarkdown(copyCases.at(-1)!)), [Buffer.from(chartFence)]);
});