mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 01:46:55 +02:00
feat: require a zero-error product typecheck and a test type-debt ratchet
Adds tsconfig.json (strict) over product code, fixes its remaining 90 diagnostics (type-only, interface corrections, and explicit narrowing), and adds a typecheck job to the required free-tests aggregate running bun run typecheck, the test-code ratchet (identity -> count baseline, fails on new, repeated, or unlocked fixed diagnostics), and the lib/cso format check. Reuses fixes from #2447 where they still applied.
This commit is contained in:
1 parent
03e5911dba
commit
42e53413da
38 files changed
+1254
-62
No files matched your search
@@ -56,6 +56,12 @@ describe('free-tests workflow wiring', () => {
|
||||
expect(aggregate.if).toBe('always()');
|
||||
expect(aggregate.needs).toContain('free-suite');
|
||||
expect(aggregate.steps.some((step: any) => step.run?.includes('--ci-verify'))).toBe(true);
|
||||
expect(aggregate.needs).toContain('typecheck');
|
||||
const gate = aggregate.steps.find((step: any) => step.env?.TYPECHECK_RESULT);
|
||||
expect(gate.env.TYPECHECK_RESULT).toBe('${{ needs.typecheck.result }}');
|
||||
expect(gate.run).toContain('test "$TYPECHECK_RESULT" = success');
|
||||
const typecheck = workflow.jobs.typecheck.steps.map((step: any) => step.run).filter(Boolean);
|
||||
expect(typecheck).toEqual(expect.arrayContaining(['bun run typecheck', 'bun run typecheck:test', 'bun run format:cso:check']));
|
||||
expect(source).not.toContain('--quick');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { compareDiagnostics, parseDiagnostics, readBaseline } from '../scripts/typecheck-test';
|
||||
|
||||
const output = [
|
||||
"test/a.test.ts(3,5): error TS2339: Property 'questions' does not exist on type 'Plan'.",
|
||||
"test/a.test.ts(9,5): error TS2339: Property 'questions' does not exist on type 'Plan'.",
|
||||
'test/b.test.ts(1,1): error TS2345: Argument of type \'string\' is not assignable to parameter of type \'number\'.',
|
||||
" Type 'string' is not assignable to type 'number'.",
|
||||
'Found 3 errors.',
|
||||
].join('\n');
|
||||
|
||||
describe('test typecheck ratchet', () => {
|
||||
test('counts repeated identical diagnostics instead of collapsing them', () => {
|
||||
expect(parseDiagnostics(output)).toEqual({
|
||||
"test/a.test.ts\tTS2339\tProperty 'questions' does not exist on type 'Plan'.": 2,
|
||||
"test/b.test.ts\tTS2345\tArgument of type 'string' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'.": 1,
|
||||
});
|
||||
});
|
||||
|
||||
test('ignores line and column so moving code does not churn the baseline', () => {
|
||||
const moved = output.replace('(3,5)', '(30,7)').replace('(9,5)', '(90,1)');
|
||||
expect(parseDiagnostics(moved)).toEqual(parseDiagnostics(output));
|
||||
});
|
||||
|
||||
test('a duplicate of an existing diagnostic and a new identity both fail', () => {
|
||||
const baseline = parseDiagnostics(output);
|
||||
const current = parseDiagnostics(output + "\ntest/a.test.ts(12,5): error TS2339: Property 'questions' does not exist on type 'Plan'.\ntest/c.test.ts(1,1): error TS2304: Cannot find name 'join'.");
|
||||
const { added, fixed } = compareDiagnostics(baseline, current);
|
||||
expect(added.map(d => [d.identity.split('\t')[0], d.baseline, d.current])).toEqual([
|
||||
['test/a.test.ts', 2, 3],
|
||||
['test/c.test.ts', 0, 1],
|
||||
]);
|
||||
expect(fixed).toEqual([]);
|
||||
});
|
||||
|
||||
test('a fixed diagnostic is reported so the smaller allowance gets locked in', () => {
|
||||
const baseline = parseDiagnostics(output);
|
||||
const { added, fixed } = compareDiagnostics(baseline, parseDiagnostics(output.split('\n').slice(1).join('\n')));
|
||||
expect(added).toEqual([]);
|
||||
expect(fixed).toHaveLength(1);
|
||||
expect(fixed[0]).toMatchObject({ baseline: 2, current: 1 });
|
||||
});
|
||||
|
||||
test('a missing or malformed baseline fails closed with the regeneration command', () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'typecheck-baseline-'));
|
||||
try {
|
||||
expect(() => readBaseline(path.join(dir, 'missing.json'))).toThrow('bun run typecheck:test --write-baseline');
|
||||
fs.writeFileSync(path.join(dir, 'bad.json'), JSON.stringify({ version: 1, diagnostics: { x: 0 } }));
|
||||
expect(() => readBaseline(path.join(dir, 'bad.json'))).toThrow('malformed');
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('the committed baseline is readable and non-empty', () => {
|
||||
const baseline = readBaseline(path.join(import.meta.dir, '..', 'scripts', 'typecheck-test-baseline.json'));
|
||||
expect(Object.keys(baseline).length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user