forgejo-pdf/worker/pdf/scripts/test-fixtures.js
codex 1ce1370983
Some checks failed
pdfexport / pdfexport-worker-fixtures (push) Has been cancelled
Add server-side Markdown→PDF export (v0.1)
2025-12-16 17:52:53 +00:00

133 lines
3.9 KiB
JavaScript

const fs = require("node:fs");
const path = require("node:path");
const { spawnSync, execFileSync } = require("node:child_process");
function parseArgs(argv) {
const out = { fixtures: "/fixtures", outDir: "/tmp/pdf-fixtures-out" };
for (let i = 2; i < argv.length; i++) {
const a = argv[i];
if (a === "--fixtures") out.fixtures = argv[++i];
else if (a === "--out") out.outDir = argv[++i];
}
return out;
}
function readManifestSHA() {
const p = "/opt/forgejo-pdf/manifest.json";
const b = fs.readFileSync(p, "utf8");
const j = JSON.parse(b);
if (!j.manifest_sha || typeof j.manifest_sha !== "string") {
throw new Error("manifest.json missing manifest_sha");
}
return j.manifest_sha;
}
function extractKnownUniqueString(md) {
const m = md.match(/KNOWN_UNIQUE_STRING:\s*([A-Za-z0-9_\-\.]+)/);
return m ? m[1] : null;
}
function containsMermaid(md) {
return /```mermaid[\s\S]*?```/m.test(md);
}
function run(cmd, args, opts = {}) {
const res = spawnSync(cmd, args, { encoding: "utf8", ...opts });
return res;
}
function main() {
const { fixtures, outDir } = parseArgs(process.argv);
fs.mkdirSync(outDir, { recursive: true });
const manifestSHA = readManifestSHA();
const pdfConfig = {
pdf: {
determinism: "strict",
timestamp: "commit_time",
typography: "professional",
mermaid: { strategy: "balanced", caption: false },
orphansWidows: { enforce: true },
footer: { enabled: true }
}
};
const files = fs
.readdirSync(fixtures)
.filter((f) => f.endsWith(".md"))
.sort();
if (files.length === 0) {
throw new Error("no fixture markdown files found");
}
for (const f of files) {
const mdPath = path.join(fixtures, f);
const md = fs.readFileSync(mdPath, "utf8");
const expected = extractKnownUniqueString(md);
if (!expected) throw new Error(`fixture missing KNOWN_UNIQUE_STRING: ${f}`);
const input = {
markdown: md,
repoMeta: {
owner: "fixture",
repo: "forgejo-pdf",
path: f,
repoID: 1,
commitSHA: "0123456789abcdef0123456789abcdef01234567",
commitTimeRFC3339: "2020-01-02T03:04:05Z"
},
config: pdfConfig,
manifestSHA
};
const jobDir = fs.mkdtempSync(path.join(outDir, "job-"));
const inPath = path.join(jobDir, "input.json");
const outPath = path.join(jobDir, "output.pdf");
fs.writeFileSync(inPath, JSON.stringify(input), "utf8");
const res = run("node", ["src/index.js", "--in", inPath, "--out", outPath], {
cwd: "/opt/forgejo-pdf"
});
if (res.status !== 0) {
throw new Error(`worker failed for ${f}: ${res.stderr.trim() || res.stdout.trim()}`);
}
const logs = res.stderr
.split("\n")
.map((l) => l.trim())
.filter(Boolean)
.map((l) => {
try {
return JSON.parse(l);
} catch {
return null;
}
})
.filter(Boolean);
const done = logs.findLast ? logs.findLast((l) => l.event === "done") : logs.reverse().find((l) => l.event === "done");
if (!done) throw new Error(`missing done log for ${f}`);
if (done.blocked_requests !== 0) throw new Error(`blocked_requests != 0 for ${f}`);
const hasMermaid = containsMermaid(md);
if (hasMermaid && (!Number.isFinite(done.mermaid_count) || done.mermaid_count < 1)) {
throw new Error(`expected mermaid_count >= 1 for ${f}`);
}
if (!hasMermaid && done.mermaid_count !== 0) {
throw new Error(`expected mermaid_count == 0 for ${f}`);
}
execFileSync("qpdf", ["--check", outPath], { stdio: "inherit" });
const text = execFileSync("pdftotext", [outPath, "-"], { encoding: "utf8" });
const normalized = text.replace(/\s+/g, "");
const expectedNorm = expected.replace(/\s+/g, "");
if (!normalized.includes(expectedNorm)) {
throw new Error(`pdftotext missing expected marker for ${f}: ${expected}`);
}
}
}
main();