539 lines
17 KiB
C#
Executable File
539 lines
17 KiB
C#
Executable File
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;
|
|
|
|
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
|
|
|
|
[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
|
|
}
|
|
|
|
[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");
|
|
}
|
|
|
|
[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
|
|
|
|
[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");
|
|
}
|
|
|
|
[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
|
|
|
|
[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");
|
|
}
|
|
|
|
[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);
|
|
}
|
|
|
|
[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");
|
|
}
|
|
|
|
[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");
|
|
}
|
|
|
|
[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");
|
|
}
|
|
|
|
[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");
|
|
}
|
|
|
|
[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");
|
|
}
|
|
|
|
[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
|
|
|
|
[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);
|
|
}
|
|
|
|
[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);
|
|
}
|
|
|
|
[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
|
|
|
|
[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);
|
|
}
|
|
|
|
[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
|
|
|
|
public void Dispose()
|
|
{
|
|
_db?.Dispose();
|
|
|
|
if (File.Exists(_dbPath))
|
|
File.Delete(_dbPath);
|
|
if (File.Exists(_walPath))
|
|
File.Delete(_walPath);
|
|
}
|
|
}
|