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