using System.Data.Common; using EntitlementBLL.Common; using EntitlementBLL.Demo; using EntitlementDAL.DTOs; using EntitlementDAL.Interfaces; using GB5Shared.DTO.Framework.CommonConfig; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using Microsoft.Extensions.Options; using Moq; using Xunit; namespace EntitlementTests; /// /// Covers tracker §49's own "explicitly NOT built yet" item 3: admin authoring for /// MDEMOVARIANT/MDEMOPOOLINSTANCE, previously only reachable via direct SQL. DemoCatalogBLL is /// deliberately separate from DemoSessionBLL — this is one-time catalog registration, not the /// checkout/extension/expiry lifecycle. /// public class DemoCatalogBLLTests { private static (DemoCatalogBLL Svc, Mock DemoDAL) BuildService() { var demoDAL = new Mock(); var queryExecutor = new Mock(); queryExecutor.Setup(q => q.BeginTransactionAsync(It.IsAny())).ReturnsAsync((DbTransaction)null!); queryExecutor.Setup(q => q.CommitAsync(It.IsAny())).Returns(Task.CompletedTask); queryExecutor.Setup(q => q.RollbackAsync(It.IsAny())).Returns(Task.CompletedTask); queryExecutor .Setup(q => q.QueryAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new List { 9001 }); var dbConfig = new Mock>(); dbConfig.Setup(o => o.Value).Returns(new Gb5SystemDTO { DataBaseType = 0 }); var autoNumber = new AutoNumber(queryExecutor.Object, dbConfig.Object); var loginFactory = new Mock(); loginFactory.Setup(f => f.Create(It.IsAny(), It.IsAny())) .Returns((int clientId, int userId) => new LoginDTO { ClientId = clientId, UserId = userId }); var svc = new DemoCatalogBLL(demoDAL.Object, queryExecutor.Object, autoNumber, loginFactory.Object); return (svc, demoDAL); } [Fact] public async Task Test_CreateVariantAsync_MissingVariantCode_Throws() { var (svc, demoDAL) = BuildService(); await Assert.ThrowsAsync(() => svc.CreateVariantAsync( new DemoVariantDTO { VariantCode = "", VariantName = "X", DbModelId = 1 }, CancellationToken.None)); demoDAL.Verify(d => d.SaveVariantAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Test_CreateVariantAsync_MissingDbModelId_Throws() { var (svc, demoDAL) = BuildService(); await Assert.ThrowsAsync(() => svc.CreateVariantAsync( new DemoVariantDTO { VariantCode = "MFG-IN", VariantName = "Manufacturing India", DbModelId = 0 }, CancellationToken.None)); demoDAL.Verify(d => d.SaveVariantAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Test_CreateVariantAsync_ValidRequest_AllocatesIdAndSaves() { var (svc, demoDAL) = BuildService(); var result = await svc.CreateVariantAsync( new DemoVariantDTO { VariantCode = "MFG-IN", VariantName = "Manufacturing India", DbModelId = 1 }, CancellationToken.None); Assert.Equal(9000, result.DemoVariantId); // AutoNumber StartNumber = returnedValue(9001) - 1 demoDAL.Verify(d => d.SaveVariantAsync( It.Is(v => v.VariantCode == "MFG-IN" && v.DemoVariantId == 9000), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] public async Task Test_CreatePoolInstanceAsync_VariantNotFound_ThrowsWithoutSaving() { var (svc, demoDAL) = BuildService(); demoDAL.Setup(d => d.GetVariantByIdAsync(5, It.IsAny(), It.IsAny())) .ReturnsAsync((DemoVariantDTO?)null); await Assert.ThrowsAsync(() => svc.CreatePoolInstanceAsync( new DemoPoolInstanceDTO { DemoVariantId = 5, SwClientDatabaseId = 42 }, CancellationToken.None)); demoDAL.Verify(d => d.SavePoolInstanceAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Test_CreatePoolInstanceAsync_ValidRequest_ForcesAvailableStateRegardlessOfInput() { var (svc, demoDAL) = BuildService(); demoDAL.Setup(d => d.GetVariantByIdAsync(5, It.IsAny(), It.IsAny())) .ReturnsAsync(new DemoVariantDTO { DemoVariantId = 5, VariantCode = "MFG-IN" }); var result = await svc.CreatePoolInstanceAsync( new DemoPoolInstanceDTO { DemoVariantId = 5, SwClientDatabaseId = 42, PoolState = (byte)DemoPoolState.Retired }, CancellationToken.None); Assert.Equal((byte)DemoPoolState.Available, result.PoolState); demoDAL.Verify(d => d.SavePoolInstanceAsync( It.Is(p => p.SwClientDatabaseId == 42 && p.PoolState == (byte)DemoPoolState.Available), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] public async Task Test_UpdateVariantAsync_VariantNotFound_ThrowsWithoutUpdating() { var (svc, demoDAL) = BuildService(); demoDAL.Setup(d => d.GetVariantByIdAsync(5, It.IsAny(), It.IsAny())) .ReturnsAsync((DemoVariantDTO?)null); await Assert.ThrowsAsync(() => svc.UpdateVariantAsync( new DemoVariantDTO { DemoVariantId = 5, VariantName = "Manufacturing India v2", DbModelId = 1 }, CancellationToken.None)); demoDAL.Verify(d => d.UpdateVariantAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Test_UpdateVariantAsync_MissingVariantName_ThrowsWithoutUpdating() { var (svc, demoDAL) = BuildService(); await Assert.ThrowsAsync(() => svc.UpdateVariantAsync( new DemoVariantDTO { DemoVariantId = 5, VariantName = "", DbModelId = 1 }, CancellationToken.None)); demoDAL.Verify(d => d.UpdateVariantAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Test_UpdateVariantAsync_ValidRequest_Updates() { var (svc, demoDAL) = BuildService(); demoDAL.Setup(d => d.GetVariantByIdAsync(5, It.IsAny(), It.IsAny())) .ReturnsAsync(new DemoVariantDTO { DemoVariantId = 5, VariantCode = "MFG-IN", VariantName = "Manufacturing India", DbModelId = 1 }); var result = await svc.UpdateVariantAsync( new DemoVariantDTO { DemoVariantId = 5, VariantName = "Manufacturing India v2", DbModelId = 1, PoolTargetSize = 3 }, CancellationToken.None); Assert.Equal("Manufacturing India v2", result.VariantName); demoDAL.Verify(d => d.UpdateVariantAsync( It.Is(v => v.DemoVariantId == 5 && v.VariantName == "Manufacturing India v2" && v.PoolTargetSize == 3), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] public async Task Test_UpdatePoolInstanceAsync_PoolInstanceNotFound_ThrowsWithoutUpdating() { var (svc, demoDAL) = BuildService(); demoDAL.Setup(d => d.GetPoolInstanceByIdAsync(7, It.IsAny(), It.IsAny())) .ReturnsAsync((DemoPoolInstanceDTO?)null); await Assert.ThrowsAsync(() => svc.UpdatePoolInstanceAsync( new DemoPoolInstanceDTO { DemoPoolInstanceId = 7, SwClientDatabaseId = 99 }, CancellationToken.None)); demoDAL.Verify(d => d.UpdatePoolInstanceAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task Test_UpdatePoolInstanceAsync_ValidRequest_Updates() { var (svc, demoDAL) = BuildService(); demoDAL.Setup(d => d.GetPoolInstanceByIdAsync(7, It.IsAny(), It.IsAny())) .ReturnsAsync(new DemoPoolInstanceDTO { DemoPoolInstanceId = 7, DemoVariantId = 5, SwClientDatabaseId = 42 }); var result = await svc.UpdatePoolInstanceAsync( new DemoPoolInstanceDTO { DemoPoolInstanceId = 7, SwClientDatabaseId = 99, LocalClientId = 12 }, CancellationToken.None); Assert.Equal(99, result.SwClientDatabaseId); demoDAL.Verify(d => d.UpdatePoolInstanceAsync( It.Is(p => p.DemoPoolInstanceId == 7 && p.SwClientDatabaseId == 99 && p.LocalClientId == 12), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } }