using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Shared;
using System.Linq;
namespace ZB.MOM.WW.CBDD.Tests;
///
/// 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.)
///
public class SourceGeneratorFeaturesTests : IDisposable
{
private readonly string _dbPath;
private readonly string _walPath;
private readonly Shared.TestDbContext _db;
///
/// Initializes a new instance of the class.
///
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
///
/// Tests derived entity inherits id from base class.
///
[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
}
///
/// Tests derived entity update works with inherited id.
///
[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");
}
///
/// Tests derived entity query works with inherited properties.
///
[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
///
/// Tests computed properties are not serialized.
///
[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");
}
///
/// Tests computed properties update does not break.
///
[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
///
/// Tests hash set serializes and deserializes.
///
[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>();
retrieved.Tags.Count.ShouldBe(3);
retrieved.Tags.ShouldContain("tag1");
retrieved.Tags.ShouldContain("tag2");
retrieved.Tags.ShouldContain("tag3");
}
///
/// Tests iset serializes and deserializes.
///
[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>();
retrieved.Numbers.Count.ShouldBe(3); // Only unique values
retrieved.Numbers.ShouldContain(10);
retrieved.Numbers.ShouldContain(20);
retrieved.Numbers.ShouldContain(30);
}
///
/// Tests linked list serializes and deserializes.
///
[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");
}
///
/// Tests queue serializes and deserializes.
///
[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");
}
///
/// Tests stack serializes and deserializes.
///
[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");
}
///
/// Tests hash set with nested objects serializes and deserializes.
///
[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>();
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");
}
///
/// Tests iset with nested objects serializes and deserializes.
///
[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>();
retrieved.FavoriteCities.Count.ShouldBe(3);
var cityNames = retrieved.FavoriteCities.Select(c => c.Name).ToList();
cityNames.ShouldContain("Paris");
cityNames.ShouldContain("Tokyo");
cityNames.ShouldContain("London");
}
///
/// Tests advanced collections all types in single entity.
///
[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
///
/// Tests entity with private setters can be deserialized.
///
[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);
}
///
/// Tests entity with private setters update works.
///
[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);
}
///
/// Tests entity with private setters query works.
///
[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
///
/// Tests entity with init setters can be deserialized.
///
[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);
}
///
/// Tests entity with init setters query works.
///
[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
///
/// Disposes the resources used by this instance.
///
public void Dispose()
{
_db?.Dispose();
if (File.Exists(_dbPath))
File.Delete(_dbPath);
if (File.Exists(_walPath))
File.Delete(_walPath);
}
}