Skip to content

Commit a0a1a44

Browse files
committed
Add tests
1 parent b6caa41 commit a0a1a44

File tree

1 file changed

+324
-0
lines changed

1 file changed

+324
-0
lines changed
Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
package io.modelcontextprotocol.kotlin.sdk
2+
3+
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
4+
import kotlinx.serialization.json.buildJsonObject
5+
import kotlinx.serialization.json.put
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
import kotlin.test.assertIs
9+
10+
class RequestSerializerTest {
11+
12+
// Client Result Tests
13+
@Test
14+
fun `should deserialize CreateMessageResult polymorphically`() {
15+
val json = """{
16+
"model": "test-model",
17+
"role": "assistant",
18+
"content": {
19+
"type": "text",
20+
"text": "Hello"
21+
},
22+
"stopReason": "endTurn"
23+
}"""
24+
25+
val decoded = McpJson.decodeFromString<RequestResult>(json)
26+
27+
assertIs<CreateMessageResult>(decoded)
28+
assertEquals("test-model", decoded.model)
29+
assertEquals(Role.assistant, decoded.role)
30+
assertEquals(StopReason.EndTurn, decoded.stopReason)
31+
}
32+
33+
@Test
34+
fun `should deserialize ListRootsResult polymorphically`() {
35+
val json = """{
36+
"roots": [
37+
{
38+
"uri": "file:///test",
39+
"name": "Test Root"
40+
}
41+
]
42+
}"""
43+
44+
val decoded = McpJson.decodeFromString<RequestResult>(json)
45+
46+
assertIs<ListRootsResult>(decoded)
47+
assertEquals(1, decoded.roots.size)
48+
assertEquals("file:///test", decoded.roots[0].uri)
49+
assertEquals("Test Root", decoded.roots[0].name)
50+
}
51+
52+
@Test
53+
fun `should deserialize CreateElicitationResult polymorphically`() {
54+
val json = """{
55+
"action": "accept",
56+
"content": {
57+
"timezone": "Europe/Amsterdam"
58+
}
59+
}"""
60+
61+
val decoded = McpJson.decodeFromString<RequestResult>(json)
62+
63+
assertIs<CreateElicitationResult>(decoded)
64+
assertEquals(CreateElicitationResult.Action.accept, decoded.action)
65+
}
66+
67+
// Server Result Tests
68+
@Test
69+
fun `should deserialize ListToolsResult polymorphically`() {
70+
val json = """{
71+
"tools": [
72+
{
73+
"name": "test-tool",
74+
"description": "A test tool",
75+
"inputSchema": {
76+
"type": "object",
77+
"properties": {}
78+
}
79+
}
80+
]
81+
}"""
82+
83+
val decoded = McpJson.decodeFromString<RequestResult>(json)
84+
85+
assertIs<ListToolsResult>(decoded)
86+
assertEquals(1, decoded.tools.size)
87+
assertEquals("test-tool", decoded.tools[0].name)
88+
assertEquals("A test tool", decoded.tools[0].description)
89+
}
90+
91+
@Test
92+
fun `should deserialize ListResourcesResult polymorphically`() {
93+
val json = """{
94+
"resources": [
95+
{
96+
"uri": "file:///test.txt",
97+
"name": "test.txt",
98+
"mimeType": "text/plain"
99+
}
100+
]
101+
}"""
102+
103+
val decoded = McpJson.decodeFromString<RequestResult>(json)
104+
105+
assertIs<ListResourcesResult>(decoded)
106+
assertEquals(1, decoded.resources.size)
107+
assertEquals("file:///test.txt", decoded.resources[0].uri)
108+
assertEquals("test.txt", decoded.resources[0].name)
109+
assertEquals("text/plain", decoded.resources[0].mimeType)
110+
}
111+
112+
@Test
113+
fun `should deserialize ListResourceTemplatesResult polymorphically`() {
114+
val json = """{
115+
"resourceTemplates": [
116+
{
117+
"uriTemplate": "file:///templates/{name}",
118+
"name": "template",
119+
"mimeType": "text/plain"
120+
}
121+
]
122+
}"""
123+
124+
val decoded = McpJson.decodeFromString<RequestResult>(json)
125+
126+
assertIs<ListResourceTemplatesResult>(decoded)
127+
assertEquals(1, decoded.resourceTemplates.size)
128+
assertEquals("file:///templates/{name}", decoded.resourceTemplates[0].uriTemplate)
129+
assertEquals("template", decoded.resourceTemplates[0].name)
130+
assertEquals("text/plain", decoded.resourceTemplates[0].mimeType)
131+
}
132+
133+
@Test
134+
fun `should deserialize ListPromptsResult polymorphically`() {
135+
val json = """{
136+
"prompts": [
137+
{
138+
"name": "test-prompt",
139+
"description": "A test prompt"
140+
}
141+
]
142+
}"""
143+
144+
val decoded = McpJson.decodeFromString<RequestResult>(json)
145+
146+
assertIs<ListPromptsResult>(decoded)
147+
assertEquals(1, decoded.prompts.size)
148+
assertEquals("test-prompt", decoded.prompts[0].name)
149+
assertEquals("A test prompt", decoded.prompts[0].description)
150+
}
151+
152+
@Test
153+
fun `should deserialize InitializeResult polymorphically`() {
154+
val json = """{
155+
"capabilities": {
156+
"logging": {},
157+
"prompts": {
158+
"listChanged": true
159+
},
160+
"resources": {
161+
"subscribe": true,
162+
"listChanged": true
163+
},
164+
"tools": {
165+
"listChanged": true
166+
}
167+
},
168+
"protocolVersion": "2024-11-05",
169+
"serverInfo": {
170+
"name": "Test Server",
171+
"version": "1.0.0"
172+
}
173+
}"""
174+
175+
val decoded = McpJson.decodeFromString<RequestResult>(json)
176+
177+
assertIs<InitializeResult>(decoded)
178+
assertEquals("2024-11-05", decoded.protocolVersion)
179+
assertEquals("Test Server", decoded.serverInfo.name)
180+
assertEquals("1.0.0", decoded.serverInfo.version)
181+
}
182+
183+
@Test
184+
fun `should deserialize GetPromptResult polymorphically`() {
185+
val json = """{
186+
"description": "A test prompt",
187+
"messages": [
188+
{
189+
"role": "user",
190+
"content": {
191+
"type": "text",
192+
"text": "Hello"
193+
}
194+
}
195+
]
196+
}"""
197+
198+
val decoded = McpJson.decodeFromString<RequestResult>(json)
199+
200+
assertIs<GetPromptResult>(decoded)
201+
assertEquals("A test prompt", decoded.description)
202+
assertEquals(1, decoded.messages.size)
203+
assertEquals(Role.user, decoded.messages[0].role)
204+
}
205+
206+
@Test
207+
fun `should deserialize CompleteResult polymorphically`() {
208+
val json = """{
209+
"completion": {
210+
"values": ["option1", "option2"],
211+
"total": 2,
212+
"hasMore": false
213+
}
214+
}"""
215+
216+
val decoded = McpJson.decodeFromString<RequestResult>(json)
217+
218+
assertIs<CompleteResult>(decoded)
219+
assertEquals(2, decoded.completion.values.size)
220+
assertEquals("option1", decoded.completion.values[0])
221+
assertEquals("option2", decoded.completion.values[1])
222+
assertEquals(2, decoded.completion.total)
223+
assertEquals(false, decoded.completion.hasMore)
224+
}
225+
226+
@Test
227+
fun `should deserialize ReadResourceResult polymorphically`() {
228+
val json = """{
229+
"contents": [
230+
{
231+
"uri": "file:///test.txt",
232+
"mimeType": "text/plain",
233+
"text": "Hello World"
234+
}
235+
]
236+
}"""
237+
238+
val decoded = McpJson.decodeFromString<RequestResult>(json)
239+
240+
assertIs<ReadResourceResult>(decoded)
241+
assertEquals(1, decoded.contents.size)
242+
assertIs<TextResourceContents>(decoded.contents[0])
243+
val textContent = decoded.contents[0] as TextResourceContents
244+
assertEquals("file:///test.txt", textContent.uri)
245+
assertEquals("text/plain", textContent.mimeType)
246+
assertEquals("Hello World", textContent.text)
247+
}
248+
249+
@Test
250+
fun `should deserialize CallToolResult polymorphically`() {
251+
val json = """{
252+
"content": [
253+
{
254+
"type": "text",
255+
"text": "Tool result"
256+
}
257+
],
258+
"isError": false
259+
}"""
260+
261+
val decoded = McpJson.decodeFromString<RequestResult>(json)
262+
263+
assertIs<CallToolResult>(decoded)
264+
assertEquals(1, decoded.content.size)
265+
assertIs<TextContent>(decoded.content[0])
266+
assertEquals("Tool result", (decoded.content[0] as TextContent).text)
267+
assertEquals(false, decoded.isError)
268+
}
269+
270+
@Test
271+
fun `should deserialize CompatibilityCallToolResult polymorphically`() {
272+
val json = """{
273+
"toolResult": {"result": "Legacy tool result"},
274+
"content": [],
275+
"isError": false
276+
}"""
277+
278+
val decoded = McpJson.decodeFromString<RequestResult>(json)
279+
280+
assertIs<CompatibilityCallToolResult>(decoded)
281+
assertEquals(buildJsonObject { put("result", "Legacy tool result") }, decoded.toolResult)
282+
}
283+
284+
// Fallback Test
285+
@Test
286+
fun `should deserialize EmptyRequestResult for unknown result type`() {
287+
val json = """{"unknownField": "value"}"""
288+
289+
val decoded = McpJson.decodeFromString<RequestResult>(json)
290+
291+
assertIs<EmptyRequestResult>(decoded)
292+
assertEquals(EmptyJsonObject, decoded._meta)
293+
}
294+
295+
@Test
296+
fun `should handle empty JSON object`() {
297+
val json = """{}"""
298+
299+
val decoded = McpJson.decodeFromString<RequestResult>(json)
300+
301+
assertIs<EmptyRequestResult>(decoded)
302+
}
303+
304+
// Priority Test - Client results should take precedence over server results
305+
@Test
306+
fun `should prioritize client results over server results when both match`() {
307+
// This JSON could potentially match both CreateMessageResult (client) and some server result
308+
// but CreateMessageResult should be selected first due to the order in RequestResultPolymorphicSerializer
309+
val json = """{
310+
"model": "test-model",
311+
"role": "assistant",
312+
"content": {
313+
"type": "text",
314+
"text": "Test message"
315+
},
316+
"stopReason": "endTurn"
317+
}"""
318+
319+
val decoded = McpJson.decodeFromString<RequestResult>(json)
320+
321+
assertIs<CreateMessageResult>(decoded)
322+
assertEquals("test-model", decoded.model)
323+
}
324+
}

0 commit comments

Comments
 (0)