test: guard the reduced suite against new test-of-test files

- test/test-of-test-ratchet.test.ts records the 228 free tests that import only
  test/ code and fails on a new one, naming the owner test to extend instead;
  a stale baseline entry fails with the remove instruction.
- test/helpers/resolve-repo-path.ts is the one specifier/literal resolver for
  the ratchet and the touchfile closure invariant, with its own unit tests.
- CONTRIBUTING "Test tiers" describes the paid-failure workflow (fix, then one
  row in the detector's owner test) and the ratchet; TEST_PORTFOLIO gains the
  detector -> owner-test table and no longer claims an Autoplan chain eval.
- TODOS: automatic exclusion policy for chronically red periodic files (P3),
  the deferred native-completion table collapse, the unused CEO payment
  seeder; the PTY readiness item is narrowed to the paid runner.
- docs/test-audit-2026-09.md collects the triage, security mapping, inventories,
  selection proof, behavior-commit decisions and retained false positives.
This commit is contained in:
garrytan committed 2026-09-29 09:16:19 +00:00
1 parent 67084a5caf
commit 13e4f37201
8 files changed
+848 -26

No files matched your search

+44
View File
@@ -0,0 +1,44 @@
import { describe, expect, test } from 'bun:test';
import * as path from 'path';
import { directSpecifiers, resolveRepoLiteral, resolveRepoSpecifier } from './resolve-repo-path';
const ROOT = path.resolve(import.meta.dir, '../..');
describe('resolve-repo-path', () => {
test('relative specifiers resolve to repo files, with and without extensions', () => {
expect(resolveRepoSpecifier(ROOT, 'test/touchfiles.test.ts', './helpers/touchfiles')).toBe('test/helpers/touchfiles.ts');
expect(resolveRepoSpecifier(ROOT, 'test/touchfiles.test.ts', '../lib/eval-model')).toBe('lib/eval-model.ts');
expect(resolveRepoSpecifier(ROOT, 'lib/code-intelligence/gbrain-adapter.ts', '../egress-receipt.js')).toBe('lib/egress-receipt.ts');
expect(resolveRepoSpecifier(ROOT, 'test/touchfiles.test.ts', './helpers/no-such-module')).toBeNull();
});
test('bare packages and bun:/node: builtins never resolve', () => {
for (const specifier of ['bun:test', 'node:fs', 'fs', '@anthropic-ai/sdk', 'ts-morph']) {
expect(resolveRepoSpecifier(ROOT, 'test/touchfiles.test.ts', specifier)).toBeNull();
}
});
test('direct specifiers include imports, re-exports, require and literal dynamic import, not type-only imports', () => {
const source = [
"import { a } from './a';",
"import type { T } from './types';",
"export type { U } from './more-types';",
"export { b } from '../lib/b';",
"import {\n c,\n d,\n} from './multi';",
"import './side-effect';",
"const e = require('./e');",
"const f = await import('./f');",
"const g = await import(name);",
].join('\n');
expect(directSpecifiers(source)).toEqual(['./a', '../lib/b', './multi', './side-effect', './e', './f']);
});
test('path literals resolve only when the repo path exists', () => {
expect(resolveRepoLiteral(ROOT, 'bin/gstack-config')).toBe('bin/gstack-config');
expect(resolveRepoLiteral(ROOT, 'test/fixtures/')).toBe('test/fixtures');
expect(resolveRepoLiteral(ROOT, path.join('test', 'helpers', 'touchfiles.ts'))).toBe('test/helpers/touchfiles.ts');
expect(resolveRepoLiteral(ROOT, 'test/fixtures/no-such-fixture.json')).toBeNull();
expect(resolveRepoLiteral(ROOT, '/etc/passwd')).toBeNull();
expect(resolveRepoLiteral(ROOT, '../outside')).toBeNull();
});
});
+44
View File
@@ -0,0 +1,44 @@
/**
* Resolve a module specifier or path literal from a repo file to a repo-relative
* path, or null when it names no file in the checkout. Bare packages and
* `bun:` / `node:` builtins never resolve. Shared by the touchfile closure
* invariant and the test-of-test ratchet.
*/
import * as fs from 'fs';
import * as path from 'path';
const isFile = (candidate: string) => fs.existsSync(candidate) && fs.statSync(candidate).isFile();
/** A relative module specifier (`./x`, `../lib/y.js`) → repo-relative file, or null. */
export function resolveRepoSpecifier(root: string, fromFile: string, specifier: string): string | null {
if (!specifier.startsWith('.')) return null;
const base = path.resolve(path.dirname(path.join(root, fromFile)), specifier);
for (const candidate of [base, `${base}.ts`, base.replace(/\.js$/, '.ts'), `${base}.tsx`, path.join(base, 'index.ts')]) {
if (isFile(candidate)) return toRelative(root, candidate);
}
return null;
}
/** A repo-rooted path literal (`test/fixtures/x.json`, `bin/gstack-x`) → itself when it exists, else null. */
export function resolveRepoLiteral(root: string, literal: string): string | null {
const clean = literal.replace(/\/+$/, '');
if (!clean || path.isAbsolute(clean) || clean.startsWith('..')) return null;
return fs.existsSync(path.join(root, clean)) ? clean : null;
}
function toRelative(root: string, absolute: string): string | null {
const relative = path.relative(root, absolute).split(path.sep).join('/');
return relative.startsWith('..') ? null : relative;
}
/** Direct module specifiers of a source file: static imports/re-exports, require() and literal dynamic import(); `import type` excluded. */
export function directSpecifiers(source: string): string[] {
const out: string[] = [];
const typeOnly = /^\s*(?:import|export)\s+type\s/;
for (const match of source.matchAll(/^[ \t]*(?:import|export)\b[^;'"`]*?from\s*(['"])([^'"]+)\1|^[ \t]*import\s*(['"])([^'"]+)\3/gm)) {
if (typeOnly.test(match[0])) continue;
out.push(match[2] ?? match[4]!);
}
for (const match of source.matchAll(/\b(?:require|import)\s*\(\s*(['"])([^'"]+)\1\s*\)/g)) out.push(match[2]!);
return out;
}
+5 -13
View File
@@ -8,6 +8,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { matchGlob } from './test-selection';
import { resolveRepoLiteral, resolveRepoSpecifier } from './resolve-repo-path';
export interface ClosureEntry {
/** Repo-relative dependency path. */
@@ -19,15 +20,6 @@ export interface ClosureEntry {
const LITERAL = /test\/(?:helpers|fixtures)\/[A-Za-z0-9_.\-/]+[A-Za-z0-9_]/g;
const scanner = new Bun.Transpiler({ loader: 'tsx' });
function resolveImport(root: string, from: string, specifier: string): string | null {
if (!specifier.startsWith('.')) return null;
const base = path.resolve(path.dirname(path.join(root, from)), specifier);
for (const candidate of [base, `${base}.ts`, base.replace(/\.js$/, '.ts'), path.join(base, 'index.ts')]) {
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) return path.relative(root, candidate).split(path.sep).join('/');
}
return null;
}
/** Selection itself is diffed by map (diffTouchfileMaps), and test files are never dependencies. */
const SELECTION_MODULES = new Set(['test/helpers/touchfiles-data.ts', 'test/helpers/touchfiles.ts', 'test/helpers/test-selection.ts']);
const inScope = (file: string) => (file.startsWith('test/helpers/') || file.startsWith('test/fixtures/')) &&
@@ -47,15 +39,15 @@ export function paidTestClosure(root: string, testFile: string, boundary: Readon
let imports: Array<{ path: string }> = [];
try { imports = scanner.scanImports(source); } catch { imports = []; }
for (const { path: specifier } of imports) {
const target = resolveImport(root, file, specifier);
const target = resolveRepoSpecifier(root, file, specifier);
if (!target || !inScope(target) || seen.has(target)) continue;
seen.set(target, [...chain, target]);
queue.push({ file: target, chain: [...chain, target] });
}
for (const match of source.match(LITERAL) ?? []) {
const absolute = path.join(root, match);
if (!inScope(match) || !fs.existsSync(absolute)) continue;
const literal = fs.statSync(absolute).isDirectory() ? `${match.replace(/\/$/, '')}/**` : match;
const found = inScope(match) ? resolveRepoLiteral(root, match) : null;
if (!found) continue;
const literal = fs.statSync(path.join(root, found)).isDirectory() ? `${found}/**` : found;
if (seen.has(literal)) continue;
seen.set(literal, [...chain, `"${match}"`]);
if (!literal.endsWith('/**')) queue.push({ file: literal, chain: [...chain, `"${match}"`] });