Skip to content

Commit c020b3c

Browse files
test: add uts for mcpgo and toolsets pkg
1 parent 2fcde75 commit c020b3c

File tree

5 files changed

+625
-1
lines changed

5 files changed

+625
-1
lines changed

pkg/mcpgo/server_test.go

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
package mcpgo
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/mark3labs/mcp-go/mcp"
8+
"github.com/mark3labs/mcp-go/server"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestNewServer(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
serverName string
16+
version string
17+
options []ServerOption
18+
expectName string
19+
expectVer string
20+
}{
21+
{
22+
name: "Create server with no options",
23+
serverName: "test-server",
24+
version: "1.0.0",
25+
options: nil,
26+
expectName: "test-server",
27+
expectVer: "1.0.0",
28+
},
29+
{
30+
name: "Create server with all options",
31+
serverName: "test-server-all",
32+
version: "1.0.4",
33+
options: []ServerOption{
34+
WithLogging(),
35+
WithResourceCapabilities(true, true),
36+
WithToolCapabilities(true),
37+
},
38+
expectName: "test-server-all",
39+
expectVer: "1.0.4",
40+
},
41+
}
42+
43+
for _, tc := range tests {
44+
t.Run(tc.name, func(t *testing.T) {
45+
srv := NewServer(tc.serverName, tc.version, tc.options...)
46+
47+
assert.Equal(t, tc.expectName, srv.name)
48+
assert.Equal(t, tc.expectVer, srv.version)
49+
assert.NotNil(t, srv.mcpServer)
50+
})
51+
}
52+
}
53+
54+
func TestToMCPServerTool(t *testing.T) {
55+
simpleHandler := func(
56+
ctx context.Context,
57+
req CallToolRequest,
58+
) (*ToolResult, error) {
59+
return NewToolResultText("test-result"), nil
60+
}
61+
62+
tests := []struct {
63+
name string
64+
tool Tool
65+
expectName string
66+
}{
67+
{
68+
name: "Simple tool with no parameters",
69+
tool: NewTool(
70+
"simple-tool",
71+
"A simple test tool",
72+
[]ToolParameter{},
73+
simpleHandler,
74+
),
75+
expectName: "simple-tool",
76+
},
77+
{
78+
name: "Tool with string parameter",
79+
tool: NewTool(
80+
"string-param-tool",
81+
"A tool with a string parameter",
82+
[]ToolParameter{
83+
WithString("str_param", Description("A string parameter"), Required()),
84+
},
85+
simpleHandler,
86+
),
87+
expectName: "string-param-tool",
88+
},
89+
{
90+
name: "Tool with mixed parameters",
91+
tool: NewTool(
92+
"mixed-param-tool",
93+
"A tool with mixed parameters",
94+
[]ToolParameter{
95+
WithString("str_param", Description("A string parameter"), Required()),
96+
WithNumber(
97+
"num_param",
98+
Description("A number parameter"),
99+
Min(0),
100+
Max(100),
101+
),
102+
WithBoolean("bool_param", Description("A boolean parameter")),
103+
WithObject("obj_param", Description("An object parameter")),
104+
WithArray("arr_param", Description("An array parameter")),
105+
},
106+
simpleHandler,
107+
),
108+
expectName: "mixed-param-tool",
109+
},
110+
}
111+
112+
for _, tc := range tests {
113+
t.Run(tc.name, func(t *testing.T) {
114+
mcpTool := tc.tool.toMCPServerTool()
115+
116+
assert.Equal(t, tc.expectName, mcpTool.Tool.Name)
117+
assert.NotNil(t, mcpTool.Handler)
118+
})
119+
}
120+
}
121+
122+
func TestToMCPServerToolHandler(t *testing.T) {
123+
t.Run("SuccessHandler", func(t *testing.T) {
124+
testHandler := func(
125+
ctx context.Context,
126+
req CallToolRequest,
127+
) (*ToolResult, error) {
128+
assert.Equal(t, "test-tool", req.Name,
129+
"Expected tool name to be 'test-tool'")
130+
131+
textParam, exists := req.Arguments["text"]
132+
assert.True(t, exists, "Expected 'text' parameter to exist")
133+
if !exists {
134+
return NewToolResultError("text parameter missing"), nil
135+
}
136+
137+
textValue, ok := textParam.(string)
138+
assert.True(t, ok, "Expected text parameter to be string")
139+
if !ok {
140+
return NewToolResultError("text parameter not a string"), nil
141+
}
142+
143+
assert.Equal(t, "hello world", textValue,
144+
"Expected text value to be 'hello world'")
145+
146+
return NewToolResultText("success: " + textValue), nil
147+
}
148+
149+
tool := NewTool(
150+
"test-tool",
151+
"Test tool",
152+
[]ToolParameter{
153+
WithString("text", Description("Text parameter"), Required()),
154+
},
155+
testHandler,
156+
)
157+
158+
mcpTool := tool.toMCPServerTool()
159+
160+
mcpReq := mcp.CallToolRequest{
161+
Request: mcp.Request{},
162+
Params: struct {
163+
Name string `json:"name"`
164+
Arguments map[string]interface{} `json:"arguments,omitempty"`
165+
Meta *struct {
166+
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
167+
} `json:"_meta,omitempty"`
168+
}{
169+
Name: "test-tool",
170+
Arguments: map[string]interface{}{
171+
"text": "hello world",
172+
},
173+
Meta: nil,
174+
},
175+
}
176+
177+
result, err := mcpTool.Handler(context.Background(), mcpReq)
178+
assert.NoError(t, err)
179+
assert.NotNil(t, result)
180+
assert.False(t, result.IsError)
181+
182+
textContent, ok := result.Content[0].(mcp.TextContent)
183+
assert.True(t, ok, "Expected TextContent")
184+
assert.Equal(t, "success: hello world", textContent.Text)
185+
})
186+
187+
t.Run("ErrorHandler", func(t *testing.T) {
188+
testHandler := func(
189+
ctx context.Context,
190+
req CallToolRequest,
191+
) (*ToolResult, error) {
192+
return NewToolResultError("intentional error"), nil
193+
}
194+
195+
tool := NewTool(
196+
"error-tool",
197+
"A tool that always returns an error",
198+
[]ToolParameter{},
199+
testHandler,
200+
)
201+
202+
mcpTool := tool.toMCPServerTool()
203+
204+
mcpReq := mcp.CallToolRequest{
205+
Request: mcp.Request{},
206+
Params: struct {
207+
Name string `json:"name"`
208+
Arguments map[string]interface{} `json:"arguments,omitempty"`
209+
Meta *struct {
210+
ProgressToken mcp.ProgressToken `json:"progressToken,omitempty"`
211+
} `json:"_meta,omitempty"`
212+
}{
213+
Name: "error-tool",
214+
Arguments: map[string]interface{}{},
215+
Meta: nil,
216+
},
217+
}
218+
219+
result, err := mcpTool.Handler(context.Background(), mcpReq)
220+
assert.NoError(t, err)
221+
assert.NotNil(t, result)
222+
assert.True(t, result.IsError)
223+
224+
errorContent, ok := result.Content[0].(mcp.TextContent)
225+
assert.True(t, ok, "Expected TextContent")
226+
assert.Equal(t, "intentional error", errorContent.Text)
227+
})
228+
}
229+
230+
func TestSchemaAdditionsForToolCreation(t *testing.T) {
231+
simpleHandler := func(
232+
ctx context.Context,
233+
req CallToolRequest,
234+
) (*ToolResult, error) {
235+
return NewToolResultText("test-result"), nil
236+
}
237+
238+
comprehensiveTool := NewTool(
239+
"comprehensive-tool",
240+
"A comprehensive test tool with all schema additions",
241+
[]ToolParameter{
242+
WithString("str_param",
243+
Description("A string parameter"),
244+
Required(),
245+
Min(5),
246+
Max(100),
247+
Pattern("^[a-z]+$"),
248+
Enum("option1", "option2", "option3"),
249+
),
250+
WithNumber("num_param",
251+
Description("A number parameter"),
252+
Min(0),
253+
Max(1000),
254+
DefaultValue(50),
255+
),
256+
WithBoolean("bool_param",
257+
Description("A boolean parameter"),
258+
DefaultValue(true),
259+
),
260+
WithObject("obj_param",
261+
Description("An object parameter"),
262+
MinProperties(1),
263+
MaxProperties(10),
264+
),
265+
WithArray("arr_param",
266+
Description("An array parameter"),
267+
Min(1),
268+
Max(5),
269+
),
270+
},
271+
simpleHandler,
272+
)
273+
274+
mcpTool := comprehensiveTool.toMCPServerTool()
275+
276+
assert.Equal(t, "comprehensive-tool", mcpTool.Tool.Name)
277+
278+
for _, param := range comprehensiveTool.parameters {
279+
switch param.Name {
280+
case "str_param":
281+
assert.Equal(t, "string", param.Schema["type"])
282+
assert.Equal(t, true, param.Schema["required"])
283+
assert.Equal(t, 5, param.Schema["minLength"])
284+
case "num_param":
285+
assert.Equal(t, "number", param.Schema["type"])
286+
assert.Equal(t, float64(0), param.Schema["minimum"])
287+
288+
t.Logf(
289+
"Default value type: %T, value: %v",
290+
param.Schema["default"],
291+
param.Schema["default"],
292+
)
293+
294+
switch v := param.Schema["default"].(type) {
295+
case int:
296+
assert.Equal(t, 50, v)
297+
case float64:
298+
assert.Equal(t, 50.0, v)
299+
default:
300+
t.Errorf(
301+
"Expected default value to be int or float64, got %T",
302+
param.Schema["default"],
303+
)
304+
}
305+
}
306+
}
307+
}
308+
309+
func TestOptionSetterBehavior(t *testing.T) {
310+
setter := &mark3labsOptionSetter{
311+
mcpOptions: []server.ServerOption{},
312+
}
313+
314+
validOption := server.WithLogging()
315+
err := setter.SetOption(validOption)
316+
assert.NoError(t, err)
317+
assert.Len(t, setter.mcpOptions, 1, "Expected 1 option to be added")
318+
319+
invalidOption := "not an option"
320+
err = setter.SetOption(invalidOption)
321+
assert.NoError(t, err)
322+
assert.Len(t, setter.mcpOptions, 1, "Expected still 1 option")
323+
}

pkg/mcpgo/stdio.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var ErrInvalidServerImplementation = errors.New(
1616
)
1717

1818
// NewStdioServer creates a new stdio transport server
19-
func NewStdioServer(mcpServer Server) (*mark3labsStdioImpl, error) {
19+
func NewStdioServer(mcpServer Server) (TransportServer, error) {
2020
sImpl, ok := mcpServer.(*mark3labsImpl)
2121
if !ok {
2222
return nil, fmt.Errorf("%w: expected *mark3labsImpl, got %T",

pkg/mcpgo/stdio_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package mcpgo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
// mockInvalidServer implements Server but not mark3labsImpl
10+
type mockInvalidServer struct{}
11+
12+
func (m *mockInvalidServer) AddTools(tools ...Tool) {}
13+
14+
// TestNewStdioServer tests the creation of a stdio server
15+
func TestNewStdioServer(t *testing.T) {
16+
t.Run("Success case", func(t *testing.T) {
17+
server := NewServer("test-server", "1.0.0")
18+
19+
stdioServer, err := NewStdioServer(server)
20+
21+
assert.NoError(t, err)
22+
assert.NotNil(t, stdioServer)
23+
24+
// Verify interface implementation
25+
var _ TransportServer = stdioServer
26+
})
27+
28+
t.Run("Invalid server implementation", func(t *testing.T) {
29+
invalidServer := &mockInvalidServer{}
30+
31+
stdioServer, err := NewStdioServer(invalidServer)
32+
33+
assert.Error(t, err)
34+
assert.Nil(t, stdioServer)
35+
assert.ErrorIs(t, err, ErrInvalidServerImplementation)
36+
})
37+
}

0 commit comments

Comments
 (0)