Move 29 clustering/routing test files from NATS.Server.Tests to a dedicated NATS.Server.Clustering.Tests project. Update namespaces, replace private GetFreePort/ReadUntilAsync helpers with TestUtilities calls, and extract TestServerFactory/ClusterTestServer to TestUtilities to fix cross-project reference from JetStreamStartupTests.
137 lines
4.8 KiB
C#
137 lines
4.8 KiB
C#
// Reference: golang/nats-server/server/route.go — S2/Snappy compression for routes
|
|
// Tests for RouteCompressionCodec: compression, decompression, negotiation, detection.
|
|
|
|
using System.Text;
|
|
using NATS.Server.Routes;
|
|
|
|
namespace NATS.Server.Clustering.Tests.Routes;
|
|
|
|
/// <summary>
|
|
/// Tests for route S2/Snappy compression codec, matching Go's route compression
|
|
/// behavior using IronSnappy.
|
|
/// </summary>
|
|
public class RouteS2CompressionTests
|
|
{
|
|
[Fact]
|
|
public void Compress_Fast_ProducesValidOutput()
|
|
{
|
|
var data = Encoding.UTF8.GetBytes("NATS route compression test payload");
|
|
var compressed = RouteCompressionCodec.Compress(data, RouteCompressionLevel.Fast);
|
|
|
|
compressed.ShouldNotBeNull();
|
|
compressed.Length.ShouldBeGreaterThan(0);
|
|
|
|
// Compressed output should be decompressible
|
|
var decompressed = RouteCompressionCodec.Decompress(compressed);
|
|
decompressed.ShouldBe(data);
|
|
}
|
|
|
|
[Fact]
|
|
public void Compress_Decompress_RoundTrips()
|
|
{
|
|
var original = Encoding.UTF8.GetBytes("Hello NATS! This is a test of round-trip compression.");
|
|
|
|
foreach (var level in new[] { RouteCompressionLevel.Fast, RouteCompressionLevel.Better, RouteCompressionLevel.Best })
|
|
{
|
|
var compressed = RouteCompressionCodec.Compress(original, level);
|
|
var restored = RouteCompressionCodec.Decompress(compressed);
|
|
restored.ShouldBe(original, $"Round-trip failed for level {level}");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Compress_EmptyData_ReturnsEmpty()
|
|
{
|
|
var result = RouteCompressionCodec.Compress(ReadOnlySpan<byte>.Empty, RouteCompressionLevel.Fast);
|
|
result.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void Compress_Off_ReturnsOriginal()
|
|
{
|
|
var data = Encoding.UTF8.GetBytes("uncompressed payload");
|
|
var result = RouteCompressionCodec.Compress(data, RouteCompressionLevel.Off);
|
|
|
|
result.ShouldBe(data);
|
|
}
|
|
|
|
[Fact]
|
|
public void Decompress_CorruptedData_Throws()
|
|
{
|
|
var garbage = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04 };
|
|
|
|
Should.Throw<Exception>(() => RouteCompressionCodec.Decompress(garbage));
|
|
}
|
|
|
|
[Fact]
|
|
public void NegotiateCompression_BothOff_ReturnsOff()
|
|
{
|
|
var result = RouteCompressionCodec.NegotiateCompression("off", "off");
|
|
result.ShouldBe(RouteCompressionLevel.Off);
|
|
}
|
|
|
|
[Fact]
|
|
public void NegotiateCompression_OneFast_ReturnsFast()
|
|
{
|
|
// When both are fast, result is fast
|
|
var result = RouteCompressionCodec.NegotiateCompression("fast", "fast");
|
|
result.ShouldBe(RouteCompressionLevel.Fast);
|
|
|
|
// When one is off, result is off (off wins)
|
|
var result2 = RouteCompressionCodec.NegotiateCompression("fast", "off");
|
|
result2.ShouldBe(RouteCompressionLevel.Off);
|
|
}
|
|
|
|
[Fact]
|
|
public void NegotiateCompression_MismatchLevels_ReturnsMinimum()
|
|
{
|
|
// fast (1) vs best (3) => fast (minimum)
|
|
var result = RouteCompressionCodec.NegotiateCompression("fast", "best");
|
|
result.ShouldBe(RouteCompressionLevel.Fast);
|
|
|
|
// better (2) vs best (3) => better (minimum)
|
|
var result2 = RouteCompressionCodec.NegotiateCompression("better", "best");
|
|
result2.ShouldBe(RouteCompressionLevel.Better);
|
|
|
|
// fast (1) vs better (2) => fast (minimum)
|
|
var result3 = RouteCompressionCodec.NegotiateCompression("fast", "better");
|
|
result3.ShouldBe(RouteCompressionLevel.Fast);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsCompressed_ValidSnappy_ReturnsTrue()
|
|
{
|
|
var data = Encoding.UTF8.GetBytes("This is test data for Snappy compression detection");
|
|
var compressed = RouteCompressionCodec.Compress(data, RouteCompressionLevel.Fast);
|
|
|
|
RouteCompressionCodec.IsCompressed(compressed).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsCompressed_PlainText_ReturnsFalse()
|
|
{
|
|
var plainText = Encoding.UTF8.GetBytes("PUB test.subject 5\r\nhello\r\n");
|
|
|
|
RouteCompressionCodec.IsCompressed(plainText).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void RoundTrip_LargePayload_Compresses()
|
|
{
|
|
// 10KB payload of repeated data should compress well
|
|
var largePayload = new byte[10240];
|
|
var pattern = Encoding.UTF8.GetBytes("NATS route payload ");
|
|
for (var i = 0; i < largePayload.Length; i++)
|
|
largePayload[i] = pattern[i % pattern.Length];
|
|
|
|
var compressed = RouteCompressionCodec.Compress(largePayload, RouteCompressionLevel.Fast);
|
|
|
|
// Compressed should be smaller than original for repetitive data
|
|
compressed.Length.ShouldBeLessThan(largePayload.Length);
|
|
|
|
// Round-trip should restore original
|
|
var restored = RouteCompressionCodec.Decompress(compressed);
|
|
restored.ShouldBe(largePayload);
|
|
}
|
|
}
|