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

125 lines
3.3 KiB
Go

package analysis
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
// buildEdgeGraph constructs a small in-memory graph from (from,to) call
// edges. Node IDs follow the "<pkg>/file.go::Sym" shape so packageOfID
// resolves a meaningful package directory.
func buildEdgeGraph(t *testing.T, edges [][2]string) graph.Store {
t.Helper()
g := graph.New()
seen := map[string]bool{}
add := func(id string) {
if seen[id] {
return
}
seen[id] = true
path := id
if i := indexOfSep(id); i >= 0 {
path = id[:i]
}
g.AddNode(&graph.Node{ID: id, Kind: graph.KindFunction, Name: id, FilePath: path})
}
for _, e := range edges {
add(e[0])
add(e[1])
}
for _, e := range edges {
g.AddEdge(&graph.Edge{From: e[0], To: e[1], Kind: graph.EdgeCalls})
}
return g
}
func indexOfSep(s string) int {
for i := 0; i+1 < len(s); i++ {
if s[i] == ':' && s[i+1] == ':' {
return i
}
}
return -1
}
func TestWalkCacheKeyDeterministic(t *testing.T) {
g := buildEdgeGraph(t, [][2]string{
{"pkg/a.go::A", "pkg/b.go::B"},
{"pkg/b.go::B", "pkg/c.go::C"},
})
s1 := BuildAdjacencySnapshot(g)
s2 := BuildAdjacencySnapshot(g)
k1 := s1.WalkCacheKey([]string{"pkg/a.go::A"}, 0)
k2 := s2.WalkCacheKey([]string{"pkg/a.go::A"}, 0)
if k1 == "" {
t.Fatal("expected non-empty key")
}
if k1 != k2 {
t.Fatalf("key not deterministic across identical snapshots: %q vs %q", k1, k2)
}
// Seed order must not matter (sorted internally).
if got := s1.WalkCacheKey([]string{"pkg/a.go::A", "pkg/b.go::B"}, 0); got == k1 {
t.Fatal("different seed set should produce a different key")
}
}
func TestWalkCacheKeyPackageInvalidation(t *testing.T) {
base := [][2]string{
{"pkg/a.go::A", "pkg/b.go::B"},
{"other/x.go::X", "other/y.go::Y"},
}
g1 := buildEdgeGraph(t, base)
s1 := BuildAdjacencySnapshot(g1)
seeds := []string{"other/x.go::X"}
k1 := s1.WalkCacheKey(seeds, 0)
// Change a package UNRELATED to the seed's dependency set (pkg/*).
// The seed walk (other/*) must keep the same key — the incremental
// property: an edit to pkg/ doesn't bust an other/ walk.
g2 := buildEdgeGraph(t, append([][2]string{
{"pkg/a.go::A", "pkg/b.go::B2new"},
}, base...))
s2 := BuildAdjacencySnapshot(g2)
k2 := s2.WalkCacheKey(seeds, 0)
if k1 != k2 {
t.Fatalf("unrelated-package edit must not change the walk key: %q vs %q", k1, k2)
}
// Now change the seed's OWN package: the key must change (miss).
g3 := buildEdgeGraph(t, [][2]string{
{"pkg/a.go::A", "pkg/b.go::B"},
{"other/x.go::X", "other/y.go::Ynew"}, // seed's out-edge target changed
})
s3 := BuildAdjacencySnapshot(g3)
k3 := s3.WalkCacheKey(seeds, 0)
if k1 == k3 {
t.Fatal("changing the seed's dependency package must change the walk key")
}
}
func TestPackageRootsPopulated(t *testing.T) {
g := buildEdgeGraph(t, [][2]string{
{"pkg/a.go::A", "pkg/b.go::B"},
{"other/x.go::X", "other/y.go::Y"},
})
s := BuildAdjacencySnapshot(g)
if s.PackageRootCount() < 2 {
t.Fatalf("expected >=2 package roots, got %d", s.PackageRootCount())
}
}
func TestPackageOfID(t *testing.T) {
cases := map[string]string{
"gortex/internal/mcp/server.go::NewServer": "gortex/internal/mcp",
"pkg/a.go::A": "pkg",
"top.go::Sym": "",
"a/b/c/f.go::S": "a/b/c",
}
for id, want := range cases {
if got := packageOfID(id); got != want {
t.Errorf("packageOfID(%q) = %q, want %q", id, got, want)
}
}
}