using System.Threading;
using System.Threading.Tasks;
using GB5Shared.DTO.Framework.Login;
using GB5Shared.DTO.Promotion;
using Moq;
using Newtonsoft.Json;
using WiBLL.Promotion;
using WiBLL.WiSubProcess;
using WiDAL.CustomCode.Promotion;
using WiDAL.DTOs;
using Xunit;
namespace WiTests;
///
/// WiSubProcessDTO declares a WiProcessCode property, but WiSubProcessBLL.GetById's real query
/// never populates it (only WIPROCESSNAME is joined) — these tests assert the adapter always
/// resolves WiProcessCode via the lookup DAL instead of trusting that DTO field.
///
public class WiSubProcessPromotionAdapterTests
{
private static LoginDTO Login() => new() { ClientId = -1, UserId = 7 };
private static WiSubProcessDTO CleanDto(int wiSubProcessId = 42) => new()
{
WiSubProcessId = wiSubProcessId,
WiProcessId = 10,
WiSubProcessCode = "SUBASSY1",
WiSubProcessName = "Sub-Assembly 1",
DisplayOrder = 1,
IsActive = 1,
SortOrder = 1
};
private static (Mock bll, Mock processLookup, Mock subProcessLookup) Mocks()
=> (new Mock(), new Mock(), new Mock());
private static WiSubProcessPromotionAdapter NewAdapter(
(Mock bll, Mock processLookup, Mock subProcessLookup) m)
=> new(m.bll.Object, m.processLookup.Object, m.subProcessLookup.Object);
private static async Task BuildBundle(WiSubProcessDTO dto)
{
var m = Mocks();
m.bll.Setup(b => b.GetById(dto.WiSubProcessId, It.IsAny(), It.IsAny()))
.ReturnsAsync(JsonConvert.SerializeObject(dto));
m.processLookup.Setup(l => l.GetWiProcessCodeById(dto.WiProcessId, It.IsAny(), It.IsAny()))
.ReturnsAsync("ASSEMBLY");
var adapter = NewAdapter(m);
return await adapter.ExportForPromotionAsync(dto.WiSubProcessId, Login(), CancellationToken.None);
}
[Fact]
public async Task Validate_CleanWiSubProcess_IsOk()
{
var bundle = await BuildBundle(CleanDto());
var adapter = NewAdapter(Mocks());
var result = await adapter.ValidateForPromotionAsync(bundle, CancellationToken.None);
Assert.True(result.IsPromotable);
}
[Fact]
public async Task Export_NeverTrustsDtoWiProcessCode_AlwaysResolvesViaLookup()
{
var dtoWithBogusCode = CleanDto();
dtoWithBogusCode.WiProcessCode = "SHOULD_BE_IGNORED"; // GetById never actually populates this in production
var m = Mocks();
m.bll.Setup(b => b.GetById(dtoWithBogusCode.WiSubProcessId, It.IsAny(), It.IsAny()))
.ReturnsAsync(JsonConvert.SerializeObject(dtoWithBogusCode));
m.processLookup.Setup(l => l.GetWiProcessCodeById(dtoWithBogusCode.WiProcessId, It.IsAny(), It.IsAny()))
.ReturnsAsync("REAL_CODE_FROM_LOOKUP");
var adapter = NewAdapter(m);
var bundle = await adapter.ExportForPromotionAsync(dtoWithBogusCode.WiSubProcessId, Login(), CancellationToken.None);
Assert.Contains("REAL_CODE_FROM_LOOKUP", bundle.PayloadJson);
Assert.DoesNotContain("SHOULD_BE_IGNORED", bundle.PayloadJson);
}
[Fact]
public async Task Import_FreshInsert_ResolvesWiProcessAndSaves()
{
var bundle = await BuildBundle(CleanDto());
var m = Mocks();
m.subProcessLookup.Setup(l => l.GetWiSubProcessIdByCode("SUBASSY1", It.IsAny(), It.IsAny()))
.ReturnsAsync((int?)null);
m.processLookup.Setup(l => l.GetWiProcessIdByCode("ASSEMBLY", It.IsAny(), It.IsAny()))
.ReturnsAsync(101);
WiSubProcessDTO? captured = null;
m.bll.Setup(b => b.Save(It.IsAny(), It.IsAny(), It.IsAny()))
.Callback((d, _, _) =>
{
captured = d;
d.WiSubProcessId = 904;
})
.ReturnsAsync("Saved");
var adapter = NewAdapter(m);
var newId = await adapter.ImportPromotedAsync(bundle, destinationSourceType: 3, existingDestinationId: null, Login(), CancellationToken.None);
Assert.Equal(904, newId);
Assert.NotNull(captured);
Assert.Equal(101, captured!.WiProcessId);
Assert.Equal("SUBASSY1", captured.WiSubProcessCode);
}
}