Implement checkpoint modes with docs/tests and reorganize project file layout
This commit is contained in:
43
tests/CBDD.Tests/Context/AutoInitTests.cs
Executable file
43
tests/CBDD.Tests/Context/AutoInitTests.cs
Executable file
@@ -0,0 +1,43 @@
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests
|
||||
{
|
||||
public class AutoInitTests : System.IDisposable
|
||||
{
|
||||
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()
|
||||
{
|
||||
using var db = new Shared.TestDbContext(DbPath);
|
||||
|
||||
// Verify Collection is not null (initialized by generated method)
|
||||
db.AutoInitEntities.ShouldNotBeNull();
|
||||
|
||||
// Verify we can use it
|
||||
db.AutoInitEntities.Insert(new AutoInitEntity { Id = 1, Name = "Test" });
|
||||
var stored = db.AutoInitEntities.FindById(1);
|
||||
stored.ShouldNotBeNull();
|
||||
stored.Name.ShouldBe("Test");
|
||||
}
|
||||
}
|
||||
}
|
||||
128
tests/CBDD.Tests/Context/DbContextInheritanceTests.cs
Executable file
128
tests/CBDD.Tests/Context/DbContextInheritanceTests.cs
Executable file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
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()
|
||||
{
|
||||
// Verify parent collections are initialized (from TestDbContext)
|
||||
_db.Users.ShouldNotBeNull();
|
||||
_db.People.ShouldNotBeNull();
|
||||
_db.Products.ShouldNotBeNull();
|
||||
_db.AnnotatedUsers.ShouldNotBeNull();
|
||||
_db.ComplexDocuments.ShouldNotBeNull();
|
||||
_db.TestDocuments.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies extended context collections are initialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Should_Initialize_Own_Collections()
|
||||
{
|
||||
// Verify extended context's own collection is initialized
|
||||
_db.ExtendedEntities.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies parent collections are usable from the extended context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Can_Use_Parent_Collections()
|
||||
{
|
||||
// Insert into parent collection
|
||||
var user = new User { Name = "TestUser", Age = 30 };
|
||||
_db.Users.Insert(user);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Verify we can read it back
|
||||
var retrieved = _db.Users.FindById(user.Id);
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Name.ShouldBe("TestUser");
|
||||
retrieved.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies extended collections are usable from the extended context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ExtendedContext_Can_Use_Own_Collections()
|
||||
{
|
||||
// Insert into extended collection
|
||||
var entity = new ExtendedEntity
|
||||
{
|
||||
Id = 1,
|
||||
Description = "Test Extended Entity",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
_db.ExtendedEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Verify we can read it back
|
||||
var retrieved = _db.ExtendedEntities.FindById(1);
|
||||
retrieved.ShouldNotBeNull();
|
||||
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()
|
||||
{
|
||||
// Insert into parent collection
|
||||
var person = new Person { Id = 100, Name = "John", Age = 25 };
|
||||
_db.People.Insert(person);
|
||||
|
||||
// Insert into extended collection
|
||||
var extended = new ExtendedEntity
|
||||
{
|
||||
Id = 200,
|
||||
Description = "Related to John",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
_db.ExtendedEntities.Insert(extended);
|
||||
|
||||
_db.SaveChanges();
|
||||
|
||||
// Verify both
|
||||
var retrievedPerson = _db.People.FindById(100);
|
||||
var retrievedExtended = _db.ExtendedEntities.FindById(200);
|
||||
|
||||
retrievedPerson.ShouldNotBeNull();
|
||||
retrievedPerson.Name.ShouldBe("John");
|
||||
|
||||
retrievedExtended.ShouldNotBeNull();
|
||||
retrievedExtended.Description.ShouldBe("Related to John");
|
||||
}
|
||||
}
|
||||
249
tests/CBDD.Tests/Context/DbContextTests.cs
Executable file
249
tests/CBDD.Tests/Context/DbContextTests.cs
Executable file
@@ -0,0 +1,249 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Core.Compression;
|
||||
using ZB.MOM.WW.CBDD.Core.Storage;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
using System.Security.Cryptography;
|
||||
using System.IO.Compression;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
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()
|
||||
{
|
||||
using var db = new Shared.TestDbContext(_dbPath);
|
||||
|
||||
var user = new User { Name = "Alice", Age = 30 };
|
||||
var id = db.Users.Insert(user);
|
||||
|
||||
var found = db.Users.FindById(id);
|
||||
found.ShouldNotBeNull();
|
||||
found.Name.ShouldBe("Alice");
|
||||
found.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies multiple CRUD operations execute correctly in one context.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_MultipleOperations_Work()
|
||||
{
|
||||
using var db = new Shared.TestDbContext(_dbPath);
|
||||
|
||||
// Insert
|
||||
var alice = new User { Name = "Alice", Age = 30 };
|
||||
var bob = new User { Name = "Bob", Age = 25 };
|
||||
|
||||
var id1 = db.Users.Insert(alice);
|
||||
var id2 = db.Users.Insert(bob);
|
||||
|
||||
// FindAll
|
||||
var all = db.Users.FindAll().ToList();
|
||||
all.Count.ShouldBe(2);
|
||||
|
||||
// Update
|
||||
alice.Age = 31;
|
||||
db.Users.Update(alice).ShouldBeTrue();
|
||||
|
||||
var updated = db.Users.FindById(id1);
|
||||
updated!.Age.ShouldBe(31);
|
||||
|
||||
// Delete
|
||||
db.Users.Delete(id2).ShouldBeTrue();
|
||||
db.Users.Count().ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies disposing and reopening context preserves persisted data.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_Dispose_ReleasesResources()
|
||||
{
|
||||
_dbPath = Path.Combine(Path.GetTempPath(), $"test_dbcontext_reopen.db");
|
||||
var totalUsers = 0;
|
||||
// First context - insert and dispose (auto-checkpoint)
|
||||
using (var db = new Shared.TestDbContext(_dbPath))
|
||||
{
|
||||
db.Users.Insert(new User { Name = "Test", Age = 20 });
|
||||
db.SaveChanges(); // Explicitly save changes to ensure data is in WAL
|
||||
var beforeCheckpointTotalUsers = db.Users.FindAll().Count();
|
||||
db.ForceCheckpoint(); // Force checkpoint to ensure data is persisted to main file
|
||||
totalUsers = db.Users.FindAll().Count();
|
||||
var countedUsers = db.Users.Count();
|
||||
totalUsers.ShouldBe(beforeCheckpointTotalUsers);
|
||||
} // Dispose → Commit → ForceCheckpoint → Write to PageFile
|
||||
|
||||
// Should be able to open again and see persisted data
|
||||
using var db2 = new Shared.TestDbContext(_dbPath);
|
||||
|
||||
totalUsers.ShouldBe(1);
|
||||
db2.Users.FindAll().Count().ShouldBe(totalUsers);
|
||||
db2.Users.Count().ShouldBe(totalUsers);
|
||||
}
|
||||
private static string ComputeFileHash(string path)
|
||||
{
|
||||
using var stream = File.OpenRead(path);
|
||||
using var sha256 = SHA256.Create();
|
||||
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()
|
||||
{
|
||||
var dbPath = Path.Combine(Path.GetTempPath(), $"test_dbfile_{Guid.NewGuid()}.db");
|
||||
|
||||
// 1. Crea e chiudi database vuoto
|
||||
using (var db = new Shared.TestDbContext(dbPath))
|
||||
{
|
||||
db.Users.Insert(new User { Name = "Pippo", Age = 42 });
|
||||
}
|
||||
var initialSize = new FileInfo(dbPath).Length;
|
||||
var initialHash = ComputeFileHash(dbPath);
|
||||
|
||||
// 2. Riapri, inserisci, chiudi
|
||||
using (var db = new Shared.TestDbContext(dbPath))
|
||||
{
|
||||
db.Users.Insert(new User { Name = "Test", Age = 42 });
|
||||
db.ForceCheckpoint(); // Forza persistenza
|
||||
}
|
||||
var afterInsertSize = new FileInfo(dbPath).Length;
|
||||
var afterInsertHash = ComputeFileHash(dbPath);
|
||||
|
||||
// 3. Verifica che dimensione e hash siano cambiati
|
||||
afterInsertSize.ShouldNotBe(initialSize);
|
||||
afterInsertHash.ShouldNotBe(initialHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the WAL file path is auto-derived from database path.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_AutoDerivesWalPath()
|
||||
{
|
||||
using var db = new Shared.TestDbContext(_dbPath);
|
||||
db.Users.Insert(new User { Name = "Test", Age = 20 });
|
||||
|
||||
var walPath = Path.ChangeExtension(_dbPath, ".wal");
|
||||
File.Exists(walPath).ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies custom page file and compression options support roundtrip data access.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_WithCustomPageFileAndCompressionOptions_ShouldSupportRoundTrip()
|
||||
{
|
||||
var dbPath = Path.Combine(Path.GetTempPath(), $"test_dbcontext_compression_{Guid.NewGuid():N}.db");
|
||||
var options = new CompressionOptions
|
||||
{
|
||||
EnableCompression = true,
|
||||
MinSizeBytes = 0,
|
||||
MinSavingsPercent = 0,
|
||||
Codec = CompressionCodec.Brotli,
|
||||
Level = CompressionLevel.Fastest
|
||||
};
|
||||
|
||||
var config = new PageFileConfig
|
||||
{
|
||||
PageSize = 16 * 1024,
|
||||
InitialFileSize = 1024 * 1024,
|
||||
Access = MemoryMappedFileAccess.ReadWrite
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
using var db = new Shared.TestDbContext(dbPath, config, options);
|
||||
var payload = string.Concat(Enumerable.Repeat("compressible-", 3000));
|
||||
var id = db.Users.Insert(new User { Name = payload, Age = 77 });
|
||||
db.SaveChanges();
|
||||
|
||||
var loaded = db.Users.FindById(id);
|
||||
loaded.ShouldNotBeNull();
|
||||
loaded.Name.ShouldBe(payload);
|
||||
db.GetCompressionStats().CompressedDocumentCount.ShouldBeGreaterThanOrEqualTo(1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupDbFiles(dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies compact API returns stats and preserves data consistency.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DbContext_CompactApi_ShouldReturnStatsAndPreserveData()
|
||||
{
|
||||
var dbPath = Path.Combine(Path.GetTempPath(), $"test_dbcontext_compact_{Guid.NewGuid():N}.db");
|
||||
try
|
||||
{
|
||||
using var db = new Shared.TestDbContext(dbPath);
|
||||
for (var i = 0; i < 120; i++)
|
||||
{
|
||||
db.Users.Insert(new User { Name = $"compact-{i:D3}", Age = i % 20 });
|
||||
}
|
||||
|
||||
db.SaveChanges();
|
||||
db.Users.Count().ShouldBe(120);
|
||||
db.SaveChanges();
|
||||
|
||||
var stats = db.Compact(new CompactionOptions
|
||||
{
|
||||
EnableTailTruncation = true,
|
||||
DefragmentSlottedPages = true,
|
||||
NormalizeFreeList = true
|
||||
});
|
||||
|
||||
stats.OnlineMode.ShouldBeFalse();
|
||||
db.Users.Count().ShouldBe(120);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanupDbFiles(dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes test resources and cleans up generated files.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
CleanupDbFiles(_dbPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupDbFiles(string dbPath)
|
||||
{
|
||||
if (File.Exists(dbPath)) File.Delete(dbPath);
|
||||
|
||||
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
||||
if (File.Exists(walPath)) File.Delete(walPath);
|
||||
|
||||
var markerPath = $"{dbPath}.compact.state";
|
||||
if (File.Exists(markerPath)) File.Delete(markerPath);
|
||||
}
|
||||
}
|
||||
598
tests/CBDD.Tests/Context/SourceGeneratorFeaturesTests.cs
Executable file
598
tests/CBDD.Tests/Context/SourceGeneratorFeaturesTests.cs
Executable file
@@ -0,0 +1,598 @@
|
||||
using ZB.MOM.WW.CBDD.Bson;
|
||||
using ZB.MOM.WW.CBDD.Shared;
|
||||
using System.Linq;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for Source Generator enhancements:
|
||||
/// 1. Property inheritance from base classes (including Id)
|
||||
/// 2. Exclusion of computed getter-only properties
|
||||
/// 3. Recognition of advanced collection types (HashSet, ISet, LinkedList, etc.)
|
||||
/// </summary>
|
||||
public class SourceGeneratorFeaturesTests : IDisposable
|
||||
{
|
||||
private readonly string _dbPath;
|
||||
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");
|
||||
_walPath = Path.Combine(Path.GetTempPath(), $"test_sg_features_{Guid.NewGuid()}.wal");
|
||||
|
||||
_db = new Shared.TestDbContext(_dbPath);
|
||||
}
|
||||
|
||||
#region Inheritance Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests derived entity inherits id from base class.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DerivedEntity_InheritsId_FromBaseClass()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new DerivedEntity
|
||||
{
|
||||
Name = "Test Entity",
|
||||
Description = "Testing inheritance",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// Act
|
||||
var id = _db.DerivedEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.DerivedEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Id.ShouldBe(id); // Id from base class should work
|
||||
retrieved.Name.ShouldBe("Test Entity");
|
||||
retrieved.Description.ShouldBe("Testing inheritance");
|
||||
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()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new DerivedEntity
|
||||
{
|
||||
Name = "Original",
|
||||
Description = "Original Description",
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
var id = _db.DerivedEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Act
|
||||
var retrieved = _db.DerivedEntities.FindById(id);
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Name = "Updated";
|
||||
retrieved.Description = "Updated Description";
|
||||
_db.DerivedEntities.Update(retrieved);
|
||||
_db.SaveChanges();
|
||||
|
||||
var updated = _db.DerivedEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
updated.ShouldNotBeNull();
|
||||
updated.Id.ShouldBe(id);
|
||||
updated.Name.ShouldBe("Updated");
|
||||
updated.Description.ShouldBe("Updated Description");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests derived entity query works with inherited properties.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void DerivedEntity_Query_WorksWithInheritedProperties()
|
||||
{
|
||||
// Arrange
|
||||
var now = DateTime.UtcNow;
|
||||
var entity1 = new DerivedEntity { Name = "Entity1", CreatedAt = now.AddDays(-2) };
|
||||
var entity2 = new DerivedEntity { Name = "Entity2", CreatedAt = now.AddDays(-1) };
|
||||
var entity3 = new DerivedEntity { Name = "Entity3", CreatedAt = now };
|
||||
|
||||
_db.DerivedEntities.Insert(entity1);
|
||||
_db.DerivedEntities.Insert(entity2);
|
||||
_db.DerivedEntities.Insert(entity3);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Act - Query using inherited property
|
||||
var recent = _db.DerivedEntities.Find(e => e.CreatedAt >= now.AddDays(-1.5)).ToList();
|
||||
|
||||
// Assert
|
||||
recent.Count.ShouldBe(2);
|
||||
recent.ShouldContain(e => e.Name == "Entity2");
|
||||
recent.ShouldContain(e => e.Name == "Entity3");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Computed Properties Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests computed properties are not serialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ComputedProperties_AreNotSerialized()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithComputedProperties
|
||||
{
|
||||
FirstName = "John",
|
||||
LastName = "Doe",
|
||||
BirthYear = 1990
|
||||
};
|
||||
|
||||
// Act
|
||||
var id = _db.ComputedPropertyEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.ComputedPropertyEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.FirstName.ShouldBe("John");
|
||||
retrieved.LastName.ShouldBe("Doe");
|
||||
retrieved.BirthYear.ShouldBe(1990);
|
||||
|
||||
// Computed properties should still work after deserialization
|
||||
retrieved.FullName.ShouldBe("John Doe");
|
||||
(retrieved.Age >= 34).ShouldBeTrue(); // Born in 1990, so at least 34 in 2024+
|
||||
retrieved.DisplayInfo.ShouldContain("John Doe");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests computed properties update does not break.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ComputedProperties_UpdateDoesNotBreak()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithComputedProperties
|
||||
{
|
||||
FirstName = "Jane",
|
||||
LastName = "Smith",
|
||||
BirthYear = 1985
|
||||
};
|
||||
var id = _db.ComputedPropertyEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Act - Update stored properties
|
||||
var retrieved = _db.ComputedPropertyEntities.FindById(id);
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.FirstName = "Janet";
|
||||
retrieved.BirthYear = 1986;
|
||||
_db.ComputedPropertyEntities.Update(retrieved);
|
||||
_db.SaveChanges();
|
||||
|
||||
var updated = _db.ComputedPropertyEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
updated.ShouldNotBeNull();
|
||||
updated.FirstName.ShouldBe("Janet");
|
||||
updated.LastName.ShouldBe("Smith");
|
||||
updated.BirthYear.ShouldBe(1986);
|
||||
updated.FullName.ShouldBe("Janet Smith"); // Computed property reflects new data
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Advanced Collections Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests hash set serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HashSet_SerializesAndDeserializes()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test HashSet"
|
||||
};
|
||||
entity.Tags.Add("tag1");
|
||||
entity.Tags.Add("tag2");
|
||||
entity.Tags.Add("tag3");
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Tags.ShouldNotBeNull();
|
||||
retrieved.Tags.ShouldBeOfType<HashSet<string>>();
|
||||
retrieved.Tags.Count.ShouldBe(3);
|
||||
retrieved.Tags.ShouldContain("tag1");
|
||||
retrieved.Tags.ShouldContain("tag2");
|
||||
retrieved.Tags.ShouldContain("tag3");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests iset serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ISet_SerializesAndDeserializes()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test ISet"
|
||||
};
|
||||
entity.Numbers.Add(10);
|
||||
entity.Numbers.Add(20);
|
||||
entity.Numbers.Add(30);
|
||||
entity.Numbers.Add(10); // Duplicate - should be ignored by set
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Numbers.ShouldNotBeNull();
|
||||
retrieved.Numbers.ShouldBeAssignableTo<ISet<int>>();
|
||||
retrieved.Numbers.Count.ShouldBe(3); // Only unique values
|
||||
retrieved.Numbers.ShouldContain(10);
|
||||
retrieved.Numbers.ShouldContain(20);
|
||||
retrieved.Numbers.ShouldContain(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests linked list serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LinkedList_SerializesAndDeserializes()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test LinkedList"
|
||||
};
|
||||
entity.History.AddLast("first");
|
||||
entity.History.AddLast("second");
|
||||
entity.History.AddLast("third");
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.History.ShouldNotBeNull();
|
||||
// LinkedList may be deserialized as List, then need conversion
|
||||
var historyList = retrieved.History.ToList();
|
||||
historyList.Count.ShouldBe(3);
|
||||
historyList[0].ShouldBe("first");
|
||||
historyList[1].ShouldBe("second");
|
||||
historyList[2].ShouldBe("third");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests queue serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Queue_SerializesAndDeserializes()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test Queue"
|
||||
};
|
||||
entity.PendingItems.Enqueue("item1");
|
||||
entity.PendingItems.Enqueue("item2");
|
||||
entity.PendingItems.Enqueue("item3");
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.PendingItems.ShouldNotBeNull();
|
||||
retrieved.PendingItems.Count.ShouldBe(3);
|
||||
var items = retrieved.PendingItems.ToList();
|
||||
items.ShouldContain("item1");
|
||||
items.ShouldContain("item2");
|
||||
items.ShouldContain("item3");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests stack serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Stack_SerializesAndDeserializes()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test Stack"
|
||||
};
|
||||
entity.UndoStack.Push("action1");
|
||||
entity.UndoStack.Push("action2");
|
||||
entity.UndoStack.Push("action3");
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.UndoStack.ShouldNotBeNull();
|
||||
retrieved.UndoStack.Count.ShouldBe(3);
|
||||
var items = retrieved.UndoStack.ToList();
|
||||
items.ShouldContain("action1");
|
||||
items.ShouldContain("action2");
|
||||
items.ShouldContain("action3");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests hash set with nested objects serializes and deserializes.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void HashSet_WithNestedObjects_SerializesAndDeserializes()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test Nested HashSet"
|
||||
};
|
||||
entity.Addresses.Add(new Address { Street = "123 Main St", City = new City { Name = "NYC", ZipCode = "10001" } });
|
||||
entity.Addresses.Add(new Address { Street = "456 Oak Ave", City = new City { Name = "LA", ZipCode = "90001" } });
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Addresses.ShouldNotBeNull();
|
||||
retrieved.Addresses.ShouldBeOfType<HashSet<Address>>();
|
||||
retrieved.Addresses.Count.ShouldBe(2);
|
||||
|
||||
var addressList = retrieved.Addresses.ToList();
|
||||
addressList.ShouldContain(a => a.Street == "123 Main St" && a.City.Name == "NYC");
|
||||
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()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Test Nested ISet"
|
||||
};
|
||||
entity.FavoriteCities.Add(new City { Name = "Paris", ZipCode = "75001" });
|
||||
entity.FavoriteCities.Add(new City { Name = "Tokyo", ZipCode = "100-0001" });
|
||||
entity.FavoriteCities.Add(new City { Name = "London", ZipCode = "SW1A" });
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.FavoriteCities.ShouldNotBeNull();
|
||||
retrieved.FavoriteCities.ShouldBeAssignableTo<ISet<City>>();
|
||||
retrieved.FavoriteCities.Count.ShouldBe(3);
|
||||
|
||||
var cityNames = retrieved.FavoriteCities.Select(c => c.Name).ToList();
|
||||
cityNames.ShouldContain("Paris");
|
||||
cityNames.ShouldContain("Tokyo");
|
||||
cityNames.ShouldContain("London");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests advanced collections all types in single entity.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void AdvancedCollections_AllTypesInSingleEntity()
|
||||
{
|
||||
// Arrange - Test all collection types at once
|
||||
var entity = new EntityWithAdvancedCollections
|
||||
{
|
||||
Name = "Complete Test"
|
||||
};
|
||||
|
||||
entity.Tags.Add("tag1");
|
||||
entity.Tags.Add("tag2");
|
||||
|
||||
entity.Numbers.Add(1);
|
||||
entity.Numbers.Add(2);
|
||||
|
||||
entity.History.AddLast("h1");
|
||||
entity.History.AddLast("h2");
|
||||
|
||||
entity.PendingItems.Enqueue("p1");
|
||||
entity.PendingItems.Enqueue("p2");
|
||||
|
||||
entity.UndoStack.Push("u1");
|
||||
entity.UndoStack.Push("u2");
|
||||
|
||||
entity.Addresses.Add(new Address { Street = "Street1" });
|
||||
entity.FavoriteCities.Add(new City { Name = "City1" });
|
||||
|
||||
// Act
|
||||
var id = _db.AdvancedCollectionEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.AdvancedCollectionEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Name.ShouldBe("Complete Test");
|
||||
retrieved.Tags.Count.ShouldBe(2);
|
||||
retrieved.Numbers.Count.ShouldBe(2);
|
||||
retrieved.History.Count.ShouldBe(2);
|
||||
retrieved.PendingItems.Count.ShouldBe(2);
|
||||
retrieved.UndoStack.Count.ShouldBe(2);
|
||||
retrieved.Addresses.Count().ShouldBe(1);
|
||||
retrieved.FavoriteCities.Count().ShouldBe(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Setters Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with private setters can be deserialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithPrivateSetters_CanBeDeserialized()
|
||||
{
|
||||
// Arrange
|
||||
var entity = EntityWithPrivateSetters.Create("John Doe", 30);
|
||||
|
||||
// Act
|
||||
var id = _db.PrivateSetterEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.PrivateSetterEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Id.ShouldBe(id);
|
||||
retrieved.Name.ShouldBe("John Doe");
|
||||
retrieved.Age.ShouldBe(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with private setters update works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithPrivateSetters_Update_Works()
|
||||
{
|
||||
// Arrange
|
||||
var entity1 = EntityWithPrivateSetters.Create("Alice", 25);
|
||||
var id1 = _db.PrivateSetterEntities.Insert(entity1);
|
||||
|
||||
var entity2 = EntityWithPrivateSetters.Create("Bob", 35);
|
||||
entity2.GetType().GetProperty("Id")!.SetValue(entity2, id1); // Force same Id
|
||||
|
||||
_db.PrivateSetterEntities.Update(entity2);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Act
|
||||
var retrieved = _db.PrivateSetterEntities.FindById(id1);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Id.ShouldBe(id1);
|
||||
retrieved.Name.ShouldBe("Bob");
|
||||
retrieved.Age.ShouldBe(35);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with private setters query works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithPrivateSetters_Query_Works()
|
||||
{
|
||||
// Arrange
|
||||
var entity1 = EntityWithPrivateSetters.Create("Charlie", 20);
|
||||
var entity2 = EntityWithPrivateSetters.Create("Diana", 30);
|
||||
var entity3 = EntityWithPrivateSetters.Create("Eve", 40);
|
||||
|
||||
_db.PrivateSetterEntities.Insert(entity1);
|
||||
_db.PrivateSetterEntities.Insert(entity2);
|
||||
_db.PrivateSetterEntities.Insert(entity3);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Act
|
||||
var adults = _db.PrivateSetterEntities.Find(e => e.Age >= 30).ToList();
|
||||
|
||||
// Assert
|
||||
adults.Count.ShouldBe(2);
|
||||
adults.ShouldContain(e => e.Name == "Diana");
|
||||
adults.ShouldContain(e => e.Name == "Eve");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Init-Only Setters Tests
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with init setters can be deserialized.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithInitSetters_CanBeDeserialized()
|
||||
{
|
||||
// Arrange
|
||||
var entity = new EntityWithInitSetters
|
||||
{
|
||||
Id = ObjectId.NewObjectId(),
|
||||
Name = "Jane Doe",
|
||||
Age = 28,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// Act
|
||||
var id = _db.InitSetterEntities.Insert(entity);
|
||||
_db.SaveChanges();
|
||||
var retrieved = _db.InitSetterEntities.FindById(id);
|
||||
|
||||
// Assert
|
||||
retrieved.ShouldNotBeNull();
|
||||
retrieved.Id.ShouldBe(id);
|
||||
retrieved.Name.ShouldBe("Jane Doe");
|
||||
retrieved.Age.ShouldBe(28);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests entity with init setters query works.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EntityWithInitSetters_Query_Works()
|
||||
{
|
||||
// Arrange
|
||||
var entity1 = new EntityWithInitSetters { Id = ObjectId.NewObjectId(), Name = "Alpha", Age = 20, CreatedAt = DateTime.UtcNow };
|
||||
var entity2 = new EntityWithInitSetters { Id = ObjectId.NewObjectId(), Name = "Beta", Age = 30, CreatedAt = DateTime.UtcNow };
|
||||
var entity3 = new EntityWithInitSetters { Id = ObjectId.NewObjectId(), Name = "Gamma", Age = 40, CreatedAt = DateTime.UtcNow };
|
||||
|
||||
_db.InitSetterEntities.Insert(entity1);
|
||||
_db.InitSetterEntities.Insert(entity2);
|
||||
_db.InitSetterEntities.Insert(entity3);
|
||||
_db.SaveChanges();
|
||||
|
||||
// Act
|
||||
var results = _db.InitSetterEntities.Find(e => e.Age > 25).ToList();
|
||||
|
||||
// Assert
|
||||
results.Count.ShouldBe(2);
|
||||
results.ShouldContain(e => e.Name == "Beta");
|
||||
results.ShouldContain(e => e.Name == "Gamma");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this instance.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_db?.Dispose();
|
||||
|
||||
if (File.Exists(_dbPath))
|
||||
File.Delete(_dbPath);
|
||||
if (File.Exists(_walPath))
|
||||
File.Delete(_walPath);
|
||||
}
|
||||
}
|
||||
260
tests/CBDD.Tests/Context/TestDbContext.cs
Executable file
260
tests/CBDD.Tests/Context/TestDbContext.cs
Executable file
@@ -0,0 +1,260 @@
|
||||
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.Core.Transactions;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Test context with manual collection initialization
|
||||
/// (Source Generator will automate this in the future)
|
||||
/// </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!;
|
||||
/// <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!;
|
||||
/// <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!;
|
||||
/// <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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<AnnotatedUser>();
|
||||
modelBuilder.Entity<User>().ToCollection("users");
|
||||
modelBuilder.Entity<ComplexUser>().ToCollection("complex_users");
|
||||
modelBuilder.Entity<AutoInitEntity>().ToCollection("auto_init_entities");
|
||||
modelBuilder.Entity<Person>().ToCollection("people_collection").HasIndex(p => p.Age);
|
||||
modelBuilder.Entity<PersonV2>().ToCollection("peoplev2_collection").HasIndex(p => p.Age);
|
||||
modelBuilder.Entity<Product>().ToCollection("products_collection").HasIndex(p => p.Price);
|
||||
modelBuilder.Entity<IntEntity>().HasKey(e => e.Id);
|
||||
modelBuilder.Entity<StringEntity>().HasKey(e => e.Id);
|
||||
modelBuilder.Entity<GuidEntity>().HasKey(e => e.Id);
|
||||
modelBuilder.Entity<CustomKeyEntity>().HasKey(e => e.Code);
|
||||
modelBuilder.Entity<AsyncDoc>().ToCollection("async_docs");
|
||||
modelBuilder.Entity<SchemaUser>().ToCollection("schema_users").HasKey(e => e.Id);
|
||||
modelBuilder.Entity<TestDocument>();
|
||||
modelBuilder.Entity<OrderDocument>();
|
||||
modelBuilder.Entity<ComplexDocument>();
|
||||
|
||||
modelBuilder.Entity<VectorEntity>()
|
||||
.ToCollection("vector_items")
|
||||
.HasVectorIndex(x => x.Embedding, dimensions: 3, metric: VectorMetric.L2, name: "idx_vector");
|
||||
|
||||
modelBuilder.Entity<GeoEntity>()
|
||||
.ToCollection("geo_items")
|
||||
.HasSpatialIndex(x => x.Location, name: "idx_spatial");
|
||||
|
||||
modelBuilder.Entity<Order>()
|
||||
.HasKey(x => x.Id)
|
||||
.HasConversion<OrderIdConverter>();
|
||||
|
||||
// Source Generator Feature Tests
|
||||
modelBuilder.Entity<DerivedEntity>().ToCollection("derived_entities");
|
||||
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<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<TemporalEntity>().ToCollection("temporal_entities").HasKey(e => e.Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes ForceCheckpoint.
|
||||
/// </summary>
|
||||
public void ForceCheckpoint()
|
||||
{
|
||||
Engine.Checkpoint();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes ForceCheckpoint with the requested checkpoint mode.
|
||||
/// </summary>
|
||||
/// <param name="mode">Checkpoint mode to execute.</param>
|
||||
public CheckpointResult ForceCheckpoint(CheckpointMode mode)
|
||||
{
|
||||
return Engine.Checkpoint(mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Storage.
|
||||
/// </summary>
|
||||
public StorageEngine Storage => Engine;
|
||||
}
|
||||
35
tests/CBDD.Tests/Context/TestExtendedDbContext.cs
Executable file
35
tests/CBDD.Tests/Context/TestExtendedDbContext.cs
Executable file
@@ -0,0 +1,35 @@
|
||||
using ZB.MOM.WW.CBDD.Core.Collections;
|
||||
using ZB.MOM.WW.CBDD.Core.Metadata;
|
||||
|
||||
namespace ZB.MOM.WW.CBDD.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Extended test context that inherits from TestDbContext.
|
||||
/// Used to verify that collection initialization works correctly with inheritance.
|
||||
/// </summary>
|
||||
public partial class TestExtendedDbContext : TestDbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the extended entities.
|
||||
/// </summary>
|
||||
public DocumentCollection<int, ExtendedEntity> ExtendedEntities { get; set; } = null!;
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<ExtendedEntity>()
|
||||
.ToCollection("extended_entities")
|
||||
.HasKey(e => e.Id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user