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
231 lines
6.5 KiB
Go
231 lines
6.5 KiB
Go
package scip
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
func TestSCIPProtobufRoundTrip(t *testing.T) {
|
|
index := &SCIPIndex{
|
|
Documents: []SCIPDocument{
|
|
{
|
|
RelativePath: "main.go",
|
|
Occurrences: []SCIPOccurrence{
|
|
{
|
|
Range: []int32{9, 5, 9, 9}, // line 10 (0-indexed=9), col 5-9
|
|
Symbol: "scip-go gomod example.com v1.0.0 main.Foo().",
|
|
SymbolRoles: 1, // Definition
|
|
},
|
|
{
|
|
Range: []int32{14, 2, 14, 5},
|
|
Symbol: "scip-go gomod example.com v1.0.0 main.Bar().",
|
|
SymbolRoles: 0, // Reference
|
|
},
|
|
},
|
|
Symbols: []SCIPSymbolInfo{
|
|
{
|
|
Symbol: "scip-go gomod example.com v1.0.0 main.Foo().",
|
|
Documentation: []string{"func Foo() error"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Encode to protobuf.
|
|
data := encodeSCIPForTesting(index)
|
|
require.NotEmpty(t, data)
|
|
|
|
// Decode back.
|
|
decoded, err := decodeSCIPProtobuf(data)
|
|
require.NoError(t, err)
|
|
require.Len(t, decoded.Documents, 1)
|
|
|
|
doc := decoded.Documents[0]
|
|
assert.Equal(t, "main.go", doc.RelativePath)
|
|
require.Len(t, doc.Occurrences, 2)
|
|
|
|
// Check definition.
|
|
assert.Equal(t, int32(9), doc.Occurrences[0].Range[0])
|
|
assert.True(t, doc.Occurrences[0].IsDefinition())
|
|
assert.Equal(t, 10, doc.Occurrences[0].StartLine()) // 0-indexed → 1-indexed
|
|
|
|
// Check reference.
|
|
assert.False(t, doc.Occurrences[1].IsDefinition())
|
|
|
|
// Check symbol info.
|
|
require.Len(t, doc.Symbols, 1)
|
|
assert.Contains(t, doc.Symbols[0].Documentation[0], "func Foo()")
|
|
}
|
|
|
|
func TestSCIPJSONParsing(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
jsonFile := filepath.Join(tmpDir, "index.scip")
|
|
|
|
jsonData := `{
|
|
"documents": [
|
|
{
|
|
"relative_path": "pkg/handler.go",
|
|
"occurrences": [
|
|
{"range": [4, 5, 4, 11], "symbol": "pkg.Handle()", "symbol_roles": 1},
|
|
{"range": [10, 2, 10, 8], "symbol": "pkg.Handle()", "symbol_roles": 0}
|
|
],
|
|
"symbols": [
|
|
{
|
|
"symbol": "pkg.Handle()",
|
|
"documentation": ["func Handle(w http.ResponseWriter, r *http.Request)"],
|
|
"relationships": [
|
|
{"symbol": "http.Handler", "is_implementation": true}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}`
|
|
|
|
require.NoError(t, os.WriteFile(jsonFile, []byte(jsonData), 0644))
|
|
|
|
index, err := ParseSCIPFile(jsonFile)
|
|
require.NoError(t, err)
|
|
require.Len(t, index.Documents, 1)
|
|
|
|
doc := index.Documents[0]
|
|
assert.Equal(t, "pkg/handler.go", doc.RelativePath)
|
|
require.Len(t, doc.Occurrences, 2)
|
|
require.Len(t, doc.Symbols, 1)
|
|
require.Len(t, doc.Symbols[0].Relationships, 1)
|
|
assert.True(t, doc.Symbols[0].Relationships[0].IsImplementation)
|
|
}
|
|
|
|
func TestEnrichFromIndex(t *testing.T) {
|
|
logger := zap.NewNop()
|
|
p := NewProvider("scip-go", nil, []string{"go"}, 120, logger)
|
|
|
|
g := graph.New()
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go::Foo", Kind: graph.KindFunction, Name: "Foo",
|
|
FilePath: "main.go", StartLine: 10, EndLine: 20, Language: "go",
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "main.go::Bar", Kind: graph.KindFunction, Name: "Bar",
|
|
FilePath: "main.go", StartLine: 22, EndLine: 30, Language: "go",
|
|
})
|
|
|
|
// Add an INFERRED edge.
|
|
g.AddEdge(&graph.Edge{
|
|
From: "main.go::Bar", To: "main.go::Foo", Kind: graph.EdgeCalls,
|
|
Confidence: 0.7, ConfidenceLabel: "INFERRED",
|
|
})
|
|
|
|
// Create a SCIP index that confirms the call.
|
|
index := &SCIPIndex{
|
|
Documents: []SCIPDocument{
|
|
{
|
|
RelativePath: "main.go",
|
|
Occurrences: []SCIPOccurrence{
|
|
{
|
|
Range: []int32{9, 5, 9, 8}, // line 10 (Foo definition)
|
|
Symbol: "main.Foo()",
|
|
SymbolRoles: 1, // Definition
|
|
},
|
|
{
|
|
Range: []int32{21, 5, 21, 8}, // line 22 (Bar definition)
|
|
Symbol: "main.Bar()",
|
|
SymbolRoles: 1, // Definition
|
|
},
|
|
{
|
|
Range: []int32{24, 2, 24, 5}, // line 25 (call to Foo from Bar)
|
|
Symbol: "main.Foo()",
|
|
SymbolRoles: 0, // Reference
|
|
},
|
|
},
|
|
Symbols: []SCIPSymbolInfo{
|
|
{
|
|
Symbol: "main.Foo()",
|
|
Documentation: []string{"func Foo() error"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
result := p.enrichFromIndex(g, index, "/tmp/repo")
|
|
|
|
assert.Greater(t, result.SymbolsCovered, 0)
|
|
assert.Greater(t, result.EdgesConfirmed, 0)
|
|
|
|
// Verify the edge was confirmed.
|
|
edges := g.GetOutEdges("main.go::Bar")
|
|
require.NotEmpty(t, edges)
|
|
for _, e := range edges {
|
|
if e.To == "main.go::Foo" && e.Kind == graph.EdgeCalls {
|
|
assert.Equal(t, 1.0, e.Confidence)
|
|
assert.Equal(t, "EXTRACTED", e.ConfidenceLabel)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestEnrichFromIndex_DefinitionsOnly verifies the C# / .NET coverage
|
|
// helper fast path: definitions are mapped (coverage counted) but the
|
|
// reference-edge pass is skipped, so no edge is confirmed or added.
|
|
func TestEnrichFromIndex_DefinitionsOnly(t *testing.T) {
|
|
p := NewProvider("scip-dotnet", nil, []string{"csharp"}, 120, zap.NewNop()).
|
|
WithDefinitionsOnly()
|
|
|
|
g := graph.New()
|
|
g.AddNode(&graph.Node{
|
|
ID: "M.cs::Foo", Kind: graph.KindMethod, Name: "Foo",
|
|
FilePath: "M.cs", StartLine: 10, EndLine: 20, Language: "csharp",
|
|
})
|
|
g.AddNode(&graph.Node{
|
|
ID: "M.cs::Bar", Kind: graph.KindMethod, Name: "Bar",
|
|
FilePath: "M.cs", StartLine: 22, EndLine: 30, Language: "csharp",
|
|
})
|
|
g.AddEdge(&graph.Edge{
|
|
From: "M.cs::Bar", To: "M.cs::Foo", Kind: graph.EdgeCalls,
|
|
Confidence: 0.7, ConfidenceLabel: "INFERRED",
|
|
})
|
|
|
|
index := &SCIPIndex{Documents: []SCIPDocument{{
|
|
RelativePath: "M.cs",
|
|
Occurrences: []SCIPOccurrence{
|
|
{Range: []int32{9, 5, 9, 8}, Symbol: "M.Foo()", SymbolRoles: 1},
|
|
{Range: []int32{21, 5, 21, 8}, Symbol: "M.Bar()", SymbolRoles: 1},
|
|
{Range: []int32{24, 2, 24, 5}, Symbol: "M.Foo()", SymbolRoles: 0}, // reference
|
|
},
|
|
}}}
|
|
|
|
result := p.enrichFromIndex(g, index, "/tmp/repo")
|
|
assert.Greater(t, result.SymbolsCovered, 0, "definitions must still be mapped")
|
|
assert.Equal(t, 0, result.EdgesConfirmed, "reference pass must be skipped")
|
|
assert.Equal(t, 0, result.EdgesAdded, "reference pass must be skipped")
|
|
|
|
edges := g.GetOutEdges("M.cs::Bar")
|
|
require.NotEmpty(t, edges)
|
|
assert.Equal(t, 0.7, edges[0].Confidence, "INFERRED edge must stay unconfirmed")
|
|
}
|
|
|
|
func TestExtractSymbolName(t *testing.T) {
|
|
tests := []struct {
|
|
symbol string
|
|
want string
|
|
}{
|
|
{"scip-go gomod github.com/foo/bar v1.0.0 pkg.Foo().", "Foo"},
|
|
{"scip-go gomod github.com/foo/bar v1.0.0 internal/graph/Graph.AddNode().", "AddNode"},
|
|
{"scip-go gomod github.com/foo/bar v1.0.0 internal/graph/Node#Kind.", "Kind"},
|
|
{"", ""},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := extractSymbolName(tt.symbol)
|
|
assert.Equal(t, tt.want, got, "for symbol %q", tt.symbol)
|
|
}
|
|
}
|