xdebug-mcp/internal/tools/tools_test.go
thibaud-leclere 3943bfb8cc feat: add get_callers and get_callees tools
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 09:52:51 +02:00

100 lines
2.8 KiB
Go

package tools_test
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"forge.lclr.dev/AI/xdebug-mcp/internal/cachegrind"
"forge.lclr.dev/AI/xdebug-mcp/internal/tools"
)
func makeTestProfile() *cachegrind.Profile {
main := &cachegrind.Function{Name: "main", File: "index.php", Costs: []int64{3500, 1700}}
query := &cachegrind.Function{Name: "query", File: "index.php", Costs: []int64{2000, 900}}
connect := &cachegrind.Function{Name: "connect", File: "index.php", Costs: []int64{250, 100}}
call1 := &cachegrind.Call{Caller: main, Callee: query, Count: 1, Costs: []int64{1500, 700}}
call2 := &cachegrind.Call{Caller: query, Callee: connect, Count: 2, Costs: []int64{500, 200}}
main.Calls = []*cachegrind.Call{call1}
query.CalledBy = []*cachegrind.Call{call1}
query.Calls = []*cachegrind.Call{call2}
connect.CalledBy = []*cachegrind.Call{call2}
return &cachegrind.Profile{
Cmd: "index.php",
Events: []string{"Time_(10ns)", "Memory_(bytes)"},
Functions: []*cachegrind.Function{main, query, connect},
ByName: map[string][]*cachegrind.Function{
"main": {main},
"query": {query},
"connect": {connect},
},
}
}
func TestAnalyze_TopN(t *testing.T) {
p := makeTestProfile()
result := tools.Analyze(p, 3)
assert.Contains(t, result, "index.php")
assert.Contains(t, result, "Functions: 3 total")
assert.Contains(t, result, "main")
assert.Contains(t, result, "query")
assert.Contains(t, result, "connect")
// main must appear before query (higher cost)
assert.Less(t, strings.Index(result, "main"), strings.Index(result, "query"))
}
func TestAnalyze_TopNLimited(t *testing.T) {
p := makeTestProfile()
result := tools.Analyze(p, 1)
assert.Contains(t, result, "main")
assert.NotContains(t, result, "query") // only top 1
}
// require is used to avoid unused import error if tests change
var _ = require.New
func TestCallers_Found(t *testing.T) {
p := makeTestProfile()
result := tools.Callers(p, "query", 10)
assert.Contains(t, result, "main")
assert.Contains(t, result, "calls=1")
require.NotContains(t, result, "not found")
}
func TestCallers_ContainsFallback(t *testing.T) {
p := makeTestProfile()
// "uer" is a substring of "query"
result := tools.Callers(p, "uer", 10)
assert.Contains(t, result, "main")
}
func TestCallers_NotFound(t *testing.T) {
p := makeTestProfile()
result := tools.Callers(p, "nonexistent_xyz", 10)
assert.Contains(t, result, "not found")
}
func TestCallees_Found(t *testing.T) {
p := makeTestProfile()
result := tools.Callees(p, "query", 10)
assert.Contains(t, result, "connect")
assert.Contains(t, result, "calls=2")
}
func TestCallees_NotFound(t *testing.T) {
p := makeTestProfile()
result := tools.Callees(p, "connect", 10) // connect has no callees
assert.Contains(t, result, "no outgoing calls")
}