Implement checkpoint modes with docs/tests and reorganize project file layout
All checks were successful
NuGet Publish / build-and-pack (push) Successful in 46s
NuGet Publish / publish-to-gitea (push) Successful in 53s

This commit is contained in:
Joseph Doherty
2026-02-21 07:56:36 -05:00
parent 3ffd468c79
commit 4c6aaa5a3f
96 changed files with 744 additions and 249 deletions

View File

@@ -0,0 +1,152 @@
using ZB.MOM.WW.CBDD.Core;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;
using ZB.MOM.WW.CBDD.Shared;
using Xunit;
namespace ZB.MOM.WW.CBDD.Tests;
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()
{
var ct = TestContext.Current.CancellationToken;
using (var db = new Shared.TestDbContext(_dbPath))
{
using (var txn = await db.BeginTransactionAsync(ct))
{
db.AsyncDocs.Insert(new AsyncDoc { Id = 1, Name = "Async1" });
db.AsyncDocs.Insert(new AsyncDoc { Id = 2, Name = "Async2" });
await db.SaveChangesAsync(ct);
}
}
// Verify with new storage engine instance
using var db2 = new Shared.TestDbContext(_dbPath);
var doc1 = db2.AsyncDocs.FindById(1);
doc1.ShouldNotBeNull();
doc1.Name.ShouldBe("Async1");
var doc2 = db2.AsyncDocs.FindById(2);
doc2.ShouldNotBeNull();
doc2.Name.ShouldBe("Async2");
}
/// <summary>
/// Executes Async_Transaction_Rollback_Should_Discard_Data.
/// </summary>
[Fact]
public async Task Async_Transaction_Rollback_Should_Discard_Data()
{
var ct = TestContext.Current.CancellationToken;
using var db = new Shared.TestDbContext(_dbPath);
using (var txn = await db.BeginTransactionAsync(ct))
{
db.AsyncDocs.Insert(new AsyncDoc { Id = 3, Name = "RollbackMe" });
}
var doc = db.AsyncDocs.FindById(3);
doc.ShouldBeNull();
}
/// <summary>
/// Executes Bulk_Async_Insert_Should_Persist_Data.
/// </summary>
[Fact]
public async Task Bulk_Async_Insert_Should_Persist_Data()
{
using var db = new Shared.TestDbContext(_dbPath);
var docs = Enumerable.Range(1, 100).Select(i => new AsyncDoc { Id = i + 5000, Name = $"Bulk{i}" });
var ids = await db.AsyncDocs.InsertBulkAsync(docs);
ids.Count.ShouldBe(100);
var doc50 = db.AsyncDocs.FindById(5050);
doc50.ShouldNotBeNull();
doc50.Name.ShouldBe("Bulk50");
}
/// <summary>
/// Executes Bulk_Async_Update_Should_Persist_Changes.
/// </summary>
[Fact]
public async Task Bulk_Async_Update_Should_Persist_Changes()
{
using var db = new Shared.TestDbContext(_dbPath);
// 1. Insert 100 docs
var docs = Enumerable.Range(1, 100).Select(i => new AsyncDoc { Id = i + 6000, Name = $"Original{i}" }).ToList();
await db.AsyncDocs.InsertBulkAsync(docs);
// 2. Update all docs
foreach (var doc in docs)
{
doc.Name = $"Updated{doc.Id - 6000}";
}
var count = await db.AsyncDocs.UpdateBulkAsync(docs);
count.ShouldBe(100);
// 3. Verify updates
var doc50 = db.AsyncDocs.FindById(6050);
doc50.ShouldNotBeNull();
doc50.Name.ShouldBe("Updated50");
}
/// <summary>
/// Executes High_Concurrency_Async_Commits.
/// </summary>
[Fact]
public async Task High_Concurrency_Async_Commits()
{
var ct = TestContext.Current.CancellationToken;
using var db = new Shared.TestDbContext(Path.Combine(Path.GetTempPath(), $"cbdd_async_concurrency_{Guid.NewGuid()}.db"));
int threadCount = 2;
int docsPerThread = 50;
var tasks = Enumerable.Range(0, threadCount).Select(async i =>
{
// Test mix of implicit and explicit transactions
for (int j = 0; j < docsPerThread; j++)
{
int id = (i * docsPerThread) + j + 8000;
await db.AsyncDocs.InsertAsync(new AsyncDoc { Id = id, Name = $"Thread{i}_Doc{j}" });
}
});
await Task.WhenAll(tasks);
await db.SaveChangesAsync(ct);
// Verify count
var count = db.AsyncDocs.Scan(_ => true).Count();
count.ShouldBe(threadCount * docsPerThread);
}
}

View File

@@ -0,0 +1,140 @@
using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;
using ZB.MOM.WW.CBDD.Shared;
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;
using Xunit;
using static ZB.MOM.WW.CBDD.Tests.SchemaTests;
namespace ZB.MOM.WW.CBDD.Tests;
public class BulkOperationsTests : IDisposable
{
private readonly string _dbPath;
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");
_walPath = Path.Combine(Path.GetTempPath(), $"test_bulk_{Guid.NewGuid()}.wal");
_dbContext = new Shared.TestDbContext(_dbPath);
}
/// <summary>
/// Executes Dispose.
/// </summary>
public void Dispose()
{
_dbContext.Dispose();
}
/// <summary>
/// Executes UpdateBulk_UpdatesMultipleDocuments.
/// </summary>
[Fact]
public void UpdateBulk_UpdatesMultipleDocuments()
{
// Arrange: Insert 100 users
var users = new List<User>();
for (int i = 0; i < 100; i++)
{
users.Add(new User { Id = ObjectId.NewObjectId(), Name = $"User {i}", Age = 20 });
}
_dbContext.Users.InsertBulk(users);
_dbContext.SaveChanges();
// Modify users
foreach (var u in users)
{
u.Age = 30; // In-place update (int is same size)
if (u.Name.EndsWith("0")) u.Name += "_Modified_Longer"; // Force move update
}
// Act
var updatedCount = _dbContext.Users.UpdateBulk(users);
_dbContext.SaveChanges();
// Assert
updatedCount.ShouldBe(100);
// Verify changes
foreach (var u in users)
{
var stored = _dbContext.Users.FindById(u.Id);
stored.ShouldNotBeNull();
stored.Age.ShouldBe(30);
stored.Name.ShouldBe(u.Name);
}
}
/// <summary>
/// Executes DeleteBulk_RemovesMultipleDocuments.
/// </summary>
[Fact]
public void DeleteBulk_RemovesMultipleDocuments()
{
// Arrange: Insert 100 users
var users = new List<User>();
for (int i = 0; i < 100; i++)
{
users.Add(new User { Id = ObjectId.NewObjectId(), Name = $"User {i}", Age = 20 });
}
_dbContext.Users.InsertBulk(users);
_dbContext.SaveChanges();
var idsToDelete = users.Take(50).Select(u => u.Id).ToList();
// Act
var deletedCount = _dbContext.Users.DeleteBulk(idsToDelete);
_dbContext.SaveChanges();
// Assert
deletedCount.ShouldBe(50);
// Verify deleted
foreach (var id in idsToDelete)
{
_dbContext.Users.FindById(id).ShouldBeNull();
}
// Verify remaining
var remaining = users.Skip(50).ToList();
foreach (var u in remaining)
{
_dbContext.Users.FindById(u.Id).ShouldNotBeNull();
}
// Verify count
// Note: Count() is not fully implemented efficiently yet (iterates everything), but FindAll().Count() works
_dbContext.Users.FindAll().Count().ShouldBe(50);
}
/// <summary>
/// Executes DeleteBulk_WithTransaction_Rollworks.
/// </summary>
[Fact]
public void DeleteBulk_WithTransaction_Rollworks()
{
// Arrange
var user = new User { Id = ObjectId.NewObjectId(), Name = "Txn User", Age = 20 };
_dbContext.Users.Insert(user);
_dbContext.SaveChanges();
_dbContext.Users.FindById(user.Id).ShouldNotBeNull();
using (var txn = _dbContext.BeginTransaction())
{
_dbContext.Users.DeleteBulk(new[] { user.Id });
txn.Rollback();
}
// Assert: Should still exist
_dbContext.Users.FindById(user.Id).ShouldNotBeNull();
}
}

View File

@@ -0,0 +1,95 @@
using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;
using ZB.MOM.WW.CBDD.Shared;
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;
using Xunit;
namespace ZB.MOM.WW.CBDD.Tests;
public class DocumentCollectionDeleteTests : IDisposable
{
private readonly string _dbPath;
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");
_walPath = Path.Combine(Path.GetTempPath(), $"test_delete_{Guid.NewGuid()}.wal");
_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()
{
var user = new User { Id = ObjectId.NewObjectId(), Name = "To Delete", Age = 10 };
_dbContext.Users.Insert(user);
_dbContext.SaveChanges();
// Verify inserted
_dbContext.Users.FindById(user.Id).ShouldNotBeNull();
// Delete
var deleted = _dbContext.Users.Delete(user.Id);
_dbContext.SaveChanges();
// Assert
deleted.ShouldBeTrue("Delete returned false");
// Verify deleted from storage
_dbContext.Users.FindById(user.Id).ShouldBeNull();
// Verify Index is clean (FindAll uses index scan)
var all = _dbContext.Users.FindAll();
all.ShouldBeEmpty();
}
/// <summary>
/// Verifies delete returns false for a non-existent document.
/// </summary>
[Fact]
public void Delete_NonExistent_ReturnsFalse()
{
var id = ObjectId.NewObjectId();
var deleted = _dbContext.Users.Delete(id);
_dbContext.SaveChanges();
deleted.ShouldBeFalse();
}
/// <summary>
/// Verifies deletes inside a transaction commit successfully.
/// </summary>
[Fact]
public void Delete_WithTransaction_CommitsSuccessfully()
{
var user = new User { Id = ObjectId.NewObjectId(), Name = "Txn Delete", Age = 20 };
_dbContext.Users.Insert(user);
_dbContext.SaveChanges();
using (var txn = _dbContext.BeginTransaction())
{
_dbContext.Users.Delete(user.Id);
_dbContext.SaveChanges();
}
// Verify
_dbContext.Users.FindById(user.Id).ShouldBeNull();
}
}

View File

@@ -0,0 +1,72 @@
using ZB.MOM.WW.CBDD.Core.Indexing;
using ZB.MOM.WW.CBDD.Shared;
namespace ZB.MOM.WW.CBDD.Tests;
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()
{
_db.VectorItems.Insert(new VectorEntity { Title = "A", Embedding = [1f, 1f, 1f] });
_db.VectorItems.Insert(new VectorEntity { Title = "B", Embedding = [2f, 2f, 2f] });
_db.SaveChanges();
_db.VectorItems.CreateVectorIndex(v => v.Embedding, 3, VectorMetric.DotProduct, "idx_vector_extra");
var indexNames = _db.VectorItems.GetIndexes().Select(x => x.Name).ToList();
indexNames.ShouldContain("idx_vector_extra");
_db.VectorItems.DropIndex("idx_vector_extra").ShouldBeTrue();
_db.VectorItems.DropIndex("idx_vector_extra").ShouldBeFalse();
_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()
{
var first = _db.People.EnsureIndex(p => p.Age, name: "idx_people_age");
var second = _db.People.EnsureIndex(p => p.Age, name: "idx_people_age");
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();
if (File.Exists(_dbPath)) File.Delete(_dbPath);
var wal = Path.ChangeExtension(_dbPath, ".wal");
if (File.Exists(wal)) File.Delete(wal);
}
}

View File

@@ -0,0 +1,249 @@
using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;
using ZB.MOM.WW.CBDD.Shared;
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;
namespace ZB.MOM.WW.CBDD.Tests;
public class DocumentCollectionTests : IDisposable
{
private readonly string _dbPath;
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");
_walPath = Path.Combine(Path.GetTempPath(), $"test_collection_{Guid.NewGuid()}.wal");
_db = new Shared.TestDbContext(_dbPath);
}
/// <summary>
/// Verifies insert and find-by-id operations.
/// </summary>
[Fact]
public void Insert_And_FindById_Works()
{
// Arrange
var user = new User { Name = "Alice", Age = 30 };
// Act
var id = _db.Users.Insert(user);
_db.SaveChanges();
var found = _db.Users.FindById(id);
// Assert
found.ShouldNotBeNull();
found.Id.ShouldBe(id);
found.Name.ShouldBe("Alice");
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()
{
// Act
var found = _db.Users.FindById(ObjectId.NewObjectId());
// Assert
found.ShouldBeNull();
}
/// <summary>
/// Verifies find-all returns all entities.
/// </summary>
[Fact]
public void FindAll_Returns_All_Entities()
{
// Arrange
_db.Users.Insert(new User { Name = "Alice", Age = 30 });
_db.Users.Insert(new User { Name = "Bob", Age = 25 });
_db.Users.Insert(new User { Name = "Charlie", Age = 35 });
_db.SaveChanges();
// Act
var all = _db.Users.FindAll().ToList();
// Assert
all.Count.ShouldBe(3);
all.ShouldContain(u => u.Name == "Alice");
all.ShouldContain(u => u.Name == "Bob");
all.ShouldContain(u => u.Name == "Charlie");
}
/// <summary>
/// Verifies update modifies an existing entity.
/// </summary>
[Fact]
public void Update_Modifies_Entity()
{
// Arrange
var user = new User { Name = "Alice", Age = 30 };
var id = _db.Users.Insert(user);
_db.SaveChanges();
// Act
user.Age = 31;
var updated = _db.Users.Update(user);
_db.SaveChanges();
// Assert
updated.ShouldBeTrue();
var found = _db.Users.FindById(id);
found.ShouldNotBeNull();
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()
{
// Arrange
var user = new User { Id = ObjectId.NewObjectId(), Name = "Ghost", Age = 99 };
// Act
var updated = _db.Users.Update(user);
_db.SaveChanges();
// Assert
updated.ShouldBeFalse();
}
/// <summary>
/// Verifies delete removes an entity.
/// </summary>
[Fact]
public void Delete_Removes_Entity()
{
// Arrange
var user = new User { Name = "Alice", Age = 30 };
var id = _db.Users.Insert(user);
_db.SaveChanges();
// Act
var deleted = _db.Users.Delete(id);
_db.SaveChanges();
// Assert
deleted.ShouldBeTrue();
_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()
{
// Act
var deleted = _db.Users.Delete(ObjectId.NewObjectId());
_db.SaveChanges();
// Assert
deleted.ShouldBeFalse();
}
/// <summary>
/// Verifies count returns the correct entity count.
/// </summary>
[Fact]
public void Count_Returns_Correct_Count()
{
// Arrange
_db.Users.Insert(new User { Name = "Alice", Age = 30 });
_db.Users.Insert(new User { Name = "Bob", Age = 25 });
_db.SaveChanges();
// Act
var count = _db.Users.Count();
// Assert
count.ShouldBe(2);
}
/// <summary>
/// Verifies predicate queries filter entities correctly.
/// </summary>
[Fact]
public void Find_With_Predicate_Filters_Correctly()
{
// Arrange
_db.Users.Insert(new User { Name = "Alice", Age = 30 });
_db.Users.Insert(new User { Name = "Bob", Age = 25 });
_db.Users.Insert(new User { Name = "Charlie", Age = 35 });
_db.SaveChanges();
// Act
var over30 = _db.Users.Find(u => u.Age > 30).ToList();
// Assert
over30.Count().ShouldBe(1);
over30[0].Name.ShouldBe("Charlie");
}
/// <summary>
/// Verifies bulk insert stores multiple entities.
/// </summary>
[Fact]
public void InsertBulk_Inserts_Multiple_Entities()
{
// Arrange
var users = new[]
{
new User { Name = "User1", Age = 20 },
new User { Name = "User2", Age = 21 },
new User { Name = "User3", Age = 22 }
};
// Act
var count = _db.Users.InsertBulk(users);
_db.SaveChanges();
// Assert
count.Count.ShouldBe(3);
_db.Users.Count().ShouldBe(3);
}
/// <summary>
/// Verifies inserts preserve an explicitly assigned identifier.
/// </summary>
[Fact]
public void Insert_With_SpecifiedId_RetainsId()
{
// Arrange
var id = ObjectId.NewObjectId();
var user = new User { Id = id, Name = "SpecifiedID", Age = 40 };
// Act
var insertedId = _db.Users.Insert(user);
_db.SaveChanges();
// Assert
insertedId.ShouldBe(id);
var found = _db.Users.FindById(id);
found.ShouldNotBeNull();
found.Id.ShouldBe(id);
found.Name.ShouldBe("SpecifiedID");
}
/// <summary>
/// Releases test resources.
/// </summary>
public void Dispose()
{
_db?.Dispose();
}
}

View File

@@ -0,0 +1,71 @@
using ZB.MOM.WW.CBDD.Core;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
using ZB.MOM.WW.CBDD.Core.Transactions;
using ZB.MOM.WW.CBDD.Shared;
using ZB.MOM.WW.CBDD.Shared.TestDbContext_TestDbContext_Mappers;
using Xunit;
namespace ZB.MOM.WW.CBDD.Tests;
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()
{
var users = new List<User>();
for (int i = 0; i < 50; i++)
{
users.Add(new User { Id = ZB.MOM.WW.CBDD.Bson.ObjectId.NewObjectId(), Name = $"User {i}", Age = 20 });
}
_db.Users.InsertBulk(users);
_db.SaveChanges();
var insertedUsers = _db.Users.FindAll().ToList();
insertedUsers.Count.ShouldBe(50);
}
/// <summary>
/// Verifies bulk inserts spanning multiple pages persist correctly.
/// </summary>
[Fact]
public void InsertBulk_SpanningMultiplePages_PersistsCorrectly()
{
// 16KB page. User ~50 bytes. 400 users -> ~20KB -> 2 pages.
var users = new List<User>();
for (int i = 0; i < 400; i++)
{
users.Add(new User { Id = ZB.MOM.WW.CBDD.Bson.ObjectId.NewObjectId(), Name = $"User {i} with some long padding text to ensure we fill space {new string('x', 50)}", Age = 20 });
}
_db.Users.InsertBulk(users);
_db.SaveChanges();
_db.Users.Count().ShouldBe(400);
}
}

View File

@@ -0,0 +1,304 @@
using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Shared;
namespace ZB.MOM.WW.CBDD.Tests;
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()
{
var collection = _db.Set<ObjectId, User>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.Users);
}
/// <summary>
/// Tests set shorthand returns correct collection.
/// </summary>
[Fact]
public void Set_Shorthand_ReturnsCorrectCollection()
{
var collection = _db.Set<User>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.Users);
}
/// <summary>
/// Tests set int returns correct collection.
/// </summary>
[Fact]
public void Set_Int_ReturnsCorrectCollection()
{
var collection = _db.Set<int, Person>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.People);
}
/// <summary>
/// Tests set string returns correct collection.
/// </summary>
[Fact]
public void Set_String_ReturnsCorrectCollection()
{
var collection = _db.Set<string, StringEntity>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.StringEntities);
}
/// <summary>
/// Tests set guid returns correct collection.
/// </summary>
[Fact]
public void Set_Guid_ReturnsCorrectCollection()
{
var collection = _db.Set<Guid, GuidEntity>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.GuidEntities);
}
/// <summary>
/// Tests set custom key returns correct collection.
/// </summary>
[Fact]
public void Set_CustomKey_ReturnsCorrectCollection()
{
var collection = _db.Set<OrderId, Order>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.Orders);
}
/// <summary>
/// Tests set all object id collections return correct instances.
/// </summary>
[Fact]
public void Set_AllObjectIdCollections_ReturnCorrectInstances()
{
_db.Set<ObjectId, AnnotatedUser>().ShouldBeSameAs(_db.AnnotatedUsers);
_db.Set<ObjectId, ComplexUser>().ShouldBeSameAs(_db.ComplexUsers);
_db.Set<ObjectId, TestDocument>().ShouldBeSameAs(_db.TestDocuments);
_db.Set<ObjectId, OrderDocument>().ShouldBeSameAs(_db.OrderDocuments);
_db.Set<ObjectId, ComplexDocument>().ShouldBeSameAs(_db.ComplexDocuments);
_db.Set<ObjectId, PersonV2>().ShouldBeSameAs(_db.PeopleV2);
_db.Set<ObjectId, VectorEntity>().ShouldBeSameAs(_db.VectorItems);
_db.Set<ObjectId, GeoEntity>().ShouldBeSameAs(_db.GeoItems);
}
/// <summary>
/// Tests set all int collections return correct instances.
/// </summary>
[Fact]
public void Set_AllIntCollections_ReturnCorrectInstances()
{
_db.Set<int, AutoInitEntity>().ShouldBeSameAs(_db.AutoInitEntities);
_db.Set<int, Product>().ShouldBeSameAs(_db.Products);
_db.Set<int, IntEntity>().ShouldBeSameAs(_db.IntEntities);
_db.Set<int, AsyncDoc>().ShouldBeSameAs(_db.AsyncDocs);
_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()
{
var users = _db.Set<User>();
var user = new User { Name = "Alice", Age = 30 };
var id = users.Insert(user);
var found = users.FindById(id);
found.ShouldNotBeNull();
found.Name.ShouldBe("Alice");
found.Age.ShouldBe(30);
}
/// <summary>
/// Tests set with int key can perform operations.
/// </summary>
[Fact]
public void Set_WithIntKey_CanPerformOperations()
{
var products = _db.Set<int, Product>();
var product = new Product { Id = 1, Title = "Widget", Price = 9.99m };
products.Insert(product);
var found = products.FindById(1);
found.ShouldNotBeNull();
found.Title.ShouldBe("Widget");
found.Price.ShouldBe(9.99m);
}
}
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()
{
var collection = _db.Set<int, ExtendedEntity>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.ExtendedEntities);
}
/// <summary>
/// Tests set parent collection returns correct instance.
/// </summary>
[Fact]
public void Set_ParentCollection_ReturnsCorrectInstance()
{
var collection = _db.Set<ObjectId, User>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.Users);
}
/// <summary>
/// Tests set parent shorthand returns correct instance.
/// </summary>
[Fact]
public void Set_ParentShorthand_ReturnsCorrectInstance()
{
var collection = _db.Set<User>();
collection.ShouldNotBeNull();
collection.ShouldBeSameAs(_db.Users);
}
/// <summary>
/// Tests set parent int collection returns correct instance.
/// </summary>
[Fact]
public void Set_ParentIntCollection_ReturnsCorrectInstance()
{
_db.Set<int, Person>().ShouldBeSameAs(_db.People);
_db.Set<int, Product>().ShouldBeSameAs(_db.Products);
}
/// <summary>
/// Tests set parent custom key returns correct instance.
/// </summary>
[Fact]
public void Set_ParentCustomKey_ReturnsCorrectInstance()
{
var collection = _db.Set<OrderId, Order>();
collection.ShouldNotBeNull();
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()
{
var entities = _db.Set<int, ExtendedEntity>();
var entity = new ExtendedEntity { Id = 1, Description = "Test", CreatedAt = DateTime.UtcNow };
entities.Insert(entity);
var found = entities.FindById(1);
found.ShouldNotBeNull();
found.Description.ShouldBe("Test");
}
/// <summary>
/// Tests set parent collection can perform operations.
/// </summary>
[Fact]
public void Set_ParentCollection_CanPerformOperations()
{
var users = _db.Set<User>();
var user = new User { Name = "Bob", Age = 25 };
var id = users.Insert(user);
var found = users.FindById(id);
found.ShouldNotBeNull();
found.Name.ShouldBe("Bob");
}
}