Readme updated and detect test files in kotlin and swift
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
[](https://www.npmjs.com/package/@colbymchenry/codegraph)
|
[](https://www.npmjs.com/package/@colbymchenry/codegraph)
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://nodejs.org/)
|
[](https://nodejs.org/)
|
||||||
|
|
||||||
[](#)
|
[](#)
|
||||||
[](#)
|
[](#)
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* isTestFile heuristic — test-file detection used to deprioritize test code in
|
||||||
|
* search/explore ranking.
|
||||||
|
*
|
||||||
|
* Regression coverage for the cold-query fix: the heuristic previously only
|
||||||
|
* knew Java/JS/Python conventions, so Kotlin (`*Test.kt`, `jvmTest/`), Swift
|
||||||
|
* (`*Tests.swift`), and camelCase test source-set dirs slipped through — which
|
||||||
|
* let OkHttp's tests flood `codegraph_explore` results on a plain-language
|
||||||
|
* query. The false-positive guards matter just as much: `latest.kt` /
|
||||||
|
* `manifest.kt` / a `RealCall.kt` production file must NOT be flagged.
|
||||||
|
*/
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { isTestFile } from '../src/search/query-utils';
|
||||||
|
|
||||||
|
describe('isTestFile', () => {
|
||||||
|
it('flags Kotlin test files and source sets', () => {
|
||||||
|
expect(isTestFile('okhttp/src/jvmTest/kotlin/okhttp3/CallTest.kt')).toBe(true);
|
||||||
|
expect(isTestFile('okhttp/src/commonTest/kotlin/okhttp3/CompressionInterceptorTest.kt')).toBe(true);
|
||||||
|
expect(isTestFile('app/src/androidTest/java/com/example/FooTest.kt')).toBe(true);
|
||||||
|
expect(isTestFile('module/src/integrationTest/kotlin/BarSpec.kt')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('flags Swift test files', () => {
|
||||||
|
expect(isTestFile('Tests/SessionTests.swift')).toBe(true);
|
||||||
|
expect(isTestFile('Sources/FooTest.swift')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still flags the previously-supported conventions', () => {
|
||||||
|
expect(isTestFile('foo/test_bar.py')).toBe(true);
|
||||||
|
expect(isTestFile('pkg/bar_test.go')).toBe(true);
|
||||||
|
expect(isTestFile('src/foo.test.ts')).toBe(true);
|
||||||
|
expect(isTestFile('src/foo.spec.ts')).toBe(true);
|
||||||
|
expect(isTestFile('com/example/FooTest.java')).toBe(true);
|
||||||
|
expect(isTestFile('com/example/FooTestCase.java')).toBe(true);
|
||||||
|
expect(isTestFile('project/__tests__/foo.ts')).toBe(true);
|
||||||
|
expect(isTestFile('project/tests/foo.rb')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does NOT flag production files that merely contain "test" lowercase', () => {
|
||||||
|
// The fix is capital-led so camelCase boundaries distinguish these.
|
||||||
|
expect(isTestFile('src/latest/loader.kt')).toBe(false);
|
||||||
|
expect(isTestFile('lib/manifest.kt')).toBe(false);
|
||||||
|
expect(isTestFile('okhttp/src/jvmMain/kotlin/okhttp3/internal/connection/RealCall.kt')).toBe(false);
|
||||||
|
expect(isTestFile('src/contestEntry.ts')).toBe(false);
|
||||||
|
expect(isTestFile('pkg/greatest.go')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does NOT flag ordinary production source', () => {
|
||||||
|
expect(isTestFile('src/flask/app.py')).toBe(false);
|
||||||
|
expect(isTestFile('src/vs/workbench/api/common/extensionHostMain.ts')).toBe(false);
|
||||||
|
expect(isTestFile('okhttp/src/commonJvmAndroid/kotlin/okhttp3/OkHttpClient.kt')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
+32
-25
@@ -207,36 +207,43 @@ export function scorePathRelevance(filePath: string, query: string): number {
|
|||||||
*/
|
*/
|
||||||
export function isTestFile(filePath: string): boolean {
|
export function isTestFile(filePath: string): boolean {
|
||||||
const lower = filePath.toLowerCase();
|
const lower = filePath.toLowerCase();
|
||||||
const fileName = path.basename(lower);
|
const fileName = path.basename(filePath); // original case — needed for camelCase boundaries
|
||||||
|
const lowerName = fileName.toLowerCase();
|
||||||
|
|
||||||
// Common test file patterns
|
// --- Filename patterns ---
|
||||||
return (
|
if (
|
||||||
fileName.startsWith('test_') ||
|
lowerName.startsWith('test_') || // python: test_foo.py
|
||||||
fileName.startsWith('test.') ||
|
lowerName.startsWith('test.') ||
|
||||||
fileName.endsWith('.test.ts') ||
|
// separator-delimited: foo_test.go, foo.test.ts, foo-spec.rb, bar_spec.py
|
||||||
fileName.endsWith('.test.js') ||
|
/[._-](test|tests|spec|specs)\.[a-z0-9]+$/.test(lowerName) ||
|
||||||
fileName.endsWith('.test.tsx') ||
|
// CamelCase suffix (Java/Kotlin/Swift/C#/Scala): FooTest.kt, BarTests.swift,
|
||||||
fileName.endsWith('.test.jsx') ||
|
// BazSpec.scala, QuxTestCase.java. Capital-led so "latest.kt"/"manifest.kt"
|
||||||
fileName.endsWith('.spec.ts') ||
|
// (lowercase "test") are NOT matched.
|
||||||
fileName.endsWith('.spec.js') ||
|
/(?:Test|Tests|TestCase|Tester|Spec|Specs)\.[A-Za-z0-9]+$/.test(fileName)
|
||||||
fileName.endsWith('_test.go') ||
|
) {
|
||||||
fileName.endsWith('_test.py') ||
|
return true;
|
||||||
fileName.endsWith('_test.rs') ||
|
}
|
||||||
fileName.endsWith('Tests.java') ||
|
|
||||||
fileName.endsWith('Test.java') ||
|
// --- Directory patterns ---
|
||||||
fileName.endsWith('Tester.java') ||
|
if (
|
||||||
fileName.endsWith('TestCase.java') ||
|
lower.includes('/tests/') || lower.includes('/test/') ||
|
||||||
lower.includes('/tests/') ||
|
lower.includes('/__tests__/') || lower.includes('/spec/') ||
|
||||||
lower.includes('/test/') ||
|
lower.includes('/specs/') || lower.includes('/testlib/') ||
|
||||||
lower.includes('/__tests__/') ||
|
|
||||||
lower.includes('/spec/') ||
|
|
||||||
lower.includes('/testlib/') ||
|
|
||||||
lower.includes('/testing/') ||
|
lower.includes('/testing/') ||
|
||||||
|
lower.startsWith('test/') || lower.startsWith('tests/') ||
|
||||||
|
lower.startsWith('spec/') || lower.startsWith('specs/') ||
|
||||||
|
// CamelCase test source-set dirs (Kotlin Multiplatform / Gradle / Xcode):
|
||||||
|
// jvmTest/, commonTest/, androidTest/, iosTest/, integrationTest/. Capital-led
|
||||||
|
// so "latest/" / "manifest/" are not matched.
|
||||||
|
/(?:^|\/)[A-Za-z0-9]*(?:Test|Tests|Spec)\//.test(filePath)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Non-production directories: examples, samples, benchmarks, fixtures, demos.
|
// Non-production directories: examples, samples, benchmarks, fixtures, demos.
|
||||||
// Check both mid-path (/integration/) and start-of-path (integration/) since
|
// Check both mid-path (/integration/) and start-of-path (integration/) since
|
||||||
// file paths may be stored as relative paths without a leading slash.
|
// file paths may be stored as relative paths without a leading slash.
|
||||||
matchesNonProductionDir(lower)
|
return matchesNonProductionDir(lower);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user