File: SnappyCompressionTests.cs
Web Access
Project: src\tests\Aspire.MongoDB.Driver.v2.Tests\Aspire.MongoDB.Driver.v2.Tests.csproj (Aspire.MongoDB.Driver.v2.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.IO.Compression;
using MongoDB.Driver.Core.Compression;
using Snappier;
using Xunit;
 
namespace Aspire.MongoDB.Driver.Tests;
 
public class SnappyCompressionTests
{
    [Theory]
    [InlineData(0, false)]
    [InlineData(1, false)]
    [InlineData(1024, false)]
    [InlineData(131072, false)]
    [InlineData(131072, true)]
    public void DriverCompressorRoundTrips(int length, bool randomize)
    {
        var data = new byte[length];
        if (randomize)
        {
            new Random(42).NextBytes(data);
        }
        else
        {
            Array.Fill(data, (byte)'a');
        }
 
        var compressor = CreateDriverCompressor();
        using var input = new MemoryStream(data);
        using var compressed = new MemoryStream();
        compressor.Compress(input, compressed);
 
        compressed.Position = 0;
        using var decompressed = new MemoryStream();
        compressor.Decompress(compressed, decompressed);
 
        Assert.Equal(data, decompressed.ToArray());
    }
 
    [Fact]
    public void DriverCompressorReadsRawSnappyBlock()
    {
        // Raw Snappy: uncompressed length 5, literal tag (5 - 1) << 2, then "hello".
        // MongoDB uses raw blocks, not the framed SnappyStream format.
        // https://github.com/google/snappy/blob/main/format_description.txt
        byte[] data = [0x05, 0x10, (byte)'h', (byte)'e', (byte)'l', (byte)'l', (byte)'o'];
        using var input = new MemoryStream(data);
        using var output = new MemoryStream();
 
        CreateDriverCompressor().Decompress(input, output);
 
        Assert.Equal("hello"u8.ToArray(), output.ToArray());
    }
 
    [Fact]
    public void FramedStreamRoundTrips()
    {
        var data = new byte[131072];
        new Random(42).NextBytes(data);
        using var compressed = new MemoryStream();
        using (var compressor = new SnappyStream(compressed, CompressionMode.Compress, leaveOpen: true))
        {
            compressor.Write(data);
        }
 
        compressed.Position = 0;
        using var decompressor = new SnappyStream(compressed, CompressionMode.Decompress);
        using var output = new MemoryStream();
        decompressor.CopyTo(output);
 
        Assert.Equal(data, output.ToArray());
    }
 
    [Fact]
    public async Task MalformedFramedStreamThrows()
    {
        // The advisory's 15-byte frame has a compressed chunk containing only a checksum.
        // Keep the test's wait bounded if the vulnerable non-terminating decoder is restored.
        // https://github.com/advisories/GHSA-pggp-6c3x-2xmx
        await Task.Run(() =>
        {
            byte[] data = [0x00, 0x04, 0x00, 0x00, 0x64, 0x4e, 0x6c, 0x71, 0x79, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64];
            using var input = new MemoryStream(data);
            using var decompressor = new SnappyStream(input, CompressionMode.Decompress);
            using var output = new MemoryStream();
 
            Assert.Throws<InvalidDataException>(() => decompressor.CopyTo(output));
        }).WaitAsync(TimeSpan.FromSeconds(30), TestContext.Current.CancellationToken);
    }
 
    private static ICompressor CreateDriverCompressor()
    {
        // The v2 implementation is internal. Exercise the shipped driver binary, rather than
        // duplicating its Snappier calls, to catch binary incompatibility with the dependency.
        var type = typeof(ICompressor).Assembly.GetType("MongoDB.Driver.Core.Compression.SnappyCompressor", throwOnError: true)!;
 
        return Assert.IsAssignableFrom<ICompressor>(Activator.CreateInstance(type));
    }
}