
NegotiationTest.pudu
Pudu58 lines4.6 KB
1/** @Test.Domain.Protocol.NegotiationTest.Suite — the era compatibility matrix */2module PuduLangMcp.Domain.Protocol.NegotiationTest34import Std.Io as Io5import Std.Json as Json6import Std.Test as Test7import PuduLangMcp.Constants.Protocol as Protocol8import PuduLangMcp.Domain.Protocol.Negotiation as Negotiation9import PuduLangMcp.Errors.RpcError as RpcError101112fn modern(version: Json.Json, capabilities: Option[Json.Json]) -> Json.Json {13 var meta = [(Protocol.META_PROTOCOL_VERSION, version)]14 if let Some(given) = capabilities { meta = meta.push((Protocol.META_CLIENT_CAPABILITIES, given)) }15 Json.object(&[(Protocol.META_KEY, Json.object(&meta))])16}171819fn codeOf(outcome: &Result[Negotiation.Era, RpcError.ProtocolError]) -> Int {20 match outcome {21 case Ok(_) => 022 case Err(problem) => RpcError.code(&problem)23 }24}252627fn main() -> Int {28 let empty = Json.object(&[])29 let capabilities = Some(Json.object(&[]))30 let good = modern(Json.Text(Protocol.MODERN_VERSION), capabilities)31 let legacy = Negotiation.Legacy("2025-06-18")32 let checks = Test.suite("Domain.Protocol.Negotiation", &[33 Test.equals("a modern request is admitted as modern", &Negotiation.admit(&Negotiation.Unopened, "tools/list", &good), &Ok(Negotiation.Modern)),34 Test.equals("a modern request ignores a legacy session", &Negotiation.admit(&legacy, "tools/list", &good), &Ok(Negotiation.Modern)),35 Test.equals("an unknown version is unsupported", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "tools/list", &modern(Json.Text("1900-01-01"), capabilities))), &Protocol.UNSUPPORTED_PROTOCOL_VERSION),36 Test.equals("a legacy version in metadata is unsupported", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "tools/list", &modern(Json.Text("2025-11-25"), capabilities))), &Protocol.UNSUPPORTED_PROTOCOL_VERSION),37 Test.equals("a version differing in case is unsupported", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "x", &modern(Json.Text("2026-07-28 "), capabilities))), &Protocol.UNSUPPORTED_PROTOCOL_VERSION),38 Test.equals("a non-string version is invalid params", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "x", &modern(Json.Number(2026), capabilities))), &Protocol.INVALID_PARAMS),39 Test.equals("missing capabilities are invalid params", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "x", &modern(Json.Text(Protocol.MODERN_VERSION), None))), &Protocol.INVALID_PARAMS),40 Test.equals("non-object capabilities are invalid params", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "x", &modern(Json.Text(Protocol.MODERN_VERSION), Some(Json.list(&[]))))), &Protocol.INVALID_PARAMS),41 Test.equals("initialize without metadata is legacy at the asked version", &Negotiation.admit(&Negotiation.Unopened, "initialize", &Json.object(&[("protocolVersion", Json.Text("2025-03-26"))])), &Ok(Negotiation.LegacyEra("2025-03-26"))),42 Test.equals("initialize at an unknown version falls back to the latest legacy", &Negotiation.admit(&Negotiation.Unopened, "initialize", &Json.object(&[("protocolVersion", Json.Text("2030-01-01"))])), &Ok(Negotiation.LegacyEra(Protocol.LATEST_LEGACY_VERSION))),43 Test.equals("initialize with no version falls back to the latest legacy", &Negotiation.negotiateLegacy(&empty), &Protocol.LATEST_LEGACY_VERSION),44 Test.equals("initialize may renegotiate inside a legacy session", &Negotiation.admit(&legacy, "initialize", &Json.object(&[("protocolVersion", Json.Text("2024-11-05"))])), &Ok(Negotiation.LegacyEra("2024-11-05"))),45 Test.equals("a legacy session serves requests without metadata", &Negotiation.admit(&legacy, "tools/list", &empty), &Ok(Negotiation.LegacyEra("2025-06-18"))),46 Test.equals("an unopened session refuses requests without metadata", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "tools/list", &empty)), &Protocol.INVALID_PARAMS),47 Test.equals("metadata that is not an object counts as absent", &codeOf(&Negotiation.admit(&Negotiation.Unopened, "tools/list", &Json.object(&[(Protocol.META_KEY, Json.Text("x"))]))), &Protocol.INVALID_PARAMS),48 Test.equals("a modern initialize is admitted, for dispatch to refuse", &Negotiation.admit(&Negotiation.Unopened, "initialize", &good), &Ok(Negotiation.Modern)),49 Test.that("initialize is the handshake", Negotiation.isHandshake("initialize")),50 Test.not("another method is not the handshake", Negotiation.isHandshake("server/discover"))51 ])52 let ran = Test.run(&checks)53 for failure in Test.failuresOf(&ran) {54 let _reported = Io.writeErrorLine(failure)55 }56 Test.report(&ran)57}58