Reformat / cleanup
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.IO.Compression;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
@@ -10,12 +11,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class CompressionCompatibilityTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies opening legacy uncompressed files with compression enabled does not mutate database bytes.
|
||||
/// Verifies opening legacy uncompressed files with compression enabled does not mutate database bytes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OpeningLegacyUncompressedFile_WithCompressionEnabled_ShouldNotMutateDbFile()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var idList = new List<ObjectId>();
|
||||
|
||||
try
|
||||
@@ -28,8 +29,8 @@ public class CompressionCompatibilityTests
|
||||
db.ForceCheckpoint();
|
||||
}
|
||||
|
||||
var beforeSize = new FileInfo(dbPath).Length;
|
||||
var beforeHash = ComputeFileHash(dbPath);
|
||||
long beforeSize = new FileInfo(dbPath).Length;
|
||||
string beforeHash = ComputeFileHash(dbPath);
|
||||
|
||||
var compressionOptions = new CompressionOptions
|
||||
{
|
||||
@@ -47,8 +48,8 @@ public class CompressionCompatibilityTests
|
||||
reopened.Users.Count().ShouldBe(2);
|
||||
}
|
||||
|
||||
var afterSize = new FileInfo(dbPath).Length;
|
||||
var afterHash = ComputeFileHash(dbPath);
|
||||
long afterSize = new FileInfo(dbPath).Length;
|
||||
string afterHash = ComputeFileHash(dbPath);
|
||||
|
||||
afterSize.ShouldBe(beforeSize);
|
||||
afterHash.ShouldBe(beforeHash);
|
||||
@@ -60,12 +61,12 @@ public class CompressionCompatibilityTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies mixed compressed and uncompressed documents remain readable after partial migration.
|
||||
/// Verifies mixed compressed and uncompressed documents remain readable after partial migration.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MixedFormatDocuments_ShouldRemainReadableAfterPartialMigration()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
ObjectId legacyId;
|
||||
ObjectId compressedId;
|
||||
|
||||
@@ -125,7 +126,7 @@ public class CompressionCompatibilityTests
|
||||
|
||||
for (ushort slotIndex = 0; slotIndex < header.SlotCount; slotIndex++)
|
||||
{
|
||||
var slotOffset = SlottedPageHeader.Size + (slotIndex * SlotEntry.Size);
|
||||
int slotOffset = SlottedPageHeader.Size + slotIndex * SlotEntry.Size;
|
||||
var slot = SlotEntry.ReadFrom(buffer.AsSpan(slotOffset, SlotEntry.Size));
|
||||
if ((slot.Flags & SlotFlags.Deleted) != 0)
|
||||
continue;
|
||||
@@ -149,7 +150,7 @@ public class CompressionCompatibilityTests
|
||||
|
||||
private static string BuildPayload(int approxLength)
|
||||
{
|
||||
var builder = new System.Text.StringBuilder(approxLength + 256);
|
||||
var builder = new StringBuilder(approxLength + 256);
|
||||
var i = 0;
|
||||
while (builder.Length < approxLength)
|
||||
{
|
||||
@@ -163,14 +164,16 @@ public class CompressionCompatibilityTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"compression_compat_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"compression_compat_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
if (File.Exists(markerPath)) File.Delete(markerPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
@@ -10,12 +11,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class CompressionCorruptionTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies corrupted compressed payload checksum triggers invalid data errors.
|
||||
/// Verifies corrupted compressed payload checksum triggers invalid data errors.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithBadChecksum_ShouldThrowInvalidData()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = CompressionEnabledOptions();
|
||||
|
||||
try
|
||||
@@ -23,7 +24,7 @@ public class CompressionCorruptionTests
|
||||
using var db = new TestDbContext(dbPath, options);
|
||||
var id = InsertCheckpointAndCorrupt(db, header =>
|
||||
{
|
||||
var currentChecksum = BinaryPrimitives.ReadUInt32LittleEndian(header.Slice(12, 4));
|
||||
uint currentChecksum = BinaryPrimitives.ReadUInt32LittleEndian(header.Slice(12, 4));
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(header.Slice(12, 4), currentChecksum + 1);
|
||||
});
|
||||
|
||||
@@ -38,21 +39,19 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies invalid original length metadata triggers invalid data errors.
|
||||
/// Verifies invalid original length metadata triggers invalid data errors.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithBadOriginalLength_ShouldThrowInvalidData()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = CompressionEnabledOptions();
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath, options);
|
||||
var id = InsertCheckpointAndCorrupt(db, header =>
|
||||
{
|
||||
BinaryPrimitives.WriteInt32LittleEndian(header.Slice(4, 4), -1);
|
||||
});
|
||||
var id = InsertCheckpointAndCorrupt(db,
|
||||
header => { BinaryPrimitives.WriteInt32LittleEndian(header.Slice(4, 4), -1); });
|
||||
|
||||
var ex = Should.Throw<InvalidDataException>(() => db.Users.FindById(id));
|
||||
ex.Message.ShouldContain("decompress");
|
||||
@@ -64,21 +63,19 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies oversized declared decompressed length enforces safety guardrails.
|
||||
/// Verifies oversized declared decompressed length enforces safety guardrails.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithOversizedDeclaredLength_ShouldEnforceGuardrail()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
var options = CompressionEnabledOptions(maxDecompressedSizeBytes: 2048);
|
||||
string dbPath = NewDbPath();
|
||||
var options = CompressionEnabledOptions(2048);
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath, options);
|
||||
var id = InsertCheckpointAndCorrupt(db, header =>
|
||||
{
|
||||
BinaryPrimitives.WriteInt32LittleEndian(header.Slice(4, 4), 2049);
|
||||
});
|
||||
var id = InsertCheckpointAndCorrupt(db,
|
||||
header => { BinaryPrimitives.WriteInt32LittleEndian(header.Slice(4, 4), 2049); });
|
||||
|
||||
var ex = Should.Throw<InvalidDataException>(() => db.Users.FindById(id));
|
||||
ex.Message.ShouldContain("invalid decompressed length");
|
||||
@@ -91,12 +88,12 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies invalid codec identifiers in compressed headers trigger invalid data errors.
|
||||
/// Verifies invalid codec identifiers in compressed headers trigger invalid data errors.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithInvalidCodecId_ShouldThrowInvalidData()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = CompressionEnabledOptions();
|
||||
|
||||
try
|
||||
@@ -128,7 +125,7 @@ public class CompressionCorruptionTests
|
||||
db.SaveChanges();
|
||||
db.ForceCheckpoint();
|
||||
|
||||
var (pageId, slot, _) = FindFirstCompressedSlot(db.Storage);
|
||||
(uint pageId, var slot, _) = FindFirstCompressedSlot(db.Storage);
|
||||
((slot.Flags & SlotFlags.HasOverflow) != 0).ShouldBeFalse();
|
||||
|
||||
var page = new byte[db.Storage.PageSize];
|
||||
@@ -152,7 +149,7 @@ public class CompressionCorruptionTests
|
||||
|
||||
for (ushort slotIndex = 0; slotIndex < header.SlotCount; slotIndex++)
|
||||
{
|
||||
var slotOffset = SlottedPageHeader.Size + (slotIndex * SlotEntry.Size);
|
||||
int slotOffset = SlottedPageHeader.Size + slotIndex * SlotEntry.Size;
|
||||
var slot = SlotEntry.ReadFrom(buffer.AsSpan(slotOffset, SlotEntry.Size));
|
||||
if ((slot.Flags & SlotFlags.Deleted) != 0)
|
||||
continue;
|
||||
@@ -178,11 +175,9 @@ public class CompressionCorruptionTests
|
||||
};
|
||||
}
|
||||
|
||||
private delegate void HeaderMutator(Span<byte> header);
|
||||
|
||||
private static string BuildPayload(int approxLength)
|
||||
{
|
||||
var builder = new System.Text.StringBuilder(approxLength + 256);
|
||||
var builder = new StringBuilder(approxLength + 256);
|
||||
var i = 0;
|
||||
while (builder.Length < approxLength)
|
||||
{
|
||||
@@ -196,14 +191,18 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"compression_corruption_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"compression_corruption_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
if (File.Exists(markerPath)) File.Delete(markerPath);
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void HeaderMutator(Span<byte> header);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
@@ -9,12 +10,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class CompressionInsertReadTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests insert with threshold should store mixed compressed and uncompressed slots.
|
||||
/// Tests insert with threshold should store mixed compressed and uncompressed slots.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_WithThreshold_ShouldStoreMixedCompressedAndUncompressedSlots()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -50,12 +51,12 @@ public class CompressionInsertReadTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests find by id should read mixed compressed and uncompressed documents.
|
||||
/// Tests find by id should read mixed compressed and uncompressed documents.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindById_ShouldReadMixedCompressedAndUncompressedDocuments()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -98,12 +99,12 @@ public class CompressionInsertReadTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests insert when codec throws should fallback to uncompressed storage.
|
||||
/// Tests insert when codec throws should fallback to uncompressed storage.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_WhenCodecThrows_ShouldFallbackToUncompressedStorage()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -152,7 +153,7 @@ public class CompressionInsertReadTests
|
||||
|
||||
for (ushort slotIndex = 0; slotIndex < header.SlotCount; slotIndex++)
|
||||
{
|
||||
var slotOffset = SlottedPageHeader.Size + (slotIndex * SlotEntry.Size);
|
||||
int slotOffset = SlottedPageHeader.Size + slotIndex * SlotEntry.Size;
|
||||
var slot = SlotEntry.ReadFrom(buffer.AsSpan(slotOffset, SlotEntry.Size));
|
||||
if ((slot.Flags & SlotFlags.Deleted) != 0)
|
||||
continue;
|
||||
@@ -168,7 +169,7 @@ public class CompressionInsertReadTests
|
||||
|
||||
private static string BuildPayload(int approxLength)
|
||||
{
|
||||
var builder = new System.Text.StringBuilder(approxLength + 256);
|
||||
var builder = new StringBuilder(approxLength + 256);
|
||||
var i = 0;
|
||||
while (builder.Length < approxLength)
|
||||
{
|
||||
@@ -182,11 +183,13 @@ public class CompressionInsertReadTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"compression_insert_read_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"compression_insert_read_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
@@ -196,25 +199,29 @@ public class CompressionInsertReadTests
|
||||
private sealed class FailingBrotliCodec : ICompressionCodec
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the codec.
|
||||
/// Gets or sets the codec.
|
||||
/// </summary>
|
||||
public CompressionCodec Codec => CompressionCodec.Brotli;
|
||||
|
||||
/// <summary>
|
||||
/// Tests compress.
|
||||
/// Tests compress.
|
||||
/// </summary>
|
||||
/// <param name="input">Payload bytes to compress.</param>
|
||||
/// <param name="level">Compression level.</param>
|
||||
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level)
|
||||
=> throw new InvalidOperationException("Forced codec failure for test coverage.");
|
||||
{
|
||||
throw new InvalidOperationException("Forced codec failure for test coverage.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests decompress.
|
||||
/// Tests decompress.
|
||||
/// </summary>
|
||||
/// <param name="input">Compressed payload bytes.</param>
|
||||
/// <param name="expectedLength">Expected decompressed payload length.</param>
|
||||
/// <param name="maxDecompressedSizeBytes">Maximum allowed decompressed size.</param>
|
||||
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
|
||||
=> throw new InvalidOperationException("This codec should not be used for reads in this scenario.");
|
||||
{
|
||||
throw new InvalidOperationException("This codec should not be used for reads in this scenario.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO.Compression;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Text;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
@@ -9,12 +10,12 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
public class CompressionOverflowTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests insert compressed document spanning overflow pages should round trip.
|
||||
/// Tests insert compressed document spanning overflow pages should round trip.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_CompressedDocumentSpanningOverflowPages_ShouldRoundTrip()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -28,7 +29,7 @@ public class CompressionOverflowTests
|
||||
{
|
||||
using var db = new TestDbContext(dbPath, TinyPageConfig(), options);
|
||||
|
||||
var payload = BuildPayload(300_000);
|
||||
string payload = BuildPayload(300_000);
|
||||
var id = db.Users.Insert(new User { Name = payload, Age = 40 });
|
||||
db.SaveChanges();
|
||||
|
||||
@@ -47,12 +48,12 @@ public class CompressionOverflowTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests update should transition across compression thresholds.
|
||||
/// Tests update should transition across compression thresholds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_ShouldTransitionAcrossCompressionThresholds()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
string dbPath = NewDbPath();
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
@@ -123,13 +124,13 @@ public class CompressionOverflowTests
|
||||
|
||||
for (ushort slotIndex = 0; slotIndex < header.SlotCount; slotIndex++)
|
||||
{
|
||||
var slotOffset = SlottedPageHeader.Size + (slotIndex * SlotEntry.Size);
|
||||
int slotOffset = SlottedPageHeader.Size + slotIndex * SlotEntry.Size;
|
||||
var slot = SlotEntry.ReadFrom(buffer.AsSpan(slotOffset, SlotEntry.Size));
|
||||
if ((slot.Flags & SlotFlags.Deleted) != 0)
|
||||
continue;
|
||||
|
||||
var isCompressed = (slot.Flags & SlotFlags.Compressed) != 0;
|
||||
var hasOverflow = (slot.Flags & SlotFlags.HasOverflow) != 0;
|
||||
bool isCompressed = (slot.Flags & SlotFlags.Compressed) != 0;
|
||||
bool hasOverflow = (slot.Flags & SlotFlags.HasOverflow) != 0;
|
||||
if (isCompressed)
|
||||
compressed++;
|
||||
if (isCompressed && hasOverflow)
|
||||
@@ -152,7 +153,7 @@ public class CompressionOverflowTests
|
||||
|
||||
private static string BuildPayload(int approxLength)
|
||||
{
|
||||
var builder = new System.Text.StringBuilder(approxLength + 256);
|
||||
var builder = new StringBuilder(approxLength + 256);
|
||||
var i = 0;
|
||||
while (builder.Length < approxLength)
|
||||
{
|
||||
@@ -166,14 +167,16 @@ public class CompressionOverflowTests
|
||||
}
|
||||
|
||||
private static string NewDbPath()
|
||||
=> Path.Combine(Path.GetTempPath(), $"compression_overflow_{Guid.NewGuid():N}.db");
|
||||
{
|
||||
return Path.Combine(Path.GetTempPath(), $"compression_overflow_{Guid.NewGuid():N}.db");
|
||||
}
|
||||
|
||||
private static void CleanupFiles(string dbPath)
|
||||
{
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
string walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
if (File.Exists(markerPath)) File.Delete(markerPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user