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
177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
package semantic
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
func TestSymbolMap(t *testing.T) {
|
|
m := NewSymbolMap()
|
|
|
|
m.Add("scip::Foo", "main.go::Foo")
|
|
m.Add("scip::Bar", "lib.go::Bar")
|
|
|
|
assert.Equal(t, 2, m.Size())
|
|
|
|
id, ok := m.GortexID("scip::Foo")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "main.go::Foo", id)
|
|
|
|
ext, ok := m.ExternalID("lib.go::Bar")
|
|
assert.True(t, ok)
|
|
assert.Equal(t, "scip::Bar", ext)
|
|
|
|
_, ok = m.GortexID("scip::Unknown")
|
|
assert.False(t, ok)
|
|
}
|
|
|
|
func TestMatchNodeByFileLine(t *testing.T) {
|
|
g := graph.New()
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go::main", Kind: graph.KindFunction, Name: "main",
|
|
FilePath: "main.go", StartLine: 10, EndLine: 20,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go::helper", Kind: graph.KindFunction, Name: "helper",
|
|
FilePath: "main.go", StartLine: 22, EndLine: 30,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go", Kind: graph.KindFile, Name: "main.go",
|
|
FilePath: "main.go", StartLine: 1, EndLine: 30,
|
|
})
|
|
|
|
// Exact start line match.
|
|
n := MatchNodeByFileLine(g, "main.go", 10)
|
|
assert.NotNil(t, n)
|
|
assert.Equal(t, "main.go::main", n.ID)
|
|
|
|
// Within range.
|
|
n = MatchNodeByFileLine(g, "main.go", 15)
|
|
assert.NotNil(t, n)
|
|
assert.Equal(t, "main.go::main", n.ID)
|
|
|
|
// Second function.
|
|
n = MatchNodeByFileLine(g, "main.go", 25)
|
|
assert.NotNil(t, n)
|
|
assert.Equal(t, "main.go::helper", n.ID)
|
|
|
|
// Line in gap between functions — may or may not find a match.
|
|
n = MatchNodeByFileLine(g, "main.go", 21)
|
|
// Within tolerance of 2 lines from helper (line 22), should find it.
|
|
if n != nil {
|
|
assert.Equal(t, "main.go::helper", n.ID)
|
|
}
|
|
}
|
|
|
|
func TestMatchNodeByQualName(t *testing.T) {
|
|
g := graph.New()
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go::Foo", Kind: graph.KindFunction, Name: "Foo",
|
|
QualName: "github.com/test/pkg.Foo", FilePath: "main.go",
|
|
})
|
|
|
|
n := MatchNodeByQualName(g, "github.com/test/pkg.Foo")
|
|
assert.NotNil(t, n)
|
|
assert.Equal(t, "main.go::Foo", n.ID)
|
|
|
|
n = MatchNodeByQualName(g, "unknown")
|
|
assert.Nil(t, n)
|
|
}
|
|
|
|
func TestMatchNodeByNameInFile(t *testing.T) {
|
|
g := graph.New()
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go::Foo", Kind: graph.KindFunction, Name: "Foo",
|
|
FilePath: "main.go",
|
|
})
|
|
|
|
n := MatchNodeByNameInFile(g, "Foo", "main.go")
|
|
assert.NotNil(t, n)
|
|
assert.Equal(t, "main.go::Foo", n.ID)
|
|
|
|
n = MatchNodeByNameInFile(g, "Foo", "other.go")
|
|
assert.Nil(t, n)
|
|
}
|
|
|
|
func TestParseGortexID(t *testing.T) {
|
|
tests := []struct {
|
|
id string
|
|
wantFile string
|
|
wantSym string
|
|
}{
|
|
{"main.go::Foo", "main.go", "Foo"},
|
|
{"pkg/auth/token.go::ValidateToken", "pkg/auth/token.go", "ValidateToken"},
|
|
{"main.go", "main.go", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
file, sym := ParseGortexID(tt.id)
|
|
assert.Equal(t, tt.wantFile, file, "file for %s", tt.id)
|
|
assert.Equal(t, tt.wantSym, sym, "sym for %s", tt.id)
|
|
}
|
|
}
|
|
|
|
func TestNormalizeFilePath(t *testing.T) {
|
|
result := NormalizeFilePath("/home/user/repo/pkg/foo.go", "/home/user/repo")
|
|
assert.Equal(t, "pkg/foo.go", result)
|
|
}
|
|
|
|
func TestMatchCallableByFileLine(t *testing.T) {
|
|
g := graph.New()
|
|
// Function with a param decoy sharing its declaration line — the
|
|
// zero-height param span wins MatchNodeByFileLine's innermost tie,
|
|
// which is exactly what the callable matcher must NOT return.
|
|
g.AddNode(&graph.Node{
|
|
ID: "a.go::TestFoo", Kind: graph.KindFunction, Name: "TestFoo",
|
|
FilePath: "a.go", StartLine: 10, EndLine: 20,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "a.go::TestFoo#param:t", Kind: graph.KindParam, Name: "t",
|
|
FilePath: "a.go", StartLine: 10, EndLine: 10,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "a.go::TestFoo#closure:1", Kind: graph.KindClosure, Name: "closure",
|
|
FilePath: "a.go", StartLine: 14, EndLine: 16,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "a.go::Recv.M", Kind: graph.KindMethod, Name: "M",
|
|
FilePath: "a.go", StartLine: 25, EndLine: 27,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "a.go::topVar", Kind: graph.KindVariable, Name: "topVar",
|
|
FilePath: "a.go", StartLine: 40, EndLine: 40,
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "a.go", Kind: graph.KindFile, Name: "a.go",
|
|
FilePath: "a.go", StartLine: 1, EndLine: 50,
|
|
})
|
|
|
|
cases := []struct {
|
|
name string
|
|
line int
|
|
want string // "" = nil expected
|
|
}{
|
|
{"declaration line beats param decoy", 10, "a.go::TestFoo"},
|
|
{"body line inside function", 12, "a.go::TestFoo"},
|
|
{"innermost closure wins inside its span", 15, "a.go::TestFoo#closure:1"},
|
|
{"method declaration line", 25, "a.go::Recv.M"},
|
|
{"near-miss within tolerance snaps to method", 23, "a.go::Recv.M"},
|
|
{"variable line far from any callable", 40, ""},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
n := MatchCallableByFileLine(g, "a.go", tc.line)
|
|
if tc.want == "" {
|
|
assert.Nil(t, n)
|
|
return
|
|
}
|
|
if assert.NotNil(t, n) {
|
|
assert.Equal(t, tc.want, n.ID)
|
|
}
|
|
})
|
|
}
|
|
}
|