@@ -8,75 +8,67 @@ import (
88 "testing"
99 "time"
1010
11+ "github.com/modelcontextprotocol/go-sdk/mcp"
1112 "github.com/stackrox/stackrox-mcp/internal/app"
1213 "github.com/stackrox/stackrox-mcp/internal/config"
1314 "github.com/stackrox/stackrox-mcp/internal/testutil"
1415 "github.com/stretchr/testify/assert"
1516 "github.com/stretchr/testify/require"
1617)
1718
18- // Common response structures
19- type (
20- // MCPContent represents MCP tool response content
21- MCPContent struct {
22- Type string `json:"type"`
23- Text string `json:"text"`
24- }
25-
26- // MCPToolResult represents standard MCP tool call result
27- MCPToolResult struct {
28- Content []MCPContent `json:"content"`
29- }
30-
31- // MCPListToolsResult represents tools/list response
32- MCPListToolsResult struct {
33- Tools []map [string ]any `json:"tools"`
34- }
35- )
19+ // setupInitializedClient creates an initialized MCP client for testing.
20+ func setupInitializedClient (t * testing.T ) * testutil.MCPTestClient {
21+ t .Helper ()
3622
37- // TestIntegration_Initialize verifies the MCP initialize handshake.
38- func TestIntegration_Initialize (t * testing.T ) {
3923 client , err := createMCPClient (t )
4024 require .NoError (t , err , "Failed to create MCP client" )
41- defer client .Close ()
25+ t .Cleanup (func () { client .Close () })
26+
27+ return client
28+ }
4229
43- resp , err := client .Initialize ()
30+ // callToolAndGetResult calls a tool and verifies it succeeds.
31+ func callToolAndGetResult (t * testing.T , client * testutil.MCPTestClient , toolName string , args map [string ]any ) * mcp.CallToolResult {
32+ t .Helper ()
33+
34+ ctx := context .Background ()
35+ result , err := client .CallTool (ctx , toolName , args )
4436 require .NoError (t , err )
37+ testutil .RequireNoError (t , result )
4538
46- testutil .RequireNoError (t , resp )
39+ return result
40+ }
4741
48- var result map [string ]any
49- testutil .UnmarshalResult (t , resp , & result )
42+ // getTextContent extracts text from the first content item.
43+ func getTextContent (t * testing.T , result * mcp.CallToolResult ) string {
44+ t .Helper ()
45+ require .NotEmpty (t , result .Content , "should have content in response" )
46+
47+ textContent , ok := result .Content [0 ].(* mcp.TextContent )
48+ require .True (t , ok , "expected TextContent, got %T" , result .Content [0 ])
5049
51- assert .Contains (t , result , "protocolVersion" , "initialize response should include protocolVersion" )
52- assert .Contains (t , result , "serverInfo" , "initialize response should include serverInfo" )
53- assert .Contains (t , result , "capabilities" , "initialize response should include capabilities" )
50+ return textContent .Text
5451}
5552
5653// TestIntegration_ListTools verifies that all expected tools are registered.
5754func TestIntegration_ListTools (t * testing.T ) {
5855 client := setupInitializedClient (t )
5956
60- // List tools
61- resp , err := client .ListTools ()
57+ ctx := context . Background ()
58+ result , err := client .ListTools (ctx )
6259 require .NoError (t , err )
63- testutil .RequireNoError (t , resp )
64-
65- var result MCPListToolsResult
66- testutil .UnmarshalResult (t , resp , & result )
6760
6861 // Verify we have tools registered
6962 assert .NotEmpty (t , result .Tools , "should have tools registered" )
7063
7164 // Check for specific tools we expect
7265 toolNames := make ([]string , 0 , len (result .Tools ))
7366 for _ , tool := range result .Tools {
74- if name , ok := tool ["name" ].(string ); ok {
75- toolNames = append (toolNames , name )
76- }
67+ toolNames = append (toolNames , tool .Name )
7768 }
7869
79- assert .Subset (t , toolNames , []string {"list_clusters" , "get_deployments_for_cve" })
70+ assert .Contains (t , toolNames , "get_deployments_for_cve" , "should have get_deployments_for_cve tool" )
71+ assert .Contains (t , toolNames , "list_clusters" , "should have list_clusters tool" )
8072}
8173
8274// TestIntegration_ToolCalls tests successful tool calls using table-driven tests.
@@ -113,9 +105,7 @@ func TestIntegration_ToolCalls(t *testing.T) {
113105 client := setupInitializedClient (t )
114106 result := callToolAndGetResult (t , client , tt .toolName , tt .args )
115107
116- require .NotEmpty (t , result .Content , "should have content in response" )
117-
118- responseText := result .Content [0 ].Text
108+ responseText := getTextContent (t , result )
119109 for _ , expected := range tt .expectedInText {
120110 assert .Contains (t , responseText , expected )
121111 }
@@ -141,43 +131,16 @@ func TestIntegration_ToolCallErrors(t *testing.T) {
141131 t .Run (name , func (t * testing.T ) {
142132 client := setupInitializedClient (t )
143133
144- resp , err := client . CallTool ( tt . toolName , tt . args )
145- require . NoError ( t , err )
134+ ctx := context . Background ( )
135+ _ , err := client . CallTool ( ctx , tt . toolName , tt . args )
146136
147- require .NotNil (t , resp .Error , "should receive error" )
148- assert .Contains (t , resp .Error .Message , tt .expectedErrorMsg )
137+ // Validation errors are returned as protocol errors, not tool errors
138+ require .Error (t , err , "should receive protocol error for invalid params" )
139+ assert .Contains (t , err .Error (), tt .expectedErrorMsg )
149140 })
150141 }
151142}
152143
153- // setupInitializedClient creates and initializes an MCP client
154- func setupInitializedClient (t * testing.T ) * testutil.MCPClient {
155- t .Helper ()
156-
157- client , err := createMCPClient (t )
158- require .NoError (t , err , "Failed to create MCP client" )
159- t .Cleanup (func () { client .Close () })
160-
161- initResp , err := client .Initialize ()
162- require .NoError (t , err )
163- testutil .RequireNoError (t , initResp )
164-
165- return client
166- }
167-
168- // callToolAndGetResult is a helper that calls a tool and unmarshals result
169- func callToolAndGetResult (t * testing.T , client * testutil.MCPClient , toolName string , args map [string ]any ) * MCPToolResult {
170- t .Helper ()
171-
172- resp , err := client .CallTool (toolName , args )
173- require .NoError (t , err )
174- testutil .RequireNoError (t , resp )
175-
176- var result MCPToolResult
177- testutil .UnmarshalResult (t , resp , & result )
178- return & result
179- }
180-
181144// createTestConfig creates a test configuration for the MCP server.
182145func createTestConfig () * config.Config {
183146 return & config.Config {
@@ -206,7 +169,7 @@ func createTestConfig() *config.Config {
206169}
207170
208171// createMCPClient is a helper function that creates an MCP client with the test configuration.
209- func createMCPClient (t * testing.T ) (* testutil.MCPClient , error ) {
172+ func createMCPClient (t * testing.T ) (* testutil.MCPTestClient , error ) {
210173 t .Helper ()
211174
212175 cfg := createTestConfig ()
@@ -216,5 +179,5 @@ func createMCPClient(t *testing.T) (*testutil.MCPClient, error) {
216179 return app .Run (ctx , cfg , stdin , stdout )
217180 }
218181
219- return testutil .NewMCPClient (t , runFunc )
182+ return testutil .NewMCPTestClient (t , runFunc )
220183}
0 commit comments