ci(server): fail lint:size and lint:boundaries on stale baseline entries

Both server ratchets only logged an entry the tree had moved past. A
file that shrank could grow back to its old size entry unseen, a
deleted file left its allowance to the next file at its path, and a
boundary violation that was fixed stayed allowed until someone ran
--update. The client ratchets now fail on such entries, so the server
ones do the same.

lint:size fails on an entry above its file's size or for a file that is
gone, and lint:boundaries on an entry that no longer occurs; both tell
to run --update, which still only lowers or drops. Both baselines match
the tree today.
This commit is contained in:
Maurice
2026-10-08 13:58:19 +02:00
parent bd267c67f8
commit 809ec386fc
5 changed files with 77 additions and 18 deletions
@@ -170,7 +170,7 @@ describe('import-boundaries.mjs', () => {
expect(out).toContain('FAIL dbImportsNest: db/orm.ts -> nest/database/request-context.ts');
});
it('BOUND-008: a baselined violation passes; a gone one is reported and --update drops it without adding', () => {
it('BOUND-008: a baselined violation passes; a gone one fails and --update drops it without adding', () => {
const files = {
'nest/b/b.helpers.ts': 'export const h = 1;\n',
'nest/a/a.service.ts': "import { h } from '../b/b.helpers';\nexport const a = h;\n",
@@ -182,7 +182,10 @@ describe('import-boundaries.mjs', () => {
expect(check.status).toBe(1);
expect(check.out).toContain('FAIL domainInternals: c -> b/b.helpers.ts');
expect(check.out).not.toContain('FAIL domainInternals: a -> b/b.helpers.ts');
expect(check.out).toContain('1 baseline entry no longer occurs');
expect(check.out).toContain(
'FAIL domainInternals: z -> b/gone.ts is in scripts/import-boundaries-baseline.json but no longer occurs.',
);
expect(check.out).toContain('npm run lint:boundaries -- --update');
expect(run(dir, '--update').status).toBe(1);
const written: unknown = JSON.parse(
@@ -191,6 +194,18 @@ describe('import-boundaries.mjs', () => {
expect(written).toEqual({ ...EMPTY, domainInternals: ['a -> b/b.helpers.ts'] });
});
it('BOUND-011: a stale entry alone fails, and passes once --update dropped it', () => {
const files = { 'nest/a/a.service.ts': 'export const a = 1;\n' };
const dir = serverRoot(files, { ...EMPTY, domainCycles: ['a -> b', 'b -> a'] });
const check = run(dir);
expect(check.status).toBe(1);
expect(check.out).toContain(
'FAIL domainCycles: a -> b is in scripts/import-boundaries-baseline.json but no longer occurs.',
);
expect(run(dir, '--update').status).toBe(0);
expect(run(dir).status).toBe(0);
});
it('BOUND-009: fails closed on a missing or malformed baseline, a missing src/ and an unresolvable import', () => {
const file = { 'x/a.ts': 'export const a = 1;\n' };
expect(run(serverRoot(file, null)).out).toContain('cannot be read');
+20 -2
View File
@@ -75,10 +75,28 @@ describe('size-lint.mjs', () => {
expect(out).toContain('FAIL src/nest/big/big.service.ts: 1001 lines, the limit is 1000.');
});
it('SIZE-003: fails when a baselined file grows past its entry, passes at or under it', () => {
it('SIZE-003: fails when a baselined file grows past its entry, passes at it', () => {
expect(run(serverRoot({ 'src/a.ts': lines(1201) }, { 'src/a.ts': 1200 })).status).toBe(1);
expect(run(serverRoot({ 'src/a.ts': lines(1200) }, { 'src/a.ts': 1200 })).status).toBe(0);
expect(run(serverRoot({ 'src/a.ts': lines(1100) }, { 'src/a.ts': 1200 })).out).toContain('run with --update');
});
it('SIZE-010: fails an entry above its file, or for a file that is gone, until --update lowers it', () => {
const shrunk = run(serverRoot({ 'src/a.ts': lines(1100) }, { 'src/a.ts': 1200 }));
expect(shrunk.status).toBe(1);
expect(shrunk.out).toContain(
'FAIL src/a.ts is held at 1200 in scripts/size-baseline.json, but it has 1100 lines now.',
);
expect(shrunk.out).toContain('npm run lint:size -- --update');
// Back under the limit, the entry would let it grow past the limit again.
expect(run(serverRoot({ 'src/a.ts': lines(900) }, { 'src/a.ts': 1200 })).status).toBe(1);
// A deleted file would hand its allowance to the next file at its path.
const gone = run(serverRoot({ 'src/b.ts': lines(1) }, { 'src/a.ts': 1200 }));
expect(gone.status).toBe(1);
expect(gone.out).toContain('FAIL src/a.ts is held at 1200 in scripts/size-baseline.json, but the file is gone.');
const dir = serverRoot({ 'src/a.ts': lines(1100) }, { 'src/a.ts': 1200, 'src/gone.ts': 1300 });
expect(run(dir, '--update').status).toBe(0);
expect(run(dir).status).toBe(0);
});
it('SIZE-004: counts a long line once per 120 columns, so joining lines buys nothing', () => {