refactor: Refactor serialization tests (#527)
1. Migrate test-utils module to multiplatform, add serialization
utilities
- Migrated `test-utils` to support Kotlin Multiplatform (JVM, JS,
Native).
- Added `verifySerialization`, `verifyDeserialization`,
`verifySerializationRoundTrip` utilities to streamline and validate JSON
serialization/deserialization
2. Refactor serialization tests to use test serialization utilities
- Updated serialization tests across the project to use
`verifySerialization` and `verifyDeserialization` utilities.
- Improved consistency and ensured proper round-trip validation for JSON
deserialization.
3. Remove some detekt baseline files and suppress `LongMethod` findings
- Deleted detekt baseline XML files for test sources.
- Suppressed `LongMethod` findings in relevant tests using `@Suppress`
annotations.
- Replaced empty method implementations with explicit `// noop` comments
for clarity.
- Adjusted min test coverage threshold in `build.gradle.kts`.
## Motivation and Context
To improve serialization testing
## How Has This Been Tested?
Unit tests
## Breaking Changes
No
## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [x] Refactoring
## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
This commit is contained in:
parent
1b255a2baf
commit
367618d109
21 changed files with 858 additions and 559 deletions
|
|
@ -121,10 +121,8 @@ kotlin {
|
|||
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation(project(":test-utils"))
|
||||
implementation(kotlin("test"))
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
implementation(libs.kotest.assertions.core)
|
||||
implementation(libs.kotest.assertions.json)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>EmptyFunctionBlock:ProtocolTest.kt:RecordingTransport${}</ID>
|
||||
<ID>EmptyFunctionBlock:ProtocolTest.kt:TestProtocol${}</ID>
|
||||
<ID>LongMethod:ElicitationTest.kt:ElicitationTest$@Test fun `should serialize ElicitRequest with requested schema`</ID>
|
||||
<ID>LongMethod:SamplingDslTest.kt:SamplingDslTest$@Test fun `buildCreateMessageRequest should build with all fields`</ID>
|
||||
<ID>LongMethod:SamplingTest.kt:SamplingTest$@Test fun `should serialize CreateMessageRequest with all fields`</ID>
|
||||
<ID>LongMethod:ToolsTest.kt:ToolsTest$@Test fun `should serialize Tool with annotations and schemas`</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>EmptyFunctionBlock:ProtocolTest.kt:RecordingTransport${}</ID>
|
||||
<ID>EmptyFunctionBlock:ProtocolTest.kt:TestProtocol${}</ID>
|
||||
<ID>LongMethod:ElicitationTest.kt:ElicitationTest$@Test fun `should serialize ElicitRequest with requested schema`</ID>
|
||||
<ID>LongMethod:SamplingDslTest.kt:SamplingDslTest$@Test fun `buildCreateMessageRequest should build with all fields`</ID>
|
||||
<ID>LongMethod:SamplingTest.kt:SamplingTest$@Test fun `should serialize CreateMessageRequest with all fields`</ID>
|
||||
<ID>LongMethod:ToolsTest.kt:ToolsTest$@Test fun `should serialize Tool with annotations and schemas`</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -154,9 +154,15 @@ class ProtocolTest {
|
|||
}
|
||||
|
||||
private class TestProtocol : Protocol(null) {
|
||||
override fun assertCapabilityForMethod(method: Method) {}
|
||||
override fun assertNotificationCapability(method: Method) {}
|
||||
override fun assertRequestHandlerCapability(method: Method) {}
|
||||
override fun assertCapabilityForMethod(method: Method) {
|
||||
// noop
|
||||
}
|
||||
override fun assertNotificationCapability(method: Method) {
|
||||
// noop
|
||||
}
|
||||
override fun assertRequestHandlerCapability(method: Method) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordingTransport : Transport {
|
||||
|
|
@ -164,7 +170,9 @@ private class RecordingTransport : Transport {
|
|||
private var onMessageCallback: (suspend (JSONRPCMessage) -> Unit)? = null
|
||||
private var onCloseCallback: (() -> Unit)? = null
|
||||
|
||||
override suspend fun start() {}
|
||||
override suspend fun start() {
|
||||
// noop
|
||||
}
|
||||
|
||||
override suspend fun send(message: JSONRPCMessage, options: TransportSendOptions?) {
|
||||
sentMessages.send(message)
|
||||
|
|
@ -178,7 +186,9 @@ private class RecordingTransport : Transport {
|
|||
onCloseCallback = block
|
||||
}
|
||||
|
||||
override fun onError(block: (Throwable) -> Unit) {}
|
||||
override fun onError(block: (Throwable) -> Unit) {
|
||||
// noop
|
||||
}
|
||||
|
||||
override fun onMessage(block: suspend (JSONRPCMessage) -> Unit) {
|
||||
onMessageCallback = block
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerializationRoundTrip
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlin.test.Test
|
||||
|
|
@ -15,14 +18,16 @@ class CapabilitiesTest {
|
|||
name = "test-server",
|
||||
version = "1.0.0",
|
||||
)
|
||||
val json = McpJson.encodeToString(implementation)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
implementation,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "test-server",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -36,9 +41,10 @@ class CapabilitiesTest {
|
|||
Icon(src = "https://example.com/icon.png"),
|
||||
),
|
||||
)
|
||||
val json = McpJson.encodeToString(implementation)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
implementation,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "test-server",
|
||||
"version": "1.0.0",
|
||||
|
|
@ -48,7 +54,8 @@ class CapabilitiesTest {
|
|||
{"src": "https://example.com/icon.png"}
|
||||
]
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -62,7 +69,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val implementation = McpJson.decodeFromString<Implementation>(json)
|
||||
val implementation = verifyDeserialization<Implementation>(McpJson, json)
|
||||
|
||||
assertEquals("test-server", implementation.name)
|
||||
assertEquals("2.1.3-beta", implementation.version)
|
||||
|
|
@ -79,7 +86,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val implementation = McpJson.decodeFromString<Implementation>(json)
|
||||
val implementation = verifyDeserialization<Implementation>(McpJson, json)
|
||||
|
||||
assertEquals("minimal-server", implementation.name)
|
||||
assertEquals("0.1.0", implementation.version)
|
||||
|
|
@ -97,13 +104,7 @@ class CapabilitiesTest {
|
|||
websiteUrl = "https://test.com",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(original)
|
||||
val decoded = McpJson.decodeFromString<Implementation>(json)
|
||||
|
||||
assertEquals(original.name, decoded.name)
|
||||
assertEquals(original.version, decoded.version)
|
||||
assertEquals(original.title, decoded.title)
|
||||
assertEquals(original.websiteUrl, decoded.websiteUrl)
|
||||
verifySerializationRoundTrip(original, McpJson)
|
||||
}
|
||||
|
||||
// ClientCapabilities tests
|
||||
|
|
@ -120,13 +121,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ClientCapabilities(
|
||||
sampling = ClientCapabilities.sampling,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"sampling": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -134,13 +137,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ClientCapabilities(
|
||||
roots = ClientCapabilities.Roots(),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"roots": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -148,15 +153,17 @@ class CapabilitiesTest {
|
|||
val capabilities = ClientCapabilities(
|
||||
roots = ClientCapabilities.Roots(listChanged = true),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"roots": {
|
||||
"listChanged": true
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -164,13 +171,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ClientCapabilities(
|
||||
elicitation = ClientCapabilities.elicitation,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"elicitation": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -186,9 +195,10 @@ class CapabilitiesTest {
|
|||
val capabilities = ClientCapabilities(
|
||||
experimental = experimental,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"experimental": {
|
||||
"customFeature": {
|
||||
|
|
@ -196,7 +206,8 @@ class CapabilitiesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -210,9 +221,10 @@ class CapabilitiesTest {
|
|||
elicitation = ClientCapabilities.elicitation,
|
||||
experimental = experimental,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"sampling": {},
|
||||
"roots": {
|
||||
|
|
@ -225,7 +237,8 @@ class CapabilitiesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -240,7 +253,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ClientCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ClientCapabilities>(McpJson, json)
|
||||
|
||||
assertEquals(EmptyJsonObject, capabilities.sampling)
|
||||
assertEquals(true, capabilities.roots?.listChanged)
|
||||
|
|
@ -251,7 +264,7 @@ class CapabilitiesTest {
|
|||
fun `should deserialize empty ClientCapabilities from JSON`() {
|
||||
val json = "{}"
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ClientCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ClientCapabilities>(McpJson, json)
|
||||
|
||||
assertNull(capabilities.sampling)
|
||||
assertNull(capabilities.roots)
|
||||
|
|
@ -267,12 +280,7 @@ class CapabilitiesTest {
|
|||
elicitation = ClientCapabilities.elicitation,
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(original)
|
||||
val decoded = McpJson.decodeFromString<ClientCapabilities>(json)
|
||||
|
||||
assertEquals(original.sampling, decoded.sampling)
|
||||
assertEquals(original.roots?.listChanged, decoded.roots?.listChanged)
|
||||
assertEquals(original.elicitation, decoded.elicitation)
|
||||
verifySerializationRoundTrip(original, McpJson)
|
||||
}
|
||||
|
||||
// ServerCapabilities tests
|
||||
|
|
@ -289,13 +297,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"tools": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -303,15 +313,17 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
tools = ServerCapabilities.Tools(listChanged = true),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"tools": {
|
||||
"listChanged": true
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -319,13 +331,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"resources": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -333,15 +347,17 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(listChanged = true),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"resources": {
|
||||
"listChanged": true
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -349,15 +365,17 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
resources = ServerCapabilities.Resources(subscribe = true),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"resources": {
|
||||
"subscribe": true
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -368,16 +386,18 @@ class CapabilitiesTest {
|
|||
subscribe = false,
|
||||
),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"resources": {
|
||||
"listChanged": true,
|
||||
"subscribe": false
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -385,13 +405,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"prompts": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -399,15 +421,17 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
prompts = ServerCapabilities.Prompts(listChanged = false),
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"prompts": {
|
||||
"listChanged": false
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -415,13 +439,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
logging = ServerCapabilities.Logging,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"logging": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -429,13 +455,15 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
completions = ServerCapabilities.Completions,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"completions": {}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -451,9 +479,10 @@ class CapabilitiesTest {
|
|||
val capabilities = ServerCapabilities(
|
||||
experimental = experimental,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"experimental": {
|
||||
"customCapability": {
|
||||
|
|
@ -461,7 +490,8 @@ class CapabilitiesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -480,9 +510,10 @@ class CapabilitiesTest {
|
|||
completions = ServerCapabilities.Completions,
|
||||
experimental = experimental,
|
||||
)
|
||||
val json = McpJson.encodeToString(capabilities)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
capabilities,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"tools": {
|
||||
"listChanged": true
|
||||
|
|
@ -502,7 +533,8 @@ class CapabilitiesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -524,7 +556,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ServerCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ServerCapabilities>(McpJson, json)
|
||||
|
||||
assertEquals(true, capabilities.tools?.listChanged)
|
||||
assertEquals(false, capabilities.resources?.listChanged)
|
||||
|
|
@ -538,7 +570,7 @@ class CapabilitiesTest {
|
|||
fun `should deserialize empty ServerCapabilities from JSON`() {
|
||||
val json = "{}"
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ServerCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ServerCapabilities>(McpJson, json)
|
||||
|
||||
assertNull(capabilities.tools)
|
||||
assertNull(capabilities.resources)
|
||||
|
|
@ -560,14 +592,7 @@ class CapabilitiesTest {
|
|||
logging = ServerCapabilities.Logging,
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(original)
|
||||
val decoded = McpJson.decodeFromString<ServerCapabilities>(json)
|
||||
|
||||
assertEquals(original.tools?.listChanged, decoded.tools?.listChanged)
|
||||
assertEquals(original.resources?.listChanged, decoded.resources?.listChanged)
|
||||
assertEquals(original.resources?.subscribe, decoded.resources?.subscribe)
|
||||
assertEquals(original.prompts?.listChanged, decoded.prompts?.listChanged)
|
||||
assertEquals(original.logging, decoded.logging)
|
||||
verifySerializationRoundTrip(original, McpJson)
|
||||
}
|
||||
|
||||
// Additional nested type tests
|
||||
|
|
@ -579,7 +604,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ClientCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ClientCapabilities>(McpJson, json)
|
||||
|
||||
assertNull(capabilities.roots?.listChanged)
|
||||
}
|
||||
|
|
@ -594,7 +619,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ServerCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ServerCapabilities>(McpJson, json)
|
||||
|
||||
assertNull(capabilities.tools?.listChanged)
|
||||
assertNull(capabilities.resources?.listChanged)
|
||||
|
|
@ -612,7 +637,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ClientCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ClientCapabilities>(McpJson, json)
|
||||
|
||||
// Should not fail - additionalProperties are allowed
|
||||
assertEquals("customValue", capabilities.sampling?.get("customProperty")?.toString()?.trim('"'))
|
||||
|
|
@ -628,7 +653,7 @@ class CapabilitiesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val capabilities = McpJson.decodeFromString<ServerCapabilities>(json)
|
||||
val capabilities = verifyDeserialization<ServerCapabilities>(McpJson, json)
|
||||
|
||||
// Should not fail - additionalProperties are allowed
|
||||
assertEquals("debug", capabilities.logging?.get("level")?.toString()?.trim('"'))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerializationRoundTrip
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertIs
|
||||
import kotlin.test.assertNotEquals
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
|
|
@ -12,29 +14,30 @@ class CommonTypeTest {
|
|||
|
||||
@Test
|
||||
fun `should have correct latest protocol version`() {
|
||||
assertNotEquals("", LATEST_PROTOCOL_VERSION)
|
||||
assertEquals("2025-06-18", LATEST_PROTOCOL_VERSION)
|
||||
LATEST_PROTOCOL_VERSION shouldBe "2025-06-18"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should have correct supported protocol versions`() {
|
||||
assertIs<List<String>>(SUPPORTED_PROTOCOL_VERSIONS)
|
||||
assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains(LATEST_PROTOCOL_VERSION))
|
||||
assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains("2025-03-26"))
|
||||
assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains("2024-11-05"))
|
||||
assertEquals(3, SUPPORTED_PROTOCOL_VERSIONS.size)
|
||||
SUPPORTED_PROTOCOL_VERSIONS shouldContainExactlyInAnyOrder listOf(
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
"2025-03-26",
|
||||
"2024-11-05",
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize Icon with minimal fields`() {
|
||||
val icon = Icon(src = "https://example.com/icon.png")
|
||||
val json = McpJson.encodeToString(icon)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
icon,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"src": "https://example.com/icon.png"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -45,16 +48,18 @@ class CommonTypeTest {
|
|||
sizes = listOf("48x48", "96x96"),
|
||||
theme = Icon.Theme.Light,
|
||||
)
|
||||
val json = McpJson.encodeToString(icon)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
icon,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"src": "https://example.com/icon.png",
|
||||
"mimeType": "image/png",
|
||||
"sizes": ["48x48", "96x96"],
|
||||
"theme": "light"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -68,7 +73,7 @@ class CommonTypeTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val icon = McpJson.decodeFromString<Icon>(json)
|
||||
val icon = verifyDeserialization<Icon>(McpJson, json)
|
||||
|
||||
assertEquals("https://example.com/icon.png", icon.src)
|
||||
assertEquals("image/png", icon.mimeType)
|
||||
|
|
@ -80,7 +85,7 @@ class CommonTypeTest {
|
|||
fun `should deserialize Icon with minimal fields from JSON`() {
|
||||
val json = """{"src": "https://example.com/icon.png"}"""
|
||||
|
||||
val icon = McpJson.decodeFromString<Icon>(json)
|
||||
val icon = verifyDeserialization<Icon>(McpJson, json)
|
||||
|
||||
assertEquals("https://example.com/icon.png", icon.src)
|
||||
assertNull(icon.mimeType)
|
||||
|
|
@ -111,8 +116,8 @@ class CommonTypeTest {
|
|||
|
||||
@Test
|
||||
fun `should deserialize Role from lowercase strings`() {
|
||||
val user = McpJson.decodeFromString<Role>("\"user\"")
|
||||
val assistant = McpJson.decodeFromString<Role>("\"assistant\"")
|
||||
val user = verifyDeserialization<Role>(McpJson, "\"user\"")
|
||||
val assistant = verifyDeserialization<Role>(McpJson, "\"assistant\"")
|
||||
|
||||
assertEquals(Role.User, user)
|
||||
assertEquals(Role.Assistant, assistant)
|
||||
|
|
@ -126,28 +131,32 @@ class CommonTypeTest {
|
|||
lastModified = "2025-01-12T15:00:58Z",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(annotations)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
annotations,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"audience": ["user", "assistant"],
|
||||
"priority": 0.8,
|
||||
"lastModified": "2025-01-12T15:00:58Z"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize Annotations with minimal fields`() {
|
||||
val annotations = Annotations(priority = 0.5)
|
||||
|
||||
val json = McpJson.encodeToString(annotations)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
annotations,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"priority": 0.5
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -160,7 +169,7 @@ class CommonTypeTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val annotations = McpJson.decodeFromString<Annotations>(json)
|
||||
val annotations = verifyDeserialization<Annotations>(McpJson, json)
|
||||
|
||||
assertEquals(listOf(Role.User), annotations.audience)
|
||||
assertEquals(0.9, annotations.priority)
|
||||
|
|
@ -175,11 +184,6 @@ class CommonTypeTest {
|
|||
lastModified = "2025-01-12T15:00:58Z",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(original)
|
||||
val decoded = McpJson.decodeFromString<Annotations>(json)
|
||||
|
||||
assertEquals(original.audience, decoded.audience)
|
||||
assertEquals(original.priority, decoded.priority)
|
||||
assertEquals(original.lastModified, decoded.lastModified)
|
||||
verifySerializationRoundTrip(original, McpJson)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
|
@ -25,9 +27,10 @@ class CompletionTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "completion/complete",
|
||||
"params": {
|
||||
|
|
@ -41,7 +44,8 @@ class CompletionTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -67,9 +71,10 @@ class CompletionTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "completion/complete",
|
||||
"params": {
|
||||
|
|
@ -92,7 +97,8 @@ class CompletionTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -121,7 +127,7 @@ class CompletionTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<CompleteRequest>(json)
|
||||
val request = verifyDeserialization<CompleteRequest>(McpJson, json)
|
||||
assertEquals(Method.Defined.CompletionComplete, request.method)
|
||||
|
||||
val params = request.params
|
||||
|
|
@ -148,9 +154,10 @@ class CompletionTest {
|
|||
},
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"completion": {
|
||||
"values": [
|
||||
|
|
@ -164,7 +171,8 @@ class CompletionTest {
|
|||
"source": "cache"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -182,7 +190,7 @@ class CompletionTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<CompleteResult>(json)
|
||||
val result = verifyDeserialization<CompleteResult>(McpJson, json)
|
||||
assertEquals(listOf("README.md", "CONTRIBUTING.md"), result.completion.values)
|
||||
assertEquals(2, result.completion.total)
|
||||
assertEquals(false, result.completion.hasMore)
|
||||
|
|
@ -209,7 +217,7 @@ class CompletionTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val decoded = McpJson.decodeFromString<CompleteResult>(json)
|
||||
val decoded = verifyDeserialization<CompleteResult>(McpJson, json)
|
||||
assertEquals(listOf("foo"), decoded.completion.values)
|
||||
assertNull(decoded.completion.total)
|
||||
assertNull(decoded.completion.hasMore)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
|
@ -15,14 +16,16 @@ class ContentTest {
|
|||
fun `should serialize TextContent with minimal fields`() {
|
||||
val content = TextContent(text = "Hello, MCP!")
|
||||
|
||||
val json = McpJson.encodeToString(content)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
content,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Hello, MCP!"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -33,9 +36,10 @@ class ContentTest {
|
|||
meta = buildJsonObject { put("origin", "assistant") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(content)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
content,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Need help?",
|
||||
|
|
@ -46,12 +50,8 @@ class ContentTest {
|
|||
"origin": "assistant"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
val decoded = McpJson.decodeFromString<MediaContent>(json) as TextContent
|
||||
assertEquals("Need help?", decoded.text)
|
||||
assertEquals(listOf(Role.User), decoded.annotations?.audience)
|
||||
assertEquals("assistant", decoded.meta?.get("origin")?.jsonPrimitive?.content)
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -61,15 +61,17 @@ class ContentTest {
|
|||
mimeType = "image/png",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(content)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
content,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "image",
|
||||
"data": "aW1hZ2VEYXRh",
|
||||
"mimeType": "image/png"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -83,7 +85,7 @@ class ContentTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val content = McpJson.decodeFromString<MediaContent>(json) as ImageContent
|
||||
val content = verifyDeserialization<MediaContent>(McpJson, json) as ImageContent
|
||||
assertEquals("Zm9vYmFy", content.data)
|
||||
assertEquals("image/jpeg", content.mimeType)
|
||||
assertEquals(0.6, content.annotations?.priority)
|
||||
|
|
@ -98,9 +100,10 @@ class ContentTest {
|
|||
meta = buildJsonObject { put("durationMs", 1200) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(content)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
content,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "audio",
|
||||
"data": "YXVkaW9kYXRh",
|
||||
|
|
@ -109,7 +112,8 @@ class ContentTest {
|
|||
"durationMs": 1200
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -123,7 +127,7 @@ class ContentTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val content = McpJson.decodeFromString<MediaContent>(json) as AudioContent
|
||||
val content = verifyDeserialization<MediaContent>(McpJson, json) as AudioContent
|
||||
assertEquals("audio/mpeg", content.mimeType)
|
||||
assertEquals("YmF6", content.data)
|
||||
assertEquals("user", content.meta?.get("speaker")?.jsonPrimitive?.content)
|
||||
|
|
@ -136,15 +140,17 @@ class ContentTest {
|
|||
uri = "file:///workspace/README.md",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(resource)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
resource,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "resource_link",
|
||||
"name": "README",
|
||||
"uri": "file:///workspace/README.md"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -161,9 +167,10 @@ class ContentTest {
|
|||
meta = buildJsonObject { put("etag", "1234") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(resource)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
resource,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "resource_link",
|
||||
"name": "README",
|
||||
|
|
@ -184,7 +191,8 @@ class ContentTest {
|
|||
"etag": "1234"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -199,7 +207,7 @@ class ContentTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val resource = McpJson.decodeFromString<ContentBlock>(json) as ResourceLink
|
||||
val resource = verifyDeserialization<ContentBlock>(McpJson, json) as ResourceLink
|
||||
assertEquals("config", resource.name)
|
||||
assertEquals("file:///config.yaml", resource.uri)
|
||||
assertEquals(512, resource.size)
|
||||
|
|
@ -220,9 +228,10 @@ class ContentTest {
|
|||
meta = buildJsonObject { put("source", "analysis") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(embedded)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
embedded,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"type": "resource",
|
||||
"resource": {
|
||||
|
|
@ -240,7 +249,8 @@ class ContentTest {
|
|||
"source": "analysis"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -257,7 +267,7 @@ class ContentTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val embedded = McpJson.decodeFromString<ContentBlock>(json) as EmbeddedResource
|
||||
val embedded = verifyDeserialization<ContentBlock>(McpJson, json) as EmbeddedResource
|
||||
assertEquals(ContentTypes.EMBEDDED_RESOURCE, embedded.type)
|
||||
assertNull(embedded.annotations)
|
||||
assertEquals("base64", embedded.meta?.get("encoding")?.jsonPrimitive?.content)
|
||||
|
|
@ -292,7 +302,7 @@ class ContentTest {
|
|||
]
|
||||
""".trimIndent()
|
||||
|
||||
val content = McpJson.decodeFromString<List<ContentBlock>>(json)
|
||||
val content = verifyDeserialization<List<ContentBlock>>(McpJson, json)
|
||||
assertEquals(3, content.size)
|
||||
assertIs<TextContent>(content[0])
|
||||
assertIs<ResourceLink>(content[1])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.int
|
||||
|
|
@ -16,6 +18,7 @@ import kotlin.test.assertNull
|
|||
class ElicitationTest {
|
||||
|
||||
@Test
|
||||
@Suppress("LongMethod")
|
||||
fun `should serialize ElicitRequest with requested schema`() {
|
||||
val request = ElicitRequest(
|
||||
ElicitRequestParams(
|
||||
|
|
@ -54,9 +57,10 @@ class ElicitationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "elicitation/create",
|
||||
"params": {
|
||||
|
|
@ -84,7 +88,8 @@ class ElicitationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -111,7 +116,7 @@ class ElicitationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<ElicitRequest>(json)
|
||||
val request = verifyDeserialization<ElicitRequest>(McpJson, json)
|
||||
assertEquals(Method.Defined.ElicitationCreate, request.method)
|
||||
|
||||
val params = request.params
|
||||
|
|
@ -163,7 +168,7 @@ class ElicitationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val decoded = McpJson.decodeFromString<ElicitResult>(json)
|
||||
val decoded = verifyDeserialization<ElicitResult>(McpJson, json)
|
||||
assertEquals(ElicitResult.Action.Accept, decoded.action)
|
||||
|
||||
val content = decoded.content
|
||||
|
|
@ -188,7 +193,7 @@ class ElicitationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ElicitResult>(json)
|
||||
val result = verifyDeserialization<ElicitResult>(McpJson, json)
|
||||
assertEquals(ElicitResult.Action.Decline, result.action)
|
||||
assertNull(result.content)
|
||||
assertEquals("User skipped", result.meta?.get("reason")?.jsonPrimitive?.content)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
|
|
@ -14,6 +15,7 @@ import kotlin.test.assertNull
|
|||
class InitializeTest {
|
||||
|
||||
@Test
|
||||
@Suppress("LongMethod")
|
||||
fun `should serialize InitializeRequest with capabilities and meta`() {
|
||||
val request = InitializeRequest(
|
||||
InitializeRequestParams(
|
||||
|
|
@ -41,9 +43,10 @@ class InitializeTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
|
|
@ -73,7 +76,8 @@ class InitializeTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -106,7 +110,7 @@ class InitializeTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<InitializeRequest>(json)
|
||||
val request = verifyDeserialization<InitializeRequest>(McpJson, json)
|
||||
assertEquals(Method.Defined.Initialize, request.method)
|
||||
|
||||
val params = request.params
|
||||
|
|
@ -149,9 +153,10 @@ class InitializeTest {
|
|||
meta = buildJsonObject { put("issuedAt", "2025-01-12T15:00:58Z") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {
|
||||
|
|
@ -180,7 +185,8 @@ class InitializeTest {
|
|||
"issuedAt": "2025-01-12T15:00:58Z"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -201,7 +207,7 @@ class InitializeTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<InitializeResult>(json)
|
||||
val result = verifyDeserialization<InitializeResult>(McpJson, json)
|
||||
|
||||
assertEquals("2025-03-26", result.protocolVersion)
|
||||
assertEquals("result-server", result.serverInfo.name)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package io.modelcontextprotocol.kotlin.sdk.types
|
|||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.int
|
||||
|
|
@ -43,7 +45,8 @@ class JsonRpcTest {
|
|||
|
||||
@Test
|
||||
fun `should convert JSONRPCRequest to Request`() {
|
||||
val jsonRpc = McpJson.decodeFromString<JSONRPCRequest>(
|
||||
val jsonRpc = verifyDeserialization<JSONRPCRequest>(
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"id": 17,
|
||||
|
|
@ -101,7 +104,8 @@ class JsonRpcTest {
|
|||
|
||||
@Test
|
||||
fun `should convert JSONRPCNotification to Notification`() {
|
||||
val jsonRpc = McpJson.decodeFromString<JSONRPCNotification>(
|
||||
val jsonRpc = verifyDeserialization<JSONRPCNotification>(
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/message",
|
||||
|
|
@ -147,9 +151,10 @@ class JsonRpcTest {
|
|||
},
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"id": "req-1",
|
||||
"method": "tools/list",
|
||||
|
|
@ -159,7 +164,8 @@ class JsonRpcTest {
|
|||
},
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -175,7 +181,7 @@ class JsonRpcTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<JSONRPCRequest>(json)
|
||||
val request = verifyDeserialization<JSONRPCRequest>(McpJson, json)
|
||||
|
||||
val id = request.id
|
||||
assertIs<RequestId.NumberId>(id)
|
||||
|
|
@ -196,9 +202,10 @@ class JsonRpcTest {
|
|||
},
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/log",
|
||||
"params": {
|
||||
|
|
@ -207,7 +214,8 @@ class JsonRpcTest {
|
|||
},
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -223,7 +231,7 @@ class JsonRpcTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<JSONRPCNotification>(json)
|
||||
val notification = verifyDeserialization<JSONRPCNotification>(McpJson, json)
|
||||
assertEquals("notifications/progress", notification.method)
|
||||
val params = notification.params?.jsonObject
|
||||
assertNotNull(params)
|
||||
|
|
@ -240,9 +248,10 @@ class JsonRpcTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(response)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
response,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"id": "call-1",
|
||||
"result": {
|
||||
|
|
@ -252,7 +261,8 @@ class JsonRpcTest {
|
|||
},
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -269,7 +279,7 @@ class JsonRpcTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val response = McpJson.decodeFromString<JSONRPCResponse>(json)
|
||||
val response = verifyDeserialization<JSONRPCResponse>(McpJson, json)
|
||||
val result = response.result
|
||||
assertIs<EmptyResult>(result)
|
||||
assertEquals(RequestId.NumberId(7L), response.id)
|
||||
|
|
@ -289,9 +299,10 @@ class JsonRpcTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(error)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
error,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"id": 99,
|
||||
"error": {
|
||||
|
|
@ -303,7 +314,8 @@ class JsonRpcTest {
|
|||
},
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -323,7 +335,7 @@ class JsonRpcTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val error = McpJson.decodeFromString<JSONRPCError>(json)
|
||||
val error = verifyDeserialization<JSONRPCError>(McpJson, json)
|
||||
assertEquals(RequestId("req-404"), error.id)
|
||||
assertEquals(RPCError.ErrorCode.INVALID_PARAMS, error.error.code)
|
||||
assertEquals("Invalid params", error.error.message)
|
||||
|
|
@ -348,7 +360,7 @@ class JsonRpcTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val message = McpJson.decodeFromString<JSONRPCMessage>(json)
|
||||
val message = verifyDeserialization<JSONRPCMessage>(McpJson, json)
|
||||
val request = assertIs<JSONRPCRequest>(message)
|
||||
assertEquals("sampling/create", request.method)
|
||||
val params = request.params?.jsonObject
|
||||
|
|
@ -373,7 +385,7 @@ class JsonRpcTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val message = McpJson.decodeFromString<JSONRPCMessage>(json)
|
||||
val message = verifyDeserialization<JSONRPCMessage>(McpJson, json)
|
||||
val error = assertIs<JSONRPCError>(message)
|
||||
assertEquals(RPCError.ErrorCode.REQUEST_TIMEOUT, error.error.code)
|
||||
val data = error.error.data?.jsonObject
|
||||
|
|
@ -388,9 +400,10 @@ class JsonRpcTest {
|
|||
params = buildJsonObject { put("message", "Polymorphic") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(message)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
message,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/log",
|
||||
"params": {
|
||||
|
|
@ -398,7 +411,8 @@ class JsonRpcTest {
|
|||
},
|
||||
"jsonrpc": "2.0"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
|
|
@ -47,9 +48,10 @@ class LoggingTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "logging/setLevel",
|
||||
"params": {
|
||||
|
|
@ -59,7 +61,8 @@ class LoggingTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -76,7 +79,7 @@ class LoggingTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<SetLevelRequest>(json)
|
||||
val request = verifyDeserialization<SetLevelRequest>(McpJson, json)
|
||||
assertEquals(Method.Defined.LoggingSetLevel, request.method)
|
||||
assertEquals(LoggingLevel.Alert, request.params.level)
|
||||
assertEquals(ProgressToken(1001), request.params.meta?.progressToken)
|
||||
|
|
@ -93,9 +96,10 @@ class LoggingTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/message",
|
||||
"params": {
|
||||
|
|
@ -109,7 +113,8 @@ class LoggingTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -124,7 +129,7 @@ class LoggingTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<LoggingMessageNotification>(json)
|
||||
val notification = verifyDeserialization<LoggingMessageNotification>(McpJson, json)
|
||||
assertEquals(Method.Defined.NotificationsMessage, notification.method)
|
||||
|
||||
val params = notification.params
|
||||
|
|
@ -153,7 +158,7 @@ class LoggingTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<LoggingMessageNotification>(json)
|
||||
val notification = verifyDeserialization<LoggingMessageNotification>(McpJson, json)
|
||||
val params = notification.params
|
||||
assertEquals(LoggingLevel.Critical, params.level)
|
||||
val data = params.data.jsonObject
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.put
|
||||
|
|
@ -20,9 +21,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/cancelled",
|
||||
"params": {
|
||||
|
|
@ -33,7 +35,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -48,7 +51,7 @@ class NotificationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<CancelledNotification>(json)
|
||||
val notification = verifyDeserialization<CancelledNotification>(McpJson, json)
|
||||
val params = notification.params
|
||||
|
||||
assertEquals(Method.Defined.NotificationsCancelled, notification.method)
|
||||
|
|
@ -65,9 +68,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/initialized",
|
||||
"params": {
|
||||
|
|
@ -76,7 +80,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -87,7 +92,7 @@ class NotificationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<InitializedNotification>(json)
|
||||
val notification = verifyDeserialization<InitializedNotification>(McpJson, json)
|
||||
|
||||
assertEquals(Method.Defined.NotificationsInitialized, notification.method)
|
||||
assertNull(notification.params)
|
||||
|
|
@ -105,9 +110,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/progress",
|
||||
"params": {
|
||||
|
|
@ -120,7 +126,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -135,7 +142,7 @@ class NotificationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<ProgressNotification>(json)
|
||||
val notification = verifyDeserialization<ProgressNotification>(McpJson, json)
|
||||
val params = notification.params
|
||||
|
||||
assertEquals(Method.Defined.NotificationsProgress, notification.method)
|
||||
|
|
@ -155,9 +162,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/resources/updated",
|
||||
"params": {
|
||||
|
|
@ -167,7 +175,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -184,7 +193,7 @@ class NotificationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<ResourceUpdatedNotification>(json)
|
||||
val notification = verifyDeserialization<ResourceUpdatedNotification>(McpJson, json)
|
||||
val params = notification.params
|
||||
|
||||
assertEquals(Method.Defined.NotificationsResourcesUpdated, notification.method)
|
||||
|
|
@ -200,9 +209,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/prompts/list_changed",
|
||||
"params": {
|
||||
|
|
@ -211,7 +221,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -222,7 +233,7 @@ class NotificationTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val notification = McpJson.decodeFromString<PromptListChangedNotification>(json)
|
||||
val notification = verifyDeserialization<PromptListChangedNotification>(McpJson, json)
|
||||
|
||||
assertEquals(Method.Defined.NotificationsPromptsListChanged, notification.method)
|
||||
assertNull(notification.params)
|
||||
|
|
@ -236,9 +247,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/resources/list_changed",
|
||||
"params": {
|
||||
|
|
@ -247,7 +259,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -258,9 +271,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/roots/list_changed",
|
||||
"params": {
|
||||
|
|
@ -269,7 +283,8 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -280,9 +295,10 @@ class NotificationTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(notification)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
notification,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "notifications/tools/list_changed",
|
||||
"params": {
|
||||
|
|
@ -291,6 +307,7 @@ class NotificationTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
|
|
@ -17,13 +18,15 @@ class PingRequestTest {
|
|||
fun `should serialize PingRequest without params`() {
|
||||
val request = PingRequest()
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "ping"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -36,9 +39,10 @@ class PingRequestTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "ping",
|
||||
"params": {
|
||||
|
|
@ -47,7 +51,8 @@ class PingRequestTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -84,7 +89,7 @@ class PingRequestTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val jsonRpc = McpJson.decodeFromString<JSONRPCRequest>(json)
|
||||
val jsonRpc = verifyDeserialization<JSONRPCRequest>(McpJson, json)
|
||||
val request = jsonRpc.fromJSON()
|
||||
|
||||
val pingRequest = assertIs<PingRequest>(request)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.double
|
||||
import kotlinx.serialization.json.int
|
||||
|
|
@ -18,13 +20,15 @@ class PromptsTest {
|
|||
fun `should serialize PromptArgument with minimal fields`() {
|
||||
val argument = PromptArgument(name = "repository")
|
||||
|
||||
val json = McpJson.encodeToString(argument)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
argument,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "repository"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -36,16 +40,18 @@ class PromptsTest {
|
|||
title = "Language",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(argument)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
argument,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "language",
|
||||
"description": "Programming language to use",
|
||||
"required": true,
|
||||
"title": "Language"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -69,9 +75,10 @@ class PromptsTest {
|
|||
meta = buildJsonObject { put("category", "status-report") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(prompt)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
prompt,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "summarize_update",
|
||||
"description": "Summarize the latest repository changes.",
|
||||
|
|
@ -97,7 +104,8 @@ class PromptsTest {
|
|||
"category": "status-report"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -127,7 +135,7 @@ class PromptsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val prompt = McpJson.decodeFromString<Prompt>(json)
|
||||
val prompt = verifyDeserialization<Prompt>(McpJson, json)
|
||||
|
||||
assertEquals("code_review", prompt.name)
|
||||
assertEquals("Generate a succinct code review.", prompt.description)
|
||||
|
|
@ -151,9 +159,10 @@ class PromptsTest {
|
|||
content = TextContent(text = "Provide a concise summary of the changes."),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(message)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
message,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": {
|
||||
|
|
@ -161,7 +170,8 @@ class PromptsTest {
|
|||
"text": "Provide a concise summary of the changes."
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -180,7 +190,7 @@ class PromptsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val message = McpJson.decodeFromString<PromptMessage>(json)
|
||||
val message = verifyDeserialization<PromptMessage>(McpJson, json)
|
||||
|
||||
assertEquals(Role.User, message.role)
|
||||
val content = message.content
|
||||
|
|
@ -203,7 +213,7 @@ class PromptsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val decoded = McpJson.decodeFromString<Reference>(json)
|
||||
val decoded = verifyDeserialization<Reference>(McpJson, json)
|
||||
val promptReference = assertIs<PromptReference>(decoded)
|
||||
assertEquals("daily-summary", promptReference.name)
|
||||
assertEquals("Daily Summary", promptReference.title)
|
||||
|
|
@ -222,9 +232,10 @@ class PromptsTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "prompts/get",
|
||||
"params": {
|
||||
|
|
@ -237,7 +248,8 @@ class PromptsTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -258,7 +270,7 @@ class PromptsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<GetPromptRequest>(json)
|
||||
val request = verifyDeserialization<GetPromptRequest>(McpJson, json)
|
||||
|
||||
assertEquals(Method.Defined.PromptsGet, request.method)
|
||||
val params = request.params
|
||||
|
|
@ -287,9 +299,10 @@ class PromptsTest {
|
|||
meta = buildJsonObject { put("generatedAt", "2025-01-12T15:00:58Z") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
|
|
@ -312,7 +325,8 @@ class PromptsTest {
|
|||
"generatedAt": "2025-01-12T15:00:58Z"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -332,7 +346,7 @@ class PromptsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<GetPromptResult>(json)
|
||||
val result = verifyDeserialization<GetPromptResult>(McpJson, json)
|
||||
|
||||
val messages = result.messages
|
||||
assertEquals(1, messages.size)
|
||||
|
|
@ -354,9 +368,10 @@ class PromptsTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "prompts/list",
|
||||
"params": {
|
||||
|
|
@ -366,20 +381,23 @@ class PromptsTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ListPromptsRequest without params`() {
|
||||
val request = ListPromptsRequest()
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "prompts/list"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -393,9 +411,10 @@ class PromptsTest {
|
|||
meta = buildJsonObject { put("page", 1) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"prompts": [
|
||||
{"name": "morning-briefing"},
|
||||
|
|
@ -406,7 +425,8 @@ class PromptsTest {
|
|||
"page": 1
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -433,7 +453,7 @@ class PromptsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ListPromptsResult>(json)
|
||||
val result = verifyDeserialization<ListPromptsResult>(McpJson, json)
|
||||
|
||||
val prompts = result.prompts
|
||||
assertEquals(1, prompts.size)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package io.modelcontextprotocol.kotlin.sdk.types
|
|||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.double
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
|
@ -41,16 +43,18 @@ class RequestTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(params)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
params,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"_meta": {
|
||||
"progressToken": "base-42",
|
||||
"origin": "test-suite"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -64,7 +68,7 @@ class RequestTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val params = McpJson.decodeFromString<BaseRequestParams>(json)
|
||||
val params = verifyDeserialization<BaseRequestParams>(McpJson, json)
|
||||
|
||||
val meta = params.meta
|
||||
assertNotNull(meta)
|
||||
|
|
@ -79,16 +83,18 @@ class RequestTest {
|
|||
meta = RequestMeta(buildJsonObject { put("progressToken", "page-req") }),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(params)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
params,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"cursor": "cursor-1",
|
||||
"_meta": {
|
||||
"progressToken": "page-req"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -102,7 +108,7 @@ class RequestTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val params = McpJson.decodeFromString<PaginatedRequestParams>(json)
|
||||
val params = verifyDeserialization<PaginatedRequestParams>(McpJson, json)
|
||||
|
||||
assertEquals("cursor-2", params.cursor)
|
||||
assertEquals(ProgressToken(99), params.meta?.progressToken)
|
||||
|
|
@ -163,22 +169,24 @@ class RequestTest {
|
|||
},
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"_meta": {
|
||||
"processedBy": "worker-1"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should deserialize EmptyResult without meta`() {
|
||||
val json = "{}"
|
||||
|
||||
val result = McpJson.decodeFromString<EmptyResult>(json)
|
||||
val result = verifyDeserialization<EmptyResult>(McpJson, json)
|
||||
|
||||
assertNull(result.meta)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
|
@ -20,14 +22,16 @@ class ResourcesTest {
|
|||
name = "README",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(resource)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
resource,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"uri": "file:///workspace/README.md",
|
||||
"name": "README"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -46,9 +50,10 @@ class ResourcesTest {
|
|||
meta = buildJsonObject { put("etag", "abc123") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(resource)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
resource,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"uri": "file:///workspace/CHANGELOG.md",
|
||||
"name": "CHANGELOG",
|
||||
|
|
@ -67,7 +72,8 @@ class ResourcesTest {
|
|||
"etag": "abc123"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -92,7 +98,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val resource = McpJson.decodeFromString<Resource>(json)
|
||||
val resource = verifyDeserialization<Resource>(McpJson, json)
|
||||
|
||||
assertEquals("file:///config/settings.json", resource.uri)
|
||||
assertEquals("config-settings", resource.name)
|
||||
|
|
@ -122,9 +128,10 @@ class ResourcesTest {
|
|||
meta = buildJsonObject { put("requiresAuth", true) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(template)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
template,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"uriTemplate": "file:///workspace/{path}",
|
||||
"name": "workspace-file",
|
||||
|
|
@ -145,7 +152,8 @@ class ResourcesTest {
|
|||
"requiresAuth": true
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -161,7 +169,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val template = McpJson.decodeFromString<ResourceTemplate>(json)
|
||||
val template = verifyDeserialization<ResourceTemplate>(McpJson, json)
|
||||
|
||||
assertEquals("db://users/{id}", template.uriTemplate)
|
||||
assertEquals("user-record", template.name)
|
||||
|
|
@ -183,7 +191,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val decoded = McpJson.decodeFromString<Reference>(json)
|
||||
val decoded = verifyDeserialization<Reference>(McpJson, json)
|
||||
val templateReference = assertIs<ResourceTemplateReference>(decoded)
|
||||
assertEquals("file:///workspace/{path}", templateReference.uri)
|
||||
assertEquals(ReferenceType.ResourceTemplate, templateReference.type)
|
||||
|
|
@ -198,9 +206,10 @@ class ResourcesTest {
|
|||
meta = buildJsonObject { put("encoding", "utf-8") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString<ResourceContents>(contents)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
contents,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"text": "Hello, MCP!",
|
||||
"uri": "file:///workspace/hello.txt",
|
||||
|
|
@ -209,7 +218,8 @@ class ResourcesTest {
|
|||
"encoding": "utf-8"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -225,7 +235,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val contents = McpJson.decodeFromString<ResourceContents>(json)
|
||||
val contents = verifyDeserialization<ResourceContents>(McpJson, json)
|
||||
val blobContents = assertIs<BlobResourceContents>(contents)
|
||||
assertEquals("YmluYXJ5ZGF0YQ==", blobContents.blob)
|
||||
assertEquals("file:///workspace/logo.png", blobContents.uri)
|
||||
|
|
@ -240,14 +250,16 @@ class ResourcesTest {
|
|||
mimeType = "application/octet-stream",
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString<ResourceContents>(contents)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
contents,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"uri": "custom://resource/42",
|
||||
"mimeType": "application/octet-stream"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -259,9 +271,10 @@ class ResourcesTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "resources/list",
|
||||
"params": {
|
||||
|
|
@ -271,20 +284,23 @@ class ResourcesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ListResourcesRequest without params`() {
|
||||
val request = ListResourcesRequest()
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "resources/list"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -298,9 +314,10 @@ class ResourcesTest {
|
|||
meta = buildJsonObject { put("page", 1) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"resources": [
|
||||
{"uri": "file:///workspace/README.md", "name": "README"},
|
||||
|
|
@ -311,7 +328,8 @@ class ResourcesTest {
|
|||
"page": 1
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -332,7 +350,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ListResourcesResult>(json)
|
||||
val result = verifyDeserialization<ListResourcesResult>(McpJson, json)
|
||||
|
||||
assertEquals("cursor-next", result.nextCursor)
|
||||
val resources = result.resources
|
||||
|
|
@ -353,9 +371,10 @@ class ResourcesTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "resources/read",
|
||||
"params": {
|
||||
|
|
@ -365,7 +384,8 @@ class ResourcesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -390,7 +410,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ReadResourceResult>(json)
|
||||
val result = verifyDeserialization<ReadResourceResult>(McpJson, json)
|
||||
|
||||
val contents = result.contents
|
||||
assertEquals(2, contents.size)
|
||||
|
|
@ -410,9 +430,10 @@ class ResourcesTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "resources/subscribe",
|
||||
"params": {
|
||||
|
|
@ -422,7 +443,8 @@ class ResourcesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -434,9 +456,10 @@ class ResourcesTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "resources/unsubscribe",
|
||||
"params": {
|
||||
|
|
@ -446,7 +469,8 @@ class ResourcesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -458,9 +482,10 @@ class ResourcesTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "resources/templates/list",
|
||||
"params": {
|
||||
|
|
@ -470,7 +495,8 @@ class ResourcesTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -483,9 +509,10 @@ class ResourcesTest {
|
|||
meta = buildJsonObject { put("page", 3) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"resourceTemplates": [
|
||||
{
|
||||
|
|
@ -498,7 +525,8 @@ class ResourcesTest {
|
|||
"page": 3
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -522,7 +550,7 @@ class ResourcesTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ListResourceTemplatesResult>(json)
|
||||
val result = verifyDeserialization<ListResourceTemplatesResult>(McpJson, json)
|
||||
|
||||
assertEquals("cursor-next", result.nextCursor)
|
||||
val templates = result.resourceTemplates
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
|
@ -15,13 +16,15 @@ class RootsTest {
|
|||
fun `should serialize Root with minimal fields`() {
|
||||
val root = Root(uri = "file:///workspace/project")
|
||||
|
||||
val json = McpJson.encodeToString(root)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
root,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"uri": "file:///workspace/project"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -32,9 +35,10 @@ class RootsTest {
|
|||
meta = buildJsonObject { put("writable", true) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(root)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
root,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"uri": "file:///workspace/docs",
|
||||
"name": "Docs",
|
||||
|
|
@ -42,7 +46,8 @@ class RootsTest {
|
|||
"writable": true
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -57,7 +62,7 @@ class RootsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val root = McpJson.decodeFromString<Root>(json)
|
||||
val root = verifyDeserialization<Root>(McpJson, json)
|
||||
|
||||
assertEquals("file:///workspace/code", root.uri)
|
||||
assertEquals("Code", root.name)
|
||||
|
|
@ -74,9 +79,10 @@ class RootsTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "roots/list",
|
||||
"params": {
|
||||
|
|
@ -85,20 +91,23 @@ class RootsTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ListRootsRequest without params`() {
|
||||
val request = ListRootsRequest()
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "roots/list"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -111,9 +120,10 @@ class RootsTest {
|
|||
meta = buildJsonObject { put("issuedAt", "2025-01-12T15:00:58Z") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"roots": [
|
||||
{"uri": "file:///workspace/project", "name": "Project"},
|
||||
|
|
@ -123,7 +133,8 @@ class RootsTest {
|
|||
"issuedAt": "2025-01-12T15:00:58Z"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -142,7 +153,7 @@ class RootsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ListRootsResult>(json)
|
||||
val result = verifyDeserialization<ListRootsResult>(McpJson, json)
|
||||
|
||||
val roots = result.roots
|
||||
assertEquals(1, roots.size)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.double
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
|
|
@ -17,13 +18,15 @@ class SamplingTest {
|
|||
fun `should serialize ModelHint`() {
|
||||
val hint = ModelHint(name = "claude-3-5-sonnet")
|
||||
|
||||
val json = McpJson.encodeToString(hint)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
hint,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "claude-3-5-sonnet"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -35,9 +38,10 @@ class SamplingTest {
|
|||
intelligencePriority = 1.0,
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(preferences)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
preferences,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"hints": [
|
||||
{"name": "haiku"},
|
||||
|
|
@ -47,7 +51,8 @@ class SamplingTest {
|
|||
"speedPriority": 0.75,
|
||||
"intelligencePriority": 1.0
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -64,9 +69,10 @@ class SamplingTest {
|
|||
content = TextContent(text = "Summarize the latest release."),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(message)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
message,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"role": "user",
|
||||
"content": {
|
||||
|
|
@ -74,10 +80,12 @@ class SamplingTest {
|
|||
"text": "Summarize the latest release."
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("LongMethod")
|
||||
fun `should serialize CreateMessageRequest with all fields`() {
|
||||
val request = CreateMessageRequest(
|
||||
CreateMessageRequestParams(
|
||||
|
|
@ -105,9 +113,10 @@ class SamplingTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "sampling/createMessage",
|
||||
"params": {
|
||||
|
|
@ -146,7 +155,8 @@ class SamplingTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -181,7 +191,7 @@ class SamplingTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<CreateMessageRequest>(json)
|
||||
val request = verifyDeserialization<CreateMessageRequest>(McpJson, json)
|
||||
|
||||
assertEquals(Method.Defined.SamplingCreateMessage, request.method)
|
||||
val params = request.params
|
||||
|
|
@ -209,9 +219,10 @@ class SamplingTest {
|
|||
meta = buildJsonObject { put("latencyMs", 850) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": {
|
||||
|
|
@ -224,7 +235,8 @@ class SamplingTest {
|
|||
"latencyMs": 850
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -244,7 +256,7 @@ class SamplingTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<CreateMessageResult>(json)
|
||||
val result = verifyDeserialization<CreateMessageResult>(McpJson, json)
|
||||
|
||||
assertEquals(Role.Assistant, result.role)
|
||||
val text = assertIs<TextContent>(result.content)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.modelcontextprotocol.kotlin.sdk.types
|
||||
|
||||
import io.kotest.assertions.json.shouldEqualJson
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
|
||||
import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.int
|
||||
|
|
@ -21,19 +22,22 @@ class ToolsTest {
|
|||
inputSchema = ToolSchema(),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(tool)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
tool,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "search",
|
||||
"inputSchema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("LongMethod")
|
||||
fun `should serialize Tool with annotations and schemas`() {
|
||||
val inputSchema = ToolSchema(
|
||||
properties = buildJsonObject {
|
||||
|
|
@ -74,9 +78,10 @@ class ToolsTest {
|
|||
meta = buildJsonObject { put("category", "search") },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(tool)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
tool,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"name": "web-search",
|
||||
"inputSchema": {
|
||||
|
|
@ -113,7 +118,8 @@ class ToolsTest {
|
|||
"category": "search"
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -139,7 +145,7 @@ class ToolsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val tool = McpJson.decodeFromString<Tool>(json)
|
||||
val tool = verifyDeserialization<Tool>(McpJson, json)
|
||||
|
||||
assertEquals("translate", tool.name)
|
||||
assertEquals("Translate Text", tool.annotations?.title)
|
||||
|
|
@ -156,9 +162,7 @@ class ToolsTest {
|
|||
fun `should serialize ToolSchema with defs`() {
|
||||
val tool = toolWithDefs()
|
||||
|
||||
val json = McpJson.encodeToString(tool)
|
||||
|
||||
json shouldEqualJson toolWithDefsJson()
|
||||
verifySerialization(tool, McpJson, toolWithDefsJson())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -188,7 +192,7 @@ class ToolsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val tool = McpJson.decodeFromString<Tool>(json)
|
||||
val tool = verifyDeserialization<Tool>(McpJson, json)
|
||||
|
||||
val schema = tool.inputSchema
|
||||
val defs = schema.defs
|
||||
|
|
@ -215,9 +219,10 @@ class ToolsTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "tools/call",
|
||||
"params": {
|
||||
|
|
@ -230,7 +235,8 @@ class ToolsTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -250,7 +256,7 @@ class ToolsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val request = McpJson.decodeFromString<CallToolRequest>(json)
|
||||
val request = verifyDeserialization<CallToolRequest>(McpJson, json)
|
||||
|
||||
assertEquals(Method.Defined.ToolsCall, request.method)
|
||||
val params = request.params
|
||||
|
|
@ -273,9 +279,10 @@ class ToolsTest {
|
|||
meta = buildJsonObject { put("elapsedMs", 1200) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"content": [
|
||||
{
|
||||
|
|
@ -294,7 +301,8 @@ class ToolsTest {
|
|||
"elapsedMs": 1200
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -318,7 +326,7 @@ class ToolsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<CallToolResult>(json)
|
||||
val result = verifyDeserialization<CallToolResult>(McpJson, json)
|
||||
|
||||
assertEquals(true, result.isError)
|
||||
val text = assertIs<TextContent>(result.content.first())
|
||||
|
|
@ -338,9 +346,10 @@ class ToolsTest {
|
|||
),
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "tools/list",
|
||||
"params": {
|
||||
|
|
@ -350,20 +359,23 @@ class ToolsTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should serialize ListToolsRequest without params`() {
|
||||
val request = ListToolsRequest()
|
||||
|
||||
val json = McpJson.encodeToString(request)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
request,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"method": "tools/list"
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -377,9 +389,10 @@ class ToolsTest {
|
|||
meta = buildJsonObject { put("page", 1) },
|
||||
)
|
||||
|
||||
val json = McpJson.encodeToString(result)
|
||||
|
||||
json shouldEqualJson """
|
||||
verifySerialization(
|
||||
result,
|
||||
McpJson,
|
||||
"""
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
|
|
@ -400,7 +413,8 @@ class ToolsTest {
|
|||
"page": 1
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -423,7 +437,7 @@ class ToolsTest {
|
|||
}
|
||||
""".trimIndent()
|
||||
|
||||
val result = McpJson.decodeFromString<ListToolsResult>(json)
|
||||
val result = verifyDeserialization<ListToolsResult>(McpJson, json)
|
||||
|
||||
assertEquals("cursor-next", result.nextCursor)
|
||||
val tools = result.tools
|
||||
|
|
@ -483,7 +497,7 @@ class ToolsTest {
|
|||
@Test
|
||||
fun `should deserialize complex tool definition`() {
|
||||
val expected = weatherTool()
|
||||
val actual = McpJson.decodeFromString<Tool>(weatherToolJson())
|
||||
val actual = verifyDeserialization<Tool>(McpJson, weatherToolJson())
|
||||
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue