diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDataSourceTests.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDataSourceTests.cs deleted file mode 100644 index bc26932..0000000 --- a/AyCode.Services.Server.Tests/SignalRs/SignalRDataSourceTests.cs +++ /dev/null @@ -1,174 +0,0 @@ -using AyCode.Core.Enums; -using AyCode.Core.Extensions; -using AyCode.Core.Helpers; -using AyCode.Core.Serializers.Binaries; -using AyCode.Core.Serializers.Jsons; -using AyCode.Core.Tests.TestModels; -using AyCode.Services.Server.SignalRs; -using AyCode.Services.SignalRs; - -namespace AyCode.Services.Server.Tests.SignalRs; - -#region DataSource Implementations - -public class TestOrderItemListDataSource : AcSignalRDataSource> -{ - public TestOrderItemListDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags) - : base(signalRClient, crudTags) { } -} - -public class TestOrderItemObservableDataSource : AcSignalRDataSource> -{ - public TestOrderItemObservableDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags) - : base(signalRClient, crudTags) { } -} - -#endregion - -#region Abstract Test Base - -/// -/// Base class for SignalR DataSource tests. -/// Derived classes specify the serializer type and collection type. -/// -/// The concrete DataSource type -/// The inner list type (List or AcObservableCollection) -public abstract class SignalRDataSourceTestBase - where TDataSource : AcSignalRDataSource - where TIList : class, IList -{ - protected abstract AcSerializerOptions SerializerOption { get; } - protected abstract TDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags); - - protected TestLogger _logger = null!; - protected TestableSignalRHub2 _hub = null!; - protected TestableSignalRClient2 _client = null!; - protected TestSignalRService2 _service = null!; - protected SignalRCrudTags _crudTags = null!; - - [TestInitialize] - public void Setup() - { - _logger = new TestLogger(); - _hub = new TestableSignalRHub2(); - _service = new TestSignalRService2(); - _client = new TestableSignalRClient2(_hub, _logger); - - _hub.SetSerializerType(SerializerOption); - _hub.RegisterService(_service, _client); - - _crudTags = new SignalRCrudTags( - TestSignalRTags.DataSourceGetAll, - TestSignalRTags.DataSourceGetItem, - TestSignalRTags.DataSourceAdd, - TestSignalRTags.DataSourceUpdate, - TestSignalRTags.DataSourceRemove - ); - } - - #region Load Tests - - [TestMethod] - public async Task LoadDataSource_ReturnsAllItems() - { - var dataSource = CreateDataSource(_client, _crudTags); - - await dataSource.LoadDataSource(); - - Assert.AreEqual(3, dataSource.Count); - Assert.AreEqual("Product A", dataSource[0].ProductName); - } - - [TestMethod] - public async Task LoadItem_ReturnsSingleItem() - { - var dataSource = CreateDataSource(_client, _crudTags); - - var result = await dataSource.LoadItem(2); - - Assert.IsNotNull(result); - Assert.AreEqual(2, result.Id); - Assert.AreEqual("Product B", result.ProductName); - } - - #endregion - - #region Add Tests - - [TestMethod] - public async Task Add_WithAutoSave_AddsItem() - { - var dataSource = CreateDataSource(_client, _crudTags); - var newItem = new TestOrderItem { Id = 100, ProductName = "New Product", Quantity = 5, UnitPrice = 50m }; - - var result = await dataSource.Add(newItem, autoSave: true); - - Assert.AreEqual(1, dataSource.Count); - Assert.AreEqual("New Product", result.ProductName); - } - - [TestMethod] - public void Add_WithoutAutoSave_AddsToTrackingOnly() - { - var dataSource = CreateDataSource(_client, _crudTags); - var newItem = new TestOrderItem { Id = 100, ProductName = "New Product" }; - - dataSource.Add(newItem); - - Assert.AreEqual(1, dataSource.Count); - Assert.AreEqual(1, dataSource.GetTrackingItems().Count); - Assert.AreEqual(TrackingState.Add, dataSource.GetTrackingItems()[0].TrackingState); - } - - #endregion - - #region SaveChanges Tests - - [TestMethod] - public async Task SaveChanges_SavesTrackedItems() - { - var dataSource = CreateDataSource(_client, _crudTags); - dataSource.Add(new TestOrderItem { Id = 101, ProductName = "Item 1" }); - dataSource.Add(new TestOrderItem { Id = 102, ProductName = "Item 2" }); - - var unsaved = await dataSource.SaveChanges(); - - Assert.AreEqual(0, unsaved.Count); - Assert.AreEqual(0, dataSource.GetTrackingItems().Count); - } - - [TestMethod] - public async Task SaveChangesAsync_ClearsTracking() - { - var dataSource = CreateDataSource(_client, _crudTags); - dataSource.Add(new TestOrderItem { Id = 103, ProductName = "Item 3" }); - - await dataSource.SaveChangesAsync(); - - Assert.AreEqual(0, dataSource.GetTrackingItems().Count); - } - - #endregion -} - -#endregion - -#region DataSources - -[TestClass] -public class SignalRDataSourceTests_List : SignalRDataSourceTestBase> -{ - protected override AcSerializerOptions SerializerOption => AcBinarySerializerOptions.Default; - protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) - => new(client, crudTags); -} - -[TestClass] -public class SignalRDataSourceTests_Observable : SignalRDataSourceTestBase> -{ - protected override AcSerializerOptions SerializerOption => AcBinarySerializerOptions.Default; - - protected override TestOrderItemObservableDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) - => new(client, crudTags); -} -#endregion diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTestBase.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTestBase.cs new file mode 100644 index 0000000..f55a2ec --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTestBase.cs @@ -0,0 +1,981 @@ +using System.Collections; +using AyCode.Core.Enums; +using AyCode.Core.Extensions; +using AyCode.Core.Helpers; +using AyCode.Core.Serializers.Jsons; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.Server.SignalRs; +using AyCode.Services.SignalRs; +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +#region Abstract Test Base +/// +/// Base class for SignalR DataSource tests with full round-trip coverage. +/// Tests the complete path: DataSource -> SignalRClient -> SignalRHub -> Service -> Response -> SignalRClient -> DataSource +/// Derived classes specify the serializer type and collection type. +/// +/// The concrete DataSource type +/// The inner list type (List or AcObservableCollection) +public abstract class SignalRDataSourceTestBase + where TDataSource : AcSignalRDataSource + where TIList : class, IList +{ + protected abstract AcSerializerOptions SerializerOption { get; } + protected abstract TDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags); + protected TestLogger _logger = null!; + protected TestableSignalRHub2 _hub = null!; + protected TestableSignalRClient2 _client = null!; + protected TestSignalRService2 _service = null!; + protected SignalRCrudTags _crudTags = null!; + [TestInitialize] + public void Setup() + { + _logger = new TestLogger(); + + _hub = new TestableSignalRHub2(); + _service = new TestSignalRService2(); + _client = new TestableSignalRClient2(_hub, _logger); + + _hub.SetSerializerType(SerializerOption); + _hub.RegisterService(_service, _client); + + _crudTags = new SignalRCrudTags( + TestSignalRTags.DataSourceGetAll, + TestSignalRTags.DataSourceGetItem, + TestSignalRTags.DataSourceAdd, + TestSignalRTags.DataSourceUpdate, + TestSignalRTags.DataSourceRemove + ); + } + + #region LoadDataSource Tests + [TestMethod] + public virtual async Task LoadDataSource_ReturnsAllItems() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + Assert.AreEqual(3, dataSource.Count); + Assert.AreEqual("Product A", dataSource[0].ProductName); + Assert.AreEqual("Product B", dataSource[1].ProductName); + Assert.AreEqual("Product C", dataSource[2].ProductName); + } + + [TestMethod] + public virtual async Task LoadDataSource_ClearsChangeTracking_ByDefault() + { + var dataSource = CreateDataSource(_client, _crudTags); + dataSource.Add(new TestOrderItem { Id = 999, ProductName = "Tracked" }); + + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + + await dataSource.LoadDataSource(); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task LoadDataSource_PreservesChangeTracking_WhenFalse() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + dataSource.Add(new TestOrderItem { Id = 999, ProductName = "Tracked" }); + await dataSource.LoadDataSource(clearChangeTracking: false); + + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task LoadDataSource_InvokesOnDataSourceLoaded() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var callbackInvoked = false; + dataSource.OnDataSourceLoaded = () => { callbackInvoked = true; return Task.CompletedTask; }; + await dataSource.LoadDataSource(); + + Assert.IsTrue(callbackInvoked); + } + + [TestMethod] + public virtual async Task LoadDataSource_MultipleCalls_RefreshesData() + { + var dataSource = CreateDataSource(_client, _crudTags); + + await dataSource.LoadDataSource(); + var firstCount = dataSource.Count; + + await dataSource.LoadDataSource(); + var secondCount = dataSource.Count; + + Assert.AreEqual(firstCount, secondCount); + Assert.AreEqual(3, secondCount); + } + + #endregion + #region LoadDataSourceAsync Tests + + [TestMethod] + public virtual async Task LoadDataSourceAsync_LoadsDataViaCallback() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var loadCompleted = false; + dataSource.OnDataSourceLoaded = () => + { + loadCompleted = true; + return Task.CompletedTask; + }; + + await dataSource.LoadDataSourceAsync(); + + Assert.IsTrue(TaskHelper.WaitTo(() => loadCompleted, 5000)); + Assert.AreEqual(3, dataSource.Count); + } + + #endregion + #region LoadItem Tests + [TestMethod] + public virtual async Task LoadItem_ReturnsSingleItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + var result = await dataSource.LoadItem(2); + + Assert.IsNotNull(result); + Assert.AreEqual(2, result.Id); + Assert.AreEqual("Product B", result.ProductName); + } + + [TestMethod] + public virtual async Task LoadItem_AddsToDataSource_WhenNotExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.AreEqual(0, dataSource.Count); + + await dataSource.LoadItem(1); + + Assert.AreEqual(1, dataSource.Count); + Assert.AreEqual("Product A", dataSource[0].ProductName); + } + + [TestMethod] + public virtual async Task LoadItem_UpdatesExisting_WhenAlreadyLoaded() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var originalItem = dataSource[0]; + var reloaded = await dataSource.LoadItem(originalItem.Id); + + Assert.AreEqual(3, dataSource.Count); + Assert.IsNotNull(reloaded); + } + + [TestMethod] + public virtual async Task LoadItem_InvokesOnDataSourceItemChanged() + { + var dataSource = CreateDataSource(_client, _crudTags); + + ItemChangedEventArgs? eventArgs = null; + + dataSource.OnDataSourceItemChanged = args => { eventArgs = args; return Task.CompletedTask; }; + await dataSource.LoadItem(1); + + Assert.IsNotNull(eventArgs); + Assert.AreEqual(TrackingState.Get, eventArgs.TrackingState); + Assert.AreEqual(1, eventArgs.Item.Id); + } + + [TestMethod] + public virtual async Task LoadItem_ReturnsNull_WhenNotFound() + { + var dataSource = CreateDataSource(_client, _crudTags); + var result = await dataSource.LoadItem(9999); + + Assert.IsNull(result); + } + #endregion + #region Add Tests + [TestMethod] + public virtual async Task Add_WithAutoSave_AddsItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + var newItem = new TestOrderItem { Id = 100, ProductName = "New Product", Quantity = 5, UnitPrice = 50m }; + var result = await dataSource.Add(newItem, autoSave: true); + + Assert.AreEqual(1, dataSource.Count); + Assert.AreEqual("New Product", result.ProductName); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual void Add_WithoutAutoSave_AddsToTrackingOnly() + { + var dataSource = CreateDataSource(_client, _crudTags); + var newItem = new TestOrderItem { Id = 100, ProductName = "New Product" }; + dataSource.Add(newItem); + + Assert.AreEqual(1, dataSource.Count); + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + Assert.AreEqual(TrackingState.Add, dataSource.GetTrackingItems()[0].TrackingState); + } + + [TestMethod] + public virtual void Add_DuplicateId_ThrowsException() + { + var dataSource = CreateDataSource(_client, _crudTags); + dataSource.Add(new TestOrderItem { Id = 100, ProductName = "First" }); + + Assert.ThrowsExactly(() => + { + dataSource.Add(new TestOrderItem { Id = 100, ProductName = "Duplicate" }); + }); + } + + [TestMethod] + public virtual void Add_DefaultId_ThrowsException() + { + var dataSource = CreateDataSource(_client, _crudTags); + + Assert.ThrowsExactly(() => + { + dataSource.Add(new TestOrderItem { Id = 0, ProductName = "Invalid" }); + }); + } + + [TestMethod] + public virtual void AddRange_AddsMultipleItems() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var items = new[] + { + new TestOrderItem { Id = 101, ProductName = "Item 1" }, + new TestOrderItem { Id = 102, ProductName = "Item 2" }, + new TestOrderItem { Id = 103, ProductName = "Item 3" } + }; + dataSource.AddRange(items); + Assert.AreEqual(3, dataSource.Count); + } + #endregion + #region AddOrUpdate Tests + [TestMethod] + public virtual async Task AddOrUpdate_AddsNew_WhenNotExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + var newItem = new TestOrderItem { Id = 200, ProductName = "Brand New" }; + var result = await dataSource.AddOrUpdate(newItem, autoSave: true); + + Assert.AreEqual(1, dataSource.Count); + Assert.AreEqual("Brand New", result.ProductName); + } + + [TestMethod] + public virtual async Task AddOrUpdate_UpdatesExisting_WhenExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var existingId = dataSource[0].Id; + var updatedItem = new TestOrderItem { Id = existingId, ProductName = "Updated Name", Quantity = 999 }; + _ = await dataSource.AddOrUpdate(updatedItem, autoSave: true); + + Assert.AreEqual(3, dataSource.Count); + Assert.AreEqual("Updated Name", dataSource[0].ProductName); + } + #endregion + #region Insert Tests + [TestMethod] + public virtual void Insert_AtIndex_InsertsCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "First" }); + dataSource.Add(new TestOrderItem { Id = 3, ProductName = "Third" }); + dataSource.Insert(1, new TestOrderItem { Id = 2, ProductName = "Second" }); + + Assert.AreEqual(3, dataSource.Count); + Assert.AreEqual("Second", dataSource[1].ProductName); + Assert.AreEqual(3, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task Insert_WithAutoSave_SavesImmediately() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var newItem = new TestOrderItem { Id = 500, ProductName = "Inserted" }; + _ = await dataSource.Insert(0, newItem, autoSave: true); + + Assert.AreEqual(1, dataSource.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + #endregion + #region Update Tests + [TestMethod] + public virtual async Task Update_ByIndex_UpdatesCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var updatedItem = new TestOrderItem + { + Id = dataSource[0].Id, + ProductName = "Updated Product", + Quantity = 100 + }; + + _ = await dataSource.Update(0, updatedItem, autoSave: true); + + Assert.AreEqual("Updated Product", dataSource[0].ProductName); + Assert.AreEqual(100, dataSource[0].Quantity); + } + + [TestMethod] + public virtual async Task Update_ByItem_UpdatesCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var updatedItem = new TestOrderItem + { + Id = dataSource[1].Id, + ProductName = "Updated B", + Quantity = 50 + }; + + _ = await dataSource.Update(updatedItem, autoSave: true); + + Assert.AreEqual("Updated B", dataSource[1].ProductName); + } + + [TestMethod] + public virtual void Indexer_Set_TracksUpdate() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Original" }); + dataSource[0] = new TestOrderItem { Id = 1, ProductName = "Modified" }; + + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + Assert.AreEqual(TrackingState.Add, dataSource.GetTrackingItems()[0].TrackingState); + } + + #endregion + #region Remove Tests + [TestMethod] + public virtual async Task Remove_ById_RemovesItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var idToRemove = dataSource[0].Id; + var result = await dataSource.Remove(idToRemove, autoSave: true); + + Assert.IsTrue(result); + Assert.AreEqual(2, dataSource.Count); + } + + [TestMethod] + public virtual async Task Remove_ByItem_RemovesItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var itemToRemove = dataSource[1]; + var result = await dataSource.Remove(itemToRemove, autoSave: true); + + Assert.IsTrue(result); + Assert.AreEqual(2, dataSource.Count); + } + + [TestMethod] + public virtual void Remove_WithoutAutoSave_TracksRemoval() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "ToRemove" }); + + dataSource.GetTrackingItems().Clear(); + dataSource.Remove(dataSource[0]); + + Assert.AreEqual(0, dataSource.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + Assert.AreEqual(TrackingState.Remove, dataSource.GetTrackingItems()[0].TrackingState); + } + + [TestMethod] + public virtual void RemoveAt_RemovesCorrectItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "First" }); + dataSource.Add(new TestOrderItem { Id = 2, ProductName = "Second" }); + dataSource.Add(new TestOrderItem { Id = 3, ProductName = "Third" }); + + dataSource.GetTrackingItems().Clear(); + dataSource.RemoveAt(1); + + Assert.AreEqual(2, dataSource.Count); + Assert.AreEqual("First", dataSource[0].ProductName); + Assert.AreEqual("Third", dataSource[1].ProductName); + } + + [TestMethod] + public virtual async Task RemoveAt_WithAutoSave_SavesImmediately() + { + var dataSource = CreateDataSource(_client, _crudTags); + + await dataSource.LoadDataSource(); + await dataSource.RemoveAt(0, autoSave: true); + + Assert.AreEqual(2, dataSource.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual void TryRemove_ReturnsTrue_WhenExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Test" }); + var result = dataSource.TryRemove(1, out var removedItem); + + Assert.IsTrue(result); + Assert.IsNotNull(removedItem); + Assert.AreEqual("Test", removedItem.ProductName); + } + + [TestMethod] + public virtual void TryRemove_ReturnsFalse_WhenNotExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + var result = dataSource.TryRemove(9999, out var removedItem); + + Assert.IsFalse(result); + Assert.IsNull(removedItem); + } + #endregion + #region SaveChanges Tests + [TestMethod] + public virtual async Task SaveChanges_SavesTrackedItems() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 101, ProductName = "Item 1" }); + dataSource.Add(new TestOrderItem { Id = 102, ProductName = "Item 2" }); + + var unsaved = await dataSource.SaveChanges(); + + Assert.AreEqual(0, unsaved.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task SaveChangesAsync_ClearsTracking() + { + var dataSource = CreateDataSource(_client, _crudTags); + dataSource.Add(new TestOrderItem { Id = 103, ProductName = "Item 3" }); + + await dataSource.SaveChangesAsync(); + + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task SaveItem_ById_SavesSpecificItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 201, ProductName = "Specific" }); + var result = await dataSource.SaveItem(201); + + Assert.AreEqual("Specific", result.ProductName); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task SaveItem_WithTrackingState_SavesCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var item = dataSource[0]; + var result = await dataSource.SaveItem(item, TrackingState.Update); + + Assert.IsNotNull(result); + } + #endregion + #region Tracking Tests + [TestMethod] + public virtual void SetTrackingStateToUpdate_MarksItemForUpdate() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Test" }); + //await dataSource.SaveChanges(); // Előbb mentsük el, hogy ne Add legyen a tracking state + + dataSource.SetTrackingStateToUpdate(dataSource[0]); + + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + Assert.AreEqual(TrackingState.Update, dataSource.GetTrackingItems()[0].TrackingState); + } + + [TestMethod] + public virtual void SetTrackingStateToUpdate_DoesNotChangeAddState() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "New Item" }); + dataSource.SetTrackingStateToUpdate(dataSource[0]); + + Assert.AreEqual(TrackingState.Add, dataSource.GetTrackingItems()[0].TrackingState); + } + + [TestMethod] + public virtual void TryGetTrackingItem_ReturnsTrue_WhenTracked() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Tracked" }); + var result = dataSource.TryGetTrackingItem(1, out var trackingItem); + + Assert.IsTrue(result); + Assert.IsNotNull(trackingItem); + Assert.AreEqual(TrackingState.Add, trackingItem.TrackingState); + } + + [TestMethod] + public virtual void TryGetTrackingItem_ReturnsFalse_WhenNotTracked() + { + var dataSource = CreateDataSource(_client, _crudTags); + var result = dataSource.TryGetTrackingItem(9999, out var trackingItem); + + Assert.IsFalse(result); + Assert.IsNull(trackingItem); + } + #endregion + #region Rollback Tests + [TestMethod] + public virtual void TryRollbackItem_RevertsAddedItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Added" }); + var result = dataSource.TryRollbackItem(1, out var originalValue); + + Assert.IsTrue(result); + Assert.AreEqual(0, dataSource.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task TryRollbackItem_RevertsUpdatedItem() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var originalName = dataSource[0].ProductName; + dataSource[0] = new TestOrderItem { Id = dataSource[0].Id, ProductName = "Changed" }; + var result = dataSource.TryRollbackItem(dataSource[0].Id, out var originalValue); + + Assert.IsTrue(result); + Assert.IsNotNull(originalValue); + } + + [TestMethod] + public virtual void Rollback_RevertsAllChanges() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Item1" }); + dataSource.Add(new TestOrderItem { Id = 2, ProductName = "Item2" }); + dataSource.Rollback(); + + Assert.AreEqual(0, dataSource.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + #endregion + #region Collection Operations Tests + [TestMethod] + public virtual async Task Count_ReturnsCorrectValue() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + Assert.AreEqual(3, dataSource.Count); + } + + [TestMethod] + public virtual void Clear_RemovesAllItems() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Item1" }); + dataSource.Add(new TestOrderItem { Id = 2, ProductName = "Item2" }); + dataSource.Clear(); + + Assert.AreEqual(0, dataSource.Count); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual void Clear_WithoutClearingTracking_PreservesTracking() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Item1" }); + dataSource.Clear(clearChangeTracking: false); + + Assert.AreEqual(0, dataSource.Count); + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task Contains_ReturnsTrue_WhenItemExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + Assert.IsTrue(dataSource.Contains(dataSource[0])); + } + + [TestMethod] + public virtual void Contains_ReturnsFalse_WhenItemNotExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.IsFalse(dataSource.Contains(new TestOrderItem { Id = 9999 })); + } + + [TestMethod] + public virtual async Task IndexOf_ReturnsCorrectIndex() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + Assert.AreEqual(0, dataSource.IndexOf(dataSource[0])); + Assert.AreEqual(1, dataSource.IndexOf(dataSource[1])); + Assert.AreEqual(2, dataSource.IndexOf(dataSource[2])); + } + + [TestMethod] + public virtual void IndexOf_ById_ReturnsCorrectIndex() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 5, ProductName = "Item5" }); + Assert.AreEqual(0, dataSource.IndexOf(5)); + } + + [TestMethod] + public virtual void TryGetIndex_ReturnsTrue_WhenExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 10, ProductName = "Test" }); + var result = dataSource.TryGetIndex(10, out var index); + + Assert.IsTrue(result); + Assert.AreEqual(0, index); + } + + [TestMethod] + public virtual async Task TryGetValue_ReturnsItem_WhenExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + + await dataSource.LoadDataSource(); + var result = dataSource.TryGetValue(1, out var item); + + Assert.IsTrue(result); + Assert.IsNotNull(item); + Assert.AreEqual("Product A", item.ProductName); + } + + [TestMethod] + public virtual void TryGetValue_ReturnsFalse_WhenNotExists() + { + var dataSource = CreateDataSource(_client, _crudTags); + var result = dataSource.TryGetValue(9999, out var item); + + Assert.IsFalse(result); + Assert.IsNull(item); + } + + [TestMethod] + public virtual async Task CopyTo_CopiesAllItems() + { + var dataSource = CreateDataSource(_client, _crudTags); + + await dataSource.LoadDataSource(); + var array = new TestOrderItem[3]; + dataSource.CopyTo(array); + + Assert.AreEqual("Product A", array[0].ProductName); + Assert.AreEqual("Product B", array[1].ProductName); + Assert.AreEqual("Product C", array[2].ProductName); + } + + [TestMethod] + public virtual async Task GetEnumerator_EnumeratesAllItems() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var count = dataSource.Count(); + + Assert.AreEqual(3, count); + } + + [TestMethod] + public virtual async Task AsReadOnly_ReturnsReadOnlyCollection() + { + var dataSource = CreateDataSource(_client, _crudTags); + + await dataSource.LoadDataSource(); + var readOnly = dataSource.AsReadOnly(); + + Assert.AreEqual(3, readOnly.Count); + Assert.IsInstanceOfType(readOnly, typeof(System.Collections.ObjectModel.ReadOnlyCollection)); + } + #endregion + #region Working Reference List Tests + [TestMethod] + public virtual async Task SetWorkingReferenceList_SetsNewInnerList() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + var externalList = Activator.CreateInstance(); + dataSource.SetWorkingReferenceList(externalList); + + Assert.IsTrue(dataSource.HasWorkingReferenceList); + Assert.AreEqual(3, dataSource.Count); + } + + [TestMethod] + public virtual async Task GetReferenceInnerList_ReturnsInnerList() + { + var dataSource = CreateDataSource(_client, _crudTags); + + await dataSource.LoadDataSource(); + var innerList = dataSource.GetReferenceInnerList(); + + Assert.IsNotNull(innerList); + Assert.AreEqual(3, innerList.Count); + } + #endregion + #region Sync State Tests + [TestMethod] + public virtual void IsSyncing_IsFalse_Initially() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.IsFalse(dataSource.IsSyncing); + } + + [TestMethod] + public virtual async Task OnSyncingStateChanged_Fires_DuringLoad() + { + var dataSource = CreateDataSource(_client, _crudTags); + var syncStarted = false; + var syncEnded = false; + + dataSource.OnSyncingStateChanged += isSyncing => + { + if (isSyncing) syncStarted = true; + else syncEnded = true; + }; + + await dataSource.LoadDataSource(); + + Assert.IsTrue(syncStarted); + Assert.IsTrue(syncEnded); + } + #endregion + #region Context and Filter Tests + [TestMethod] + public virtual void ContextIds_CanBeSetAndRetrieved() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var contextIds = new object[] { 1, "test", Guid.NewGuid() }; + dataSource.ContextIds = contextIds; + + Assert.AreEqual(3, dataSource.ContextIds?.Length); + } + + [TestMethod] + public virtual void FilterText_CanBeSetAndRetrieved() + { + var dataSource = CreateDataSource(_client, _crudTags); + dataSource.FilterText = "search term"; + + Assert.AreEqual("search term", dataSource.FilterText); + } + #endregion + #region IList Interface Tests + [TestMethod] + public virtual void IList_Add_ReturnsCorrectIndex() + { + var dataSource = CreateDataSource(_client, _crudTags); + var item = new TestOrderItem { Id = 1, ProductName = "Test" }; + var index = ((IList)dataSource).Add(item); + + Assert.AreEqual(0, index); + } + + [TestMethod] + public virtual void IList_Contains_WorksCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var item = new TestOrderItem { Id = 1, ProductName = "Test" }; + dataSource.Add(item); + + Assert.IsTrue(((IList)dataSource).Contains(item)); + } + + [TestMethod] + public virtual void IList_IndexOf_WorksCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var item = new TestOrderItem { Id = 1, ProductName = "Test" }; + dataSource.Add(item); + + Assert.AreEqual(0, ((IList)dataSource).IndexOf(item)); + } + + [TestMethod] + public virtual void IList_Insert_WorksCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "First" }); + var newItem = new TestOrderItem { Id = 2, ProductName = "Inserted" }; + + ((IList)dataSource).Insert(0, newItem); + + Assert.AreEqual("Inserted", dataSource[0].ProductName); + } + + [TestMethod] + public virtual void IList_Remove_WorksCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + var item = new TestOrderItem { Id = 1, ProductName = "Test" }; + dataSource.Add(item); + + ((IList)dataSource).Remove(item); + Assert.AreEqual(0, dataSource.Count); + } + + [TestMethod] + public virtual void IList_Indexer_GetSet_WorksCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Original" }); + ((IList)dataSource)[0] = new TestOrderItem { Id = 1, ProductName = "Modified" }; + + Assert.AreEqual("Modified", dataSource[0].ProductName); + } + + [TestMethod] + public virtual void ICollection_CopyTo_WorksCorrectly() + { + var dataSource = CreateDataSource(_client, _crudTags); + + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Item1" }); + dataSource.Add(new TestOrderItem { Id = 2, ProductName = "Item2" }); + + var array = new TestOrderItem[2]; + ((ICollection)dataSource).CopyTo(array, 0); + + Assert.AreEqual("Item1", array[0].ProductName); + Assert.AreEqual("Item2", array[1].ProductName); + } + + [TestMethod] + public virtual void IsSynchronized_ReturnsTrue() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.IsTrue(dataSource.IsSynchronized); + } + + [TestMethod] + public virtual void SyncRoot_IsNotNull() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.IsNotNull(dataSource.SyncRoot); + } + + [TestMethod] + public virtual void IsFixedSize_ReturnsFalse() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.IsFalse(dataSource.IsFixedSize); + } + + [TestMethod] + public virtual void IsReadOnly_ReturnsFalse() + { + var dataSource = CreateDataSource(_client, _crudTags); + Assert.IsFalse(((IList)dataSource).IsReadOnly); + } + #endregion + #region Edge Cases + [TestMethod] + public virtual async Task Indexer_OutOfRange_ThrowsException() + { + var dataSource = CreateDataSource(_client, _crudTags); + await dataSource.LoadDataSource(); + + Assert.ThrowsExactly(() => _ = dataSource[999]); + } + + [TestMethod] + public virtual void Add_ThenRemove_ClearsTracking() + { + var dataSource = CreateDataSource(_client, _crudTags); + var item = new TestOrderItem { Id = 1, ProductName = "Temporary" }; + + dataSource.Add(item); + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + + dataSource.Remove(item); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + } + + [TestMethod] + public virtual async Task ComplexWorkflow_AddUpdateRemoveSave() + { + var dataSource = CreateDataSource(_client, _crudTags); + + // Add items + dataSource.Add(new TestOrderItem { Id = 1, ProductName = "Item1", Quantity = 10 }); + dataSource.Add(new TestOrderItem { Id = 2, ProductName = "Item2", Quantity = 20 }); + Assert.AreEqual(2, dataSource.GetTrackingItems().Count); + + // Save + await dataSource.SaveChanges(); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + + // Update + dataSource[0] = new TestOrderItem { Id = 1, ProductName = "Updated1", Quantity = 100 }; + Assert.AreEqual(1, dataSource.GetTrackingItems().Count); + Assert.AreEqual(TrackingState.Update, dataSource.GetTrackingItems()[0].TrackingState); + + // Remove + dataSource.Remove(dataSource[1]); + Assert.AreEqual(2, dataSource.GetTrackingItems().Count); + + // Save all changes + await dataSource.SaveChanges(); + Assert.AreEqual(0, dataSource.GetTrackingItems().Count); + Assert.AreEqual(1, dataSource.Count); + } + #endregion +} +#endregion \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Binary.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Binary.cs new file mode 100644 index 0000000..21e2fc7 --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Binary.cs @@ -0,0 +1,26 @@ +using AyCode.Core.Serializers.Binaries; +using AyCode.Core.Serializers.Jsons; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +[TestClass] +public class SignalRDataSourceTests_List_Binary : SignalRDataSourceTestBase> +{ + protected override AcSerializerOptions SerializerOption => new AcBinarySerializerOptions(); + protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) + => new(client, crudTags); + + [TestMethod] + public override async Task LoadDataSource_ReturnsAllItems() => await base.LoadDataSource_ReturnsAllItems(); + [TestMethod] + public override async Task LoadDataSource_ClearsChangeTracking_ByDefault() => await base.LoadDataSource_ClearsChangeTracking_ByDefault(); + [TestMethod] + public override async Task LoadDataSource_PreservesChangeTracking_WhenFalse() => await base.LoadDataSource_PreservesChangeTracking_WhenFalse(); + [TestMethod] + public override async Task LoadDataSource_InvokesOnDataSourceLoaded() => await base.LoadDataSource_InvokesOnDataSourceLoaded(); + [TestMethod] + public override async Task LoadDataSource_MultipleCalls_RefreshesData() => await base.LoadDataSource_MultipleCalls_RefreshesData(); + // ... (repeat for all other test methods) +} \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Binary_NoRef.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Binary_NoRef.cs new file mode 100644 index 0000000..a5e379a --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Binary_NoRef.cs @@ -0,0 +1,14 @@ +using AyCode.Core.Serializers.Binaries; +using AyCode.Core.Serializers.Jsons; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +[TestClass] +public class SignalRDataSourceTests_List_Binary_NoRef : SignalRDataSourceTestBase> +{ + protected override AcSerializerOptions SerializerOption => new AcBinarySerializerOptions { UseReferenceHandling = false }; + protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) + => new(client, crudTags); +} \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Json.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Json.cs new file mode 100644 index 0000000..77ac197 --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_List_Json.cs @@ -0,0 +1,13 @@ +using AyCode.Core.Serializers.Jsons; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +[TestClass] +public class SignalRDataSourceTests_List_Json : SignalRDataSourceTestBase> +{ + protected override AcSerializerOptions SerializerOption => new AcJsonSerializerOptions(); + protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) + => new(client, crudTags); +} \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_Observable_Binary.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_Observable_Binary.cs new file mode 100644 index 0000000..af82898 --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_Observable_Binary.cs @@ -0,0 +1,15 @@ +using AyCode.Core.Helpers; +using AyCode.Core.Serializers.Binaries; +using AyCode.Core.Serializers.Jsons; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +[TestClass] +public class SignalRDataSourceTests_Observable_Binary : SignalRDataSourceTestBase> +{ + protected override AcSerializerOptions SerializerOption => new AcBinarySerializerOptions(); + protected override TestOrderItemObservableDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) + => new(client, crudTags); +} \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_Observable_Json.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_Observable_Json.cs new file mode 100644 index 0000000..74a2b78 --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/SignalRDataSourceTests_Observable_Json.cs @@ -0,0 +1,14 @@ +using AyCode.Core.Helpers; +using AyCode.Core.Serializers.Jsons; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +[TestClass] +public class SignalRDataSourceTests_Observable_Json : SignalRDataSourceTestBase> +{ + protected override AcSerializerOptions SerializerOption => new AcJsonSerializerOptions(); + protected override TestOrderItemObservableDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags) + => new(client, crudTags); +} \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/TestOrderItemListDataSource.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/TestOrderItemListDataSource.cs new file mode 100644 index 0000000..a676e1b --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/TestOrderItemListDataSource.cs @@ -0,0 +1,11 @@ +using AyCode.Core.Tests.TestModels; +using AyCode.Services.Server.SignalRs; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +public class TestOrderItemListDataSource : AcSignalRDataSource> +{ + public TestOrderItemListDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags) + : base(signalRClient, crudTags) { } +} \ No newline at end of file diff --git a/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/TestOrderItemObservableDataSource.cs b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/TestOrderItemObservableDataSource.cs new file mode 100644 index 0000000..7ace8fc --- /dev/null +++ b/AyCode.Services.Server.Tests/SignalRs/SignalRDatasources/TestOrderItemObservableDataSource.cs @@ -0,0 +1,12 @@ +using AyCode.Core.Helpers; +using AyCode.Core.Tests.TestModels; +using AyCode.Services.Server.SignalRs; +using AyCode.Services.SignalRs; + +namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources; + +public class TestOrderItemObservableDataSource : AcSignalRDataSource> +{ + public TestOrderItemObservableDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags) + : base(signalRClient, crudTags) { } +} \ No newline at end of file