Refactor and expand SignalR data source tests

Split SignalRDataSourceTests into multiple focused files and introduce an abstract base test class for comprehensive coverage. Add concrete test classes for all serializer and collection type combinations. Move data source implementations to separate files. Test coverage now includes edge cases, interface compliance, and advanced scenarios.
This commit is contained in:
Loretta 2025-12-15 19:28:20 +01:00
parent b8143e4897
commit 762088caf7
9 changed files with 1086 additions and 174 deletions

View File

@ -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<TestOrderItem, int, List<TestOrderItem>>
{
public TestOrderItemListDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags)
: base(signalRClient, crudTags) { }
}
public class TestOrderItemObservableDataSource : AcSignalRDataSource<TestOrderItem, int, AcObservableCollection<TestOrderItem>>
{
public TestOrderItemObservableDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags)
: base(signalRClient, crudTags) { }
}
#endregion
#region Abstract Test Base
/// <summary>
/// Base class for SignalR DataSource tests.
/// Derived classes specify the serializer type and collection type.
/// </summary>
/// <typeparam name="TDataSource">The concrete DataSource type</typeparam>
/// <typeparam name="TIList">The inner list type (List or AcObservableCollection)</typeparam>
public abstract class SignalRDataSourceTestBase<TDataSource, TIList>
where TDataSource : AcSignalRDataSource<TestOrderItem, int, TIList>
where TIList : class, IList<TestOrderItem>
{
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<TestOrderItemListDataSource, List<TestOrderItem>>
{
protected override AcSerializerOptions SerializerOption => AcBinarySerializerOptions.Default;
protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags)
=> new(client, crudTags);
}
[TestClass]
public class SignalRDataSourceTests_Observable : SignalRDataSourceTestBase<TestOrderItemObservableDataSource, AcObservableCollection<TestOrderItem>>
{
protected override AcSerializerOptions SerializerOption => AcBinarySerializerOptions.Default;
protected override TestOrderItemObservableDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags)
=> new(client, crudTags);
}
#endregion

View File

@ -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
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TDataSource">The concrete DataSource type</typeparam>
/// <typeparam name="TIList">The inner list type (List or AcObservableCollection)</typeparam>
public abstract class SignalRDataSourceTestBase<TDataSource, TIList>
where TDataSource : AcSignalRDataSource<TestOrderItem, int, TIList>
where TIList : class, IList<TestOrderItem>
{
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<TestOrderItem>? 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<ArgumentException>(() =>
{
dataSource.Add(new TestOrderItem { Id = 100, ProductName = "Duplicate" });
});
}
[TestMethod]
public virtual void Add_DefaultId_ThrowsException()
{
var dataSource = CreateDataSource(_client, _crudTags);
Assert.ThrowsExactly<ArgumentNullException>(() =>
{
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<TestOrderItem>));
}
#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<TIList>();
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<ArgumentOutOfRangeException>(() => _ = 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

View File

@ -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<TestOrderItemListDataSource, List<TestOrderItem>>
{
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)
}

View File

@ -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<TestOrderItemListDataSource, List<TestOrderItem>>
{
protected override AcSerializerOptions SerializerOption => new AcBinarySerializerOptions { UseReferenceHandling = false };
protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags)
=> new(client, crudTags);
}

View File

@ -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<TestOrderItemListDataSource, List<TestOrderItem>>
{
protected override AcSerializerOptions SerializerOption => new AcJsonSerializerOptions();
protected override TestOrderItemListDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags)
=> new(client, crudTags);
}

View File

@ -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<TestOrderItemObservableDataSource, AcObservableCollection<TestOrderItem>>
{
protected override AcSerializerOptions SerializerOption => new AcBinarySerializerOptions();
protected override TestOrderItemObservableDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags)
=> new(client, crudTags);
}

View File

@ -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<TestOrderItemObservableDataSource, AcObservableCollection<TestOrderItem>>
{
protected override AcSerializerOptions SerializerOption => new AcJsonSerializerOptions();
protected override TestOrderItemObservableDataSource CreateDataSource(TestableSignalRClient2 client, SignalRCrudTags crudTags)
=> new(client, crudTags);
}

View File

@ -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<TestOrderItem, int, List<TestOrderItem>>
{
public TestOrderItemListDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags)
: base(signalRClient, crudTags) { }
}

View File

@ -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<TestOrderItem, int, AcObservableCollection<TestOrderItem>>
{
public TestOrderItemObservableDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags crudTags)
: base(signalRClient, crudTags) { }
}