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
181 lines
6.3 KiB
Go
181 lines
6.3 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
// manifestEntryByID finds the manifest entry for a node ID.
|
|
func manifestEntryByID(entries []map[string]any, id string) map[string]any {
|
|
for _, e := range entries {
|
|
if e["id"] == id {
|
|
return e
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// writeManifestFixture writes a multi-line source file whose full and
|
|
// compressed renderings differ, and returns its absolute path.
|
|
func writeManifestFixture(t *testing.T, dir, name, body string) string {
|
|
t.Helper()
|
|
abs := filepath.Join(dir, name)
|
|
require.NoError(t, os.WriteFile(abs, []byte(body), 0o644))
|
|
return abs
|
|
}
|
|
|
|
// TestManifest_SkeletonizesLargeInterfaceFamily proves a focus interface
|
|
// with more than the sibling threshold of implementors is skeletonized
|
|
// (compressed, with sibling_count surfaced) rather than embedded full,
|
|
// while a control interface with a single implementor stays full.
|
|
func TestManifest_SkeletonizesLargeInterfaceFamily(t *testing.T) {
|
|
srv, dir := setupTestServer(t)
|
|
g := srv.graph
|
|
ctx := context.Background()
|
|
|
|
// Body source with a real function body so full != compressed.
|
|
bigBody := `package fam
|
|
|
|
// BigFamily is implemented by many interchangeable types.
|
|
type BigFamily interface {
|
|
Do(x int) int
|
|
Name() string
|
|
}
|
|
`
|
|
soleBody := `package fam
|
|
|
|
// Lonely has a single implementation.
|
|
type Lonely interface {
|
|
Only() error
|
|
}
|
|
`
|
|
bigPath := writeManifestFixture(t, dir, "big.go", bigBody)
|
|
solePath := writeManifestFixture(t, dir, "sole.go", soleBody)
|
|
|
|
add := func(id, name string, kind graph.NodeKind, file string, start, end int) {
|
|
g.AddNode(&graph.Node{
|
|
ID: id, Kind: kind, Name: name,
|
|
FilePath: file, Language: "go",
|
|
StartLine: start, EndLine: end,
|
|
})
|
|
}
|
|
impl := func(from, to string) {
|
|
g.AddEdge(&graph.Edge{From: from, To: to, Kind: graph.EdgeImplements, Origin: graph.OriginLSPResolved, FilePath: "x.go"})
|
|
}
|
|
|
|
// The big interface spans lines 4-7 of big.go.
|
|
add("big.go::BigFamily", "BigFamily", graph.KindInterface, bigPath, 4, 7)
|
|
// Five implementors — well past the threshold of 3.
|
|
for _, n := range []string{"A", "B", "C", "D", "E"} {
|
|
id := "impl.go::" + n
|
|
add(id, n, graph.KindType, bigPath, 1, 1)
|
|
impl(id, "big.go::BigFamily")
|
|
}
|
|
|
|
// The sole interface spans lines 4-6 of sole.go, with one implementor.
|
|
add("sole.go::Lonely", "Lonely", graph.KindInterface, solePath, 4, 6)
|
|
add("impl.go::Single", "Single", graph.KindType, solePath, 1, 1)
|
|
impl("impl.go::Single", "sole.go::Lonely")
|
|
|
|
focus := []*graph.Node{
|
|
g.GetNode("big.go::BigFamily"),
|
|
g.GetNode("sole.go::Lonely"),
|
|
}
|
|
require.NotNil(t, focus[0])
|
|
require.NotNil(t, focus[1])
|
|
|
|
mani := srv.buildContextManifest(ctx, focus, nil, 8000)
|
|
entries, _ := mani["entries"].([]map[string]any)
|
|
require.NotEmpty(t, entries)
|
|
|
|
big := manifestEntryByID(entries, "big.go::BigFamily")
|
|
require.NotNil(t, big, "big-family interface must be in the manifest")
|
|
assert.Equal(t, "focus", big["tier"])
|
|
assert.Equal(t, true, big["compressed"],
|
|
"a large interface family must be skeletonized (compressed)")
|
|
sc, _ := big["sibling_count"].(int)
|
|
assert.Equal(t, 5, sc, "sibling_count must report the implementor family size")
|
|
|
|
sole := manifestEntryByID(entries, "sole.go::Lonely")
|
|
require.NotNil(t, sole, "sole interface must be in the manifest")
|
|
assert.Equal(t, "focus", sole["tier"])
|
|
assert.Equal(t, false, sole["compressed"],
|
|
"a single-implementor interface must stay full, not skeletonized")
|
|
}
|
|
|
|
// TestManifest_SkeletonizesLargeMethodFamily proves a focus method with
|
|
// more than the threshold of overriders is skeletonized.
|
|
func TestManifest_SkeletonizesLargeMethodFamily(t *testing.T) {
|
|
srv, dir := setupTestServer(t)
|
|
g := srv.graph
|
|
ctx := context.Background()
|
|
|
|
body := `package fam
|
|
|
|
// Base.Run is overridden by many subtypes.
|
|
func (b *Base) Run(n int) int {
|
|
total := 0
|
|
for i := 0; i < n; i++ {
|
|
total += i
|
|
}
|
|
return total
|
|
}
|
|
`
|
|
p := writeManifestFixture(t, dir, "base.go", body)
|
|
g.AddNode(&graph.Node{
|
|
ID: "base.go::Base.Run", Kind: graph.KindMethod, Name: "Base.Run",
|
|
FilePath: p, Language: "go", StartLine: 4, EndLine: 10,
|
|
})
|
|
for _, n := range []string{"W", "X", "Y", "Z"} {
|
|
id := "ov.go::" + n + ".Run"
|
|
g.AddNode(&graph.Node{ID: id, Kind: graph.KindMethod, Name: n + ".Run", FilePath: p, Language: "go", StartLine: 1, EndLine: 1})
|
|
// EdgeOverrides points child -> parent; FindOverrides reads
|
|
// the inverse to collect children.
|
|
g.AddEdge(&graph.Edge{From: id, To: "base.go::Base.Run", Kind: graph.EdgeOverrides, Origin: graph.OriginLSPResolved, FilePath: "x.go"})
|
|
}
|
|
|
|
focus := []*graph.Node{g.GetNode("base.go::Base.Run")}
|
|
require.NotNil(t, focus[0])
|
|
|
|
mani := srv.buildContextManifest(ctx, focus, nil, 8000)
|
|
entries, _ := mani["entries"].([]map[string]any)
|
|
e := manifestEntryByID(entries, "base.go::Base.Run")
|
|
require.NotNil(t, e)
|
|
assert.Equal(t, true, e["compressed"], "an over-threshold method family must skeletonize")
|
|
sc, _ := e["sibling_count"].(int)
|
|
assert.Equal(t, 4, sc)
|
|
}
|
|
|
|
// TestManifest_SiblingCountCounting verifies the per-kind counting rules
|
|
// of manifestSiblingCount directly.
|
|
func TestManifest_SiblingCountCounting(t *testing.T) {
|
|
srv, _ := setupTestServer(t)
|
|
g := srv.graph
|
|
ctx := context.Background()
|
|
|
|
g.AddNode(&graph.Node{ID: "i.go::Iface", Kind: graph.KindInterface, Name: "Iface", FilePath: "i.go", Language: "go"})
|
|
for _, n := range []string{"P", "Q"} {
|
|
id := "t.go::" + n
|
|
g.AddNode(&graph.Node{ID: id, Kind: graph.KindType, Name: n, FilePath: "t.go", Language: "go"})
|
|
g.AddEdge(&graph.Edge{From: id, To: "i.go::Iface", Kind: graph.EdgeImplements, Origin: graph.OriginLSPResolved, FilePath: "x.go"})
|
|
}
|
|
// Interface family size = 2 implementors.
|
|
assert.Equal(t, 2, srv.manifestSiblingCount(ctx, g.GetNode("i.go::Iface")))
|
|
// A concrete type's co-implementor count excludes itself: P sees Q.
|
|
assert.Equal(t, 1, srv.manifestSiblingCount(ctx, g.GetNode("t.go::P")))
|
|
// A function is never a polymorphic family.
|
|
g.AddNode(&graph.Node{ID: "f.go::Fn", Kind: graph.KindFunction, Name: "Fn", FilePath: "f.go", Language: "go"})
|
|
assert.Equal(t, 0, srv.manifestSiblingCount(ctx, g.GetNode("f.go::Fn")))
|
|
assert.False(t, skeletonizableKind(graph.KindFunction))
|
|
assert.True(t, skeletonizableKind(graph.KindInterface))
|
|
assert.True(t, skeletonizableKind(graph.KindType))
|
|
assert.True(t, skeletonizableKind(graph.KindMethod))
|
|
}
|