Skip to content

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

Closed

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.

Activity

  1. maxmilian commented on Aug 5, 2026

    @maxmilian
    Contributor

    Confirmed on upstream/main (c65d56c) — and the fix you describe is already open as #1273, which is probably the most useful thing I can add here.

    #1273 ("fix(affected): recognize root-level test files", opened 2026-07-13) does exactly what this issue asks for: it deletes the six inline patterns and calls the shared predicate.

    -      const defaultTestPatterns = [ /\.spec\./, /\.test\./, ... ];
    -      function isTestFile(filePath: string): boolean {
    -        if (customFilter) return customFilter.test(filePath);
    -        return defaultTestPatterns.some(p => p.test(filePath));
    +      function matchesTestFile(filePath: string): boolean {
    +        if (customFilter) return customFilter(filePath);
    +        return isDefaultTestFile(filePath);   // from ../search/query-utils
           }

    It fixes your Go case, but nothing about it says so — the title and tests are about a root-level tests/ Python directory, so this issue and that PR read as unrelated. The Go fix falls out of the shared predicate's separator rule, /[._-](test|tests|spec|specs)\.[a-z0-9]+$/, which math_test.go matches on the _.

    I ran both predicates against the same inputs at c65d56c rather than reasoning from the regexes:

    path inline (affected) shared (query-utils)
    math_test.go false true
    tests/test_math_utils.py false true
    FooTest.kt false true
    math.test.ts true true
    math.go false false

    Three things that adds to the report:

    • Go is not the only language affected. FooTest.kt / BarTests.swift / BazSpec.scala / QuxTestCase.java all miss too — the inline list has no CamelCase-suffix rule at all. So affected silently reports "no tests" for most of the JVM and Swift ecosystems, not just Go.
    • tests/test_math_utils.py fails for a second, independent reason: /\/tests?\// requires slashes on both sides, so a repo-root tests/ directory never matches. That is fix(affected): honor custom filter glob semantics #1273's original case, and it is the same one-line-import fix.
    • No over-matching in the other direction: math.go stays false on both, so swapping the predicate doesn't turn ordinary source files into "tests".

    So I don't think this needs a second PR — it needs #1273 reviewed. It has been open three weeks with no review, is +26/−28 across one source file and one test file, and closes at least two issues rather than one. @colbymchenry if it helps, the smallest possible check is the table above: the shared predicate is already what explore / node / search use, so this is removing a divergence rather than introducing a rule.

    One thing #1273 does not carry is a Go regression test — its two new tests are Python. Worth adding math.go + math_test.go to __tests__/cli-affected-paths.test.ts before merge, since Go is the shape reported here. Happy to send that as a follow-up commit to #1273 if @Jseca666 would rather not reopen the branch, or leave it to whoever reviews.

    (Checked against upstream/main at c65d56c; the inline defaultTestPatterns block is still live at src/bin/codegraph.ts:2153.)

  2. colbymchenry commented on Sep 8, 2026

    @colbymchenry
    Owner

    Fix landed

    Landing PR: #1803 (cherry-picks upstream #1688 by @danusha2345 onto a Forge branch).

    Root cause

    codegraph affected kept six inline JS/TS test regexes (.test., .spec., /tests/, …) instead of the shared isTestPath from search/query-utils, so Go math_test.go never matched the default predicate.

    Linux verify (fail → pass)

    On Linux (9181dd1e → bc8ff11e):

    FAIL (main):

    $ codegraph affected math.go
    ℹ No test files affected by the changed files.
    
    $ codegraph affected --filter '*_test.go' math.go
    Affected test files (1):
      math_test.go
    

    PASS (PR #1803):

    $ codegraph affected math.go
    Affected test files (1):
      math_test.go
    

    Vitest: 36 passed (cli-affected-test-conventions + related). Default --depth 5 left unchanged — the secondary transitive over-reporting with --filter on large Go trees is residual / out of scope for this fix.

    Closing as completed. Upstream #1688 will be closed as superseded by #1803.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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