|
1 | 1 | using NUnit.Framework; |
2 | 2 | using SimpleBase; |
| 3 | +using System; |
3 | 4 | using System.Net; |
| 5 | +using System.Net.Sockets; |
4 | 6 |
|
5 | 7 | namespace SimpleBaseTest.Base85Test; |
6 | 8 |
|
7 | 9 | [TestFixture] |
8 | 10 | public class Rfc1924Test |
9 | 11 | { |
| 12 | + static readonly object[][] encodeTestCases = |
| 13 | + [ |
| 14 | + // Basic test cases from RFC 1924 |
| 15 | + ["1080:0:0:0:8:800:200C:417A", "4)+k&C#VzJ4br>0wv%Yp"], |
| 16 | + ["1080::8:800:200c:417a", "4)+k&C#VzJ4br>0wv%Yp"], |
| 17 | + |
| 18 | + // Edge cases |
| 19 | + ["::", "00000000000000000000"], // All zeros |
| 20 | + ["::1", "00000000000000000001"], // Loopback |
| 21 | + ["ff80::", "{{{{{{{{{{{{{{{{{{{{"], // High values |
| 22 | + ["ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"], // Maximum IPv6 |
| 23 | + |
| 24 | + // Various IPv6 formats |
| 25 | + ["2001:0db8:0000:0000:0000:ff00:0042:8329", "9jqo^BlbD-BleB1djH+jH<H"], |
| 26 | + ["2001:db8::ff00:42:8329", "9jqo^BlbD-BleB1djH+jH<H"], |
| 27 | + ["fe80::1%lo0", "{{{{{{{{{{{{{{{{{{{{"], // Link-local (without scope) |
| 28 | + |
| 29 | + // More standard examples |
| 30 | + ["2001:0db8:85a3:0000:0000:8a2e:0370:7334", "9jqo^BlbD-B8HZ%V%T0P8L"], |
| 31 | + ["2001:db8:85a3::8a2e:370:7334", "9jqo^BlbD-B8HZ%V%T0P8L"], |
| 32 | + ]; |
| 33 | + |
| 34 | + static readonly object[][] decodeTestCases = |
| 35 | + [ |
| 36 | + ["4)+k&C#VzJ4br>0wv%Yp", "1080::8:800:200c:417a"], |
| 37 | + ["00000000000000000000", "::"], |
| 38 | + ["00000000000000000001", "::1"], |
| 39 | + ["{{{{{{{{{{{{{{{{{{{{", "ff80::"], // High values mapped to readable form |
| 40 | + ["~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"], |
| 41 | + ["9jqo^BlbD-BleB1djH+jH<H", "2001:db8::ff00:42:8329"], |
| 42 | + ["9jqo^BlbD-B8HZ%V%T0P8L", "2001:db8:85a3::8a2e:370:7334"], |
| 43 | + ]; |
| 44 | + |
10 | 45 | [Test] |
11 | | - [TestCase("1080:0:0:0:8:800:200C:417A", "4)+k&C#VzJ4br>0wv%Yp")] |
12 | | - [TestCase("1080::8:800:200c:417a", "4)+k&C#VzJ4br>0wv%Yp")] |
| 46 | + [TestCaseSource(nameof(encodeTestCases))] |
13 | 47 | public void EncodeIPv6_WorksCorrectly(string ip, string expectedOutput) |
14 | 48 | { |
15 | 49 | var addr = IPAddress.Parse(ip); |
16 | 50 | Assert.That(Base85.Rfc1924.EncodeIPv6(addr), Is.EqualTo(expectedOutput)); |
17 | 51 | } |
18 | 52 |
|
19 | 53 | [Test] |
20 | | - [TestCase("4)+k&C#VzJ4br>0wv%Yp", "1080::8:800:200c:417a")] |
| 54 | + [TestCaseSource(nameof(decodeTestCases))] |
21 | 55 | public void DecodeIPv6_WorksCorrectly(string encodedText, string expectedIp) |
22 | 56 | { |
23 | 57 | var ip = Base85.Rfc1924.DecodeIPv6(encodedText); |
24 | 58 | Assert.That(ip.ToString(), Is.EqualTo(expectedIp)); |
25 | 59 | } |
| 60 | + |
| 61 | + [Test] |
| 62 | + [TestCaseSource(nameof(decodeTestCases))] |
| 63 | + public void TryDecodeIPv6_ValidInput_ReturnsTrue(string encodedText, string expectedIp) |
| 64 | + { |
| 65 | + bool result = Base85.Rfc1924.TryDecodeIPv6(encodedText, out IPAddress ip); |
| 66 | + Assert.Multiple(() => |
| 67 | + { |
| 68 | + Assert.That(result, Is.True); |
| 69 | + Assert.That(ip.ToString(), Is.EqualTo(expectedIp)); |
| 70 | + }); |
| 71 | + } |
| 72 | + |
| 73 | + [Test] |
| 74 | + public void EncodeIPv6_IPv4Address_ThrowsArgumentException() |
| 75 | + { |
| 76 | + var ipv4Address = IPAddress.Parse("192.168.1.1"); |
| 77 | + Assert.Throws<ArgumentException>(() => Base85.Rfc1924.EncodeIPv6(ipv4Address)); |
| 78 | + } |
| 79 | + |
| 80 | + [Test] |
| 81 | + [TestCase("")] |
| 82 | + [TestCase("short")] |
| 83 | + [TestCase("toolongforthisformat123")] |
| 84 | + [TestCase("4)+k&C#VzJ4br>0wv%Y")] // 19 chars instead of 20 |
| 85 | + [TestCase("4)+k&C#VzJ4br>0wv%Ypp")] // 21 chars instead of 20 |
| 86 | + public void DecodeIPv6_InvalidLength_ThrowsArgumentException(string invalidText) |
| 87 | + { |
| 88 | + Assert.Throws<ArgumentException>(() => Base85.Rfc1924.DecodeIPv6(invalidText)); |
| 89 | + } |
| 90 | + |
| 91 | + [Test] |
| 92 | + [TestCase("")] |
| 93 | + [TestCase("short")] |
| 94 | + [TestCase("toolongforthisformat123")] |
| 95 | + [TestCase("4)+k&C#VzJ4br>0wv%Y")] // 19 chars instead of 20 |
| 96 | + [TestCase("4)+k&C#VzJ4br>0wv%Ypp")] // 21 chars instead of 20 |
| 97 | + public void TryDecodeIPv6_InvalidLength_ReturnsFalse(string invalidText) |
| 98 | + { |
| 99 | + bool result = Base85.Rfc1924.TryDecodeIPv6(invalidText, out IPAddress ip); |
| 100 | + Assert.Multiple(() => |
| 101 | + { |
| 102 | + Assert.That(result, Is.False); |
| 103 | + Assert.That(ip, Is.EqualTo(IPAddress.IPv6None)); |
| 104 | + }); |
| 105 | + } |
| 106 | + |
| 107 | + // Tests for the selected code block: invalid character detection |
| 108 | + [Test] |
| 109 | + [TestCase("0000000000000000000\\")] // backslash is not in RFC1924 alphabet |
| 110 | + [TestCase("0000000000000000000 ")] // space is not in RFC1924 alphabet |
| 111 | + [TestCase("0000000000000000000\t")] // tab is not in RFC1924 alphabet |
| 112 | + [TestCase("0000000000000000000,")] // comma is not in RFC1924 alphabet |
| 113 | + [TestCase("0000000000000000000.")] // period is not in RFC1924 alphabet |
| 114 | + [TestCase("0000000000000000000/")] // forward slash is not in RFC1924 alphabet |
| 115 | + [TestCase("0000000000000000000:")] // colon is not in RFC1924 alphabet |
| 116 | + [TestCase("0000000000000000000\"")] // quote is not in RFC1924 alphabet |
| 117 | + [TestCase("0000000000000000000'")] // apostrophe is not in RFC1924 alphabet |
| 118 | + [TestCase("0000000000000000000]")] // closing bracket is not in RFC1924 alphabet |
| 119 | + [TestCase("0000000000000000000[")] // opening bracket is not in RFC1924 alphabet |
| 120 | + public void DecodeIPv6_InvalidCharacter_ThrowsInvalidOperationException(string invalidText) |
| 121 | + { |
| 122 | + var ex = Assert.Throws<InvalidOperationException>(() => Base85.Rfc1924.DecodeIPv6(invalidText)); |
| 123 | + Assert.That(ex?.Message, Does.Contain("Invalid character")); |
| 124 | + } |
| 125 | + |
| 126 | + [Test] |
| 127 | + [TestCase("0000000000000000000\\")] // backslash is not in RFC1924 alphabet |
| 128 | + [TestCase("0000000000000000000 ")] // space is not in RFC1924 alphabet |
| 129 | + [TestCase("0000000000000000000\t")] // tab is not in RFC1924 alphabet |
| 130 | + [TestCase("0000000000000000000,")] // comma is not in RFC1924 alphabet |
| 131 | + [TestCase("0000000000000000000.")] // period is not in RFC1924 alphabet |
| 132 | + [TestCase("0000000000000000000/")] // forward slash is not in RFC1924 alphabet |
| 133 | + [TestCase("0000000000000000000:")] // colon is not in RFC1924 alphabet |
| 134 | + [TestCase("0000000000000000000\"")] // quote is not in RFC1924 alphabet |
| 135 | + [TestCase("0000000000000000000'")] // apostrophe is not in RFC1924 alphabet |
| 136 | + [TestCase("0000000000000000000]")] // closing bracket is not in RFC1924 alphabet |
| 137 | + [TestCase("0000000000000000000[")] // opening bracket is not in RFC1924 alphabet |
| 138 | + public void TryDecodeIPv6_InvalidCharacter_ReturnsFalse(string invalidText) |
| 139 | + { |
| 140 | + bool result = Base85.Rfc1924.TryDecodeIPv6(invalidText, out IPAddress ip); |
| 141 | + Assert.Multiple(() => |
| 142 | + { |
| 143 | + Assert.That(result, Is.False); |
| 144 | + Assert.That(ip, Is.EqualTo(IPAddress.IPv6None)); |
| 145 | + }); |
| 146 | + } |
| 147 | + |
| 148 | + [Test] |
| 149 | + [TestCase(0)] |
| 150 | + [TestCase(1)] |
| 151 | + [TestCase(19)] |
| 152 | + public void DecodeIPv6_InvalidCharacterAtSpecificPosition_ThrowsInvalidOperationException(int position) |
| 153 | + { |
| 154 | + // Create a valid 20-character string using valid RFC1924 characters |
| 155 | + string validBase = "0000000000000000000A"; |
| 156 | + char[] chars = validBase.ToCharArray(); |
| 157 | + chars[position] = '\\'; // backslash is NOT in RFC1924 alphabet, will cause InvalidOperationException |
| 158 | + string invalidText = new string(chars); |
| 159 | + |
| 160 | + var ex = Assert.Throws<InvalidOperationException>(() => Base85.Rfc1924.DecodeIPv6(invalidText)); |
| 161 | + Assert.That(ex?.Message, Does.Contain("Invalid character")); |
| 162 | + Assert.That(ex?.Message, Does.Contain("\\")); |
| 163 | + } |
| 164 | + |
| 165 | + [Test] |
| 166 | + [TestCase(0)] |
| 167 | + [TestCase(1)] |
| 168 | + [TestCase(19)] |
| 169 | + public void TryDecodeIPv6_InvalidCharacterAtSpecificPosition_ReturnsFalse(int position) |
| 170 | + { |
| 171 | + // Create a valid 20-character string using valid RFC1924 characters |
| 172 | + string validBase = "0000000000000000000A"; |
| 173 | + char[] chars = validBase.ToCharArray(); |
| 174 | + chars[position] = '\\'; // backslash is NOT in RFC1924 alphabet, will cause failure |
| 175 | + string invalidText = new string(chars); |
| 176 | + |
| 177 | + bool result = Base85.Rfc1924.TryDecodeIPv6(invalidText, out IPAddress ip); |
| 178 | + Assert.Multiple(() => |
| 179 | + { |
| 180 | + Assert.That(result, Is.False); |
| 181 | + Assert.That(ip, Is.EqualTo(IPAddress.IPv6None)); |
| 182 | + }); |
| 183 | + } |
| 184 | + |
| 185 | + /// <summary> |
| 186 | + /// Test for out-of-bounds characters that cause IndexOutOfRangeException. |
| 187 | + /// This is a separate issue from the selected invalid character block. |
| 188 | + /// </summary> |
| 189 | + [Test] |
| 190 | + [TestCase("000000000000000000\u00FF0")] // Contains non-ASCII character - exactly 20 chars |
| 191 | + [TestCase("00000000000000000\u00FF00")] // Contains non-ASCII character - exactly 20 chars |
| 192 | + [TestCase("0000000000000\u00FF000000")] // Contains non-ASCII character in middle - exactly 20 chars |
| 193 | + public void DecodeIPv6_NonAsciiCharacter_ThrowsIndexOutOfRangeException(string invalidText) |
| 194 | + { |
| 195 | + // This tests a different path - characters that cause IndexOutOfRangeException |
| 196 | + // before reaching the selected "Invalid character" check |
| 197 | + Assert.Throws<IndexOutOfRangeException>(() => Base85.Rfc1924.DecodeIPv6(invalidText)); |
| 198 | + } |
| 199 | + |
| 200 | + /// <summary> |
| 201 | + /// Test for out-of-bounds characters in TryDecodeIPv6. |
| 202 | + /// This is a separate issue from the selected invalid character block. |
| 203 | + /// </summary> |
| 204 | + [Test] |
| 205 | + [TestCase("000000000000000000\u00FF0")] // Contains non-ASCII character - exactly 20 chars |
| 206 | + [TestCase("00000000000000000\u00FF00")] // Contains non-ASCII character - exactly 20 chars |
| 207 | + [TestCase("0000000000000\u00FF000000")] // Contains non-ASCII character in middle - exactly 20 chars |
| 208 | + public void TryDecodeIPv6_NonAsciiCharacter_ThrowsIndexOutOfRangeException(string invalidText) |
| 209 | + { |
| 210 | + // TryDecodeIPv6 should ideally handle exceptions gracefully, but currently it doesn't |
| 211 | + // for out-of-bounds characters |
| 212 | + Assert.Throws<IndexOutOfRangeException>(() => Base85.Rfc1924.TryDecodeIPv6(invalidText, out _)); |
| 213 | + } |
| 214 | + |
| 215 | + [Test] |
| 216 | + public void EncodeIPv6_RoundTrip_WorksCorrectly() |
| 217 | + { |
| 218 | + IPAddress[] originalAddresses = |
| 219 | + [ |
| 220 | + IPAddress.Parse("::"), |
| 221 | + IPAddress.Parse("::1"), |
| 222 | + IPAddress.Parse("2001:db8::1"), |
| 223 | + IPAddress.Parse("fe80::1"), |
| 224 | + IPAddress.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), |
| 225 | + IPAddress.Parse("1080::8:800:200c:417a") |
| 226 | + ]; |
| 227 | + |
| 228 | + foreach (var original in originalAddresses) |
| 229 | + { |
| 230 | + string encoded = Base85.Rfc1924.EncodeIPv6(original); |
| 231 | + IPAddress decoded = Base85.Rfc1924.DecodeIPv6(encoded); |
| 232 | + Assert.That(decoded, Is.EqualTo(original), $"Round-trip failed for {original}"); |
| 233 | + } |
| 234 | + } |
| 235 | + |
| 236 | + [Test] |
| 237 | + public void TryDecodeIPv6_RoundTrip_WorksCorrectly() |
| 238 | + { |
| 239 | + IPAddress[] originalAddresses = |
| 240 | + [ |
| 241 | + IPAddress.Parse("::"), |
| 242 | + IPAddress.Parse("::1"), |
| 243 | + IPAddress.Parse("2001:db8::1"), |
| 244 | + IPAddress.Parse("fe80::1"), |
| 245 | + IPAddress.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), |
| 246 | + IPAddress.Parse("1080::8:800:200c:417a") |
| 247 | + ]; |
| 248 | + |
| 249 | + foreach (var original in originalAddresses) |
| 250 | + { |
| 251 | + string encoded = Base85.Rfc1924.EncodeIPv6(original); |
| 252 | + bool result = Base85.Rfc1924.TryDecodeIPv6(encoded, out IPAddress decoded); |
| 253 | + Assert.Multiple(() => |
| 254 | + { |
| 255 | + Assert.That(result, Is.True, $"TryDecodeIPv6 failed for {original}"); |
| 256 | + Assert.That(decoded, Is.EqualTo(original), $"Round-trip failed for {original}"); |
| 257 | + }); |
| 258 | + } |
| 259 | + } |
| 260 | + |
| 261 | + [Test] |
| 262 | + public void EncodeIPv6_OutputLength_IsAlwaysCorrect() |
| 263 | + { |
| 264 | + IPAddress[] testAddresses = |
| 265 | + [ |
| 266 | + IPAddress.Parse("::"), |
| 267 | + IPAddress.Parse("::1"), |
| 268 | + IPAddress.Parse("2001:db8::1"), |
| 269 | + IPAddress.Parse("fe80::1"), |
| 270 | + IPAddress.Parse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"), |
| 271 | + IPAddress.Parse("1080::8:800:200c:417a") |
| 272 | + ]; |
| 273 | + |
| 274 | + foreach (var address in testAddresses) |
| 275 | + { |
| 276 | + string encoded = Base85.Rfc1924.EncodeIPv6(address); |
| 277 | + Assert.That(encoded.Length, Is.EqualTo(20), $"Encoded length is incorrect for {address}: '{encoded}'"); |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + [Test] |
| 282 | + public void Base85IPv6_InheritsFromBase85() |
| 283 | + { |
| 284 | + Assert.That(Base85.Rfc1924, Is.InstanceOf<Base85>()); |
| 285 | + Assert.That(Base85.Rfc1924, Is.InstanceOf<Base85IPv6>()); |
| 286 | + } |
| 287 | + |
| 288 | + [Test] |
| 289 | + public void Base85IPv6_HasCorrectAlphabet() |
| 290 | + { |
| 291 | + var alphabet = Base85.Rfc1924.Alphabet; |
| 292 | + Assert.Multiple(() => |
| 293 | + { |
| 294 | + Assert.That(alphabet, Is.Not.Null); |
| 295 | + Assert.That(alphabet.Length, Is.EqualTo(85)); |
| 296 | + Assert.That(alphabet.Value, Is.EqualTo("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~")); |
| 297 | + Assert.That(alphabet.HasShortcut, Is.False); |
| 298 | + }); |
| 299 | + } |
| 300 | + |
| 301 | + [Test] |
| 302 | + public void EncodeIPv6_IPv4MappedAddress_WorksCorrectly() |
| 303 | + { |
| 304 | + // IPv4-mapped IPv6 address should still be treated as IPv6 |
| 305 | + var ipv4MappedAddress = IPAddress.Parse("::ffff:192.0.2.1"); |
| 306 | + string result = Base85.Rfc1924.EncodeIPv6(ipv4MappedAddress); |
| 307 | + Assert.That(result.Length, Is.EqualTo(20)); |
| 308 | + } |
| 309 | + |
| 310 | + [Test] |
| 311 | + public void DecodeIPv6_MaxValueCharacters_WorksCorrectly() |
| 312 | + { |
| 313 | + // Test with characters at the boundaries of the alphabet |
| 314 | + string testString = "9999999999999999999A"; // Valid characters but may not represent a valid scenario |
| 315 | + |
| 316 | + // This should either decode successfully or throw an appropriate exception |
| 317 | + Assert.DoesNotThrow(() => |
| 318 | + { |
| 319 | + var result = Base85.Rfc1924.DecodeIPv6(testString); |
| 320 | + Assert.That(result, Is.Not.Null); |
| 321 | + }); |
| 322 | + } |
| 323 | + |
| 324 | + [Test] |
| 325 | + public void TryDecodeIPv6_MaxValueCharacters_ReturnsResult() |
| 326 | + { |
| 327 | + string testString = "9999999999999999999A"; |
| 328 | + bool result = Base85.Rfc1924.TryDecodeIPv6(testString, out IPAddress ip); |
| 329 | + |
| 330 | + if (result) |
| 331 | + { |
| 332 | + Assert.That(ip, Is.Not.EqualTo(IPAddress.IPv6None)); |
| 333 | + } |
| 334 | + // If it fails, that's also acceptable behavior for edge cases |
| 335 | + } |
26 | 336 | } |
0 commit comments