- Import URL from node:url to clear ESLint no-undef (coderabbit) - Throw on unreadable tracked files instead of silently skipping, so a file can't evade the cap and false-pass (coderabbit) - Emit the "baseline can be tightened" note for deleted-only stale entries too, with a correct count (greptile) - Add FNXC:CI comments to the guard and its test per AGENTS.md (greptile) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
3.3 KiB
JavaScript
86 lines
3.3 KiB
JavaScript
/*
|
|
FNXC:CI 2026-06-21-00:04:
|
|
Tests for the line-count guardrail. Pin the behavior the guard promises: a new
|
|
file at exactly MAX_LINES passes but one line over fails; grandfathered files may
|
|
sit at or below their recorded ceiling but a single line of growth is a failure;
|
|
and shrunk, under-cap, or deleted baseline entries surface as tightenable so the
|
|
ratchet can only move down. Line counting must agree whether or not the file ends
|
|
in a trailing newline.
|
|
*/
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
MAX_LINES,
|
|
countLines,
|
|
evaluate,
|
|
formatFailureMessage,
|
|
} from "../check-file-line-count.mjs";
|
|
|
|
test("countLines counts a trailing-newline file without an extra empty line", () => {
|
|
assert.equal(countLines("a\nb\nc\n"), 3);
|
|
});
|
|
|
|
test("countLines counts a file with no trailing newline", () => {
|
|
assert.equal(countLines("a\nb\nc"), 3);
|
|
});
|
|
|
|
test("countLines treats an empty file as zero lines", () => {
|
|
assert.equal(countLines(""), 0);
|
|
});
|
|
|
|
test("evaluate flags a new file over the cap", () => {
|
|
const { violations } = evaluate({ "packages/x/src/new.ts": MAX_LINES + 1 }, {});
|
|
assert.equal(violations.length, 1);
|
|
assert.equal(violations[0].grandfathered, false);
|
|
assert.equal(violations[0].ceiling, MAX_LINES);
|
|
});
|
|
|
|
test("evaluate passes a new file at exactly the cap", () => {
|
|
const { violations } = evaluate({ "packages/x/src/new.ts": MAX_LINES }, {});
|
|
assert.equal(violations.length, 0);
|
|
});
|
|
|
|
test("evaluate allows a grandfathered file at or below its recorded ceiling", () => {
|
|
const baseline = { "packages/x/src/big.ts": 5000 };
|
|
const { violations } = evaluate({ "packages/x/src/big.ts": 4800 }, baseline);
|
|
assert.equal(violations.length, 0);
|
|
});
|
|
|
|
test("evaluate flags a grandfathered file that grew past its ceiling", () => {
|
|
const baseline = { "packages/x/src/big.ts": 5000 };
|
|
const { violations } = evaluate({ "packages/x/src/big.ts": 5001 }, baseline);
|
|
assert.equal(violations.length, 1);
|
|
assert.equal(violations[0].grandfathered, true);
|
|
assert.equal(violations[0].ceiling, 5000);
|
|
});
|
|
|
|
test("evaluate reports a grandfathered file that shrank as tightenable", () => {
|
|
const baseline = { "packages/x/src/big.ts": 5000 };
|
|
const { staleBaseline } = evaluate({ "packages/x/src/big.ts": 4500 }, baseline);
|
|
assert.equal(staleBaseline.some((s) => s.reason === "shrank"), true);
|
|
});
|
|
|
|
test("evaluate reports a grandfathered file that dropped under the cap as tightenable", () => {
|
|
const baseline = { "packages/x/src/big.ts": 5000 };
|
|
const { violations, staleBaseline } = evaluate(
|
|
{ "packages/x/src/big.ts": MAX_LINES - 1 },
|
|
baseline,
|
|
);
|
|
assert.equal(violations.length, 0);
|
|
assert.equal(staleBaseline.some((s) => s.reason === "under-cap"), true);
|
|
});
|
|
|
|
test("evaluate reports a deleted baseline file as tightenable", () => {
|
|
const baseline = { "packages/x/src/gone.ts": 5000 };
|
|
const { staleBaseline } = evaluate({}, baseline);
|
|
assert.equal(staleBaseline.some((s) => s.reason === "deleted"), true);
|
|
});
|
|
|
|
test("formatFailureMessage cites the file, count, and remediation", () => {
|
|
const msg = formatFailureMessage([
|
|
{ filePath: "packages/x/src/new.ts", lines: 2500, ceiling: MAX_LINES, grandfathered: false },
|
|
]);
|
|
assert.match(msg, /packages\/x\/src\/new\.ts: 2500 lines/);
|
|
assert.match(msg, /focused modules/);
|
|
});
|