Skip to content

Commit 1b66083

Browse files
committed
Replaced serving test with mock client
1 parent 97cc889 commit 1b66083

File tree

3 files changed

+315
-644
lines changed

3 files changed

+315
-644
lines changed
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
import Foundation
2+
import Testing
3+
@testable import SwiftMCP
4+
import AnyCodable
5+
6+
/// A mock client that directly calls the Calculator's handleRequest method
7+
class MockClient {
8+
private let calculator: Calculator
9+
10+
init(calculator: Calculator) {
11+
self.calculator = calculator
12+
}
13+
14+
func send(_ request: JSONRPCMessage) async throws -> JSONRPCMessage {
15+
guard let response = await calculator.handleRequest(request) else {
16+
throw MCPError.invalidResponse
17+
}
18+
return response
19+
}
20+
}
21+
22+
@Test("Tests add function with mock client")
23+
func testAddViaMockClient() async throws {
24+
let calculator = Calculator()
25+
let client = MockClient(calculator: calculator)
26+
27+
let request = JSONRPCMessage(
28+
id: 1,
29+
method: "tools/call",
30+
params: [
31+
"name": "add",
32+
"arguments": [
33+
"a": 2,
34+
"b": 3
35+
]
36+
]
37+
)
38+
39+
let response = try await client.send(request)
40+
41+
#expect(response.id == 1)
42+
#expect(response.error == nil)
43+
#expect(response.method == nil)
44+
#expect(response.params == nil)
45+
46+
let result = unwrap(response.result)
47+
let isError = unwrap(result["isError"]?.value as? Bool)
48+
#expect(isError == false)
49+
50+
let content = unwrap(result["content"]?.value as? [[String: String]])
51+
let firstContent = unwrap(content.first)
52+
let type = unwrap(firstContent["type"])
53+
let text = unwrap(firstContent["text"])
54+
55+
#expect(type == "text")
56+
#expect(text == "5") // 2 + 3 = 5
57+
}
58+
59+
@Test("Tests greet function with mock client")
60+
func testGreetViaMockClient() async throws {
61+
let calculator = Calculator()
62+
let client = MockClient(calculator: calculator)
63+
64+
let request = JSONRPCMessage(
65+
id: 2,
66+
method: "tools/call",
67+
params: [
68+
"name": "greet",
69+
"arguments": [
70+
"name": "Oliver"
71+
]
72+
]
73+
)
74+
75+
let response = try await client.send(request)
76+
77+
#expect(response.id == 2)
78+
#expect(response.error == nil)
79+
80+
let result = unwrap(response.result)
81+
let isError = unwrap(result["isError"]?.value as? Bool)
82+
#expect(isError == false)
83+
84+
let content = unwrap(result["content"]?.value as? [[String: String]])
85+
let firstContent = unwrap(content.first)
86+
let type = unwrap(firstContent["type"])
87+
let text = unwrap(firstContent["text"])
88+
89+
#expect(type == "text")
90+
#expect(text == "Hello, Oliver!")
91+
}
92+
93+
@Test("Tests subtract function with mock client")
94+
func testSubtractViaMockClient() async throws {
95+
let calculator = Calculator()
96+
let client = MockClient(calculator: calculator)
97+
98+
let request = JSONRPCMessage(
99+
id: 3,
100+
method: "tools/call",
101+
params: [
102+
"name": "subtract",
103+
"arguments": [
104+
"a": 5,
105+
"b": 3
106+
]
107+
]
108+
)
109+
110+
let response = try await client.send(request)
111+
112+
#expect(response.id == 3)
113+
#expect(response.error == nil)
114+
115+
let result = unwrap(response.result)
116+
let isError = unwrap(result["isError"]?.value as? Bool)
117+
#expect(isError == false)
118+
119+
let content = unwrap(result["content"]?.value as? [[String: String]])
120+
let firstContent = unwrap(content.first)
121+
let type = unwrap(firstContent["type"])
122+
let text = unwrap(firstContent["text"])
123+
124+
#expect(type == "text")
125+
#expect(text == "2") // 5 - 3 = 2
126+
}
127+
128+
@Test("Tests multiply function with mock client")
129+
func testMultiplyViaMockClient() async throws {
130+
let calculator = Calculator()
131+
let client = MockClient(calculator: calculator)
132+
133+
let request = JSONRPCMessage(
134+
id: 4,
135+
method: "tools/call",
136+
params: [
137+
"name": "multiply",
138+
"arguments": [
139+
"a": 4,
140+
"b": 3
141+
]
142+
]
143+
)
144+
145+
let response = try await client.send(request)
146+
147+
#expect(response.id == 4)
148+
#expect(response.error == nil)
149+
150+
let result = unwrap(response.result)
151+
let isError = unwrap(result["isError"]?.value as? Bool)
152+
#expect(isError == false)
153+
154+
let content = unwrap(result["content"]?.value as? [[String: String]])
155+
let firstContent = unwrap(content.first)
156+
let type = unwrap(firstContent["type"])
157+
let text = unwrap(firstContent["text"])
158+
159+
#expect(type == "text")
160+
#expect(text == "12") // 4 * 3 = 12
161+
}
162+
163+
@Test("Tests divide function with mock client")
164+
func testDivideViaMockClient() async throws {
165+
let calculator = Calculator()
166+
let client = MockClient(calculator: calculator)
167+
168+
let request = JSONRPCMessage(
169+
id: 5,
170+
method: "tools/call",
171+
params: [
172+
"name": "divide",
173+
"arguments": [
174+
"numerator": 10,
175+
"denominator": 2
176+
]
177+
]
178+
)
179+
180+
let response = try await client.send(request)
181+
182+
#expect(response.id == 5)
183+
#expect(response.error == nil)
184+
185+
let result = unwrap(response.result)
186+
let isError = unwrap(result["isError"]?.value as? Bool)
187+
#expect(isError == false)
188+
189+
let content = unwrap(result["content"]?.value as? [[String: String]])
190+
let firstContent = unwrap(content.first)
191+
let type = unwrap(firstContent["type"])
192+
let text = unwrap(firstContent["text"])
193+
194+
#expect(type == "text")
195+
#expect(text == "5.0") // 10 / 2 = 5.0
196+
}
197+
198+
@Test("Tests array processing with mock client")
199+
func testArrayViaMockClient() async throws {
200+
let calculator = Calculator()
201+
let client = MockClient(calculator: calculator)
202+
203+
let request = JSONRPCMessage(
204+
id: 6,
205+
method: "tools/call",
206+
params: [
207+
"name": "testArray",
208+
"arguments": [
209+
"a": [1, 2, 3, 4, 5]
210+
]
211+
]
212+
)
213+
214+
let response = try await client.send(request)
215+
216+
#expect(response.id == 6)
217+
#expect(response.error == nil)
218+
219+
let result = unwrap(response.result)
220+
let isError = unwrap(result["isError"]?.value as? Bool)
221+
#expect(isError == false)
222+
223+
let content = unwrap(result["content"]?.value as? [[String: String]])
224+
let firstContent = unwrap(content.first)
225+
let type = unwrap(firstContent["type"])
226+
let text = unwrap(firstContent["text"])
227+
228+
#expect(type == "text")
229+
#expect(text == "1, 2, 3, 4, 5")
230+
}
231+
232+
@Test("Tests ping function with mock client")
233+
func testPingViaMockClient() async throws {
234+
let calculator = Calculator()
235+
let client = MockClient(calculator: calculator)
236+
237+
let request = JSONRPCMessage(
238+
id: 7,
239+
method: "tools/call",
240+
params: [
241+
"name": "ping",
242+
"arguments": [:]
243+
]
244+
)
245+
246+
let response = try await client.send(request)
247+
248+
#expect(response.id == 7)
249+
#expect(response.error == nil)
250+
251+
let result = unwrap(response.result)
252+
let isError = unwrap(result["isError"]?.value as? Bool)
253+
#expect(isError == false)
254+
255+
let content = unwrap(result["content"]?.value as? [[String: String]])
256+
let firstContent = unwrap(content.first)
257+
let type = unwrap(firstContent["type"])
258+
let text = unwrap(firstContent["text"])
259+
260+
#expect(type == "text")
261+
#expect(text == "pong")
262+
}
263+
264+
@Test("Tests noop function with mock client")
265+
func testNoopViaMockClient() async throws {
266+
let calculator = Calculator()
267+
let client = MockClient(calculator: calculator)
268+
269+
let request = JSONRPCMessage(
270+
id: 8,
271+
method: "tools/call",
272+
params: [
273+
"name": "noop",
274+
"arguments": [:]
275+
]
276+
)
277+
278+
let response = try await client.send(request)
279+
280+
#expect(response.id == 8)
281+
#expect(response.error == nil)
282+
283+
let result = unwrap(response.result)
284+
let isError = unwrap(result["isError"]?.value as? Bool)
285+
#expect(isError == false)
286+
287+
let content = unwrap(result["content"]?.value as? [[String: String]])
288+
let firstContent = unwrap(content.first)
289+
let type = unwrap(firstContent["type"])
290+
let text = unwrap(firstContent["text"])
291+
292+
#expect(type == "text")
293+
#expect(text == "") // noop returns empty string
294+
}
295+
296+
/// Errors that can occur during MCP client operations
297+
public enum MCPError: LocalizedError {
298+
case notConnected
299+
case invalidEndpointURL
300+
case invalidResponse
301+
case timeout
302+
303+
public var errorDescription: String? {
304+
switch self {
305+
case .notConnected:
306+
return "Client is not connected to the server"
307+
case .invalidEndpointURL:
308+
return "Invalid messages endpoint URL received from server"
309+
case .invalidResponse:
310+
return "Invalid response received from server"
311+
case .timeout:
312+
return "Request timed out"
313+
}
314+
}
315+
}

0 commit comments

Comments
 (0)