
MessageTest.pudu
Pudu94 lines5.7 KB
1/** @Test.Domain.Rpc.MessageTest.Suite — JSON-RPC line classification */2module PuduLangMcp.Domain.Rpc.MessageTest34import Std.Io as Io5import Std.Json as Json6import Std.Test as Test7import PuduLangMcp.Constants.Protocol as Protocol8import PuduLangMcp.Domain.Rpc.Message as Message9import PuduLangMcp.Errors.RpcError as RpcError101112fn refusedCode(line: Str) -> Int {13 match Message.parse(line) {14 case Message.Refused(_, problem) => RpcError.code(&problem)15 case _ => 016 }17}181920fn refusedId(line: Str) -> Option[Message.RequestId] {21 match Message.parse(line) {22 case Message.Refused(id, _) => id23 case _ => None24 }25}262728fn kindOf(line: Str) -> Str {29 match Message.parse(line) {30 case Message.Request(_, _, _) => "request"31 case Message.Notification(_, _) => "notification"32 case Message.ClientResponse => "response"33 case Message.Refused(_, _) => "refused"34 }35}363738fn requestOf(line: Str) -> Option[(Message.RequestId, Str)] {39 match Message.parse(line) {40 case Message.Request(id, method, _) => Some((id, method))41 case _ => None42 }43}444546fn paramsOf(line: Str) -> Json.Json {47 match Message.parse(line) {48 case Message.Request(_, _, params) => params49 case Message.Notification(_, params) => params50 case _ => Json.Null51 }52}535455fn main() -> Int {56 let checks = Test.suite("Domain.Rpc.Message", &[57 Test.equals("an integer id request", &requestOf("\{\"jsonrpc\":\"2.0\",\"id\":7,\"method\":\"tools/list\"\}"), &Some((Message.NumberId(7), "tools/list"))),58 Test.equals("a string id request", &requestOf("\{\"jsonrpc\":\"2.0\",\"id\":\"a-1\",\"method\":\"x\"\}"), &Some((Message.TextId("a-1"), "x"))),59 Test.equals("id zero is a valid id", &requestOf("\{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"x\"\}"), &Some((Message.NumberId(0), "x"))),60 Test.equals("a negative id is kept", &requestOf("\{\"jsonrpc\":\"2.0\",\"id\":-3,\"method\":\"x\"\}"), &Some((Message.NumberId(-3), "x"))),61 Test.differs("string and integer ids differ", &Message.TextId("1"), &Message.NumberId(1)),62 Test.equals("surrounding whitespace is accepted", &kindOf(" \{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"x\"\}\r"), &"request"),63 Test.equals("no id is a notification", &kindOf("\{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"\}"), &"notification"),64 Test.equals("a result without method is a client response", &kindOf("\{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\{\}\}"), &"response"),65 Test.equals("an error without method is a client response", &kindOf("\{\"jsonrpc\":\"2.0\",\"id\":1,\"error\":\{\"code\":1,\"message\":\"m\"\}\}"), &"response"),66 Test.equals("text that is not JSON is a parse error", &refusedCode("not json"), &Protocol.PARSE_ERROR),67 Test.equals("an empty line is a parse error", &refusedCode(""), &Protocol.PARSE_ERROR),68 Test.equals("truncated JSON is a parse error", &refusedCode("\{\"jsonrpc\":\"2.0\""), &Protocol.PARSE_ERROR),69 Test.equals("a batch is refused as invalid", &refusedCode("[\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"x\"\}]"), &Protocol.INVALID_REQUEST),70 Test.equals("a JSON number is not a message", &refusedCode("42"), &Protocol.INVALID_REQUEST),71 Test.equals("a null id is refused", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":null,\"method\":\"x\"\}"), &Protocol.INVALID_REQUEST),72 Test.equals("a fractional id is refused", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":1.5,\"method\":\"x\"\}"), &Protocol.INVALID_REQUEST),73 Test.equals("a boolean id is refused", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":true,\"method\":\"x\"\}"), &Protocol.INVALID_REQUEST),74 Test.equals("a missing jsonrpc member is refused", &refusedCode("\{\"id\":1,\"method\":\"x\"\}"), &Protocol.INVALID_REQUEST),75 Test.equals("jsonrpc 1.0 is refused", &refusedCode("\{\"jsonrpc\":\"1.0\",\"id\":1,\"method\":\"x\"\}"), &Protocol.INVALID_REQUEST),76 Test.equals("a refused envelope keeps its id", &refusedId("\{\"jsonrpc\":\"1.0\",\"id\":9,\"method\":\"x\"\}"), &Some(Message.NumberId(9))),77 Test.equals("a non-string method is refused", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":5\}"), &Protocol.INVALID_REQUEST),78 Test.equals("a missing method is refused", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":1\}"), &Protocol.INVALID_REQUEST),79 Test.equals("array params on a request are invalid params", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"x\",\"params\":[1]\}"), &Protocol.INVALID_PARAMS),80 Test.equals("null params on a request are invalid params", &refusedCode("\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"x\",\"params\":null\}"), &Protocol.INVALID_PARAMS),81 Test.equals("array params on a notification leave it a notification", &kindOf("\{\"jsonrpc\":\"2.0\",\"method\":\"x\",\"params\":[1]\}"), &"notification"),82 Test.equals("absent params are an empty object", ¶msOf("\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"x\"\}"), &Json.object(&[])),83 Test.equals("object params are kept", ¶msOf("\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"x\",\"params\":\{\"a\":1\}\}"), &Json.object(&[("a", Json.Number(1))])),84 Test.equals("a unicode method name is kept", &requestOf("\{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"añadir\"\}"), &Some((Message.NumberId(1), "añadir"))),85 Test.equals("an integer id encodes as a number", &Message.idJson(&Message.NumberId(5)), &Json.Number(5)),86 Test.equals("a string id encodes as text", &Message.idJson(&Message.TextId("q")), &Json.Text("q"))87 ])88 let ran = Test.run(&checks)89 for failure in Test.failuresOf(&ran) {90 let _reported = Io.writeErrorLine(failure)91 }92 Test.report(&ran)93}94