Files
codegraph/__tests__/prod-to-test-suite.test.ts
Colby Mchenry de9f346ceb fix(resolution): production code never names a test suite's symbol (#2209)
A test suite is not linked into the program, so a name match from
production code into one is coincidence - but only Strategy 3's receiver
guess (#2171) ruled it out. Now no strategy's name match reaches a symbol
in a test suite from a non-test file: a test source set (`src/test/`,
`jvmTest/`), a `tests/` / `__tests__/` / `spec/` / `e2e/` directory, a
`FooTest.kt` / `test_foo.py` / `foo.test.ts` / `foo_spec.rb` file.
Test-support code a project ships - `testing/`, `fakes/`, `mocks/`, a
`*-test` module - stays in reach, and `…Spec.java` (halo's `IndexSpecs`,
okhttp's `ConnectionSpec`), `post-spec.ts` models and `useTests.ts` hooks
are not test suites.

typeorm -64 (`Record<K, V>` -> a test entity), okhttp -164 (117 sample
`@Override` -> a test's nested `Override`), kotlinx.coroutines -318
(`import kotlinx.coroutines.*` onto one test file's package node), sttp -62,
Exposed -92, halo 0, element-plus -36 (`isClient` -> a test's vueuse mock),
excalidraw -10 (`actionManager.executeAction` -> the test `API` helper),
tokio -30, trpc -36, AutoMapper -37, netbox -22, cats -21, mealie -10,
pytest -8, Alamofire -7; zod, eShop, react-native-reusables unchanged.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 21:52:10 +00:00

87 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* A test suite is not linked into the program, so production code never
* means one of its symbols by name: typeorm's `Record<string, …>` bound to a
* test entity `Record`, okhttp's samples' `@Override` to a test's nested
* `Override`. Test-support code a project ships (`testing/`) stays in reach,
* and a test still reaches another test's helpers.
*/
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { CodeGraph } from '../src';
let root = '';
let cg: CodeGraph;
beforeAll(async () => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-prod-test-'));
const files: Record<string, string> = {
'package.json': JSON.stringify({ name: 'orm', private: true }),
'test/functional/entity/Record.ts': `export class Record {
id = 0;
}
`,
'test/functional/helpers.ts': `export function makeFixture(): number {
return 1;
}
`,
'test/functional/record.test.ts': `export function uses(): number {
return makeFixture();
}
`,
'src/testing/stub.ts': `export function stubConnection(): number {
return 2;
}
`,
'src/columns.ts': `export function toJson(values: Record<string, number>): string {
return JSON.stringify(values);
}
export function connect(): number {
return stubConnection();
}
`,
'src/main/java/app/Service.java': `package app;
public class Service implements Runnable {
@Override
public void run() { }
}
`,
'src/test/java/app/InterceptorTest.java': `package app;
public class InterceptorTest {
@interface Override { }
}
`,
};
for (const [rel, content] of Object.entries(files)) {
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
fs.writeFileSync(path.join(root, rel), content);
}
cg = await CodeGraph.init(root, { index: true });
});
afterAll(() => {
cg?.close();
if (root) fs.rmSync(root, { recursive: true, force: true });
});
const targetsFrom = (file: string) => {
const ids = cg.getNodesInFile(file).map((n) => n.id);
return cg.getOutgoingEdgesFrom(ids).filter((e) => e.kind !== 'contains').map((e) => cg.getNode(e.target)!.filePath);
};
describe('production code and test suites', () => {
it('production code never names a test suite’s symbol', () => {
expect(targetsFrom('src/columns.ts')).not.toContain('test/functional/entity/Record.ts');
expect(targetsFrom('src/main/java/app/Service.java')).not.toContain('src/test/java/app/InterceptorTest.java');
});
it('shipped test support stays in reach, and tests reach each other', () => {
expect(targetsFrom('src/columns.ts')).toContain('src/testing/stub.ts');
expect(targetsFrom('test/functional/record.test.ts')).toContain('test/functional/helpers.ts');
});
});