Skip to content

Go: codegraph affected never matches *_test.go — the CLI-local isTestFile duplicates and diverges from search/query-utils.ts #1507

Description

@nextCY

Env: codegraph 1.5.0 (win32-x64).

codegraph affected reports no tests for a Go file that has a sibling _test.go. It exits 0 with a plausible message, so it reads as "this code has no test coverage" rather than as a failure.

Repro

Two-file Go project:

// go.mod: module example.com/demo

// math.go
package demo
func Add(a, b int) int { return a + b }

// math_test.go
package demo
import "testing"
func TestAdd(t *testing.T) { if Add(1, 2) != 3 { t.Fatal("boom") } }
$ codegraph init .
● 5 nodes, 4 edges in 271ms

$ codegraph affected math.go
ℹ No test files affected by the changed files.          # <- expected math_test.go

$ codegraph affected --filter '*_test.go' math.go
Affected test files (1):
  math_test.go                                          # <- found, so only the predicate is wrong

The graph itself knows perfectly well:

$ codegraph node Add
**Called by ←** TestAdd (math_test.go:5)

Same shape in TypeScript works with the default glob, which isolates it to the Go naming convention:

# math.ts + math.test.ts
$ codegraph affected math.ts
Affected test files (1):
  math.test.ts

Root cause

The affected command defines its own isTestFile inline instead of importing the shared one. In src/bin/codegraph.ts, inside the affected action:

const defaultTestPatterns = [
    /\.spec\./, /\.test\./, /\/__tests__\//, /\/tests?\//, /\/e2e\//, /\/spec\//,
];
function isTestFile(filePath) {
    if (customFilter) return customFilter.test(filePath);
    return defaultTestPatterns.some(p => p.test(filePath));
}

All six are JS/TS conventions. Go uses an underscore suffix — math_test.go — which matches none of them (/\.test\./ needs a dot on both sides; Go has _test.).

Meanwhile src/search/query-utils.ts already ships a correct multi-language isTestFile, which is what explore / node / search use:

/[._-](test|tests|spec|specs)\.[a-z0-9]+$/.test(lowerName)   // math_test.go, math.test.ts, math-spec.rb
lowerName.startsWith('test_')                                 // test_math.py
/(?:Test|Tests|TestCase|Tester|Spec|Specs)\.[A-Za-z0-9]+$/    // MathTest.kt, MathTests.swift

Its comments name foo_test.go explicitly, and it even guards against false positives like latest.kt / manifest.kt. So affected is the only blind path — it never asks the shared helper.

Probably why this survived: affected is CLI-only (there is no codegraph_affected MCP tool), so agents never exercise it, and agents are the primary consumer.

Secondary: --filter uncovers an unrelated over-reporting problem

On a real-world Go repo (a few hundred files), working around the bug with --filter '*_test.go' swings the other way — the result includes test files from packages with no relationship to the changed file.

--filter only swaps the is-this-a-test predicate; the dependent BFS still runs at the default --depth 5, and five hops of transitive dependents reach most of a Go tree. The broken default glob was masking this, so the two problems cancelled into empty-but-believable output.

Suggested fix

Delete the local defaultTestPatterns / isTestFile in src/bin/codegraph.ts and import isTestFile from src/search/query-utils.ts. That one already covers Go / Python / Java / Kotlin / Swift / C# / Scala, so this is a one-line import rather than a second pattern list to keep in sync.

For the second half, a smaller default --depth for affected — or a note that 5 is very wide for compiled languages where package-level dependents fan out fast — would make the output usable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions