using System.Threading; using System.Threading.Tasks; using FrameworkBLL.Dashboard; using FrameworkBLL.Promotion.Adapters; using FrameworkDAL.CustomCode.Promotion; using FrameworkDAL.DTO.Dashboard; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Promotion; using Moq; using Newtonsoft.Json; using Xunit; namespace FrameworkTests; /// /// DashboardPromotionAdapter — parent row only in this first pass (Dashboard->Page tab placement, /// MDASHBOARDPAGE, is deliberately not bundled yet — a follow-up). Covers ModuleCode resolution, /// that OwnerId/ImageId/DefaultCriteriaConfigId are always forced to their real DB sentinel (-1, /// confirmed via schema to have no FK constraint, unlike ReportView's TemplateId/PageId), and the /// DashboardCode-already-exists-at-destination update path (DASHBOARDCODE has a real unique /// constraint, confirmed via schema). /// public class DashboardPromotionAdapterTests { private static LoginDTO Login() => new() { ClientId = -1, UserId = 7 }; private static DashboardDTO CleanDto(int dashboardId = 42) => new() { DashboardId = dashboardId, DashboardCode = "SALES_OVERVIEW", DashboardName = "Sales Overview", DashboardType = 1, // Shared ModuleId = -1, OwnerId = 99, ImageId = 5, DefaultCriteriaConfigId = 10, SortOrder = 1 }; private static (Mock bll, Mock moduleLookup, Mock dashboardLookup) Mocks() => (new Mock(), new Mock(), new Mock()); private static async Task BuildBundle(DashboardDTO dto) { var (bll, moduleLookup, dashboardLookup) = Mocks(); bll.Setup(b => b.GetDashboard(dto.DashboardId, It.IsAny(), It.IsAny())) .ReturnsAsync(JsonConvert.SerializeObject(dto)); var adapter = new DashboardPromotionAdapter(bll.Object, moduleLookup.Object, dashboardLookup.Object); return await adapter.ExportForPromotionAsync(dto.DashboardId, Login(), CancellationToken.None); } [Fact] public async Task Validate_CleanDashboard_IsOk() { var bundle = await BuildBundle(CleanDto()); var (bll, moduleLookup, dashboardLookup) = Mocks(); var adapter = new DashboardPromotionAdapter(bll.Object, moduleLookup.Object, dashboardLookup.Object); var result = await adapter.ValidateForPromotionAsync(bundle, CancellationToken.None); Assert.True(result.IsPromotable); } [Fact] public async Task Import_ForcesOwnerImageAndCriteriaConfigToSentinel_RegardlessOfOrigin() { var bundle = await BuildBundle(CleanDto()); // OwnerId=99, ImageId=5, DefaultCriteriaConfigId=10 at origin var (bll, moduleLookup, dashboardLookup) = Mocks(); dashboardLookup.Setup(l => l.GetDashboardIdByCode("SALES_OVERVIEW", It.IsAny(), It.IsAny())) .ReturnsAsync((int?)null); DashboardDTO? captured = null; bll.Setup(b => b.SaveDashboard(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((d, _, _) => { captured = new DashboardDTO { DashboardId = d.DashboardId, OwnerId = d.OwnerId, ImageId = d.ImageId, DefaultCriteriaConfigId = d.DefaultCriteriaConfigId, SourceType = d.SourceType }; d.DashboardId = 902; }) .ReturnsAsync("Saved"); var adapter = new DashboardPromotionAdapter(bll.Object, moduleLookup.Object, dashboardLookup.Object); var newId = await adapter.ImportPromotedAsync(bundle, destinationSourceType: 2, existingDestinationId: null, Login(), CancellationToken.None); Assert.Equal(902, newId); Assert.NotNull(captured); Assert.Equal(-1, captured!.OwnerId); Assert.Equal(-1, captured.ImageId); Assert.Equal(-1, captured.DefaultCriteriaConfigId); Assert.Equal((byte)2, captured.SourceType); } [Fact] public async Task Import_DashboardCodeAlreadyExistsAtDestination_UpdatesRatherThanDuplicating() { var bundle = await BuildBundle(CleanDto()); var (bll, moduleLookup, dashboardLookup) = Mocks(); dashboardLookup.Setup(l => l.GetDashboardIdByCode("SALES_OVERVIEW", It.IsAny(), It.IsAny())) .ReturnsAsync(600); int? capturedId = null; bll.Setup(b => b.SaveDashboard(It.IsAny(), It.IsAny(), It.IsAny())) .Callback((d, _, _) => capturedId = d.DashboardId) .ReturnsAsync("Updated"); var adapter = new DashboardPromotionAdapter(bll.Object, moduleLookup.Object, dashboardLookup.Object); var resultId = await adapter.ImportPromotedAsync(bundle, 2, existingDestinationId: null, Login(), CancellationToken.None); Assert.Equal(600, resultId); Assert.Equal(600, capturedId); } }