test.govlab/pipeline/core/converters/violation.converter.test.ts
test.govlab/pipeline/core/converters/violation.converter.test.ts is a file in Codebase Testing. 122 lines of code and 0 definitions.
import {
buildViolations,
parseViolations,
splitOnGaps,
stripAnsi,
withoutTrailingReturn,
} from "@govlab/pipeline/core/converters/violation.converter.ts";
import { describe, expect, it } from "vitest";
import type { StepOutput } from "@govlab/pipeline/types/report.types.ts";
const ESCAPE_CODE_POINT = 27;
const ESC = String.fromCodePoint(ESCAPE_CODE_POINT);
const STEP = "lint";
const FILE = "src/a.ts";
const LABEL = "verify-codebase";
const STAMP = "2026-01-01T00:00:00.000Z";
const FINDING = "src/b.ts\n 1:1 error broke some/rule\n";
const OTHER_FINDING = "src/a.ts\n 2:2 error also broke some/rule\n";
const output = function output(over: Partial<StepOutput> = {}): StepOutput {
return { label: "Lint", ok: false, out: "", stage: "linting", ...over };
};
describe("stripAnsi", () => {
it("removes a color sequence and keeps the text around it", () => {
expect(stripAnsi(`${ESC}[2mdim${ESC}[0m text`)).toBe("dim text");
});
it("keeps an escape that is not a terminated color sequence", () => {
expect(stripAnsi(`${ESC}[2Kline`)).toBe(`${ESC}[2Kline`);
});
it("leaves plain text untouched", () => {
expect(stripAnsi("no escapes here")).toBe("no escapes here");
});
});
describe("withoutTrailingReturn", () => {
it("drops a single trailing carriage return", () => {
expect(withoutTrailingReturn("text\r")).toBe("text");
});
it("leaves a carriage return that is not trailing", () => {
expect(withoutTrailingReturn("a\rb")).toBe("a\rb");
});
});
describe("splitOnGaps", () => {
it("splits on runs of two or more spaces and keeps single spaces inside a part", () => {
expect(splitOnGaps("3:7 error a real message some/rule")).toStrictEqual([
"3:7",
"error",
"a real message",
"some/rule",
]);
});
it("returns one part when no gap is wide enough", () => {
expect(splitOnGaps("one two three")).toStrictEqual(["one two three"]);
});
it("returns nothing for whitespace alone", () => {
expect(splitOnGaps(" ")).toStrictEqual([]);
});
});
describe("parseViolations", () => {
it("groups findings under the file header that precedes them", () => {
const parsed = parseViolations(`${FILE}\n 3:7 error broke a thing some/rule\n`, STEP);
expect(parsed.matched).toBe(true);
expect([...parsed.files.keys()]).toStrictEqual([FILE]);
expect(parsed.files.get(FILE)).toStrictEqual([
{ column: 7, line: 3, message: "broke a thing", rule: "some/rule", severity: "error", step: STEP },
]);
});
it("reads a header and its findings through terminal color codes", () => {
const colored = `${ESC}[4m${FILE}${ESC}[0m\n 1:1 ${ESC}[31merror${ESC}[0m broke some/rule\n`;
expect(parseViolations(colored, STEP).files.get(FILE)?.[0]?.severity).toBe("error");
});
it("reports no match when the output carries no file header", () => {
expect(parseViolations("nothing that looks like a finding\n", STEP).matched).toBe(false);
});
it("ignores an indented line whose position is not a line number", () => {
expect(parseViolations(`${FILE}\n summary of the run\n`, STEP).matched).toBe(false);
});
it("keeps a message with no trailing rule id instead of reading the message as the rule", () => {
const parsed = parseViolations(`${FILE}\n 2:4 error just a message\n`, STEP);
expect(parsed.files.get(FILE)?.[0]).toStrictEqual({
column: 4,
line: 2,
message: "just a message",
rule: "",
severity: "error",
step: STEP,
});
});
});
describe("buildViolations", () => {
it("skips a passing step, so a green step contributes nothing", () => {
const artifact = buildViolations([output({ ok: true, out: FINDING })], LABEL, null, STAMP);
expect(artifact.totals).toStrictEqual({ files: 0, violations: 0 });
});
it("sorts files by path so the artifact is stable across runs", () => {
const artifact = buildViolations(
[output({ out: FINDING }), output({ label: "Lint two", out: OTHER_FINDING })],
LABEL,
null,
STAMP,
);
expect(Object.keys(artifact.files)).toStrictEqual(["src/a.ts", "src/b.ts"]);
});
it("merges findings that two steps report against one file", () => {
const artifact = buildViolations(
[output({ out: FINDING }), output({ label: "Lint two", out: FINDING })],
LABEL,
null,
STAMP,
);
expect(artifact.files["src/b.ts"]).toHaveLength(2);
expect(artifact.totals).toStrictEqual({ files: 1, violations: 2 });
});
it("records unparsable output under its step", () => {
const artifact = buildViolations([output({ out: " a crash trace \n" })], LABEL, null, STAMP);
expect(artifact.unparsed["Lint"]).toBe("a crash trace");
expect(artifact.totals.files).toBe(0);
});
it("carries the label, the stamp and the stopping point through unchanged", () => {
const artifact = buildViolations([], LABEL, "linting", STAMP);
expect(artifact.label).toBe(LABEL);
expect(artifact.generatedAt).toBe(STAMP);
expect(artifact.stoppedAt).toBe("linting");
});
});