Files
genshin-assistant/src/eval/ocrEval.test.ts
T
2026-07-09 08:44:50 +02:00

45 lines
1.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { formatReport, runOcrEval } from "./ocrEvalHarness";
import { ocrEvalCorpus } from "./corpus";
// Regression gate: every case in the combined corpus is verified ground truth,
// so the parser must read every labeled field correctly. A drop here means an
// OCR/parser change regressed a previously-correct read - look at the printed
// failures. If a change intentionally alters a correct output, update the corpus
// label in the same commit (the label is the source of truth, not the code).
describe("OCR eval harness", () => {
const report = runOcrEval(ocrEvalCorpus);
it("prints the accuracy report", () => {
// Surfaced in test output for humans; not an assertion.
// eslint-disable-next-line no-console
console.log("\n" + formatReport(report) + "\n");
expect(report.totalCases).toBe(ocrEvalCorpus.length);
});
it("reads every labeled field on the eval corpus correctly", () => {
const failureSummary = report.failures
.map((failure) => {
const wrong = failure.fields
.filter((entry) => !entry.correct)
.map((entry) => `${entry.field}: expected "${entry.expected}" got "${entry.actual}"`)
.join("; ");
return `${failure.id} -> ${wrong}`;
})
.join("\n");
expect(report.fieldAccuracy, `field regressions:\n${failureSummary}`).toBe(1);
expect(report.criticalFieldAccuracy).toBe(1);
expect(report.exactRate).toBe(1);
});
it("labels every critical field at least once across the corpus", () => {
const criticalCoverage = report.perField.filter(
(entry) => ["name", "slot", "mainStat", "mainValue", "setName"].includes(entry.field) && entry.evaluated > 0,
);
// name is derived indirectly on most cases; slot/mainStat/mainValue/setName must be exercised.
expect(criticalCoverage.length).toBeGreaterThanOrEqual(4);
});
});