using ModelContextProtocol.Protocol; namespace ModelContextProtocol.Tests.Client; public class McpHeaderEncoderTests { [Theory] [InlineData("us-west1", "us-west1")] [InlineData("hello-world", "hello-world")] [InlineData("my_tool_name", "my_tool_name")] [InlineData("us west 1", "us west 1")] [InlineData("", "")] public void EncodeValue_PlainAscii_PassesThrough(string input, string expected) { var result = McpHeaderEncoder.EncodeValue(input); Assert.Equal(expected, result); } [Theory] [InlineData(" us-west1", "=?base64?IHVzLXdlc3Qx?=")] [InlineData("us-west1 ", "=?base64?dXMtd2VzdDEg?=")] [InlineData(" us-west1 ", "=?base64?IHVzLXdlc3QxIA==?=")] [InlineData("\tindented", "=?base64?CWluZGVudGVk?=")] public void EncodeValue_LeadingTrailingWhitespace_Base64Encodes(string input, string expected) { var result = McpHeaderEncoder.EncodeValue(input); Assert.Equal(expected, result); } [Fact] public void EncodeValue_NonAsciiCharacters_Base64Encodes() { var result = McpHeaderEncoder.EncodeValue("日本語"); Assert.Equal("=?base64?5pel5pys6Kqe?=", result); } [Fact] public void EncodeValue_NewlineCharacter_Base64Encodes() { var result = McpHeaderEncoder.EncodeValue("line1\nline2"); Assert.Equal("=?base64?bGluZTEKbGluZTI=?=", result); } [Fact] public void EncodeValue_CarriageReturnNewline_Base64Encodes() { var result = McpHeaderEncoder.EncodeValue("line1\r\nline2"); Assert.Equal("=?base64?bGluZTENCmxpbmUy?=", result); } [Theory] [InlineData(true, "true")] [InlineData(false, "false")] public void EncodeValue_Boolean_ConvertsToLowercase(bool input, string expected) { var result = McpHeaderEncoder.EncodeValue(input); Assert.Equal(expected, result); } [Theory] [InlineData(42, "42")] [InlineData(0, "0")] [InlineData(-1, "-1")] public void EncodeValue_Integer_ConvertsToString(object input, string expected) { var result = McpHeaderEncoder.EncodeValue(input); Assert.Equal(expected, result); } [Fact] public void EncodeValue_Null_ReturnsNull() { var result = McpHeaderEncoder.EncodeValue(null); Assert.Null(result); } [Fact] public void EncodeValue_UnsupportedType_ReturnsNull() { var result = McpHeaderEncoder.EncodeValue(new object()); Assert.Null(result); } [Theory] [InlineData("us-west1", "us-west1")] [InlineData("", "")] public void DecodeValue_PlainAscii_ReturnsAsIs(string input, string expected) { var result = McpHeaderEncoder.DecodeValue(input); Assert.Equal(expected, result); } [Fact] public void DecodeValue_Null_ReturnsNull() { var result = McpHeaderEncoder.DecodeValue(null); Assert.Null(result); } [Fact] public void DecodeValue_ValidBase64_Decodes() { var result = McpHeaderEncoder.DecodeValue("=?base64?SGVsbG8=?="); Assert.Equal("Hello", result); } [Fact] public void DecodeValue_DegenerateWrapper_ReturnsLiteralValue() { // "=?base64?=" matches both the prefix "=?base64?" and the suffix "?=" because they // overlap on the shared '?', but it is too short to contain any base64 content. It must be // returned as-is rather than throwing when the wrapper is stripped. var result = McpHeaderEncoder.DecodeValue("=?base64?="); Assert.Equal("=?base64?=", result); } [Fact] public void DecodeValue_CaseSensitivePrefix_ReturnsLiteralValue() { // Per SEP-2243: sentinel markers are case-sensitive and MUST appear exactly as shown (lowercase). // An uppercase prefix should NOT be decoded as base64. var result = McpHeaderEncoder.DecodeValue("=?BASE64?SGVsbG8=?="); Assert.Equal("=?BASE64?SGVsbG8=?=", result); } [Fact] public void DecodeValue_InvalidBase64_ReturnsNull() { var result = McpHeaderEncoder.DecodeValue("=?base64?SGVs!!!bG8=?="); Assert.Null(result); } [Fact] public void DecodeValue_ValidBase64ButInvalidUtf8_ReturnsNull() { // "//4=" is valid Base64 that decodes to the bytes 0xFF 0xFE, which are not valid UTF-8. // A strict decoder must reject this rather than substituting U+FFFD replacement characters. var result = McpHeaderEncoder.DecodeValue("=?base64?//4=?="); Assert.Null(result); } [Fact] public void DecodeValue_MissingPrefix_ReturnsLiteralValue() { var result = McpHeaderEncoder.DecodeValue("SGVsbG8="); Assert.Equal("SGVsbG8=", result); } [Fact] public void DecodeValue_MissingSuffix_ReturnsLiteralValue() { var result = McpHeaderEncoder.DecodeValue("=?base64?SGVsbG8="); Assert.Equal("=?base64?SGVsbG8=", result); } [Theory] [InlineData("us-west1")] [InlineData("Hello, 世界")] [InlineData(" padded ")] [InlineData("line1\nline2")] [InlineData("\tindented")] [InlineData("a\tb")] public void RoundTrip_EncodeDecode_PreservesValue(string original) { var encoded = McpHeaderEncoder.EncodeValue(original); Assert.NotNull(encoded); var decoded = McpHeaderEncoder.DecodeValue(encoded); Assert.Equal(original, decoded); } [Fact] public void EncodeValue_EmbeddedTab_Base64Encodes() { var result = McpHeaderEncoder.EncodeValue("col1\tcol2"); Assert.StartsWith("=?base64?", result); Assert.EndsWith("?=", result); // Verify round-trip var decoded = McpHeaderEncoder.DecodeValue(result); Assert.Equal("col1\tcol2", decoded); } [Theory] [InlineData("=?base64?literal?=")] [InlineData("=?base64?SGVsbG8=?=")] [InlineData("=?base64??=")] public void EncodeValue_SentinelCollision_Base64Encodes(string input) { var result = McpHeaderEncoder.EncodeValue(input); Assert.NotNull(result); Assert.StartsWith("=?base64?", result); Assert.EndsWith("?=", result); // The encoded value must be different from the input to avoid ambiguity Assert.NotEqual(input, result); // Verify round-trip: decode must recover the original literal value var decoded = McpHeaderEncoder.DecodeValue(result); Assert.Equal(input, decoded); } [Theory] [InlineData("=?BASE64?literal?=")] // Case-sensitive: uppercase prefix does not match sentinel [InlineData("=?base64?start")] // Missing suffix: no sentinel match [InlineData("end?=")] // Missing prefix: no sentinel match [InlineData("plain-text")] // No sentinel pattern public void EncodeValue_NonSentinelPattern_NotBase64Encoded(string input) { var result = McpHeaderEncoder.EncodeValue(input); Assert.Equal(input, result); } }