Files
zzet--gortex/internal/mcp/tools_generate_skill_graph_test.go
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

105 lines
4.4 KiB
Go

package mcp
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
func TestBuildSkillMarkdown_RendersKeySymbols(t *testing.T) {
syms := []skillSymbol{
{Name: "Run", Kind: "function", Signature: "func Run() int", RelPath: "main.go", Line: 3, FanIn: 7},
{Name: "Helper", Kind: "function", Signature: "func Helper() string", RelPath: "helpers.go", Line: 1, FanIn: 1},
}
body := buildSkillMarkdown("demo", "desc", []generateSkillRef{{RelPath: "main.go", BytesWritten: 10}}, syms)
assert.Contains(t, body, "## Key symbols")
assert.Contains(t, body, "`Run` (function)")
assert.Contains(t, body, "func Run() int")
assert.Contains(t, body, "`main.go:3`")
assert.Contains(t, body, "(7 refs)")
// References section still present.
assert.Contains(t, body, "## References")
// Higher fan-in ranks first.
assert.Less(t, strings.Index(body, "`Run`"), strings.Index(body, "`Helper`"))
}
func TestBuildSkillMarkdown_NoSymbolsOmitsSection(t *testing.T) {
body := buildSkillMarkdown("demo", "desc", []generateSkillRef{{RelPath: "x.go"}}, nil)
assert.NotContains(t, body, "## Key symbols")
assert.Contains(t, body, "## References")
}
func TestIsSkillSymbolKind(t *testing.T) {
for _, k := range []string{"function", "method", "type", "interface", "constant", "variable"} {
assert.True(t, isSkillSymbolKind(graph.NodeKind(k)), k)
}
for _, k := range []string{"param", "local", "import", "field", "file", "package"} {
assert.False(t, isSkillSymbolKind(graph.NodeKind(k)), k)
}
}
func TestDefaultSkillDescription(t *testing.T) {
withSyms := defaultSkillDescription("demo", "internal/x", 4, 12)
assert.Contains(t, withSyms, "12 symbols across 4 files")
noSyms := defaultSkillDescription("demo", "internal/x", 4, 0)
assert.NotContains(t, noSyms, "symbols across")
}
func TestTopSkillSymbols(t *testing.T) {
syms := make([]skillSymbol, 50)
assert.Len(t, topSkillSymbols(syms, 40), 40)
assert.Len(t, topSkillSymbols(syms[:5], 40), 5)
}
func TestCollapseWhitespace(t *testing.T) {
assert.Equal(t, "func F(a int) error", collapseWhitespace("func F(a\n\tint) error"))
}
// TestGenerateSkill_GraphSymbolsSurface seeds the graph with symbols for
// the bundled files (ranked by inbound references) and proves the
// generated skill leads with a graph-derived Key symbols map.
func TestGenerateSkill_GraphSymbolsSurface(t *testing.T) {
s := newGenerateSkillTestServer(t)
srcDir := seedSkillSource(t)
// File paths as the tool will look them up.
mainFP := s.repoRelative(filepath.Join(srcDir, "main.go"))
helpersFP := s.repoRelative(filepath.Join(srcDir, "helpers.go"))
runID := mainFP + "::Run"
helperID := helpersFP + "::Helper"
s.graph.AddNode(&graph.Node{ID: runID, Kind: graph.NodeKind("function"), Name: "Run", FilePath: mainFP, StartLine: 3, Meta: map[string]any{"signature": "func Run() int"}})
s.graph.AddNode(&graph.Node{ID: helperID, Kind: graph.NodeKind("function"), Name: "Helper", FilePath: helpersFP, StartLine: 1, Meta: map[string]any{"signature": "func Helper() string"}})
// Give Run more inbound references than Helper so it ranks first.
s.graph.AddNode(&graph.Node{ID: "caller-a", Kind: graph.NodeKind("function"), Name: "A", FilePath: "other.go", StartLine: 1})
s.graph.AddNode(&graph.Node{ID: "caller-b", Kind: graph.NodeKind("function"), Name: "B", FilePath: "other.go", StartLine: 2})
s.graph.AddEdge(&graph.Edge{From: "caller-a", To: runID, Kind: graph.EdgeReturns, FilePath: "other.go", Line: 1})
s.graph.AddEdge(&graph.Edge{From: "caller-b", To: runID, Kind: graph.EdgeReturns, FilePath: "other.go", Line: 2})
s.graph.AddEdge(&graph.Edge{From: "caller-a", To: helperID, Kind: graph.EdgeReturns, FilePath: "other.go", Line: 1})
out := callGenerateSkill(t, s, map[string]any{
"directory": srcDir,
"output_dir": t.TempDir(),
})
require.EqualValues(t, 2, out["symbol_count"], "both seeded symbols should be surfaced")
body, err := os.ReadFile(out["skill_path"].(string))
require.NoError(t, err)
md := string(body)
assert.Contains(t, md, "## Key symbols")
assert.Contains(t, md, "`Run` (function)")
assert.Contains(t, md, "func Run() int")
assert.Contains(t, md, "`main.go:3`")
// The auto description quotes the graph counts.
assert.Contains(t, out["description"].(string), "symbols across")
// Run (fan-in 2) ranks above Helper (fan-in 1).
assert.Less(t, strings.Index(md, "`Run`"), strings.Index(md, "`Helper`"))
}