using ZB.MOM.WW.CBDD.Core.Indexing; using ZB.MOM.WW.CBDD.Core.Storage; using ZB.MOM.WW.CBDD.Shared; using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers; namespace ZB.MOM.WW.CBDD.Tests; public class CollectionIndexManagerAndDefinitionTests { /// /// Tests find best index should prefer unique index. /// [Fact] public void FindBestIndex_Should_Prefer_Unique_Index() { var dbPath = NewDbPath(); try { using var storage = new StorageEngine(dbPath, PageFileConfig.Default); var mapper = new ZB_MOM_WW_CBDD_Shared_PersonMapper(); using var manager = new CollectionIndexManager(storage, mapper, "people_idx_pref_unique"); manager.CreateIndex(p => p.Age, name: "idx_age", unique: false); manager.CreateIndex(p => p.Age, name: "idx_age_unique", unique: true); var best = manager.FindBestIndex("Age"); best.ShouldNotBeNull(); best.Definition.Name.ShouldBe("idx_age_unique"); best.Definition.IsUnique.ShouldBeTrue(); } finally { CleanupFiles(dbPath); } } /// /// Tests find best compound index should choose longest prefix. /// [Fact] public void FindBestCompoundIndex_Should_Choose_Longest_Prefix() { var dbPath = NewDbPath(); try { using var storage = new StorageEngine(dbPath, PageFileConfig.Default); var mapper = new ZB_MOM_WW_CBDD_Shared_PersonMapper(); using var manager = new CollectionIndexManager(storage, mapper, "people_idx_compound"); manager.CreateIndex(new CollectionIndexDefinition( "idx_name", ["Name"], p => p.Name)); manager.CreateIndex(new CollectionIndexDefinition( "idx_name_age", ["Name", "Age"], p => new { p.Name, p.Age })); manager.CreateIndex(new CollectionIndexDefinition( "idx_name_age_id", ["Name", "Age", "Id"], p => new { p.Name, p.Age, p.Id })); var best = manager.FindBestCompoundIndex(["Name", "Age"]); best.ShouldNotBeNull(); best.Definition.Name.ShouldBe("idx_name_age_id"); best.Definition.PropertyPaths.Length.ShouldBe(3); } finally { CleanupFiles(dbPath); } } /// /// Tests drop index should remove metadata and be idempotent. /// [Fact] public void DropIndex_Should_Remove_Metadata_And_Be_Idempotent() { var dbPath = NewDbPath(); const string collectionName = "people_idx_drop"; try { using var storage = new StorageEngine(dbPath, PageFileConfig.Default); var mapper = new ZB_MOM_WW_CBDD_Shared_PersonMapper(); using (var manager = new CollectionIndexManager(storage, mapper, collectionName)) { manager.CreateIndex(p => p.Age, name: "idx_age", unique: false); manager.DropIndex("idx_age").ShouldBeTrue(); manager.DropIndex("idx_age").ShouldBeFalse(); manager.GetIndexInfo().ShouldBeEmpty(); } using var reloaded = new CollectionIndexManager(storage, mapper, collectionName); reloaded.GetIndexInfo().ShouldBeEmpty(); } finally { CleanupFiles(dbPath); } } /// /// Tests collection index definition should respect query support rules. /// [Fact] public void CollectionIndexDefinition_Should_Respect_Query_Support_Rules() { var definition = new CollectionIndexDefinition( "idx_name_age", ["Name", "Age"], p => new { p.Name, p.Age }); definition.CanSupportQuery("Name").ShouldBeTrue(); definition.CanSupportQuery("Age").ShouldBeFalse(); definition.CanSupportCompoundQuery(["Name"]).ShouldBeTrue(); definition.CanSupportCompoundQuery(["Name", "Age"]).ShouldBeTrue(); definition.CanSupportCompoundQuery(["Name", "Age", "Id"]).ShouldBeFalse(); definition.ToString().ShouldContain("idx_name_age"); definition.ToString().ShouldContain("Name"); } /// /// Tests collection index info to string should include diagnostics. /// [Fact] public void CollectionIndexInfo_ToString_Should_Include_Diagnostics() { var info = new CollectionIndexInfo { Name = "idx_age", PropertyPaths = ["Age"], EstimatedDocumentCount = 12, EstimatedSizeBytes = 4096 }; var text = info.ToString(); text.ShouldContain("idx_age"); text.ShouldContain("Age"); text.ShouldContain("12 docs"); } private static string NewDbPath() => Path.Combine(Path.GetTempPath(), $"idx_mgr_{Guid.NewGuid():N}.db"); private static void CleanupFiles(string dbPath) { if (File.Exists(dbPath)) File.Delete(dbPath); var walPath = Path.ChangeExtension(dbPath, ".wal"); if (File.Exists(walPath)) File.Delete(walPath); var altWalPath = dbPath + "-wal"; if (File.Exists(altWalPath)) File.Delete(altWalPath); } }