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") }