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);
}
}