Files
zzet--gortex/internal/mcp/tools_trace_path_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

127 lines
3.7 KiB
Go

package mcp
import (
"encoding/json"
"os"
"path/filepath"
"testing"
mcplib "github.com/mark3labs/mcp-go/mcp"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/indexer"
"github.com/zzet/gortex/internal/query"
)
// tracePathTestServer indexes a Go file with a known call chain
// A→B→C→D plus an isolated function so we can exercise both the found
// and not-found branches of trace_path.
func tracePathTestServer(t *testing.T) *Server {
t.Helper()
dir := t.TempDir()
src := `package main
func D() {}
func C() { D() }
func B() { C() }
func A() { B() }
func Isolated() {}
`
require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"), []byte(src), 0o644))
g := graph.New()
reg := testRegistry()
cfg := config.Default()
idx := indexer.New(g, reg, cfg.Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
idx.ResolveAll()
eng := query.NewEngine(g)
return NewServer(eng, g, idx, nil, zap.NewNop(), nil)
}
func TestHandleTracePath_FoundChain(t *testing.T) {
srv := tracePathTestServer(t)
req := mcplib.CallToolRequest{}
req.Params.Arguments = map[string]any{
"source_id": findFunctionID(t, srv, "A"),
"sink_id": findFunctionID(t, srv, "D"),
}
res, err := srv.handleTracePath(t.Context(), req)
require.NoError(t, err)
require.False(t, res.IsError, "tool errored: %v", res)
var payload struct {
Found bool `json:"found"`
Paths []struct {
Nodes []string `json:"nodes"`
Length int `json:"length"`
} `json:"paths"`
}
text := res.Content[0].(mcplib.TextContent).Text
require.NoError(t, json.Unmarshal([]byte(text), &payload))
require.True(t, payload.Found, "expected a path; got %s", text)
require.Len(t, payload.Paths, 1)
require.Equal(t, 3, payload.Paths[0].Length, "A→B→C→D is 3 hops: %s", text)
require.Len(t, payload.Paths[0].Nodes, 4)
}
func TestHandleTracePath_NotFoundDiagnosis(t *testing.T) {
srv := tracePathTestServer(t)
// A reaches B,C,D; Isolated has no incoming call edges → sink_no_in.
req := mcplib.CallToolRequest{}
req.Params.Arguments = map[string]any{
"source_id": findFunctionID(t, srv, "A"),
"sink_id": findFunctionID(t, srv, "Isolated"),
}
res, err := srv.handleTracePath(t.Context(), req)
require.NoError(t, err)
require.False(t, res.IsError)
var payload struct {
Found bool `json:"found"`
Gap *struct {
Reason string `json:"reason"`
Message string `json:"message"`
} `json:"gap"`
}
text := res.Content[0].(mcplib.TextContent).Text
require.NoError(t, json.Unmarshal([]byte(text), &payload))
require.False(t, payload.Found)
require.NotNil(t, payload.Gap, "expected a gap diagnosis: %s", text)
require.Equal(t, "sink_no_in_edges", payload.Gap.Reason)
require.NotEmpty(t, payload.Gap.Message)
}
func TestHandleTracePath_MissingSource(t *testing.T) {
srv := tracePathTestServer(t)
req := mcplib.CallToolRequest{}
req.Params.Arguments = map[string]any{"sink_id": "anything"}
res, err := srv.handleTracePath(t.Context(), req)
require.NoError(t, err)
require.True(t, res.IsError, "expected error for missing source_id")
}
func TestHandleTracePath_GCXFormat(t *testing.T) {
srv := tracePathTestServer(t)
req := mcplib.CallToolRequest{}
req.Params.Name = "trace_path"
req.Params.Arguments = map[string]any{
"source_id": findFunctionID(t, srv, "A"),
"sink_id": findFunctionID(t, srv, "D"),
"format": "gcx",
}
res, err := srv.handleTracePath(t.Context(), req)
require.NoError(t, err)
require.False(t, res.IsError)
text := res.Content[0].(mcplib.TextContent).Text
require.Contains(t, text, "trace_path.summary")
require.Contains(t, text, "trace_path.paths")
}