import { afterEach, describe, it } from "node:test"; import { expect } from "expect"; import { validateEvidenceReferences } from "../../src/evidence/validate.js"; import { createTempRepo, type TempRepo } from "../helpers/temp-repo.js"; describe("validateEvidenceReferences", () => { const repos: TempRepo[] = []; afterEach(async () => { await Promise.all(repos.splice(0).map((repo) => repo.cleanup())); }); it("reports deleted referenced files", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile( "docs/truthmark/truth/sample.md", "---\nstatus: active\nsource_of_truth:\n - ../../src/missing.ts\n---\n# Sample\n", ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/sample.md", ]); expect(diagnostics).toContainEqual( expect.objectContaining({ category: "source-traceability", severity: "error", file: "docs/truthmark/truth/sample.md", }), ); }); it("accepts source_of_truth glob references that match repository files", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile("src/index.ts", "export const value = 1;\n"); await repo.writeFile( "docs/truthmark/truth/sample.md", "---\nstatus: active\nsource_of_truth:\n - ../../../src/**/*.ts\n---\n# Sample\n", ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/sample.md", ]); expect(diagnostics).toEqual([]); }); it("resolves bare source_of_truth filenames relative to the truth doc", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile("docs/truthmark/truth/overview.md", "# Overview\n"); await repo.writeFile( "docs/truthmark/truth/sample.md", "---\nstatus: active\nsource_of_truth:\n - overview.md\n---\n# Sample\n", ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/sample.md", ]); expect(diagnostics).toEqual([]); }); it("validates final Source References bullets as traceability evidence", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile("src/index.ts", "export const value = 1;\n"); await repo.writeFile( "docs/truthmark/truth/valid.md", `--- status: active truth_kind: engineering-behavior last_reviewed: 2026-06-14 --- # Valid ## Current Implementation Behavior Uses the current source file. ## Source References - ../../../src/index.ts `, ); await repo.writeFile( "docs/truthmark/truth/missing.md", `--- status: active truth_kind: engineering-behavior last_reviewed: 2026-06-14 --- # Missing ## Current Implementation Behavior References a missing file. ## Source References - ../../../src/missing.ts `, ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/valid.md", "docs/truthmark/truth/missing.md", ]); expect(diagnostics).toContainEqual( expect.objectContaining({ category: "source-traceability", severity: "error", file: "docs/truthmark/truth/missing.md", }), ); expect(diagnostics).not.toContainEqual( expect.objectContaining({ file: "docs/truthmark/truth/valid.md", }), ); }); it("reports evidence line spans outside the file even without a content hash", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile("src/index.ts", "export const value = 1;\n"); await repo.writeFile( "docs/truthmark/truth/sample.md", `--- status: active --- # Sample \`\`\`yaml evidence: - path: ../../../src/index.ts start_line: 10 end_line: 12 \`\`\` `, ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/sample.md", ]); expect(diagnostics).toContainEqual( expect.objectContaining({ category: "source-traceability", severity: "error", file: "docs/truthmark/truth/sample.md", message: expect.stringContaining("outside the file"), }), ); }); it("treats evidence symbols as non-normative metadata", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile("src/index.ts", "export const value = 1;\n"); await repo.writeFile( "docs/truthmark/truth/symbol.md", `--- status: active --- # Symbol metadata \`\`\`yaml evidence: - path: ../../../src/index.ts symbol: missingSymbol \`\`\` `, ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/symbol.md", ]); expect(diagnostics).toEqual([]); }); it("ignores malformed non-evidence YAML while reporting malformed evidence YAML", async () => { const repo = await createTempRepo(); repos.push(repo); await repo.writeFile("src/index.ts", "export const value = 1;\n"); await repo.writeFile( "docs/truthmark/truth/non-evidence.md", `--- status: active source_of_truth: - ../../../src/index.ts --- # Non-evidence YAML \`\`\`yaml example: - value: [ \`\`\` `, ); await repo.writeFile( "docs/truthmark/truth/evidence.md", `--- status: active source_of_truth: - ../../../src/index.ts --- # Evidence YAML \`\`\`yaml evidence: - path: ../../../src/index.ts start_line: [ \`\`\` `, ); const diagnostics = await validateEvidenceReferences(repo.rootDir, [ "docs/truthmark/truth/non-evidence.md", "docs/truthmark/truth/evidence.md", ]); expect(diagnostics).toContainEqual( expect.objectContaining({ category: "source-traceability", severity: "error", file: "docs/truthmark/truth/evidence.md", message: expect.stringContaining("Malformed evidence YAML block"), }), ); expect(diagnostics).not.toContainEqual( expect.objectContaining({ category: "source-traceability", severity: "error", file: "docs/truthmark/truth/non-evidence.md", message: expect.stringContaining("Malformed evidence YAML block"), }), ); }); });