Fix audit findings for coverage, architecture checks, and XML docs
This commit is contained in:
@@ -16,6 +16,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes test database state used by advanced query tests.
|
||||
/// </summary>
|
||||
public AdvancedQueryTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_advanced_{Guid.NewGuid()}.db");
|
||||
@@ -30,12 +33,18 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies grouping by a simple key returns expected groups and counts.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GroupBy_Simple_Key_Works()
|
||||
{
|
||||
@@ -57,6 +66,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
groupC.Count().ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies grouped projection with aggregation returns expected totals.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GroupBy_With_Aggregation_Select()
|
||||
{
|
||||
@@ -77,6 +89,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results[2].Total.ShouldBe(50); // 50
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies direct aggregate operators return expected values.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Aggregations_Direct_Works()
|
||||
{
|
||||
@@ -89,6 +104,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
query.Max(x => x.Amount).ShouldBe(50);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies aggregate operators with predicates return expected values.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Aggregations_With_Predicate_Works()
|
||||
{
|
||||
@@ -98,6 +116,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
query.Sum(x => x.Amount).ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies in-memory join query execution returns expected rows.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Join_Works_InMemory()
|
||||
{
|
||||
@@ -126,6 +147,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Verifies projection of nested object properties works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Select_Project_Nested_Object()
|
||||
{
|
||||
@@ -152,6 +176,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
query[0].Street.ShouldBe("5th Ave");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies projection of nested scalar fields works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Select_Project_Nested_Field()
|
||||
{
|
||||
@@ -172,6 +199,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
cities[0].ShouldBe("New York");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies anonymous projection including nested values works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Select_Anonymous_Complex()
|
||||
{
|
||||
@@ -196,6 +226,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
result[0].City.Name.ShouldBe("New York");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies projection and retrieval of nested arrays of objects works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Select_Project_Nested_Array_Of_Objects()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,9 @@ public class ArchitectureFitnessTests
|
||||
private const string SourceGeneratorsProject = "src/CBDD.SourceGenerators/ZB.MOM.WW.CBDD.SourceGenerators.csproj";
|
||||
private const string FacadeProject = "src/CBDD/ZB.MOM.WW.CBDD.csproj";
|
||||
|
||||
/// <summary>
|
||||
/// Executes Solution_DependencyGraph_ShouldRemainAcyclic_AndFollowLayerDirection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Solution_DependencyGraph_ShouldRemainAcyclic_AndFollowLayerDirection()
|
||||
{
|
||||
@@ -40,6 +43,9 @@ public class ArchitectureFitnessTests
|
||||
.ShouldBeFalse("Project references must remain acyclic.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes HighLevelCollectionApi_ShouldNotExpandRawBsonReaderWriterSurface.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HighLevelCollectionApi_ShouldNotExpandRawBsonReaderWriterSurface()
|
||||
{
|
||||
@@ -65,6 +71,9 @@ public class ArchitectureFitnessTests
|
||||
dbContextOffenders.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes CollectionAndIndexOrchestration_ShouldUseStoragePortInternally.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CollectionAndIndexOrchestration_ShouldUseStoragePortInternally()
|
||||
{
|
||||
|
||||
@@ -11,17 +11,26 @@ public class AsyncTests : IDisposable
|
||||
{
|
||||
private readonly string _dbPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AsyncTests"/> class.
|
||||
/// </summary>
|
||||
public AsyncTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_async_{Guid.NewGuid()}.db");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
if (File.Exists(Path.ChangeExtension(_dbPath, ".wal"))) File.Delete(Path.ChangeExtension(_dbPath, ".wal"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Async_Transaction_Commit_Should_Persist_Data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Async_Transaction_Commit_Should_Persist_Data()
|
||||
{
|
||||
@@ -48,6 +57,9 @@ public class AsyncTests : IDisposable
|
||||
doc2.Name.ShouldBe("Async2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Async_Transaction_Rollback_Should_Discard_Data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Async_Transaction_Rollback_Should_Discard_Data()
|
||||
{
|
||||
@@ -63,6 +75,9 @@ public class AsyncTests : IDisposable
|
||||
doc.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Bulk_Async_Insert_Should_Persist_Data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Bulk_Async_Insert_Should_Persist_Data()
|
||||
{
|
||||
@@ -78,6 +93,9 @@ public class AsyncTests : IDisposable
|
||||
doc50.Name.ShouldBe("Bulk50");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Bulk_Async_Update_Should_Persist_Changes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Bulk_Async_Update_Should_Persist_Changes()
|
||||
{
|
||||
@@ -102,6 +120,9 @@ public class AsyncTests : IDisposable
|
||||
doc50.Name.ShouldBe("Updated50");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes High_Concurrency_Async_Commits.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task High_Concurrency_Async_Commits()
|
||||
{
|
||||
|
||||
@@ -13,6 +13,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
private readonly System.Collections.Concurrent.ConcurrentDictionary<string, ushort> _keyMap = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly System.Collections.Concurrent.ConcurrentDictionary<ushort, string> _keys = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes lookup maps used by attribute mapper tests.
|
||||
/// </summary>
|
||||
public AttributeTests()
|
||||
{
|
||||
ushort id = 1;
|
||||
@@ -25,6 +28,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies table attribute mapping resolves the expected collection name.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_Table_Attribute_Mapping()
|
||||
{
|
||||
@@ -33,6 +39,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
mapper.CollectionName.ShouldBe("test.custom_users");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies required attribute validation is enforced.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_Required_Validation()
|
||||
{
|
||||
@@ -52,6 +61,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
thrown.ShouldBeTrue("Should throw ValidationException for empty Name.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies string length attribute validation is enforced.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_StringLength_Validation()
|
||||
{
|
||||
@@ -69,6 +81,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
thrown.ShouldBeTrue("Should throw ValidationException for Name too long.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies range attribute validation is enforced.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_Range_Validation()
|
||||
{
|
||||
@@ -81,6 +96,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
thrown.ShouldBeTrue("Should throw ValidationException for Age out of range.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies column attribute maps to the expected BSON field name.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_Column_Name_Mapping()
|
||||
{
|
||||
@@ -108,6 +126,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
foundDisplayName.ShouldBeTrue("BSON field name should be 'display_name' from [Column] attribute.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies not-mapped attribute excludes properties from BSON serialization.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_NotMapped_Attribute()
|
||||
{
|
||||
|
||||
@@ -6,16 +6,25 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
{
|
||||
private const string DbPath = "autoinit.db";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoInitTests"/> class.
|
||||
/// </summary>
|
||||
public AutoInitTests()
|
||||
{
|
||||
if (File.Exists(DbPath)) File.Delete(DbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(DbPath)) File.Delete(DbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies generated collection initializers set up collections automatically.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Collections_Are_Initialized_By_Generator()
|
||||
{
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class BTreeDeleteUnderflowTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes Delete_HeavyWorkload_Should_Remain_Queryable_After_Merges.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Delete_HeavyWorkload_Should_Remain_Queryable_After_Merges()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class BsonDocumentAndBufferWriterTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies BSON document creation and typed retrieval roundtrip.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BsonDocument_Create_And_TryGet_RoundTrip()
|
||||
{
|
||||
@@ -42,6 +45,9 @@ public class BsonDocumentAndBufferWriterTests
|
||||
reader.ReadDocumentSize().ShouldBeGreaterThan(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies typed getters return false for missing fields and type mismatches.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BsonDocument_TryGet_Should_Return_False_For_Missing_Or_Wrong_Type()
|
||||
{
|
||||
@@ -64,6 +70,9 @@ public class BsonDocumentAndBufferWriterTests
|
||||
wrapped.TryGetObjectId("age", out _).ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the BSON document builder grows its internal buffer for large documents.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BsonDocumentBuilder_Should_Grow_Buffer_When_Document_Is_Large()
|
||||
{
|
||||
@@ -90,6 +99,9 @@ public class BsonDocumentAndBufferWriterTests
|
||||
value.ShouldBe(180);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies BSON buffer writer emits expected nested document and array layout.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BsonBufferWriter_Should_Write_Nested_Document_And_Array()
|
||||
{
|
||||
@@ -151,6 +163,9 @@ public class BsonDocumentAndBufferWriterTests
|
||||
reader.ReadBsonType().ShouldBe(BsonType.EndOfDocument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies single-byte and C-string span reads operate correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BsonSpanReader_ReadByte_And_ReadCStringSpan_Should_Work()
|
||||
{
|
||||
|
||||
@@ -11,12 +11,30 @@ public class BsonSchemaTests
|
||||
{
|
||||
public class SimpleEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the entity is active.
|
||||
/// </summary>
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies schema generation for a simple entity.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_SimpleEntity()
|
||||
{
|
||||
@@ -37,10 +55,20 @@ public class BsonSchemaTests
|
||||
|
||||
public class CollectionEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets tags.
|
||||
/// </summary>
|
||||
public List<string> Tags { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets scores.
|
||||
/// </summary>
|
||||
public int[] Scores { get; set; } = Array.Empty<int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies schema generation for collection fields.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_Collections()
|
||||
{
|
||||
@@ -57,9 +85,15 @@ public class BsonSchemaTests
|
||||
|
||||
public class NestedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the parent entity.
|
||||
/// </summary>
|
||||
public SimpleEntity Parent { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies schema generation for nested document fields.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_Nested()
|
||||
{
|
||||
@@ -73,9 +107,15 @@ public class BsonSchemaTests
|
||||
|
||||
public class ComplexCollectionEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets items.
|
||||
/// </summary>
|
||||
public List<SimpleEntity> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies schema generation for collections of complex types.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_ComplexCollection()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,9 @@ public class BsonSpanReaderWriterTests
|
||||
private readonly ConcurrentDictionary<string, ushort> _keyMap = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ConcurrentDictionary<ushort, string> _keys = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BsonSpanReaderWriterTests"/> class.
|
||||
/// </summary>
|
||||
public BsonSpanReaderWriterTests()
|
||||
{
|
||||
ushort id = 1;
|
||||
@@ -21,6 +24,9 @@ public class BsonSpanReaderWriterTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests write and read simple document.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WriteAndRead_SimpleDocument()
|
||||
{
|
||||
@@ -65,6 +71,9 @@ public class BsonSpanReaderWriterTests
|
||||
value3.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests write and read object id.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WriteAndRead_ObjectId()
|
||||
{
|
||||
@@ -90,6 +99,9 @@ public class BsonSpanReaderWriterTests
|
||||
readOid.ShouldBe(oid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests read write double.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReadWrite_Double()
|
||||
{
|
||||
@@ -108,6 +120,9 @@ public class BsonSpanReaderWriterTests
|
||||
val.ShouldBe(123.456);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests read write decimal128 round trip.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ReadWrite_Decimal128_RoundTrip()
|
||||
{
|
||||
@@ -127,6 +142,9 @@ public class BsonSpanReaderWriterTests
|
||||
val.ShouldBe(original);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests write and read date time.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WriteAndRead_DateTime()
|
||||
{
|
||||
@@ -155,6 +173,9 @@ public class BsonSpanReaderWriterTests
|
||||
readTime.ShouldBe(expectedTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests write and read numeric types.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WriteAndRead_NumericTypes()
|
||||
{
|
||||
@@ -185,6 +206,9 @@ public class BsonSpanReaderWriterTests
|
||||
Math.Round(reader.ReadDouble(), 5).ShouldBe(Math.Round(3.14159, 5));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests write and read binary.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WriteAndRead_Binary()
|
||||
{
|
||||
@@ -211,6 +235,9 @@ public class BsonSpanReaderWriterTests
|
||||
testData.AsSpan().SequenceEqual(readData).ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests write and read nested document.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WriteAndRead_NestedDocument()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,9 @@ public class BulkOperationsTests : IDisposable
|
||||
private readonly string _walPath;
|
||||
private readonly Shared.TestDbContext _dbContext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BulkOperationsTests"/> class.
|
||||
/// </summary>
|
||||
public BulkOperationsTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_bulk_{Guid.NewGuid()}.db");
|
||||
@@ -23,11 +26,17 @@ public class BulkOperationsTests : IDisposable
|
||||
_dbContext = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_dbContext.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes UpdateBulk_UpdatesMultipleDocuments.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void UpdateBulk_UpdatesMultipleDocuments()
|
||||
{
|
||||
@@ -64,6 +73,9 @@ public class BulkOperationsTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes DeleteBulk_RemovesMultipleDocuments.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DeleteBulk_RemovesMultipleDocuments()
|
||||
{
|
||||
@@ -103,6 +115,9 @@ public class BulkOperationsTests : IDisposable
|
||||
_dbContext.Users.FindAll().Count().ShouldBe(50);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes DeleteBulk_WithTransaction_Rollworks.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DeleteBulk_WithTransaction_Rollworks()
|
||||
{
|
||||
|
||||
@@ -13,12 +13,18 @@ public class CdcScalabilityTests : IDisposable
|
||||
private readonly Shared.TestDbContext _db;
|
||||
private readonly string _dbPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CdcScalabilityTests"/> class.
|
||||
/// </summary>
|
||||
public CdcScalabilityTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cdc_scaling_{Guid.NewGuid()}.db");
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies CDC dispatch reaches all registered subscribers.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Test_Cdc_1000_Subscribers_Receive_Events()
|
||||
{
|
||||
@@ -55,6 +61,9 @@ public class CdcScalabilityTests : IDisposable
|
||||
foreach (var sub in subscriptions) sub.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies a slow subscriber does not block other subscribers.
|
||||
/// </summary>
|
||||
[Fact(Skip = "Performance test - run manually when needed")]
|
||||
public async Task Test_Cdc_Slow_Subscriber_Does_Not_Block_Others()
|
||||
{
|
||||
@@ -99,6 +108,9 @@ public class CdcScalabilityTests : IDisposable
|
||||
slowEventCount.ShouldBe(2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
|
||||
@@ -21,11 +21,17 @@ public class CdcTests : IDisposable
|
||||
private readonly string _dbPath = $"cdc_test_{Guid.NewGuid()}.db";
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CdcTests"/> class.
|
||||
/// </summary>
|
||||
public CdcTests()
|
||||
{
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an insert operation publishes a CDC event.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Test_Cdc_Basic_Insert_Fires_Event()
|
||||
{
|
||||
@@ -47,6 +53,9 @@ public class CdcTests : IDisposable
|
||||
snapshot[0].Entity!.Name.ShouldBe("John");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies payload is omitted when CDC capture payload is disabled.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Test_Cdc_No_Payload_When_Not_Requested()
|
||||
{
|
||||
@@ -65,6 +74,9 @@ public class CdcTests : IDisposable
|
||||
snapshot[0].Entity.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies CDC events are published only for committed changes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Test_Cdc_Commit_Only()
|
||||
{
|
||||
@@ -95,6 +107,9 @@ public class CdcTests : IDisposable
|
||||
snapshot[0].DocumentId.ShouldBe(2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies update and delete operations publish CDC events.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Test_Cdc_Update_And_Delete()
|
||||
{
|
||||
@@ -125,6 +140,9 @@ public class CdcTests : IDisposable
|
||||
snapshot[2].DocumentId.ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
@@ -155,6 +173,13 @@ public class CdcTests : IDisposable
|
||||
// Simple helper to avoid System.Reactive dependency in tests
|
||||
public static class ObservableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribes to an observable sequence using an action callback.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The event type.</typeparam>
|
||||
/// <param name="observable">The observable sequence.</param>
|
||||
/// <param name="onNext">The callback for next events.</param>
|
||||
/// <returns>An <see cref="IDisposable"/> subscription.</returns>
|
||||
public static IDisposable Subscribe<T>(this IObservable<T> observable, Action<T> onNext)
|
||||
{
|
||||
return observable.Subscribe(new AnonymousObserver<T>(onNext));
|
||||
@@ -163,9 +188,28 @@ public static class ObservableExtensions
|
||||
private class AnonymousObserver<T> : IObserver<T>
|
||||
{
|
||||
private readonly Action<T> _onNext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AnonymousObserver{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="onNext">The callback for next events.</param>
|
||||
public AnonymousObserver(Action<T> onNext) => _onNext = onNext;
|
||||
|
||||
/// <summary>
|
||||
/// Handles completion.
|
||||
/// </summary>
|
||||
public void OnCompleted() { }
|
||||
|
||||
/// <summary>
|
||||
/// Handles an observable error.
|
||||
/// </summary>
|
||||
/// <param name="error">The observed error.</param>
|
||||
public void OnError(Exception error) { }
|
||||
|
||||
/// <summary>
|
||||
/// Handles the next value.
|
||||
/// </summary>
|
||||
/// <param name="value">The observed value.</param>
|
||||
public void OnNext(T value) => _onNext(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,18 @@ public class CircularReferenceTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CircularReferenceTests"/> class.
|
||||
/// </summary>
|
||||
public CircularReferenceTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_circular_test_{Guid.NewGuid()}");
|
||||
_context = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_context?.Dispose();
|
||||
@@ -39,6 +45,9 @@ public class CircularReferenceTests : IDisposable
|
||||
// Self-Reference Tests (Employee hierarchy with ObjectId references)
|
||||
// ========================================
|
||||
|
||||
/// <summary>
|
||||
/// Executes SelfReference_InsertAndQuery_ShouldWork.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SelfReference_InsertAndQuery_ShouldWork()
|
||||
{
|
||||
@@ -115,6 +124,9 @@ public class CircularReferenceTests : IDisposable
|
||||
(queriedDeveloper.DirectReportIds ?? new List<ObjectId>()).ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes SelfReference_UpdateDirectReports_ShouldPersist.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SelfReference_UpdateDirectReports_ShouldPersist()
|
||||
{
|
||||
@@ -164,6 +176,9 @@ public class CircularReferenceTests : IDisposable
|
||||
queried.DirectReportIds.ShouldContain(employee2Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes SelfReference_QueryByManagerId_ShouldWork.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SelfReference_QueryByManagerId_ShouldWork()
|
||||
{
|
||||
@@ -214,6 +229,9 @@ public class CircularReferenceTests : IDisposable
|
||||
// BEST PRACTICE for document databases
|
||||
// ========================================
|
||||
|
||||
/// <summary>
|
||||
/// Executes NtoNReferencing_InsertAndQuery_ShouldWork.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void NtoNReferencing_InsertAndQuery_ShouldWork()
|
||||
{
|
||||
@@ -279,6 +297,9 @@ public class CircularReferenceTests : IDisposable
|
||||
queriedProduct.CategoryIds.ShouldContain(categoryId2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes NtoNReferencing_UpdateRelationships_ShouldPersist.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void NtoNReferencing_UpdateRelationships_ShouldPersist()
|
||||
{
|
||||
@@ -336,6 +357,9 @@ public class CircularReferenceTests : IDisposable
|
||||
queriedProduct2.CategoryIds.ShouldContain(categoryId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes NtoNReferencing_DocumentSize_RemainSmall.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void NtoNReferencing_DocumentSize_RemainSmall()
|
||||
{
|
||||
@@ -365,6 +389,9 @@ public class CircularReferenceTests : IDisposable
|
||||
// This demonstrates why referencing is preferred for large N-N relationships
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes NtoNReferencing_QueryByProductId_ShouldWork.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void NtoNReferencing_QueryByProductId_ShouldWork()
|
||||
{
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CollectionIndexManagerAndDefinitionTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests find best index should prefer unique index.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindBestIndex_Should_Prefer_Unique_Index()
|
||||
{
|
||||
@@ -32,6 +35,9 @@ public class CollectionIndexManagerAndDefinitionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests find best compound index should choose longest prefix.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindBestCompoundIndex_Should_Choose_Longest_Prefix()
|
||||
{
|
||||
@@ -69,6 +75,9 @@ public class CollectionIndexManagerAndDefinitionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests drop index should remove metadata and be idempotent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DropIndex_Should_Remove_Metadata_And_Be_Idempotent()
|
||||
{
|
||||
@@ -97,6 +106,9 @@ public class CollectionIndexManagerAndDefinitionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests collection index definition should respect query support rules.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CollectionIndexDefinition_Should_Respect_Query_Support_Rules()
|
||||
{
|
||||
@@ -116,6 +128,9 @@ public class CollectionIndexManagerAndDefinitionTests
|
||||
definition.ToString().ShouldContain("Name");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests collection index info to string should include diagnostics.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CollectionIndexInfo_ToString_Should_Include_Diagnostics()
|
||||
{
|
||||
|
||||
@@ -6,6 +6,10 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompactionCrashRecoveryTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies compaction resumes from marker phases and preserves data.
|
||||
/// </summary>
|
||||
/// <param name="phase">The crash marker phase to resume from.</param>
|
||||
[Theory]
|
||||
[InlineData("Started")]
|
||||
[InlineData("Copied")]
|
||||
@@ -49,6 +53,9 @@ public class CompactionCrashRecoveryTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies corrupted compaction markers are recovered deterministically.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ResumeCompaction_WithCorruptedMarker_ShouldRecoverDeterministically()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompactionOfflineTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests offline compact should preserve logical data equivalence.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_ShouldPreserveLogicalDataEquivalence()
|
||||
{
|
||||
@@ -72,6 +75,9 @@ public class CompactionOfflineTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact should keep index results consistent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_ShouldKeepIndexResultsConsistent()
|
||||
{
|
||||
@@ -127,6 +133,83 @@ public class CompactionOfflineTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact should rebuild hash index metadata and preserve results.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_ShouldRebuildHashIndexMetadataAndPreserveResults()
|
||||
{
|
||||
var dbPath = NewDbPath();
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new TestDbContext(dbPath);
|
||||
|
||||
for (var i = 0; i < 300; i++)
|
||||
{
|
||||
db.People.Insert(new Person
|
||||
{
|
||||
Name = $"hash-person-{i:D4}",
|
||||
Age = i % 12
|
||||
});
|
||||
}
|
||||
|
||||
db.SaveChanges();
|
||||
db.ForceCheckpoint();
|
||||
|
||||
var expectedByAge = db.People.FindAll()
|
||||
.GroupBy(p => p.Age)
|
||||
.ToDictionary(g => g.Key, g => g.Select(x => x.Name).OrderBy(x => x).ToArray());
|
||||
|
||||
var metadata = db.Storage.GetCollectionMetadata("people_collection");
|
||||
metadata.ShouldNotBeNull();
|
||||
|
||||
var targetIndex = metadata!.Indexes
|
||||
.FirstOrDefault(index => index.PropertyPaths.Any(path => path.Equals("Age", StringComparison.OrdinalIgnoreCase)));
|
||||
targetIndex.ShouldNotBeNull();
|
||||
|
||||
targetIndex!.Type = IndexType.Hash;
|
||||
db.Storage.SaveCollectionMetadata(metadata);
|
||||
db.SaveChanges();
|
||||
|
||||
var stats = db.Compact(new CompactionOptions
|
||||
{
|
||||
DefragmentSlottedPages = true,
|
||||
NormalizeFreeList = true,
|
||||
EnableTailTruncation = true
|
||||
});
|
||||
stats.PrePageCount.ShouldBeGreaterThanOrEqualTo(stats.PostPageCount);
|
||||
|
||||
var reloadedMetadata = db.Storage.GetCollectionMetadata("people_collection");
|
||||
reloadedMetadata.ShouldNotBeNull();
|
||||
var rebuiltIndex = reloadedMetadata!.Indexes.FirstOrDefault(index => index.Name == targetIndex.Name);
|
||||
rebuiltIndex.ShouldNotBeNull();
|
||||
rebuiltIndex!.Type.ShouldBe(IndexType.Hash);
|
||||
rebuiltIndex.RootPageId.ShouldBeGreaterThan(0u);
|
||||
|
||||
var runtimeIndex = db.People.GetIndexes().FirstOrDefault(index => index.Name == targetIndex.Name);
|
||||
runtimeIndex.ShouldNotBeNull();
|
||||
runtimeIndex!.Type.ShouldBe(IndexType.Hash);
|
||||
|
||||
foreach (var age in expectedByAge.Keys.OrderBy(x => x))
|
||||
{
|
||||
var actual = db.People.FindAll(p => p.Age == age)
|
||||
.Select(x => x.Name)
|
||||
.OrderBy(x => x)
|
||||
.ToArray();
|
||||
|
||||
actual.ShouldBe(expectedByAge[age]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupFiles(dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact when tail is reclaimable should reduce file size.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_WhenTailIsReclaimable_ShouldReduceFileSize()
|
||||
{
|
||||
@@ -178,6 +261,9 @@ public class CompactionOfflineTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact with invalid primary root metadata should fail validation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_WithInvalidPrimaryRootMetadata_ShouldFailValidation()
|
||||
{
|
||||
@@ -208,6 +294,9 @@ public class CompactionOfflineTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact with invalid secondary root metadata should fail validation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_WithInvalidSecondaryRootMetadata_ShouldFailValidation()
|
||||
{
|
||||
@@ -239,6 +328,9 @@ public class CompactionOfflineTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact should report live bytes relocation and throughput telemetry.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_ShouldReportLiveBytesRelocationAndThroughputTelemetry()
|
||||
{
|
||||
@@ -290,6 +382,9 @@ public class CompactionOfflineTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests offline compact when primary index points to deleted slot should fail validation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_WhenPrimaryIndexPointsToDeletedSlot_ShouldFailValidation()
|
||||
{
|
||||
|
||||
@@ -6,6 +6,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompactionOnlineConcurrencyTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies online compaction completes without deadlock under concurrent workload.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task OnlineCompaction_WithConcurrentishWorkload_ShouldCompleteWithoutDeadlock()
|
||||
{
|
||||
|
||||
@@ -6,6 +6,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompactionWalCoordinationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies offline compaction checkpoints and leaves the WAL empty.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OfflineCompact_ShouldCheckpointAndLeaveWalEmpty()
|
||||
{
|
||||
@@ -42,6 +45,9 @@ public class CompactionWalCoordinationTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies compaction after WAL recovery preserves durable data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Compact_AfterWalRecovery_ShouldKeepDataDurable()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompressionCompatibilityTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies opening legacy uncompressed files with compression enabled does not mutate database bytes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OpeningLegacyUncompressedFile_WithCompressionEnabled_ShouldNotMutateDbFile()
|
||||
{
|
||||
@@ -56,6 +59,9 @@ public class CompressionCompatibilityTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies mixed compressed and uncompressed documents remain readable after partial migration.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MixedFormatDocuments_ShouldRemainReadableAfterPartialMigration()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompressionCorruptionTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies corrupted compressed payload checksum triggers invalid data errors.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithBadChecksum_ShouldThrowInvalidData()
|
||||
{
|
||||
@@ -34,6 +37,9 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies invalid original length metadata triggers invalid data errors.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithBadOriginalLength_ShouldThrowInvalidData()
|
||||
{
|
||||
@@ -57,6 +63,9 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies oversized declared decompressed length enforces safety guardrails.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithOversizedDeclaredLength_ShouldEnforceGuardrail()
|
||||
{
|
||||
@@ -81,6 +90,9 @@ public class CompressionCorruptionTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies invalid codec identifiers in compressed headers trigger invalid data errors.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Read_WithInvalidCodecId_ShouldThrowInvalidData()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompressionInsertReadTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests insert with threshold should store mixed compressed and uncompressed slots.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_WithThreshold_ShouldStoreMixedCompressedAndUncompressedSlots()
|
||||
{
|
||||
@@ -46,6 +49,9 @@ public class CompressionInsertReadTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests find by id should read mixed compressed and uncompressed documents.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindById_ShouldReadMixedCompressedAndUncompressedDocuments()
|
||||
{
|
||||
@@ -91,6 +97,9 @@ public class CompressionInsertReadTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests insert when codec throws should fallback to uncompressed storage.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_WhenCodecThrows_ShouldFallbackToUncompressedStorage()
|
||||
{
|
||||
@@ -186,11 +195,25 @@ public class CompressionInsertReadTests
|
||||
|
||||
private sealed class FailingBrotliCodec : ICompressionCodec
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the codec.
|
||||
/// </summary>
|
||||
public CompressionCodec Codec => CompressionCodec.Brotli;
|
||||
|
||||
/// <summary>
|
||||
/// 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.");
|
||||
|
||||
/// <summary>
|
||||
/// 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.");
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class CompressionOverflowTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests insert compressed document spanning overflow pages should round trip.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_CompressedDocumentSpanningOverflowPages_ShouldRoundTrip()
|
||||
{
|
||||
@@ -43,6 +46,9 @@ public class CompressionOverflowTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests update should transition across compression thresholds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_ShouldTransitionAcrossCompressionThresholds()
|
||||
{
|
||||
|
||||
@@ -11,6 +11,9 @@ public class CursorTests : IDisposable
|
||||
private readonly StorageEngine _storage;
|
||||
private readonly BTreeIndex _index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CursorTests"/> class.
|
||||
/// </summary>
|
||||
public CursorTests()
|
||||
{
|
||||
_testFile = Path.Combine(Path.GetTempPath(), $"docdb_cursor_test_{Guid.NewGuid()}.db");
|
||||
@@ -34,6 +37,9 @@ public class CursorTests : IDisposable
|
||||
_storage.CommitTransaction(txnId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests move to first should position at first.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MoveToFirst_ShouldPositionAtFirst()
|
||||
{
|
||||
@@ -42,6 +48,9 @@ public class CursorTests : IDisposable
|
||||
cursor.Current.Key.ShouldBe(IndexKey.Create(10));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests move to last should position at last.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MoveToLast_ShouldPositionAtLast()
|
||||
{
|
||||
@@ -50,6 +59,9 @@ public class CursorTests : IDisposable
|
||||
cursor.Current.Key.ShouldBe(IndexKey.Create(30));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests move next should traverse forward.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MoveNext_ShouldTraverseForward()
|
||||
{
|
||||
@@ -65,6 +77,9 @@ public class CursorTests : IDisposable
|
||||
cursor.MoveNext().ShouldBeFalse(); // End
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests move prev should traverse backward.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MovePrev_ShouldTraverseBackward()
|
||||
{
|
||||
@@ -80,6 +95,9 @@ public class CursorTests : IDisposable
|
||||
cursor.MovePrev().ShouldBeFalse(); // Start
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests seek should position exact or next.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Seek_ShouldPositionExact_OrNext()
|
||||
{
|
||||
@@ -99,6 +117,9 @@ public class CursorTests : IDisposable
|
||||
Should.Throw<InvalidOperationException>(() => cursor.Current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_storage.Dispose();
|
||||
|
||||
@@ -11,18 +11,27 @@ public class DbContextInheritanceTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestExtendedDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DbContextInheritanceTests"/> class.
|
||||
/// </summary>
|
||||
public DbContextInheritanceTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_inheritance_{Guid.NewGuid()}.db");
|
||||
_db = new Shared.TestExtendedDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies parent collections are initialized in the extended context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Should_Initialize_Parent_Collections()
|
||||
{
|
||||
@@ -35,6 +44,9 @@ public class DbContextInheritanceTests : IDisposable
|
||||
_db.TestDocuments.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies extended context collections are initialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Should_Initialize_Own_Collections()
|
||||
{
|
||||
@@ -42,6 +54,9 @@ public class DbContextInheritanceTests : IDisposable
|
||||
_db.ExtendedEntities.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies parent collections are usable from the extended context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Can_Use_Parent_Collections()
|
||||
{
|
||||
@@ -57,6 +72,9 @@ public class DbContextInheritanceTests : IDisposable
|
||||
retrieved.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies extended collections are usable from the extended context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Can_Use_Own_Collections()
|
||||
{
|
||||
@@ -76,6 +94,9 @@ public class DbContextInheritanceTests : IDisposable
|
||||
retrieved.Description.ShouldBe("Test Extended Entity");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies parent and extended collections can be used together.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Can_Use_Both_Parent_And_Own_Collections()
|
||||
{
|
||||
|
||||
@@ -12,11 +12,17 @@ public class DbContextTests : IDisposable
|
||||
{
|
||||
private string _dbPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes test file paths for database context tests.
|
||||
/// </summary>
|
||||
public DbContextTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_dbcontext_{Guid.NewGuid()}.db");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the basic database context lifecycle works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_BasicLifecycle_Works()
|
||||
{
|
||||
@@ -31,6 +37,9 @@ public class DbContextTests : IDisposable
|
||||
found.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies multiple CRUD operations execute correctly in one context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_MultipleOperations_Work()
|
||||
{
|
||||
@@ -59,6 +68,9 @@ public class DbContextTests : IDisposable
|
||||
db.Users.Count().ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies disposing and reopening context preserves persisted data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_Dispose_ReleasesResources()
|
||||
{
|
||||
@@ -90,6 +102,9 @@ public class DbContextTests : IDisposable
|
||||
return Convert.ToHexString(sha256.ComputeHash(stream));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies database file size and content change after insert and checkpoint.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DatabaseFile_SizeAndContent_ChangeAfterInsert()
|
||||
{
|
||||
@@ -117,6 +132,9 @@ public class DbContextTests : IDisposable
|
||||
afterInsertHash.ShouldNotBe(initialHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the WAL file path is auto-derived from database path.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_AutoDerivesWalPath()
|
||||
{
|
||||
@@ -127,6 +145,9 @@ public class DbContextTests : IDisposable
|
||||
File.Exists(walPath).ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies custom page file and compression options support roundtrip data access.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_WithCustomPageFileAndCompressionOptions_ShouldSupportRoundTrip()
|
||||
{
|
||||
@@ -165,6 +186,9 @@ public class DbContextTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies compact API returns stats and preserves data consistency.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_CompactApi_ShouldReturnStatsAndPreserveData()
|
||||
{
|
||||
@@ -197,6 +221,9 @@ public class DbContextTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and cleans up generated files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
|
||||
@@ -9,6 +9,9 @@ public class DictionaryPageTests
|
||||
{
|
||||
private const int PageSize = 16384;
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary page initialization sets expected defaults.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Initialize_ShouldSetupEmptyPage()
|
||||
{
|
||||
@@ -26,6 +29,9 @@ public class DictionaryPageTests
|
||||
freeSpaceEnd.ShouldBe((ushort)PageSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies insert adds entries and keeps them ordered.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_ShouldAddEntryAndSort()
|
||||
{
|
||||
@@ -58,6 +64,9 @@ public class DictionaryPageTests
|
||||
entries[2].Value.ShouldBe((ushort)30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies key lookup returns the expected value.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TryFind_ShouldReturnCorrectValue()
|
||||
{
|
||||
@@ -76,6 +85,9 @@ public class DictionaryPageTests
|
||||
found.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserts fail when the page is full.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Overflow_ShouldReturnFalse_WhenFull()
|
||||
{
|
||||
@@ -105,6 +117,9 @@ public class DictionaryPageTests
|
||||
inserted.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies global lookup finds keys across chained dictionary pages.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Chaining_ShouldFindKeysInLinkedPages()
|
||||
{
|
||||
@@ -158,6 +173,9 @@ public class DictionaryPageTests
|
||||
if (File.Exists(Path.ChangeExtension(dbPath, ".wal"))) File.Delete(Path.ChangeExtension(dbPath, ".wal"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies global enumeration returns keys across chained dictionary pages.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindAllGlobal_ShouldRetrieveAllKeys()
|
||||
{
|
||||
|
||||
@@ -13,12 +13,18 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly StorageEngine _storage;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DictionaryPersistenceTests"/> class.
|
||||
/// </summary>
|
||||
public DictionaryPersistenceTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_dict_{Guid.NewGuid():N}.db");
|
||||
_storage = new StorageEngine(_dbPath, PageFileConfig.Default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_storage.Dispose();
|
||||
@@ -32,22 +38,41 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
private readonly string _collectionName;
|
||||
private readonly List<string> _keys;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MockMapper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The collection name.</param>
|
||||
/// <param name="keys">The mapper keys.</param>
|
||||
public MockMapper(string name, params string[] keys)
|
||||
{
|
||||
_collectionName = name;
|
||||
_keys = keys.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string CollectionName => _collectionName;
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<string> UsedKeys => _keys;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BsonSchema GetSchema() => new BsonSchema { Title = _collectionName };
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ObjectId GetId(Dictionary<string, object> entity) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetId(Dictionary<string, object> entity, ObjectId id) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Serialize(Dictionary<string, object> entity, BsonSpanWriter writer) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Dictionary<string, object> Deserialize(BsonSpanReader reader) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies mapper registration adds all unique dictionary keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RegisterMappers_Registers_All_Unique_Keys()
|
||||
{
|
||||
@@ -73,6 +98,9 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
ids.Count.ShouldBe(4);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary keys persist across storage restarts.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Dictionary_Keys_Persist_Across_Restarts()
|
||||
{
|
||||
@@ -93,7 +121,10 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
|
||||
private class NestedMockMapper : DocumentMapperBase<ObjectId, object>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string CollectionName => "Nested";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override BsonSchema GetSchema()
|
||||
{
|
||||
var schema = new BsonSchema { Title = "Nested" };
|
||||
@@ -109,12 +140,22 @@ public class DictionaryPersistenceTests : IDisposable
|
||||
return schema;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ObjectId GetId(object entity) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetId(object entity, ObjectId id) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Serialize(object entity, BsonSpanWriter writer) => throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Deserialize(BsonSpanReader reader) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies nested schema fields are registered as dictionary keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RegisterMappers_Handles_Nested_Keys()
|
||||
{
|
||||
|
||||
@@ -14,6 +14,9 @@ public class DocumentCollectionDeleteTests : IDisposable
|
||||
private readonly string _walPath;
|
||||
private readonly Shared.TestDbContext _dbContext;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentCollectionDeleteTests"/> class.
|
||||
/// </summary>
|
||||
public DocumentCollectionDeleteTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_delete_{Guid.NewGuid()}.db");
|
||||
@@ -22,11 +25,17 @@ public class DocumentCollectionDeleteTests : IDisposable
|
||||
_dbContext = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_dbContext.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies delete removes both the document and its index entry.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Delete_RemovesDocumentAndIndexEntry()
|
||||
{
|
||||
@@ -52,6 +61,9 @@ public class DocumentCollectionDeleteTests : IDisposable
|
||||
all.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies delete returns false for a non-existent document.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Delete_NonExistent_ReturnsFalse()
|
||||
{
|
||||
@@ -61,6 +73,9 @@ public class DocumentCollectionDeleteTests : IDisposable
|
||||
deleted.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies deletes inside a transaction commit successfully.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Delete_WithTransaction_CommitsSuccessfully()
|
||||
{
|
||||
|
||||
@@ -8,12 +8,18 @@ public class DocumentCollectionIndexApiTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentCollectionIndexApiTests"/> class.
|
||||
/// </summary>
|
||||
public DocumentCollectionIndexApiTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"collection_index_api_{Guid.NewGuid():N}.db");
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies vector index creation and deletion behavior.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CreateVectorIndex_And_DropIndex_Should_Work()
|
||||
{
|
||||
@@ -32,6 +38,9 @@ public class DocumentCollectionIndexApiTests : IDisposable
|
||||
_db.VectorItems.GetIndexes().Select(x => x.Name).ShouldNotContain("idx_vector_extra");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies ensure-index returns existing indexes when already present.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EnsureIndex_Should_Return_Existing_Index_When_Already_Present()
|
||||
{
|
||||
@@ -41,12 +50,18 @@ public class DocumentCollectionIndexApiTests : IDisposable
|
||||
ReferenceEquals(first, second).ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dropping the primary index name is rejected.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DropIndex_Should_Reject_Primary_Index_Name()
|
||||
{
|
||||
Should.Throw<InvalidOperationException>(() => _db.People.DropIndex("_id"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
|
||||
@@ -13,6 +13,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
private readonly string _walPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentCollectionTests"/> class.
|
||||
/// </summary>
|
||||
public DocumentCollectionTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_collection_{Guid.NewGuid()}.db");
|
||||
@@ -21,6 +24,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies insert and find-by-id operations.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_And_FindById_Works()
|
||||
{
|
||||
@@ -39,6 +45,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
found.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies find-by-id returns null when no document is found.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindById_Returns_Null_When_Not_Found()
|
||||
{
|
||||
@@ -49,6 +58,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
found.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies find-all returns all entities.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindAll_Returns_All_Entities()
|
||||
{
|
||||
@@ -68,6 +80,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
all.ShouldContain(u => u.Name == "Charlie");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies update modifies an existing entity.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_Modifies_Entity()
|
||||
{
|
||||
@@ -89,6 +104,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
found.Age.ShouldBe(31);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies update returns false when the entity does not exist.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_Returns_False_When_Not_Found()
|
||||
{
|
||||
@@ -103,6 +121,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
updated.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies delete removes an entity.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Delete_Removes_Entity()
|
||||
{
|
||||
@@ -120,6 +141,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
_db.Users.FindById(id).ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies delete returns false when the entity does not exist.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Delete_Returns_False_When_Not_Found()
|
||||
{
|
||||
@@ -131,6 +155,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
deleted.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies count returns the correct entity count.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Count_Returns_Correct_Count()
|
||||
{
|
||||
@@ -146,6 +173,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
count.ShouldBe(2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies predicate queries filter entities correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Find_With_Predicate_Filters_Correctly()
|
||||
{
|
||||
@@ -163,6 +193,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
over30[0].Name.ShouldBe("Charlie");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies bulk insert stores multiple entities.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void InsertBulk_Inserts_Multiple_Entities()
|
||||
{
|
||||
@@ -183,6 +216,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
_db.Users.Count().ShouldBe(3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserts preserve an explicitly assigned identifier.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_With_SpecifiedId_RetainsId()
|
||||
{
|
||||
@@ -203,6 +239,9 @@ public class DocumentCollectionTests : IDisposable
|
||||
found.Name.ShouldBe("SpecifiedID");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db?.Dispose();
|
||||
|
||||
@@ -16,6 +16,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DocumentOverflowTests"/> class.
|
||||
/// </summary>
|
||||
public DocumentOverflowTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_overflow_{Guid.NewGuid()}.db");
|
||||
@@ -23,12 +26,18 @@ public class DocumentOverflowTests : IDisposable
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserting a medium-sized document succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_MediumDoc_64KB_ShouldSucceed()
|
||||
{
|
||||
@@ -50,6 +59,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
retrieved.Name.ShouldBe(largeString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserting a large document succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_LargeDoc_100KB_ShouldSucceed()
|
||||
{
|
||||
@@ -70,6 +82,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
retrieved.Name.ShouldBe(largeString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inserting a very large document succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_HugeDoc_3MB_ShouldSucceed()
|
||||
{
|
||||
@@ -93,6 +108,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
retrieved.Name.Substring(retrieved.Name.Length - 100).ShouldBe(largeString.Substring(largeString.Length - 100));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies updating from a small payload to a huge payload succeeds.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_SmallToHuge_ShouldSucceed()
|
||||
{
|
||||
@@ -114,6 +132,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
retrieved.Name.Length.ShouldBe(hugeString.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies bulk inserts with mixed payload sizes succeed.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void InsertBulk_MixedSizes_ShouldSucceed()
|
||||
{
|
||||
@@ -136,6 +157,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies huge inserts succeed with compression enabled and small page configuration.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_HugeDoc_WithCompressionEnabledAndSmallPages_ShouldSucceed()
|
||||
{
|
||||
@@ -172,6 +196,9 @@ public class DocumentOverflowTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies updates from huge to small payloads succeed with compression enabled.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Update_HugeToSmall_WithCompressionEnabled_ShouldSucceed()
|
||||
{
|
||||
|
||||
@@ -7,12 +7,18 @@ public class GeospatialStressTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes database state for geospatial stress tests.
|
||||
/// </summary>
|
||||
public GeospatialStressTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"geo_stress_{Guid.NewGuid():N}.db");
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies spatial index handles node splits and query operations under load.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SpatialIndex_Should_Handle_Node_Splits_And_Queries()
|
||||
{
|
||||
@@ -40,6 +46,9 @@ public class GeospatialStressTests : IDisposable
|
||||
near.Count.ShouldBeGreaterThan(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes generated files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
|
||||
@@ -12,12 +12,18 @@ public class GeospatialTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GeospatialTests"/> class.
|
||||
/// </summary>
|
||||
public GeospatialTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_geo_{Guid.NewGuid()}.db");
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies spatial within queries return expected results.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Can_Insert_And_Search_Within()
|
||||
{
|
||||
@@ -38,6 +44,9 @@ public class GeospatialTests : IDisposable
|
||||
results.ShouldContain(r => r.Name == "Point 2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies near queries return expected proximity results.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Can_Search_Near_Proximity()
|
||||
{
|
||||
@@ -59,6 +68,9 @@ public class GeospatialTests : IDisposable
|
||||
results.ShouldNotContain(r => r.Name == "New York Office");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies LINQ near integration returns expected results.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LINQ_Integration_Near_Works()
|
||||
{
|
||||
@@ -76,6 +88,9 @@ public class GeospatialTests : IDisposable
|
||||
results[0].Name.ShouldBe("Milan Office");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies LINQ within integration returns expected results.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LINQ_Integration_Within_Works()
|
||||
{
|
||||
@@ -94,6 +109,9 @@ public class GeospatialTests : IDisposable
|
||||
results[0].Name.ShouldBe("Milan Office");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class HashIndexTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes Insert_And_TryFind_Should_Return_Location.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Insert_And_TryFind_Should_Return_Location()
|
||||
{
|
||||
@@ -19,6 +22,9 @@ public class HashIndexTests
|
||||
found.SlotIndex.ShouldBe(location.SlotIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Unique_HashIndex_Should_Throw_On_Duplicate_Key.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Unique_HashIndex_Should_Throw_On_Duplicate_Key()
|
||||
{
|
||||
@@ -38,6 +44,9 @@ public class HashIndexTests
|
||||
index.Insert(key, new DocumentLocation(2, 2)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Remove_Should_Remove_Only_Matching_Entry.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Remove_Should_Remove_Only_Matching_Entry()
|
||||
{
|
||||
@@ -61,6 +70,9 @@ public class HashIndexTests
|
||||
index.FindAll(key).ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes FindAll_Should_Return_All_Matching_Entries.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void FindAll_Should_Return_All_Matching_Entries()
|
||||
{
|
||||
|
||||
@@ -14,6 +14,9 @@ public class IndexDirectionTests : IDisposable
|
||||
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes database state for index direction tests.
|
||||
/// </summary>
|
||||
public IndexDirectionTests()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
@@ -21,12 +24,18 @@ public class IndexDirectionTests : IDisposable
|
||||
// _db.Database.EnsureCreated(); // Not needed/doesn't exist? StorageEngine handles creation.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and deletes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies forward range scans return values in ascending order.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Range_Forward_ReturnsOrderedResults()
|
||||
{
|
||||
@@ -45,6 +54,9 @@ public class IndexDirectionTests : IDisposable
|
||||
collection.FindByLocation(results.Last())!.Age.ShouldBe(20); // Last is 20
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies backward range scans return values in descending order.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Range_Backward_ReturnsReverseOrderedResults()
|
||||
{
|
||||
@@ -63,6 +75,9 @@ public class IndexDirectionTests : IDisposable
|
||||
collection.FindByLocation(results.Last())!.Age.ShouldBe(10); // Last is 10
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies backward scans across split index pages return complete result sets.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Range_Backward_WithMultiplePages_ReturnsReverseOrderedResults()
|
||||
{
|
||||
|
||||
@@ -11,11 +11,23 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
{
|
||||
public class TestEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests optimizer identifies equality.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Optimizer_Identifies_Equality()
|
||||
{
|
||||
@@ -36,6 +48,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
result.IsRange.ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests optimizer identifies range greater than.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Optimizer_Identifies_Range_GreaterThan()
|
||||
{
|
||||
@@ -56,6 +71,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
result.IsRange.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests optimizer identifies range less than.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Optimizer_Identifies_Range_LessThan()
|
||||
{
|
||||
@@ -76,6 +94,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
result.IsRange.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests optimizer identifies range between simulated.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Optimizer_Identifies_Range_Between_Simulated()
|
||||
{
|
||||
@@ -96,6 +117,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
result.IsRange.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests optimizer identifies starts with.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Optimizer_Identifies_StartsWith()
|
||||
{
|
||||
@@ -117,6 +141,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
result.IsRange.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests optimizer ignores non indexed fields.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Optimizer_Ignores_NonIndexed_Fields()
|
||||
{
|
||||
|
||||
@@ -13,17 +13,26 @@ public class InsertBulkTests : IDisposable
|
||||
private readonly string _testFile;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InsertBulkTests"/> class.
|
||||
/// </summary>
|
||||
public InsertBulkTests()
|
||||
{
|
||||
_testFile = Path.GetTempFileName();
|
||||
_db = new Shared.TestDbContext(_testFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies bulk inserts are immediately persisted and visible.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void InsertBulk_PersistsData_ImmediatelyVisible()
|
||||
{
|
||||
@@ -41,6 +50,9 @@ public class InsertBulkTests : IDisposable
|
||||
insertedUsers.Count.ShouldBe(50);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies bulk inserts spanning multiple pages persist correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void InsertBulk_SpanningMultiplePages_PersistsCorrectly()
|
||||
{
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
private readonly string _testFile;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinqTests"/> class.
|
||||
/// </summary>
|
||||
public LinqTests()
|
||||
{
|
||||
_testFile = Path.Combine(Path.GetTempPath(), $"linq_tests_{Guid.NewGuid()}.db");
|
||||
@@ -35,6 +38,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
@@ -43,6 +49,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
if (File.Exists(wal)) File.Delete(wal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies where filters return matching documents.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Where_FiltersDocuments()
|
||||
{
|
||||
@@ -53,6 +62,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results.ShouldNotContain(d => d.Name == "Bob");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies order by returns sorted documents.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void OrderBy_SortsDocuments()
|
||||
{
|
||||
@@ -64,6 +76,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results.Last().Name.ShouldBe("Eve"); // 40
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies skip and take support pagination.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SkipTake_Pagination()
|
||||
{
|
||||
@@ -78,6 +93,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results[1].Name.ShouldBe("Alice"); // 30
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies select supports projections.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Select_Projections()
|
||||
{
|
||||
@@ -91,6 +109,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
names[0].ShouldBe("Dave");
|
||||
names[1].ShouldBe("Bob");
|
||||
}
|
||||
/// <summary>
|
||||
/// Verifies indexed where queries use index-backed filtering.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void IndexedWhere_UsedIndex()
|
||||
{
|
||||
@@ -104,6 +125,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results.ShouldNotContain(d => d.Name == "Bob"); // Age 25 (filtered out by strict >)
|
||||
results.ShouldNotContain(d => d.Name == "Dave"); // Age 20
|
||||
}
|
||||
/// <summary>
|
||||
/// Verifies starts-with predicates can use an index.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StartsWith_UsedIndex()
|
||||
{
|
||||
@@ -118,6 +142,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results[0].Name.ShouldBe("Charlie");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies range predicates can use an index.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Between_UsedIndex()
|
||||
{
|
||||
|
||||
@@ -7,6 +7,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class MaintenanceDiagnosticsAndMigrationTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies diagnostics APIs return page usage, compression, and fragmentation data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DiagnosticsApis_ShouldReturnPageUsageCompressionAndFragmentationData()
|
||||
{
|
||||
@@ -61,6 +64,9 @@ public class MaintenanceDiagnosticsAndMigrationTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies compression migration dry-run and apply modes return deterministic stats and preserve data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MigrateCompression_DryRunAndApply_ShouldReturnDeterministicStatsAndPreserveData()
|
||||
{
|
||||
|
||||
@@ -14,12 +14,18 @@ public class MetadataPersistenceTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly string _walPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MetadataPersistenceTests"/> class.
|
||||
/// </summary>
|
||||
public MetadataPersistenceTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"docdb_meta_{Guid.NewGuid()}.db");
|
||||
_walPath = Path.ChangeExtension(_dbPath, ".wal");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests index definitions are persisted and reloaded.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void IndexDefinitions_ArePersisted_AndReloaded()
|
||||
{
|
||||
@@ -59,6 +65,9 @@ public class MetadataPersistenceTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests ensure index does not recreate if index exists.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EnsureIndex_DoesNotRecreate_IfIndexExists()
|
||||
{
|
||||
@@ -91,6 +100,9 @@ public class MetadataPersistenceTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
|
||||
@@ -12,8 +12,17 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
|
||||
public class User
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
@@ -21,32 +30,62 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
|
||||
public class ComplexUser
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
[BsonId]
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
// Direct nested object
|
||||
/// <summary>
|
||||
/// Gets or sets the main address.
|
||||
/// </summary>
|
||||
public Address MainAddress { get; set; } = new();
|
||||
|
||||
// Collection of nested objects
|
||||
/// <summary>
|
||||
/// Gets or sets the other addresses.
|
||||
/// </summary>
|
||||
public List<Address> OtherAddresses { get; set; } = new();
|
||||
|
||||
// Primitive collection
|
||||
/// <summary>
|
||||
/// Gets or sets the tags.
|
||||
/// </summary>
|
||||
public List<string> Tags { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the secret.
|
||||
/// </summary>
|
||||
[BsonIgnore]
|
||||
public string Secret { get; set; } = "";
|
||||
}
|
||||
|
||||
public class Address
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the street.
|
||||
/// </summary>
|
||||
public string Street { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the city.
|
||||
/// </summary>
|
||||
public City City { get; set; } = new(); // Depth 2
|
||||
}
|
||||
|
||||
public class City
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the zip code.
|
||||
/// </summary>
|
||||
public string ZipCode { get; set; } = "";
|
||||
}
|
||||
|
||||
@@ -54,19 +93,37 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
|
||||
public class IntEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
|
||||
public class StringEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public required string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public string? Value { get; set; }
|
||||
}
|
||||
|
||||
public class GuidEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
|
||||
@@ -75,8 +132,14 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class CustomKeyEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the code.
|
||||
/// </summary>
|
||||
[System.ComponentModel.DataAnnotations.Key]
|
||||
public required string Code { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the description.
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
||||
@@ -84,121 +147,252 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
|
||||
public class AutoInitEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class Person
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
public class Product
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the price.
|
||||
/// </summary>
|
||||
public decimal Price { get; set; }
|
||||
}
|
||||
|
||||
public class AsyncDoc
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
public class SchemaUser
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the address.
|
||||
/// </summary>
|
||||
public Address Address { get; set; } = new();
|
||||
}
|
||||
|
||||
public class VectorEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the embedding.
|
||||
/// </summary>
|
||||
public float[] Embedding { get; set; } = Array.Empty<float>();
|
||||
}
|
||||
|
||||
public class GeoEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = "";
|
||||
/// <summary>
|
||||
/// Gets or sets the location.
|
||||
/// </summary>
|
||||
public (double Latitude, double Longitude) Location { get; set; }
|
||||
}
|
||||
|
||||
public record OrderId(string Value)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
public OrderId() : this(string.Empty) { }
|
||||
}
|
||||
|
||||
public class OrderIdConverter : ValueConverter<OrderId, string>
|
||||
{
|
||||
public override string ConvertToProvider(OrderId model) => model?.Value ?? string.Empty;
|
||||
public override OrderId ConvertFromProvider(string provider) => new OrderId(provider);
|
||||
/// <inheritdoc />
|
||||
public override string ConvertToProvider(OrderId model) => model?.Value ?? string.Empty;
|
||||
/// <inheritdoc />
|
||||
public override OrderId ConvertFromProvider(string provider) => new OrderId(provider);
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public OrderId Id { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the customer name.
|
||||
/// </summary>
|
||||
public string CustomerName { get; set; } = "";
|
||||
}
|
||||
|
||||
public class TestDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the category.
|
||||
/// </summary>
|
||||
public string Category { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the amount.
|
||||
/// </summary>
|
||||
public int Amount { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class OrderDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the item name.
|
||||
/// </summary>
|
||||
public string ItemName { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the quantity.
|
||||
/// </summary>
|
||||
public int Quantity { get; set; }
|
||||
}
|
||||
|
||||
public class OrderItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the price.
|
||||
/// </summary>
|
||||
public int Price { get; set; }
|
||||
}
|
||||
|
||||
public class ComplexDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the shipping address.
|
||||
/// </summary>
|
||||
public Address ShippingAddress { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the items.
|
||||
/// </summary>
|
||||
public List<OrderItem> Items { get; set; } = new();
|
||||
}
|
||||
|
||||
[Table("custom_users", Schema = "test")]
|
||||
public class AnnotatedUser
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
[Key]
|
||||
public ObjectId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Column("display_name")]
|
||||
[StringLength(50, MinimumLength = 3)]
|
||||
public string Name { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
[Range(0, 150)]
|
||||
public int Age { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the computed info.
|
||||
/// </summary>
|
||||
[NotMapped]
|
||||
public string ComputedInfo => $"{Name} ({Age})";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the location.
|
||||
/// </summary>
|
||||
[Column(TypeName = "geopoint")]
|
||||
public (double Lat, double Lon) Location { get; set; }
|
||||
}
|
||||
public class PersonV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
@@ -207,8 +401,17 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class ExtendedEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the description.
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the created at.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -219,7 +422,13 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class BaseEntityWithId
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the created at.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
|
||||
@@ -228,7 +437,13 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class DerivedEntity : BaseEntityWithId
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the description.
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -237,14 +452,35 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class EntityWithComputedProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the first name.
|
||||
/// </summary>
|
||||
public string FirstName { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the last name.
|
||||
/// </summary>
|
||||
public string LastName { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the birth year.
|
||||
/// </summary>
|
||||
public int BirthYear { get; set; }
|
||||
|
||||
// Computed properties - should NOT be serialized
|
||||
/// <summary>
|
||||
/// Gets the full name.
|
||||
/// </summary>
|
||||
public string FullName => $"{FirstName} {LastName}";
|
||||
/// <summary>
|
||||
/// Gets the age.
|
||||
/// </summary>
|
||||
public int Age => DateTime.Now.Year - BirthYear;
|
||||
/// <summary>
|
||||
/// Gets the display info.
|
||||
/// </summary>
|
||||
public string DisplayInfo => $"{FullName} (Age: {Age})";
|
||||
}
|
||||
|
||||
@@ -253,18 +489,45 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class EntityWithAdvancedCollections
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
// Various collection types that should all be recognized
|
||||
/// <summary>
|
||||
/// Gets or sets the tags.
|
||||
/// </summary>
|
||||
public HashSet<string> Tags { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the numbers.
|
||||
/// </summary>
|
||||
public ISet<int> Numbers { get; set; } = new HashSet<int>();
|
||||
/// <summary>
|
||||
/// Gets or sets the history.
|
||||
/// </summary>
|
||||
public LinkedList<string> History { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the pending items.
|
||||
/// </summary>
|
||||
public Queue<string> PendingItems { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the undo stack.
|
||||
/// </summary>
|
||||
public Stack<string> UndoStack { get; set; } = new();
|
||||
|
||||
// Nested objects in collections
|
||||
/// <summary>
|
||||
/// Gets or sets the addresses.
|
||||
/// </summary>
|
||||
public HashSet<Address> Addresses { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the favorite cities.
|
||||
/// </summary>
|
||||
public ISet<City> FavoriteCities { get; set; } = new HashSet<City>();
|
||||
}
|
||||
|
||||
@@ -273,13 +536,30 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class EntityWithPrivateSetters
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the created at.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; private set; }
|
||||
|
||||
// Factory method for creation
|
||||
public static EntityWithPrivateSetters Create(string name, int age)
|
||||
/// <summary>
|
||||
/// Executes the create operation.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="age">The age.</param>
|
||||
public static EntityWithPrivateSetters Create(string name, int age)
|
||||
{
|
||||
return new EntityWithPrivateSetters
|
||||
{
|
||||
@@ -296,9 +576,21 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class EntityWithInitSetters
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; init; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
/// <summary>
|
||||
/// Gets or sets the age.
|
||||
/// </summary>
|
||||
public int Age { get; init; }
|
||||
/// <summary>
|
||||
/// Gets or sets the created at.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; init; }
|
||||
}
|
||||
|
||||
@@ -313,10 +605,25 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class Employee
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the department.
|
||||
/// </summary>
|
||||
public string Department { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the manager id.
|
||||
/// </summary>
|
||||
public ObjectId? ManagerId { get; set; } // Reference to manager
|
||||
/// <summary>
|
||||
/// Gets or sets the direct report ids.
|
||||
/// </summary>
|
||||
public List<ObjectId>? DirectReportIds { get; set; } // References to direct reports (best practice)
|
||||
}
|
||||
|
||||
@@ -327,9 +634,21 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class CategoryRef
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the description.
|
||||
/// </summary>
|
||||
public string Description { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the product ids.
|
||||
/// </summary>
|
||||
public List<ObjectId>? ProductIds { get; set; } // Only IDs - no embedding
|
||||
}
|
||||
|
||||
@@ -340,9 +659,21 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class ProductRef
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
public ObjectId Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the price.
|
||||
/// </summary>
|
||||
public decimal Price { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the category ids.
|
||||
/// </summary>
|
||||
public List<ObjectId>? CategoryIds { get; set; } // Only IDs - no embedding
|
||||
}
|
||||
|
||||
@@ -358,12 +689,22 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
where TId : IEquatable<TId>
|
||||
where TEntity : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
[System.ComponentModel.DataAnnotations.Key]
|
||||
public virtual TId? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
protected MockBaseEntity() { }
|
||||
|
||||
protected MockBaseEntity(TId? id)
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
protected MockBaseEntity(TId? id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
@@ -377,9 +718,16 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
public abstract class MockUuidEntity<TEntity> : MockBaseEntity<string, TEntity>
|
||||
where TEntity : class
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
protected MockUuidEntity() : base() { }
|
||||
|
||||
protected MockUuidEntity(string? id) : base(id) { }
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
protected MockUuidEntity(string? id) : base(id) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -388,11 +736,24 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class MockCounter : MockUuidEntity<MockCounter>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
public MockCounter() : base() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
public MockCounter(string? id) : base(id) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public int Value { get; set; }
|
||||
}
|
||||
|
||||
@@ -401,25 +762,58 @@ namespace ZB.MOM.WW.CBDD.Shared
|
||||
/// </summary>
|
||||
public class TemporalEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the id.
|
||||
/// </summary>
|
||||
[Key]
|
||||
public ObjectId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
// DateTime types
|
||||
/// <summary>
|
||||
/// Gets or sets the created at.
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the updated at.
|
||||
/// </summary>
|
||||
public DateTimeOffset UpdatedAt { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the last accessed at.
|
||||
/// </summary>
|
||||
public DateTimeOffset? LastAccessedAt { get; set; }
|
||||
|
||||
// TimeSpan
|
||||
/// <summary>
|
||||
/// Gets or sets the duration.
|
||||
/// </summary>
|
||||
public TimeSpan Duration { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the optional duration.
|
||||
/// </summary>
|
||||
public TimeSpan? OptionalDuration { get; set; }
|
||||
|
||||
// DateOnly and TimeOnly (.NET 6+)
|
||||
/// <summary>
|
||||
/// Gets or sets the birth date.
|
||||
/// </summary>
|
||||
public DateOnly BirthDate { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the anniversary.
|
||||
/// </summary>
|
||||
public DateOnly? Anniversary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the opening time.
|
||||
/// </summary>
|
||||
public TimeOnly OpeningTime { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the closing time.
|
||||
/// </summary>
|
||||
public TimeOnly? ClosingTime { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,16 +11,25 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
{
|
||||
private const string DbPath = "nullable_string_id.db";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NullableStringIdTests"/> class.
|
||||
/// </summary>
|
||||
public NullableStringIdTests()
|
||||
{
|
||||
if (File.Exists(DbPath)) File.Delete(DbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(DbPath)) File.Delete(DbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the mock counter collection is initialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MockCounter_Collection_IsInitialized()
|
||||
{
|
||||
@@ -30,6 +39,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
db.MockCounters.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies insert and find-by-id operations work for string identifiers.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MockCounter_Insert_And_FindById_Works()
|
||||
{
|
||||
@@ -52,6 +64,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
stored.Value.ShouldBe(42);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies update operations work for string identifiers.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MockCounter_Update_Works()
|
||||
{
|
||||
@@ -77,6 +92,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
updated.Value.ShouldBe(20);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies delete operations work for string identifiers.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MockCounter_Delete_Works()
|
||||
{
|
||||
@@ -99,6 +117,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
deleted.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies query operations work for string identifiers.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MockCounter_Query_Works()
|
||||
{
|
||||
@@ -121,6 +142,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
highValues[0].Name.ShouldBe("Second");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies inherited string identifiers are stored and retrieved correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MockCounter_InheritedId_IsStoredCorrectly()
|
||||
{
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class ObjectIdTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies new object identifiers are 12 bytes long.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void NewObjectId_ShouldCreate12ByteId()
|
||||
{
|
||||
@@ -16,6 +19,9 @@ public class ObjectIdTests
|
||||
bytes.Length.ShouldBe(12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies object identifiers round-trip from their binary form.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ObjectId_ShouldRoundTrip()
|
||||
{
|
||||
@@ -29,6 +35,9 @@ public class ObjectIdTests
|
||||
restored.ShouldBe(original);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies object identifier equality behavior.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ObjectId_Equals_ShouldWork()
|
||||
{
|
||||
@@ -40,6 +49,9 @@ public class ObjectIdTests
|
||||
oid3.ShouldNotBe(oid1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies object identifier timestamps are recent UTC values.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ObjectId_Timestamp_ShouldBeRecentUtc()
|
||||
{
|
||||
|
||||
@@ -12,16 +12,25 @@ public class PrimaryKeyTests : IDisposable
|
||||
{
|
||||
private readonly string _dbPath = "primary_key_tests.db";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PrimaryKeyTests"/> class.
|
||||
/// </summary>
|
||||
public PrimaryKeyTests()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Test_Int_PrimaryKey.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_Int_PrimaryKey()
|
||||
{
|
||||
@@ -46,6 +55,9 @@ public class PrimaryKeyTests : IDisposable
|
||||
db.IntEntities.FindById(1).ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Test_String_PrimaryKey.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_String_PrimaryKey()
|
||||
{
|
||||
@@ -65,6 +77,9 @@ public class PrimaryKeyTests : IDisposable
|
||||
db.StringEntities.FindById("key1").ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Test_Guid_PrimaryKey.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_Guid_PrimaryKey()
|
||||
{
|
||||
@@ -84,6 +99,9 @@ public class PrimaryKeyTests : IDisposable
|
||||
db.GuidEntities.FindById(id).ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Test_String_PrimaryKey_With_Custom_Name.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_String_PrimaryKey_With_Custom_Name()
|
||||
{
|
||||
|
||||
@@ -11,6 +11,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
private readonly StorageEngine _storage;
|
||||
private readonly BTreeIndex _index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QueryPrimitivesTests"/> class.
|
||||
/// </summary>
|
||||
public QueryPrimitivesTests()
|
||||
{
|
||||
_testFile = Path.Combine(Path.GetTempPath(), $"docdb_test_{Guid.NewGuid()}.db");
|
||||
@@ -55,6 +58,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
_index.Insert(key, new DocumentLocation(1, 1), txnId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Equal_ShouldFindExactMatch.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Equal_ShouldFindExactMatch()
|
||||
{
|
||||
@@ -65,6 +71,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[0].Key.ShouldBe(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Equal_ShouldReturnEmpty_WhenNotFound.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Equal_ShouldReturnEmpty_WhenNotFound()
|
||||
{
|
||||
@@ -74,6 +83,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes GreaterThan_ShouldReturnMatches.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GreaterThan_ShouldReturnMatches()
|
||||
{
|
||||
@@ -85,6 +97,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[1].Key.ShouldBe(IndexKey.Create(50));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes GreaterThanOrEqual_ShouldReturnMatches.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GreaterThanOrEqual_ShouldReturnMatches()
|
||||
{
|
||||
@@ -97,6 +112,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[2].Key.ShouldBe(IndexKey.Create(50));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes LessThan_ShouldReturnMatches.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LessThan_ShouldReturnMatches()
|
||||
{
|
||||
@@ -110,6 +128,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[1].Key.ShouldBe(IndexKey.Create(10));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Between_ShouldReturnRange.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Between_ShouldReturnRange()
|
||||
{
|
||||
@@ -123,6 +144,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[2].Key.ShouldBe(IndexKey.Create(40));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes StartsWith_ShouldReturnPrefixMatches.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StartsWith_ShouldReturnPrefixMatches()
|
||||
{
|
||||
@@ -133,6 +157,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[1].Key.ShouldBe(IndexKey.Create("ABC"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Like_ShouldSupportWildcards.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Like_ShouldSupportWildcards()
|
||||
{
|
||||
@@ -148,6 +175,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
// AB ok. ABC ok. B ok.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Like_Underscore_ShouldMatchSingleChar.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Like_Underscore_ShouldMatchSingleChar()
|
||||
{
|
||||
@@ -157,6 +187,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[0].Key.ShouldBe(IndexKey.Create("AB"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes In_ShouldReturnSpecificKeys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void In_ShouldReturnSpecificKeys()
|
||||
{
|
||||
@@ -169,6 +202,9 @@ public class QueryPrimitivesTests : IDisposable
|
||||
result[2].Key.ShouldBe(IndexKey.Create(50));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_storage.Dispose();
|
||||
|
||||
@@ -11,19 +11,43 @@ public class RobustnessTests
|
||||
{
|
||||
public struct Point
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the X.
|
||||
/// </summary>
|
||||
public int X { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Y.
|
||||
/// </summary>
|
||||
public int Y { get; set; }
|
||||
}
|
||||
|
||||
public class RobustEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the NullableInts.
|
||||
/// </summary>
|
||||
public List<int?> NullableInts { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the Map.
|
||||
/// </summary>
|
||||
public Dictionary<string, int> Map { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Gets or sets the EnumerableStrings.
|
||||
/// </summary>
|
||||
public IEnumerable<string> EnumerableStrings { get; set; } = Array.Empty<string>();
|
||||
/// <summary>
|
||||
/// Gets or sets the Location.
|
||||
/// </summary>
|
||||
public Point Location { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the NullableLocation.
|
||||
/// </summary>
|
||||
public Point? NullableLocation { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes GenerateSchema_RobustnessChecks.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_RobustnessChecks()
|
||||
{
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
private readonly string _testFile;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ScanTests"/> class.
|
||||
/// </summary>
|
||||
public ScanTests()
|
||||
{
|
||||
_testFile = Path.Combine(Path.GetTempPath(), $"scan_tests_{Guid.NewGuid()}.db");
|
||||
@@ -27,6 +30,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
_db = new Shared.TestDbContext(_testFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
@@ -35,6 +41,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
if (File.Exists(wal)) File.Delete(wal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Scan_FindsMatchingDocuments.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Scan_FindsMatchingDocuments()
|
||||
{
|
||||
@@ -53,6 +62,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results.ShouldContain(d => d.Name == "Charlie");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Repro_Insert_Loop_Hang.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Repro_Insert_Loop_Hang()
|
||||
{
|
||||
@@ -65,6 +77,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
_db.SaveChanges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes ParallelScan_FindsMatchingDocuments.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ParallelScan_FindsMatchingDocuments()
|
||||
{
|
||||
|
||||
@@ -17,18 +17,27 @@ public class SchemaPersistenceTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SchemaPersistenceTests"/> class.
|
||||
/// </summary>
|
||||
public SchemaPersistenceTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"schema_test_{Guid.NewGuid()}.db");
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and removes temporary files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies BSON schema serialization and deserialization round-trips correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BsonSchema_Serialization_RoundTrip()
|
||||
{
|
||||
@@ -81,6 +90,9 @@ public class SchemaPersistenceTests : IDisposable
|
||||
schema.Equals(roundTrip).ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies collection metadata is persisted and reloaded correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_Collections_Metadata_Persistence()
|
||||
{
|
||||
@@ -103,6 +115,9 @@ public class SchemaPersistenceTests : IDisposable
|
||||
loaded.Indexes[0].Name.ShouldBe("age");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies schema versioning appends new schema versions correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_Schema_Versioning()
|
||||
{
|
||||
@@ -125,6 +140,9 @@ public class SchemaPersistenceTests : IDisposable
|
||||
schemas[1].Title.ShouldBe("V2");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies collection startup integrates schema versioning behavior.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DocumentCollection_Integrates_Schema_Versioning_On_Startup()
|
||||
{
|
||||
@@ -186,6 +204,9 @@ public class SchemaPersistenceTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies persisted documents include the schema version field.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Document_Contains_Schema_Version_Field()
|
||||
{
|
||||
|
||||
@@ -16,6 +16,9 @@ public class SchemaTests
|
||||
foreach (var k in new[] { "_id", "name", "mainaddress", "otheraddresses", "tags", "secret", "street", "city" }) _testKeyMap[k] = id++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes UsedKeys_ShouldReturnAllKeys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void UsedKeys_ShouldReturnAllKeys()
|
||||
{
|
||||
@@ -33,6 +36,9 @@ public class SchemaTests
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes GetSchema_ShouldReturnBsonSchema.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GetSchema_ShouldReturnBsonSchema()
|
||||
{
|
||||
|
||||
@@ -9,18 +9,27 @@ public class SetMethodTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SetMethodTests"/> class.
|
||||
/// </summary>
|
||||
public SetMethodTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_set_{Guid.NewGuid()}.db");
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set object id returns correct collection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_ObjectId_ReturnsCorrectCollection()
|
||||
{
|
||||
@@ -29,6 +38,9 @@ public class SetMethodTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.Users);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set shorthand returns correct collection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_Shorthand_ReturnsCorrectCollection()
|
||||
{
|
||||
@@ -37,6 +49,9 @@ public class SetMethodTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.Users);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set int returns correct collection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_Int_ReturnsCorrectCollection()
|
||||
{
|
||||
@@ -45,6 +60,9 @@ public class SetMethodTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.People);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set string returns correct collection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_String_ReturnsCorrectCollection()
|
||||
{
|
||||
@@ -53,6 +71,9 @@ public class SetMethodTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.StringEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set guid returns correct collection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_Guid_ReturnsCorrectCollection()
|
||||
{
|
||||
@@ -61,6 +82,9 @@ public class SetMethodTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.GuidEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set custom key returns correct collection.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_CustomKey_ReturnsCorrectCollection()
|
||||
{
|
||||
@@ -69,6 +93,9 @@ public class SetMethodTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.Orders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set all object id collections return correct instances.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_AllObjectIdCollections_ReturnCorrectInstances()
|
||||
{
|
||||
@@ -82,6 +109,9 @@ public class SetMethodTests : IDisposable
|
||||
_db.Set<ObjectId, GeoEntity>().ShouldBeSameAs(_db.GeoItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set all int collections return correct instances.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_AllIntCollections_ReturnCorrectInstances()
|
||||
{
|
||||
@@ -92,24 +122,36 @@ public class SetMethodTests : IDisposable
|
||||
_db.Set<int, SchemaUser>().ShouldBeSameAs(_db.SchemaUsers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set string key collections return correct instances.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_StringKeyCollections_ReturnCorrectInstances()
|
||||
{
|
||||
_db.Set<string, CustomKeyEntity>().ShouldBeSameAs(_db.CustomKeyEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set unregistered entity throws invalid operation exception.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_UnregisteredEntity_ThrowsInvalidOperationException()
|
||||
{
|
||||
Should.Throw<InvalidOperationException>(() => _db.Set<ObjectId, Address>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set wrong key type throws invalid operation exception.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_WrongKeyType_ThrowsInvalidOperationException()
|
||||
{
|
||||
Should.Throw<InvalidOperationException>(() => _db.Set<string, User>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set can perform operations.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_CanPerformOperations()
|
||||
{
|
||||
@@ -124,6 +166,9 @@ public class SetMethodTests : IDisposable
|
||||
found.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set with int key can perform operations.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_WithIntKey_CanPerformOperations()
|
||||
{
|
||||
@@ -144,18 +189,27 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
private readonly string _dbPath;
|
||||
private readonly Shared.TestExtendedDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SetMethodInheritanceTests"/> class.
|
||||
/// </summary>
|
||||
public SetMethodInheritanceTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"cbdd_set_inherit_{Guid.NewGuid()}.db");
|
||||
_db = new Shared.TestExtendedDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set own collection returns correct instance.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_OwnCollection_ReturnsCorrectInstance()
|
||||
{
|
||||
@@ -164,6 +218,9 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.ExtendedEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set parent collection returns correct instance.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_ParentCollection_ReturnsCorrectInstance()
|
||||
{
|
||||
@@ -172,6 +229,9 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.Users);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set parent shorthand returns correct instance.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_ParentShorthand_ReturnsCorrectInstance()
|
||||
{
|
||||
@@ -180,6 +240,9 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.Users);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set parent int collection returns correct instance.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_ParentIntCollection_ReturnsCorrectInstance()
|
||||
{
|
||||
@@ -187,6 +250,9 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
_db.Set<int, Product>().ShouldBeSameAs(_db.Products);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set parent custom key returns correct instance.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_ParentCustomKey_ReturnsCorrectInstance()
|
||||
{
|
||||
@@ -195,12 +261,18 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
collection.ShouldBeSameAs(_db.Orders);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set unregistered entity throws invalid operation exception.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_UnregisteredEntity_ThrowsInvalidOperationException()
|
||||
{
|
||||
Should.Throw<InvalidOperationException>(() => _db.Set<ObjectId, Address>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set own collection can perform operations.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_OwnCollection_CanPerformOperations()
|
||||
{
|
||||
@@ -214,6 +286,9 @@ public class SetMethodInheritanceTests : IDisposable
|
||||
found.Description.ShouldBe("Test");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests set parent collection can perform operations.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Set_ParentCollection_CanPerformOperations()
|
||||
{
|
||||
|
||||
@@ -16,6 +16,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
private readonly string _walPath;
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SourceGeneratorFeaturesTests"/> class.
|
||||
/// </summary>
|
||||
public SourceGeneratorFeaturesTests()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_sg_features_{Guid.NewGuid()}.db");
|
||||
@@ -26,6 +29,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
|
||||
#region Inheritance Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests derived entity inherits id from base class.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DerivedEntity_InheritsId_FromBaseClass()
|
||||
{
|
||||
@@ -50,6 +56,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.CreatedAt.Date.ShouldBe(entity.CreatedAt.Date); // Compare just date part
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests derived entity update works with inherited id.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DerivedEntity_Update_WorksWithInheritedId()
|
||||
{
|
||||
@@ -80,6 +89,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
updated.Description.ShouldBe("Updated Description");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests derived entity query works with inherited properties.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DerivedEntity_Query_WorksWithInheritedProperties()
|
||||
{
|
||||
@@ -107,6 +119,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
|
||||
#region Computed Properties Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests computed properties are not serialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ComputedProperties_AreNotSerialized()
|
||||
{
|
||||
@@ -135,6 +150,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.DisplayInfo.ShouldContain("John Doe");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests computed properties update does not break.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ComputedProperties_UpdateDoesNotBreak()
|
||||
{
|
||||
@@ -170,6 +188,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
|
||||
#region Advanced Collections Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests hash set serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HashSet_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -197,6 +218,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.Tags.ShouldContain("tag3");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests iset serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ISet_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -225,6 +249,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.Numbers.ShouldContain(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests linked list serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LinkedList_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -253,6 +280,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
historyList[2].ShouldBe("third");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests queue serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Queue_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -280,6 +310,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
items.ShouldContain("item3");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests stack serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Stack_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -307,6 +340,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
items.ShouldContain("action3");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests hash set with nested objects serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HashSet_WithNestedObjects_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -334,6 +370,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
addressList.ShouldContain(a => a.Street == "456 Oak Ave" && a.City.Name == "LA");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests iset with nested objects serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ISet_WithNestedObjects_SerializesAndDeserializes()
|
||||
{
|
||||
@@ -363,6 +402,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
cityNames.ShouldContain("London");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests advanced collections all types in single entity.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AdvancedCollections_AllTypesInSingleEntity()
|
||||
{
|
||||
@@ -411,6 +453,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
|
||||
#region Private Setters Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with private setters can be deserialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithPrivateSetters_CanBeDeserialized()
|
||||
{
|
||||
@@ -429,6 +474,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with private setters update works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithPrivateSetters_Update_Works()
|
||||
{
|
||||
@@ -452,6 +500,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.Age.ShouldBe(35);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with private setters query works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithPrivateSetters_Query_Works()
|
||||
{
|
||||
@@ -478,6 +529,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
|
||||
#region Init-Only Setters Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with init setters can be deserialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithInitSetters_CanBeDeserialized()
|
||||
{
|
||||
@@ -502,6 +556,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
retrieved.Age.ShouldBe(28);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with init setters query works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithInitSetters_Query_Works()
|
||||
{
|
||||
@@ -526,6 +583,9 @@ public class SourceGeneratorFeaturesTests : IDisposable
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db?.Dispose();
|
||||
|
||||
@@ -13,6 +13,9 @@ public class StorageEngineDictionaryTests
|
||||
if (File.Exists(Path.ChangeExtension(path, ".wal"))) File.Delete(Path.ChangeExtension(path, ".wal"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary pages are initialized and return normalized keys.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_ShouldInitializeDictionary()
|
||||
{
|
||||
@@ -32,6 +35,9 @@ public class StorageEngineDictionaryTests
|
||||
finally { Cleanup(path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary entries persist across reopen.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_ShouldPersistDictionary()
|
||||
{
|
||||
@@ -61,6 +67,9 @@ public class StorageEngineDictionaryTests
|
||||
finally { Cleanup(path); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dictionary handling scales to many keys and remains durable.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void StorageEngine_ShouldHandleManyKeys()
|
||||
{
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class StorageEngineTransactionProtocolTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies preparing an unknown transaction returns false.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void PrepareTransaction_Should_ReturnFalse_For_Unknown_Transaction()
|
||||
{
|
||||
@@ -20,6 +23,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committing a detached transaction object throws.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommitTransaction_With_TransactionObject_Should_Throw_When_Not_Active()
|
||||
{
|
||||
@@ -37,6 +43,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committing a transaction object persists writes and clears active state.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommitTransaction_With_TransactionObject_Should_Commit_Writes()
|
||||
{
|
||||
@@ -65,6 +74,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committing by identifier with no writes does not throw.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CommitTransaction_ById_With_NoWrites_Should_Not_Throw()
|
||||
{
|
||||
@@ -80,6 +92,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies committed transaction cache moves into readable state and active count is cleared.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MarkTransactionCommitted_Should_Move_Cache_And_Clear_ActiveCount()
|
||||
{
|
||||
@@ -108,6 +123,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies rollback discards uncommitted page writes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RollbackTransaction_Should_Discard_Uncommitted_Write()
|
||||
{
|
||||
@@ -140,6 +158,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies marking a transaction committed transitions state correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Transaction_MarkCommitted_Should_Transition_State()
|
||||
{
|
||||
@@ -169,6 +190,9 @@ public class StorageEngineTransactionProtocolTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies preparing then committing writes WAL data and updates transaction state.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Transaction_Prepare_Should_Write_Wal_And_Transition_State()
|
||||
{
|
||||
|
||||
@@ -11,12 +11,18 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
private readonly Shared.TestDbContext _db;
|
||||
private readonly string _dbPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TemporalTypesTests"/> class.
|
||||
/// </summary>
|
||||
public TemporalTypesTests()
|
||||
{
|
||||
_dbPath = $"temporal_test_{Guid.NewGuid()}.db";
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db?.Dispose();
|
||||
@@ -24,12 +30,18 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
File.Delete(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies temporal entity collection initialization.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TemporalEntity_Collection_IsInitialized()
|
||||
{
|
||||
_db.TemporalEntities.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies temporal fields round-trip through insert and lookup.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TemporalEntity_Insert_And_FindById_Works()
|
||||
{
|
||||
@@ -87,6 +99,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
retrieved.ClosingTime!.Value.ShouldBe(entity.ClosingTime!.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies insert behavior when optional temporal fields are null.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TemporalEntity_Insert_WithNullOptionalFields_Works()
|
||||
{
|
||||
@@ -120,6 +135,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
retrieved.ClosingTime.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies temporal entity updates persist correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TemporalEntity_Update_Works()
|
||||
{
|
||||
@@ -155,6 +173,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
retrieved.OpeningTime.ShouldBe(entity.OpeningTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies querying temporal entities by temporal fields.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TemporalEntity_Query_Works()
|
||||
{
|
||||
@@ -197,6 +218,9 @@ namespace ZB.MOM.WW.CBDD.Tests
|
||||
results[0].Name.ShouldBe("Person 1");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies edge-case TimeSpan values are persisted correctly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TimeSpan_EdgeCases_Work()
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core;
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Indexing;
|
||||
using ZB.MOM.WW.CBDD.Core.Metadata;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core;
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Indexing;
|
||||
using ZB.MOM.WW.CBDD.Core.Metadata;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Shared;
|
||||
|
||||
@@ -14,69 +14,178 @@ namespace ZB.MOM.WW.CBDD.Shared;
|
||||
/// </summary>
|
||||
public partial class TestDbContext : DocumentDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the AnnotatedUsers.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, AnnotatedUser> AnnotatedUsers { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the Orders.
|
||||
/// </summary>
|
||||
public DocumentCollection<OrderId, Order> Orders { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the TestDocuments.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, TestDocument> TestDocuments { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the OrderDocuments.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, OrderDocument> OrderDocuments { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the ComplexDocuments.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, ComplexDocument> ComplexDocuments { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the Users.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, User> Users { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the ComplexUsers.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, ComplexUser> ComplexUsers { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoInitEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, AutoInitEntity> AutoInitEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the People.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, Person> People { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the PeopleV2.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, PersonV2> PeopleV2 { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the Products.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, Product> Products { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the IntEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, IntEntity> IntEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the StringEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<string, StringEntity> StringEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the GuidEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<Guid, GuidEntity> GuidEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the CustomKeyEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<string, CustomKeyEntity> CustomKeyEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the AsyncDocs.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, AsyncDoc> AsyncDocs { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the SchemaUsers.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, SchemaUser> SchemaUsers { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the VectorItems.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, VectorEntity> VectorItems { get; set; } = null!;
|
||||
public DocumentCollection<ObjectId, GeoEntity> GeoItems { get; set; } = null!;
|
||||
|
||||
// Source Generator Feature Tests
|
||||
/// <summary>
|
||||
/// Gets or sets the GeoItems.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, GeoEntity> GeoItems { get; set; } = null!;
|
||||
|
||||
// Source Generator Feature Tests
|
||||
/// <summary>
|
||||
/// Gets or sets the DerivedEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, DerivedEntity> DerivedEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the ComputedPropertyEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, EntityWithComputedProperties> ComputedPropertyEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the AdvancedCollectionEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, EntityWithAdvancedCollections> AdvancedCollectionEntities { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the PrivateSetterEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, EntityWithPrivateSetters> PrivateSetterEntities { get; set; } = null!;
|
||||
public DocumentCollection<ObjectId, EntityWithInitSetters> InitSetterEntities { get; set; } = null!;
|
||||
|
||||
// Circular Reference Tests
|
||||
/// <summary>
|
||||
/// Gets or sets the InitSetterEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, EntityWithInitSetters> InitSetterEntities { get; set; } = null!;
|
||||
|
||||
// Circular Reference Tests
|
||||
/// <summary>
|
||||
/// Gets or sets the Employees.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, Employee> Employees { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Gets or sets the CategoryRefs.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, CategoryRef> CategoryRefs { get; set; } = null!;
|
||||
public DocumentCollection<ObjectId, ProductRef> ProductRefs { get; set; } = null!;
|
||||
|
||||
// Nullable String Id Test (UuidEntity scenario with inheritance)
|
||||
public DocumentCollection<string, MockCounter> MockCounters { get; set; } = null!;
|
||||
|
||||
// Temporal Types Test (DateTimeOffset, TimeSpan, DateOnly, TimeOnly)
|
||||
/// <summary>
|
||||
/// Gets or sets the ProductRefs.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, ProductRef> ProductRefs { get; set; } = null!;
|
||||
|
||||
// Nullable String Id Test (UuidEntity scenario with inheritance)
|
||||
/// <summary>
|
||||
/// Gets or sets the MockCounters.
|
||||
/// </summary>
|
||||
public DocumentCollection<string, MockCounter> MockCounters { get; set; } = null!;
|
||||
|
||||
// Temporal Types Test (DateTimeOffset, TimeSpan, DateOnly, TimeOnly)
|
||||
/// <summary>
|
||||
/// Gets or sets the TemporalEntities.
|
||||
/// </summary>
|
||||
public DocumentCollection<ObjectId, TemporalEntity> TemporalEntities { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestDbContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databasePath">The database path.</param>
|
||||
public TestDbContext(string databasePath)
|
||||
: this(databasePath, PageFileConfig.Default, CompressionOptions.Default)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestDbContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databasePath">The database path.</param>
|
||||
/// <param name="compressionOptions">The compression options.</param>
|
||||
public TestDbContext(string databasePath, CompressionOptions compressionOptions)
|
||||
: this(databasePath, PageFileConfig.Default, compressionOptions)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestDbContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databasePath">The database path.</param>
|
||||
/// <param name="pageFileConfig">The page file configuration.</param>
|
||||
public TestDbContext(string databasePath, PageFileConfig pageFileConfig)
|
||||
: this(databasePath, pageFileConfig, CompressionOptions.Default)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestDbContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databasePath">The database path.</param>
|
||||
/// <param name="pageFileConfig">The page file configuration.</param>
|
||||
/// <param name="compressionOptions">The compression options.</param>
|
||||
/// <param name="maintenanceOptions">The maintenance options.</param>
|
||||
public TestDbContext(
|
||||
string databasePath,
|
||||
PageFileConfig pageFileConfig,
|
||||
CompressionOptions? compressionOptions,
|
||||
MaintenanceOptions? maintenanceOptions = null)
|
||||
: base(databasePath, pageFileConfig, compressionOptions, maintenanceOptions)
|
||||
{
|
||||
}
|
||||
: base(databasePath, pageFileConfig, compressionOptions, maintenanceOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<AnnotatedUser>();
|
||||
modelBuilder.Entity<User>().ToCollection("users");
|
||||
@@ -93,8 +202,8 @@ public partial class TestDbContext : DocumentDbContext
|
||||
modelBuilder.Entity<SchemaUser>().ToCollection("schema_users").HasKey(e => e.Id);
|
||||
modelBuilder.Entity<TestDocument>();
|
||||
modelBuilder.Entity<OrderDocument>();
|
||||
modelBuilder.Entity<ComplexDocument>();
|
||||
|
||||
modelBuilder.Entity<ComplexDocument>();
|
||||
|
||||
modelBuilder.Entity<VectorEntity>()
|
||||
.ToCollection("vector_items")
|
||||
.HasVectorIndex(x => x.Embedding, dimensions: 3, metric: VectorMetric.L2, name: "idx_vector");
|
||||
@@ -112,24 +221,30 @@ public partial class TestDbContext : DocumentDbContext
|
||||
modelBuilder.Entity<EntityWithComputedProperties>().ToCollection("computed_property_entities");
|
||||
modelBuilder.Entity<EntityWithAdvancedCollections>().ToCollection("advanced_collection_entities");
|
||||
modelBuilder.Entity<EntityWithPrivateSetters>().ToCollection("private_setter_entities");
|
||||
modelBuilder.Entity<EntityWithInitSetters>().ToCollection("init_setter_entities");
|
||||
|
||||
// Circular Reference Tests
|
||||
modelBuilder.Entity<EntityWithInitSetters>().ToCollection("init_setter_entities");
|
||||
|
||||
// Circular Reference Tests
|
||||
modelBuilder.Entity<Employee>().ToCollection("employees");
|
||||
modelBuilder.Entity<CategoryRef>().ToCollection("category_refs");
|
||||
modelBuilder.Entity<ProductRef>().ToCollection("product_refs");
|
||||
|
||||
// Nullable String Id Test (UuidEntity scenario)
|
||||
modelBuilder.Entity<MockCounter>().ToCollection("mock_counters").HasKey(e => e.Id);
|
||||
|
||||
// Temporal Types Test
|
||||
modelBuilder.Entity<ProductRef>().ToCollection("product_refs");
|
||||
|
||||
// Nullable String Id Test (UuidEntity scenario)
|
||||
modelBuilder.Entity<MockCounter>().ToCollection("mock_counters").HasKey(e => e.Id);
|
||||
|
||||
// Temporal Types Test
|
||||
modelBuilder.Entity<TemporalEntity>().ToCollection("temporal_entities").HasKey(e => e.Id);
|
||||
}
|
||||
|
||||
public void ForceCheckpoint()
|
||||
{
|
||||
Engine.Checkpoint();
|
||||
}
|
||||
|
||||
public StorageEngine Storage => Engine;
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes ForceCheckpoint.
|
||||
/// </summary>
|
||||
public void ForceCheckpoint()
|
||||
{
|
||||
Engine.Checkpoint();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Storage.
|
||||
/// </summary>
|
||||
public StorageEngine Storage => Engine;
|
||||
}
|
||||
|
||||
@@ -9,17 +9,25 @@ namespace ZB.MOM.WW.CBDD.Shared;
|
||||
/// </summary>
|
||||
public partial class TestExtendedDbContext : TestDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the extended entities.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, ExtendedEntity> ExtendedEntities { get; set; } = null!;
|
||||
|
||||
public TestExtendedDbContext(string databasePath) : base(databasePath)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TestExtendedDbContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="databasePath">Database file path.</param>
|
||||
public TestExtendedDbContext(string databasePath) : base(databasePath)
|
||||
{
|
||||
InitializeCollections();
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<ExtendedEntity>()
|
||||
.ToCollection("extended_entities")
|
||||
.HasKey(e => e.Id);
|
||||
|
||||
@@ -12,12 +12,18 @@ public class ValueObjectIdTests : IDisposable
|
||||
private readonly string _dbPath = "value_object_ids.db";
|
||||
private readonly Shared.TestDbContext _db;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ValueObjectIdTests"/> class.
|
||||
/// </summary>
|
||||
public ValueObjectIdTests()
|
||||
{
|
||||
if (File.Exists(_dbPath)) File.Delete(_dbPath);
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Should_Support_ValueObject_Id_Conversion.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Should_Support_ValueObject_Id_Conversion()
|
||||
{
|
||||
@@ -36,6 +42,9 @@ public class ValueObjectIdTests : IDisposable
|
||||
retrieved.CustomerName.ShouldBe("John Doe");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes Dispose.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db.Dispose();
|
||||
|
||||
@@ -4,6 +4,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class VectorMathTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies distance calculations across all supported vector metrics.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Distance_Should_Cover_All_Metrics()
|
||||
{
|
||||
@@ -21,6 +24,9 @@ public class VectorMathTests
|
||||
MathF.Abs(cosineDistance - expectedCosine).ShouldBeLessThan(0.0001f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies cosine similarity returns zero when one vector has zero magnitude.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CosineSimilarity_Should_Return_Zero_For_ZeroMagnitude_Vector()
|
||||
{
|
||||
@@ -30,6 +36,9 @@ public class VectorMathTests
|
||||
VectorMath.CosineSimilarity(v1, v2).ShouldBe(0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies dot product throws for mismatched vector lengths.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DotProduct_Should_Throw_For_Length_Mismatch()
|
||||
{
|
||||
@@ -39,6 +48,9 @@ public class VectorMathTests
|
||||
Should.Throw<ArgumentException>(() => VectorMath.DotProduct(v1, v2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies squared Euclidean distance throws for mismatched vector lengths.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EuclideanDistanceSquared_Should_Throw_For_Length_Mismatch()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
public class VectorSearchTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies basic vector-search query behavior.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Test_VectorSearch_Basic()
|
||||
{
|
||||
|
||||
@@ -10,12 +10,21 @@ public class VisibilityTests
|
||||
public class VisibilityEntity
|
||||
{
|
||||
// Should be included
|
||||
/// <summary>
|
||||
/// Gets or sets the normal prop.
|
||||
/// </summary>
|
||||
public int NormalProp { get; set; }
|
||||
|
||||
// Should be included (serialization usually writes it)
|
||||
/// <summary>
|
||||
/// Gets or sets the private set prop.
|
||||
/// </summary>
|
||||
public int PrivateSetProp { get; private set; }
|
||||
|
||||
// Should be included
|
||||
/// <summary>
|
||||
/// Gets or sets the init prop.
|
||||
/// </summary>
|
||||
public int InitProp { get; init; }
|
||||
|
||||
// Fields - typically included in BSON if public, but reflection need GetFields
|
||||
@@ -25,9 +34,16 @@ public class VisibilityTests
|
||||
private int _privateField;
|
||||
|
||||
// Helper to set private
|
||||
/// <summary>
|
||||
/// Tests set private.
|
||||
/// </summary>
|
||||
/// <param name="val">Value assigned to the private field.</param>
|
||||
public void SetPrivate(int val) => _privateField = val;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests generate schema visibility checks.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void GenerateSchema_VisibilityChecks()
|
||||
{
|
||||
|
||||
@@ -18,6 +18,10 @@ public class WalIndexTests : IDisposable
|
||||
private readonly Shared.TestDbContext _db;
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WalIndexTests"/> class.
|
||||
/// </summary>
|
||||
/// <param name="output">Test output sink.</param>
|
||||
public WalIndexTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
@@ -28,6 +32,9 @@ public class WalIndexTests : IDisposable
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies index writes are recorded in the WAL.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void IndexWritesAreLoggedToWal()
|
||||
{
|
||||
@@ -87,6 +94,9 @@ public class WalIndexTests : IDisposable
|
||||
return (PageType)pageData[4]; // Casting byte to PageType
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies offline compaction leaves the WAL empty.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Compact_ShouldLeaveWalEmpty_AfterOfflineRun()
|
||||
{
|
||||
@@ -110,6 +120,9 @@ public class WalIndexTests : IDisposable
|
||||
new FileInfo(_walPath).Length.ShouldBe(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies WAL recovery followed by compaction preserves data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Recover_WithCommittedWal_ThenCompact_ShouldPreserveData()
|
||||
{
|
||||
@@ -153,6 +166,9 @@ public class WalIndexTests : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases test resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user