using System.IO.Compression; using ZB.MOM.WW.CBDD.Core.Compression; using ZB.MOM.WW.CBDD.Core.Storage; using ZB.MOM.WW.CBDD.Shared; namespace ZB.MOM.WW.CBDD.Tests; public class MaintenanceDiagnosticsAndMigrationTests { /// /// Verifies diagnostics APIs return page usage, compression, and fragmentation data. /// [Fact] public void DiagnosticsApis_ShouldReturnPageUsageCompressionAndFragmentationData() { var dbPath = NewDbPath(); try { var options = new CompressionOptions { EnableCompression = true, MinSizeBytes = 0, MinSavingsPercent = 0, Codec = CompressionCodec.Brotli, Level = CompressionLevel.Fastest }; using var db = new TestDbContext(dbPath, options); for (var i = 0; i < 40; i++) { db.Users.Insert(new User { Name = BuildPayload(i, 9000), Age = i }); } db.SaveChanges(); db.ForceCheckpoint(); var byType = db.GetPageUsageByPageType(); byType.Count.ShouldBeGreaterThan(0); byType.Any(x => x.PageType == PageType.Data && x.PageCount > 0).ShouldBeTrue(); var byCollection = db.GetPageUsageByCollection(); byCollection.Any(x => x.CollectionName.Equals("users", StringComparison.OrdinalIgnoreCase)).ShouldBeTrue(); var compressionByCollection = db.GetCompressionRatioByCollection(); var usersCompression = compressionByCollection.First(x => x.CollectionName.Equals("users", StringComparison.OrdinalIgnoreCase)); usersCompression.DocumentCount.ShouldBeGreaterThan(0); usersCompression.BytesBeforeCompression.ShouldBeGreaterThan(0); usersCompression.BytesAfterCompression.ShouldBeGreaterThan(0); var freeList = db.GetFreeListSummary(); freeList.PageCount.ShouldBeGreaterThan(0u); var fragmentation = db.GetFragmentationMap(); fragmentation.Pages.Count.ShouldBeGreaterThan(0); } finally { CleanupFiles(dbPath); } } /// /// Verifies compression migration dry-run and apply modes return deterministic stats and preserve data. /// [Fact] public void MigrateCompression_DryRunAndApply_ShouldReturnDeterministicStatsAndPreserveData() { var dbPath = NewDbPath(); try { using var db = new TestDbContext(dbPath, CompressionOptions.Default); var ids = new List(); for (var i = 0; i < 60; i++) { ids.Add(db.Users.Insert(new User { Name = BuildPayload(i, 12000), Age = i % 17 })); } db.SaveChanges(); db.ForceCheckpoint(); var dryRun = db.MigrateCompression(new CompressionMigrationOptions { DryRun = true, Codec = CompressionCodec.Deflate, Level = CompressionLevel.Fastest, MinSizeBytes = 0, MinSavingsPercent = 0, IncludeCollections = ["users"] }); dryRun.DryRun.ShouldBeTrue(); dryRun.DocumentsScanned.ShouldBeGreaterThan(0); dryRun.BytesBefore.ShouldBeGreaterThan(0); dryRun.BytesEstimatedAfter.ShouldBeGreaterThan(0); var apply = db.MigrateCompression(new CompressionMigrationOptions { DryRun = false, Codec = CompressionCodec.Deflate, Level = CompressionLevel.Fastest, MinSizeBytes = 0, MinSavingsPercent = 0, IncludeCollections = ["users"] }); apply.DryRun.ShouldBeFalse(); apply.DocumentsScanned.ShouldBeGreaterThan(0); foreach (var id in ids) { var user = db.Users.FindById(id); user.ShouldNotBeNull(); user!.Name.Length.ShouldBeGreaterThan(1000); } } finally { CleanupFiles(dbPath); } } private static string BuildPayload(int seed, int approxLength) { var builder = new System.Text.StringBuilder(approxLength + 128); var i = 0; while (builder.Length < approxLength) { builder.Append("diag-migrate-"); builder.Append(seed.ToString("D4")); builder.Append('-'); builder.Append(i.ToString("D6")); builder.Append('|'); i++; } return builder.ToString(); } private static string NewDbPath() => Path.Combine(Path.GetTempPath(), $"maint_diag_migrate_{Guid.NewGuid():N}.db"); private static void CleanupFiles(string dbPath) { var walPath = Path.ChangeExtension(dbPath, ".wal"); var markerPath = $"{dbPath}.compact.state"; var tempPath = $"{dbPath}.compact.tmp"; var backupPath = $"{dbPath}.compact.bak"; if (File.Exists(dbPath)) File.Delete(dbPath); if (File.Exists(walPath)) File.Delete(walPath); if (File.Exists(markerPath)) File.Delete(markerPath); if (File.Exists(tempPath)) File.Delete(tempPath); if (File.Exists(backupPath)) File.Delete(backupPath); } }