""" Tests for Stegasoo compression module. """ import pytest from stegasoo.compression import ( COMPRESSION_MAGIC, HAS_LZ4, MIN_COMPRESS_SIZE, CompressionAlgorithm, CompressionError, algorithm_name, compress, decompress, estimate_compressed_size, get_available_algorithms, get_compression_ratio, ) class TestCompress: """Tests for compress function.""" def test_compress_small_data_not_compressed(self): """Small data should not be compressed (overhead not worth it).""" small_data = b"hello" result = compress(small_data) # Should have magic header but NONE algorithm assert result.startswith(COMPRESSION_MAGIC) assert result[4] == CompressionAlgorithm.NONE def test_compress_zlib_reduces_size(self): """Zlib should reduce size for compressible data.""" # Highly compressible data data = b"A" * 1000 result = compress(data, CompressionAlgorithm.ZLIB) assert len(result) < len(data) assert result.startswith(COMPRESSION_MAGIC) assert result[4] == CompressionAlgorithm.ZLIB def test_compress_incompressible_data(self): """Incompressible data should be stored uncompressed.""" import os # Random data doesn't compress well data = os.urandom(500) result = compress(data, CompressionAlgorithm.ZLIB) # Should fall back to NONE if compression didn't help assert result.startswith(COMPRESSION_MAGIC) def test_compress_none_algorithm(self): """NONE algorithm should just wrap data.""" data = b"Test data" * 100 result = compress(data, CompressionAlgorithm.NONE) assert result.startswith(COMPRESSION_MAGIC) assert result[4] == CompressionAlgorithm.NONE # Data should be after 9-byte header assert result[9:] == data @pytest.mark.skipif(not HAS_LZ4, reason="LZ4 not installed") def test_compress_lz4(self): """LZ4 compression should work if available.""" data = b"B" * 1000 result = compress(data, CompressionAlgorithm.LZ4) assert len(result) < len(data) assert result.startswith(COMPRESSION_MAGIC) assert result[4] == CompressionAlgorithm.LZ4 class TestDecompress: """Tests for decompress function.""" def test_decompress_zlib(self): """Decompression should restore original data.""" original = b"Hello, World! " * 100 compressed = compress(original, CompressionAlgorithm.ZLIB) result = decompress(compressed) assert result == original def test_decompress_none(self): """Uncompressed wrapped data should decompress correctly.""" original = b"Small data" wrapped = compress(original, CompressionAlgorithm.NONE) result = decompress(wrapped) assert result == original def test_decompress_no_magic(self): """Data without magic header should be returned as-is.""" data = b"Not compressed at all" result = decompress(data) assert result == data def test_decompress_truncated_header(self): """Truncated header should raise CompressionError.""" bad_data = COMPRESSION_MAGIC + b"\x01" # Too short with pytest.raises(CompressionError, match="Truncated"): decompress(bad_data) @pytest.mark.skipif(not HAS_LZ4, reason="LZ4 not installed") def test_decompress_lz4(self): """LZ4 decompression should work.""" original = b"LZ4 test data " * 100 compressed = compress(original, CompressionAlgorithm.LZ4) result = decompress(compressed) assert result == original def test_roundtrip_large_data(self): """Large data should survive compress/decompress roundtrip.""" import os original = os.urandom(50000) compressed = compress(original) result = decompress(compressed) assert result == original class TestUtilities: """Tests for utility functions.""" def test_compression_ratio_compressed(self): """Ratio should be < 1 for well-compressed data.""" original = b"X" * 1000 compressed = compress(original) ratio = get_compression_ratio(original, compressed) assert ratio < 1.0 def test_compression_ratio_empty(self): """Empty data should return ratio of 1.0.""" ratio = get_compression_ratio(b"", b"") assert ratio == 1.0 def test_estimate_compressed_size_small(self): """Small data estimation should be accurate.""" data = b"Test " * 100 estimate = estimate_compressed_size(data) actual = len(compress(data)) # Should be within 20% for small data assert abs(estimate - actual) / actual < 0.2 def test_available_algorithms(self): """Should always include NONE and ZLIB.""" algos = get_available_algorithms() assert CompressionAlgorithm.NONE in algos assert CompressionAlgorithm.ZLIB in algos def test_algorithm_name(self): """Algorithm names should be human-readable.""" assert "Zlib" in algorithm_name(CompressionAlgorithm.ZLIB) assert "None" in algorithm_name(CompressionAlgorithm.NONE) assert "LZ4" in algorithm_name(CompressionAlgorithm.LZ4) class TestEdgeCases: """Edge case tests.""" def test_empty_data(self): """Empty data should be handled gracefully.""" result = compress(b"") assert decompress(result) == b"" def test_exact_min_size(self): """Data at exactly MIN_COMPRESS_SIZE should be compressed.""" data = b"x" * MIN_COMPRESS_SIZE result = compress(data, CompressionAlgorithm.ZLIB) assert result.startswith(COMPRESSION_MAGIC) assert decompress(result) == data def test_binary_data(self): """Binary data with null bytes should work.""" data = b"\x00\x01\x02\x03" * 500 compressed = compress(data) assert decompress(compressed) == data def test_unicode_after_encoding(self): """UTF-8 encoded Unicode should compress correctly.""" text = "Hello, δΈ–η•Œ! πŸŽ‰ " * 100 data = text.encode("utf-8") compressed = compress(data) result = decompress(compressed) assert result.decode("utf-8") == text