// 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;
///
/// Tests for route S2/Snappy compression codec, matching Go's route compression
/// behavior using IronSnappy.
///
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.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(() => 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);
}
}