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