Ensure Request decoder handles missing/null params correctly (#22)
This commit is contained in:
parent
2510dda70e
commit
75322c3baf
1 changed files with 177 additions and 18 deletions
|
|
@ -23,33 +23,33 @@ struct RequestTests {
|
|||
|
||||
@Test("Request initialization with parameters")
|
||||
func testRequestInitialization() throws {
|
||||
let id: ID = "test-id"
|
||||
let params = TestMethod.Parameters(value: "test")
|
||||
let request = Request<TestMethod>(id: id, method: TestMethod.name, params: params)
|
||||
let id: ID = 1
|
||||
let params = CallTool.Parameters(name: "test-tool")
|
||||
let request = Request<CallTool>(id: id, method: CallTool.name, params: params)
|
||||
|
||||
#expect(request.id == id)
|
||||
#expect(request.method == TestMethod.name)
|
||||
#expect(request.params.value == "test")
|
||||
#expect(request.method == CallTool.name)
|
||||
#expect(request.params.name == "test-tool")
|
||||
}
|
||||
|
||||
@Test("Request encoding and decoding")
|
||||
func testRequestEncodingDecoding() throws {
|
||||
let request = TestMethod.request(id: "test-id", TestMethod.Parameters(value: "test"))
|
||||
let request = CallTool.request(id: 1, CallTool.Parameters(name: "test-tool"))
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
||||
let data = try encoder.encode(request)
|
||||
let decoded = try decoder.decode(Request<TestMethod>.self, from: data)
|
||||
let decoded = try decoder.decode(Request<CallTool>.self, from: data)
|
||||
|
||||
#expect(decoded.id == request.id)
|
||||
#expect(decoded.method == request.method)
|
||||
#expect(decoded.params.value == request.params.value)
|
||||
#expect(decoded.params.name == request.params.name)
|
||||
}
|
||||
|
||||
@Test("Empty parameters request encoding")
|
||||
func testEmptyParametersRequestEncoding() throws {
|
||||
let request = EmptyMethod.request(id: "test-id")
|
||||
let request = EmptyMethod.request(id: 1)
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
let decoder = JSONDecoder()
|
||||
|
|
@ -66,14 +66,14 @@ struct RequestTests {
|
|||
func testEmptyParametersRequestDecoding() throws {
|
||||
// Create a minimal JSON string
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"empty.method"}
|
||||
{"jsonrpc":"2.0","id":1,"method":"empty.method"}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decoded = try decoder.decode(Request<EmptyMethod>.self, from: data)
|
||||
|
||||
#expect(decoded.id == "test-id")
|
||||
#expect(decoded.id == 1)
|
||||
#expect(decoded.method == EmptyMethod.name)
|
||||
}
|
||||
|
||||
|
|
@ -81,14 +81,14 @@ struct RequestTests {
|
|||
func testNotRequiredParametersRequestDecodingWithParams() throws {
|
||||
// Test decoding when params field is present
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"ping","params":{}}
|
||||
{"jsonrpc":"2.0","id":1,"method":"ping","params":{}}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decoded = try decoder.decode(Request<Ping>.self, from: data)
|
||||
|
||||
#expect(decoded.id == "test-id")
|
||||
#expect(decoded.id == 1)
|
||||
#expect(decoded.method == Ping.name)
|
||||
}
|
||||
|
||||
|
|
@ -96,14 +96,14 @@ struct RequestTests {
|
|||
func testNotRequiredParametersRequestDecodingWithoutParams() throws {
|
||||
// Test decoding when params field is missing
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"ping"}
|
||||
{"jsonrpc":"2.0","id":1,"method":"ping"}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decoded = try decoder.decode(Request<Ping>.self, from: data)
|
||||
|
||||
#expect(decoded.id == "test-id")
|
||||
#expect(decoded.id == 1)
|
||||
#expect(decoded.method == Ping.name)
|
||||
}
|
||||
|
||||
|
|
@ -111,14 +111,153 @@ struct RequestTests {
|
|||
func testNotRequiredParametersRequestDecodingWithNullParams() throws {
|
||||
// Test decoding when params field is null
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"ping","params":null}
|
||||
{"jsonrpc":"2.0","id":1,"method":"ping","params":null}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decoded = try decoder.decode(Request<Ping>.self, from: data)
|
||||
|
||||
#expect(decoded.id == "test-id")
|
||||
#expect(decoded.id == 1)
|
||||
#expect(decoded.method == Ping.name)
|
||||
}
|
||||
|
||||
@Test("Required parameters request decoding - missing params")
|
||||
func testRequiredParametersRequestDecodingMissingParams() throws {
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/call"}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
#expect(throws: DecodingError.self) {
|
||||
_ = try decoder.decode(Request<CallTool>.self, from: data)
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Required parameters request decoding - null params")
|
||||
func testRequiredParametersRequestDecodingNullParams() throws {
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":null}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
#expect(throws: DecodingError.self) {
|
||||
_ = try decoder.decode(Request<CallTool>.self, from: data)
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Empty parameters request decoding - with null params")
|
||||
func testEmptyParametersRequestDecodingNullParams() throws {
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"empty.method","params":null}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decoded = try decoder.decode(Request<EmptyMethod>.self, from: data)
|
||||
|
||||
#expect(decoded.id == 1)
|
||||
#expect(decoded.method == EmptyMethod.name)
|
||||
}
|
||||
|
||||
@Test("Empty parameters request decoding - with empty object params")
|
||||
func testEmptyParametersRequestDecodingEmptyParams() throws {
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"empty.method","params":{}}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
let decoded = try decoder.decode(Request<EmptyMethod>.self, from: data)
|
||||
|
||||
#expect(decoded.id == 1)
|
||||
#expect(decoded.method == EmptyMethod.name)
|
||||
}
|
||||
|
||||
@Test("Initialize request decoding - requires params")
|
||||
func testInitializeRequestDecodingRequiresParams() throws {
|
||||
// Test missing params field
|
||||
let missingParams = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"initialize"}
|
||||
"""
|
||||
let decoder = JSONDecoder()
|
||||
#expect(throws: DecodingError.self) {
|
||||
_ = try decoder.decode(
|
||||
Request<Initialize>.self, from: missingParams.data(using: .utf8)!)
|
||||
}
|
||||
|
||||
// Test null params
|
||||
let nullParams = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"initialize","params":null}
|
||||
"""
|
||||
#expect(throws: DecodingError.self) {
|
||||
_ = try decoder.decode(Request<Initialize>.self, from: nullParams.data(using: .utf8)!)
|
||||
}
|
||||
|
||||
// Verify that empty object params works (since fields have defaults)
|
||||
let emptyParams = """
|
||||
{"jsonrpc":"2.0","id":"test-id","method":"initialize","params":{}}
|
||||
"""
|
||||
let decoded = try decoder.decode(
|
||||
Request<Initialize>.self, from: emptyParams.data(using: .utf8)!)
|
||||
#expect(decoded.params.protocolVersion == Version.latest)
|
||||
#expect(decoded.params.clientInfo.name == "unknown")
|
||||
}
|
||||
|
||||
@Test("Invalid parameters request decoding")
|
||||
func testInvalidParametersRequestDecoding() throws {
|
||||
let jsonString = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"invalid":"value"}}
|
||||
"""
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
|
||||
let decoder = JSONDecoder()
|
||||
#expect(throws: DecodingError.self) {
|
||||
_ = try decoder.decode(Request<CallTool>.self, from: data)
|
||||
}
|
||||
}
|
||||
|
||||
@Test("NotRequired parameters request decoding")
|
||||
func testNotRequiredParametersRequestDecoding() throws {
|
||||
// Test with missing params
|
||||
let missingParams = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
|
||||
"""
|
||||
let decoder = JSONDecoder()
|
||||
let decodedMissing = try decoder.decode(
|
||||
Request<ListTools>.self,
|
||||
from: missingParams.data(using: .utf8)!)
|
||||
#expect(decodedMissing.id == 1)
|
||||
#expect(decodedMissing.method == ListTools.name)
|
||||
#expect(decodedMissing.params.cursor == nil)
|
||||
|
||||
// Test with null params
|
||||
let nullParams = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":null}
|
||||
"""
|
||||
let decodedNull = try decoder.decode(
|
||||
Request<ListTools>.self,
|
||||
from: nullParams.data(using: .utf8)!)
|
||||
#expect(decodedNull.params.cursor == nil)
|
||||
|
||||
// Test with empty object params
|
||||
let emptyParams = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
|
||||
"""
|
||||
let decodedEmpty = try decoder.decode(
|
||||
Request<ListTools>.self,
|
||||
from: emptyParams.data(using: .utf8)!)
|
||||
#expect(decodedEmpty.params.cursor == nil)
|
||||
|
||||
// Test with provided cursor
|
||||
let withCursor = """
|
||||
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{"cursor":"next-page"}}
|
||||
"""
|
||||
let decodedWithCursor = try decoder.decode(
|
||||
Request<ListTools>.self,
|
||||
from: withCursor.data(using: .utf8)!)
|
||||
#expect(decodedWithCursor.params.cursor == "next-page")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue