import { describe, expect, test } from "bun:test" import { readFileSync } from "node:fs" import { join } from "node:path" import { indexClaims } from "./claims-index.mjs" import { bindingChecks } from "./fidelity-binding.mjs" import { buildContext } from "./gates-static.mjs" import { parseHtml } from "./html-lite.mjs" type Defect = { code: string; message: string } const FIX = join(import.meta.dir, "tests", "fixtures", "fidelity") const LEDGER = JSON.parse(readFileSync(join(FIX, "ledger-verify.json"), "utf8")) const CLAIMS = indexClaims(readFileSync(join(FIX, "claim-graph.md"), "utf8")) function run(body: string, claims: unknown = CLAIMS): Defect[] { const html = /

Findings

\n${body}` return bindingChecks(buildContext(parseHtml(html), {}), claims, LEDGER) as Defect[] } const codes = (defects: Defect[]) => defects.map((defect) => defect.code) const fact = (id: string, text: string) => `${text}` describe("sentence binding", () => { test("#given a findings paragraph with no marker #when checked #then unbound_sentence", () => { expect(codes(run("

The bridge is popular with tourists.

"))).toEqual(["unbound_sentence"]) }) test("#given the same paragraph marked as opinion #when checked #then clean", () => { expect(run('

The bridge is popular with tourists.

')).toEqual([]) }) test("#given a citation, a fact, or a claim on each sentence #when checked #then clean", () => { const body = `

Tourism grew [S4]. Units reached ${fact("F2", "1,237")}.

The design follows the standard.

` expect(run(body)).toEqual([]) }) test("#given one unbound sentence after a cited one #when checked #then only that sentence is flagged", () => { expect(codes(run("

Tourism grew [S4]. Everyone loves it.

"))).toEqual(["unbound_sentence"]) }) test("#given an unbound paragraph outside findings (appendix, closing, sources) #when checked #then out of scope", () => { const body = "

Appendix

Plain words.

How this report was made

Plain words.

Sources

Plain words.

" expect(run(body)).toEqual([]) }) }) describe("claim status", () => { test("#given a data-claim id that is not in the claim index #when checked #then claim_status_invalid", () => { expect(codes(run('

The design is the safest.

'))).toEqual(["claim_status_invalid"]) }) test("#given a refuted claim cited in findings #when checked #then claim_status_invalid", () => { expect(codes(run('

The project finished early.

'))).toEqual(["claim_status_invalid"]) }) test("#given the refuted claim in a Contradictions section #when checked #then clean", () => { expect(run('

Contradictions

The project finished early.

')).toEqual([]) }) test("#given a normal-risk partial claim not in verified-claims #when checked #then clean", () => { expect(run('

Revenue grew about ten percent.

')).toEqual([]) }) test("#given a high-risk claim missing from verified-claims #when checked #then claim_status_invalid", () => { const claims = { claims: [{ claim_id: "C7", statement: "x", risk: "high", status: "supported", in_verified: false }], errors: [] } expect(codes(run('

A strong statement.

', claims))).toEqual(["claim_status_invalid"]) }) }) describe("conflicting readings", () => { test("#given two 12 % rows with YoY and QoQ periods in one sentence #when checked #then conflicting_readings", () => { const body = `

Revenue grew ${fact("F3", "12%")} and ${fact("F4", "12%")} [S4].

` expect(codes(run(body))).toEqual(["conflicting_readings"]) }) test("#given the two readings in separate sentences #when checked #then clean", () => { const body = `

Revenue grew ${fact("F3", "12%")} [S4]. Quarterly growth was ${fact("F4", "12%")} [S4].

` expect(run(body)).toEqual([]) }) })