Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

276 lines
13 KiB
Go

package indexer
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
func TestMarkTestSymbolsAndEmitEdges_GoStyle(t *testing.T) {
g := graph.New()
// Two files: one prod, one test.
g.AddNode(&graph.Node{ID: "pkg/foo.go", Kind: graph.KindFile, Name: "pkg/foo.go", FilePath: "pkg/foo.go", Language: "go"})
g.AddNode(&graph.Node{ID: "pkg/foo_test.go", Kind: graph.KindFile, Name: "pkg/foo_test.go", FilePath: "pkg/foo_test.go", Language: "go"})
// Subject under test plus a helper, both in pkg/foo.go.
g.AddNode(&graph.Node{ID: "pkg/foo.go::Foo", Kind: graph.KindFunction, Name: "Foo", FilePath: "pkg/foo.go", Language: "go"})
g.AddNode(&graph.Node{ID: "pkg/foo.go::helper", Kind: graph.KindFunction, Name: "helper", FilePath: "pkg/foo.go", Language: "go"})
// Test function in pkg/foo_test.go.
g.AddNode(&graph.Node{ID: "pkg/foo_test.go::TestFoo", Kind: graph.KindFunction, Name: "TestFoo", FilePath: "pkg/foo_test.go", Language: "go"})
// Calls.
g.AddEdge(&graph.Edge{From: "pkg/foo_test.go::TestFoo", To: "pkg/foo.go::Foo", Kind: graph.EdgeCalls, FilePath: "pkg/foo_test.go", Line: 10})
g.AddEdge(&graph.Edge{From: "pkg/foo.go::Foo", To: "pkg/foo.go::helper", Kind: graph.EdgeCalls, FilePath: "pkg/foo.go", Line: 5})
marked, emitted := markTestSymbolsAndEmitEdges(g)
if marked != 1 {
t.Fatalf("expected 1 test symbol marked, got %d", marked)
}
if emitted != 1 {
t.Fatalf("expected 1 EdgeTests, got %d", emitted)
}
// TestFoo must be flagged.
testFn := g.GetNode("pkg/foo_test.go::TestFoo")
if isTest, _ := testFn.Meta["is_test"].(bool); !isTest {
t.Fatalf("TestFoo should be flagged is_test, got %v", testFn.Meta["is_test"])
}
// EdgeTests must point from TestFoo → Foo.
var found bool
for _, e := range g.AllEdges() {
if e.Kind == graph.EdgeTests && e.From == "pkg/foo_test.go::TestFoo" && e.To == "pkg/foo.go::Foo" {
found = true
if e.Line != 10 {
t.Errorf("EdgeTests line = %d, want 10", e.Line)
}
}
}
if !found {
t.Fatalf("EdgeTests TestFoo→Foo not found")
}
// Foo → helper is a prod-to-prod call; no EdgeTests should be emitted.
for _, e := range g.AllEdges() {
if e.Kind == graph.EdgeTests && e.From == "pkg/foo.go::Foo" {
t.Fatalf("unexpected EdgeTests from prod fn: %+v", e)
}
}
}
func TestMarkTestSymbolsAndEmitEdges_NameAloneIsNotEnough(t *testing.T) {
g := graph.New()
// A production file holding a function whose name happens to match
// the Go test prefix — e.g. TestRole in testpattern.go. go test
// never picks this up, so it must not be flagged.
g.AddNode(&graph.Node{ID: "pkg/p.go", Kind: graph.KindFile, Name: "pkg/p.go", FilePath: "pkg/p.go", Language: "go"})
g.AddNode(&graph.Node{ID: "pkg/p.go::TestRole", Kind: graph.KindFunction, Name: "TestRole", FilePath: "pkg/p.go", Language: "go"})
g.AddNode(&graph.Node{ID: "pkg/p.go::BenchmarkConfig", Kind: graph.KindFunction, Name: "BenchmarkConfig", FilePath: "pkg/p.go", Language: "go"})
marked, _ := markTestSymbolsAndEmitEdges(g)
if marked != 0 {
t.Fatalf("expected 0 test symbols marked for prod file, got %d", marked)
}
for _, id := range []string{"pkg/p.go::TestRole", "pkg/p.go::BenchmarkConfig"} {
n := g.GetNode(id)
if v, _ := n.Meta["is_test"].(bool); v {
t.Fatalf("%s in a non-test file must not be flagged is_test", id)
}
if _, ok := n.Meta["test_role"]; ok {
t.Fatalf("%s in a non-test file must not carry test_role", id)
}
}
}
func TestMarkTestSymbolsAndEmitEdges_RoleRefinement(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "x_test.go", Kind: graph.KindFile, Name: "x_test.go", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::TestA", Kind: graph.KindFunction, Name: "TestA", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::BenchmarkA", Kind: graph.KindFunction, Name: "BenchmarkA", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::FuzzA", Kind: graph.KindFunction, Name: "FuzzA", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::ExampleA", Kind: graph.KindFunction, Name: "ExampleA", FilePath: "x_test.go", Language: "go"})
// Support code in the test file with no recognised prefix → "test".
g.AddNode(&graph.Node{ID: "x_test.go::setup", Kind: graph.KindFunction, Name: "setup", FilePath: "x_test.go", Language: "go"})
markTestSymbolsAndEmitEdges(g)
want := map[string]string{
"x_test.go::TestA": "test",
"x_test.go::BenchmarkA": "benchmark",
"x_test.go::FuzzA": "fuzz",
"x_test.go::ExampleA": "example",
"x_test.go::setup": "test",
}
for id, role := range want {
got, _ := g.GetNode(id).Meta["test_role"].(string)
if got != role {
t.Errorf("%s: test_role = %q, want %q", id, got, role)
}
}
}
func TestMarkTestSymbolsAndEmitEdges_PythonStyle(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "app/svc.py", Kind: graph.KindFile, Name: "app/svc.py", FilePath: "app/svc.py", Language: "python"})
g.AddNode(&graph.Node{ID: "tests/test_svc.py", Kind: graph.KindFile, Name: "tests/test_svc.py", FilePath: "tests/test_svc.py", Language: "python"})
g.AddNode(&graph.Node{ID: "app/svc.py::greet", Kind: graph.KindFunction, Name: "greet", FilePath: "app/svc.py", Language: "python"})
g.AddNode(&graph.Node{ID: "tests/test_svc.py::test_greet", Kind: graph.KindFunction, Name: "test_greet", FilePath: "tests/test_svc.py", Language: "python"})
g.AddEdge(&graph.Edge{From: "tests/test_svc.py::test_greet", To: "app/svc.py::greet", Kind: graph.EdgeCalls, FilePath: "tests/test_svc.py", Line: 3})
_, emitted := markTestSymbolsAndEmitEdges(g)
if emitted != 1 {
t.Fatalf("expected 1 EdgeTests, got %d", emitted)
}
}
func TestMarkTestSymbolsAndEmitEdges_DropsTestToTestCalls(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "x_test.go", Kind: graph.KindFile, Name: "x_test.go", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::TestA", Kind: graph.KindFunction, Name: "TestA", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::setupFixture", Kind: graph.KindFunction, Name: "setupFixture", FilePath: "x_test.go", Language: "go"})
// Test calls a helper in the same test file — no EdgeTests should emit.
g.AddEdge(&graph.Edge{From: "x_test.go::TestA", To: "x_test.go::setupFixture", Kind: graph.EdgeCalls, FilePath: "x_test.go", Line: 4})
_, emitted := markTestSymbolsAndEmitEdges(g)
if emitted != 0 {
t.Fatalf("expected 0 EdgeTests for test→test call, got %d", emitted)
}
}
func TestMarkTestSymbolsAndEmitEdges_RustInlineCfgTest(t *testing.T) {
g := graph.New()
// A production-path Rust file: src/lib.rs is NOT a test file by name
// or directory, but it holds an inline `#[cfg(test)] mod tests`
// whose functions carry #[test].
g.AddNode(&graph.Node{ID: "src/lib.rs", Kind: graph.KindFile, Name: "src/lib.rs", FilePath: "src/lib.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "src/lib.rs::add", Kind: graph.KindFunction, Name: "add", FilePath: "src/lib.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "src/lib.rs::it_adds", Kind: graph.KindFunction, Name: "it_adds", FilePath: "src/lib.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "src/lib.rs::bench_add", Kind: graph.KindFunction, Name: "bench_add", FilePath: "src/lib.rs", Language: "rust"})
// Annotation nodes + EdgeAnnotated edges, as emitRustAnnotationEdges
// would emit them. #[test] → annotation::rust::test; #[bench] →
// annotation::rust::bench; #[cfg(test)] on the module → annotation::rust::cfg.
g.AddNode(&graph.Node{ID: "annotation::rust::test", Kind: graph.KindType, Name: "test", FilePath: "src/lib.rs", Language: "rust", Meta: map[string]any{"kind": "annotation", "synthetic": true}})
g.AddNode(&graph.Node{ID: "annotation::rust::bench", Kind: graph.KindType, Name: "bench", FilePath: "src/lib.rs", Language: "rust", Meta: map[string]any{"kind": "annotation", "synthetic": true}})
g.AddNode(&graph.Node{ID: "annotation::rust::cfg", Kind: graph.KindType, Name: "cfg", FilePath: "src/lib.rs", Language: "rust", Meta: map[string]any{"kind": "annotation", "synthetic": true}})
g.AddEdge(&graph.Edge{From: "src/lib.rs::it_adds", To: "annotation::rust::test", Kind: graph.EdgeAnnotated, FilePath: "src/lib.rs", Line: 12})
g.AddEdge(&graph.Edge{From: "src/lib.rs::bench_add", To: "annotation::rust::bench", Kind: graph.EdgeAnnotated, FilePath: "src/lib.rs", Line: 18})
// The test calls the subject under test.
g.AddEdge(&graph.Edge{From: "src/lib.rs::it_adds", To: "src/lib.rs::add", Kind: graph.EdgeCalls, FilePath: "src/lib.rs", Line: 13})
marked, emitted := markTestSymbolsAndEmitEdges(g)
if marked != 2 {
t.Fatalf("expected 2 annotation-driven test symbols marked, got %d", marked)
}
if emitted != 1 {
t.Fatalf("expected 1 EdgeTests (it_adds→add), got %d", emitted)
}
itAdds := g.GetNode("src/lib.rs::it_adds")
if v, _ := itAdds.Meta["is_test"].(bool); !v {
t.Fatalf("it_adds (#[test] in prod file) should be flagged is_test")
}
if r, _ := itAdds.Meta["test_role"].(string); r != "test" {
t.Errorf("it_adds test_role = %q, want test", r)
}
if r, _ := itAdds.Meta["test_runner"].(string); r != "cargo-test" {
t.Errorf("it_adds test_runner = %q, want cargo-test", r)
}
bench := g.GetNode("src/lib.rs::bench_add")
if r, _ := bench.Meta["test_role"].(string); r != "benchmark" {
t.Errorf("bench_add test_role = %q, want benchmark", r)
}
// The non-test production function must NOT be flagged.
if v, _ := g.GetNode("src/lib.rs::add").Meta["is_test"].(bool); v {
t.Fatalf("add (no test annotation) must not be flagged is_test")
}
// EdgeTests must point it_adds → add.
var found bool
for _, e := range g.AllEdges() {
if e.Kind == graph.EdgeTests && e.From == "src/lib.rs::it_adds" && e.To == "src/lib.rs::add" {
found = true
}
}
if !found {
t.Fatalf("EdgeTests it_adds→add not found")
}
}
func TestMarkTestSymbolsAndEmitEdges_JavaAtTestInProdPathFile(t *testing.T) {
g := graph.New()
// Service.java is a production-path file (no Test/Tests suffix) but
// holds a @Test method — JUnit discovers it by annotation.
g.AddNode(&graph.Node{ID: "src/Service.java", Kind: graph.KindFile, Name: "src/Service.java", FilePath: "src/Service.java", Language: "java"})
g.AddNode(&graph.Node{ID: "src/Service.java::Service.handle", Kind: graph.KindMethod, Name: "handle", FilePath: "src/Service.java", Language: "java"})
g.AddNode(&graph.Node{ID: "src/Service.java::Service.shouldHandle", Kind: graph.KindMethod, Name: "shouldHandle", FilePath: "src/Service.java", Language: "java"})
g.AddNode(&graph.Node{ID: "annotation::java::Test", Kind: graph.KindType, Name: "Test", FilePath: "src/Service.java", Language: "java", Meta: map[string]any{"kind": "annotation", "synthetic": true}})
g.AddEdge(&graph.Edge{From: "src/Service.java::Service.shouldHandle", To: "annotation::java::Test", Kind: graph.EdgeAnnotated, FilePath: "src/Service.java", Line: 20})
g.AddEdge(&graph.Edge{From: "src/Service.java::Service.shouldHandle", To: "src/Service.java::Service.handle", Kind: graph.EdgeCalls, FilePath: "src/Service.java", Line: 21})
marked, emitted := markTestSymbolsAndEmitEdges(g)
if marked != 1 {
t.Fatalf("expected 1 @Test method marked, got %d", marked)
}
if emitted != 1 {
t.Fatalf("expected 1 EdgeTests, got %d", emitted)
}
m := g.GetNode("src/Service.java::Service.shouldHandle")
if v, _ := m.Meta["is_test"].(bool); !v {
t.Fatalf("@Test method should be flagged is_test")
}
if r, _ := m.Meta["test_runner"].(string); r != "junit" {
t.Errorf("test_runner = %q, want junit", r)
}
// A plain @Component / non-test annotation must not flag the symbol.
if v, _ := g.GetNode("src/Service.java::Service.handle").Meta["is_test"].(bool); v {
t.Fatalf("non-annotated production method must not be flagged is_test")
}
}
func TestMarkTestSymbolsAndEmitEdges_NonTestAnnotationIgnored(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "src/m.rs", Kind: graph.KindFile, Name: "src/m.rs", FilePath: "src/m.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "src/m.rs::Widget", Kind: graph.KindType, Name: "Widget", FilePath: "src/m.rs", Language: "rust"})
// #[derive(Debug)] is an annotation but not a test.
g.AddNode(&graph.Node{ID: "annotation::rust::Debug", Kind: graph.KindType, Name: "Debug", FilePath: "src/m.rs", Language: "rust", Meta: map[string]any{"kind": "annotation", "synthetic": true}})
g.AddEdge(&graph.Edge{From: "src/m.rs::Widget", To: "annotation::rust::Debug", Kind: graph.EdgeAnnotated, FilePath: "src/m.rs", Line: 3})
marked, _ := markTestSymbolsAndEmitEdges(g)
if marked != 0 {
t.Fatalf("non-test annotation must not flag any symbol, got %d marked", marked)
}
}
func TestMarkTestSymbolsAndEmitEdges_DedupesParallelCalls(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "x_test.go", Kind: graph.KindFile, Name: "x_test.go", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x.go", Kind: graph.KindFile, Name: "x.go", FilePath: "x.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x_test.go::TestA", Kind: graph.KindFunction, Name: "TestA", FilePath: "x_test.go", Language: "go"})
g.AddNode(&graph.Node{ID: "x.go::F", Kind: graph.KindFunction, Name: "F", FilePath: "x.go", Language: "go"})
// Same call twice on different lines (the test calls F at line 5 and 12).
g.AddEdge(&graph.Edge{From: "x_test.go::TestA", To: "x.go::F", Kind: graph.EdgeCalls, FilePath: "x_test.go", Line: 5})
g.AddEdge(&graph.Edge{From: "x_test.go::TestA", To: "x.go::F", Kind: graph.EdgeCalls, FilePath: "x_test.go", Line: 12})
_, emitted := markTestSymbolsAndEmitEdges(g)
if emitted != 1 {
t.Fatalf("expected 1 deduped EdgeTests, got %d", emitted)
}
}