using System.Threading;
using System.Threading.Tasks;
using FrameworkBLL.Promotion;
using FrameworkDAL.CustomCode.Promotion;
using FrameworkDAL.DTO.Promotion;
using GB5Shared.DTO.Framework.Login;
using GB5Shared.Validation;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
namespace FrameworkTests;
///
/// PromotionCounterpartyBLL — the admin CRUD layer for MPROMOTIONCOUNTERPARTY that was previously
/// missing entirely (only an internal, active-only read existed, used by PromotionPackageCrypto).
/// Covers the RSA PEM validation (fail fast at save time, not at the first real encrypt attempt)
/// and insert-vs-update decided by EnvironmentCode existence, since this table's PK is the natural
/// key itself, not an AutoNumber ID.
///
public class PromotionCounterpartyBLLTests
{
// A real, throwaway RSA public key — genuinely parses, not just a plausible-looking string.
private const string ValidPem = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuJW1kHlYeFIqtxwiP/vU
DXGdBuC5EASKjqQ2rSoz1zPYTN/4GEQnNcFGAUCOVuv1JQFzb51B/OAQwTq5k0+e
+hWoUd2dh6F+qM2Xb2T1KqgjRbR454k1fBrgdSP+ls5x7Qw/6iGhXb33HGVNRGnp
+W8osWU+d45mTSKNEEKd6ExNrg+/P5wDi5FbA1x5v2Dg2QMiIk++DuQLqyf55H+A
azdORGHQ5HWaQ/SDhf6qMVCLkNqt36lumM8fltc4M62+51+wvFeM70Lp2KiDZvk1
8EK1eVRtsJXB6fh5RdZSCvQFZ4OSeXabIjNAj3onofIY1xHCb2l9VAr3IM1jbJHh
XwIDAQAB
-----END PUBLIC KEY-----
""";
private static LoginDTO Login() => new() { ClientId = -1, UserId = 7 };
private static PromotionCounterpartyDTO CleanDto(string environmentCode = "DEVSYS") => new()
{
EnvironmentCode = environmentCode,
DisplayName = "Central DEVSYS",
PublicKeyPem = ValidPem,
IsActive = true
};
private static (Mock dal, Mock validation) Mocks()
=> (new Mock(), new Mock());
private static PromotionCounterpartyBLL NewBll((Mock dal, Mock validation) m)
=> new(m.dal.Object, m.validation.Object, NullLogger.Instance);
[Fact]
public async Task Save_FreshEnvironmentCode_Inserts()
{
var m = Mocks();
m.dal.Setup(d => d.ExistsByEnvironmentCode("DEVSYS", It.IsAny(), It.IsAny()))
.ReturnsAsync(false);
PromotionCounterpartyDTO? captured = null;
m.dal.Setup(d => d.InsertCounterparty(It.IsAny(), It.IsAny(), It.IsAny()))
.Callback((d, _, _) => captured = d)
.ReturnsAsync(1);
var bll = NewBll(m);
var result = await bll.SavePromotionCounterparty(CleanDto(), Login(), CancellationToken.None);
Assert.Contains("DEVSYS", result);
Assert.NotNull(captured);
Assert.Equal(7, captured!.CreatedById);
m.dal.Verify(d => d.UpdateCounterparty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
}
[Fact]
public async Task Save_ExistingEnvironmentCode_Updates()
{
var m = Mocks();
m.dal.Setup(d => d.ExistsByEnvironmentCode("DEVSYS", It.IsAny(), It.IsAny()))
.ReturnsAsync(true);
m.dal.Setup(d => d.UpdateCounterparty(It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(1);
var bll = NewBll(m);
var result = await bll.SavePromotionCounterparty(CleanDto(), Login(), CancellationToken.None);
m.dal.Verify(d => d.InsertCounterparty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
m.dal.Verify(d => d.UpdateCounterparty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);
}
[Fact]
public async Task Save_MalformedPublicKeyPem_ThrowsBeforeTouchingDal()
{
var dto = CleanDto();
dto.PublicKeyPem = "not a real PEM at all";
var m = Mocks();
var bll = NewBll(m);
await Assert.ThrowsAsync(() =>
bll.SavePromotionCounterparty(dto, Login(), CancellationToken.None));
m.dal.Verify(d => d.InsertCounterparty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
m.dal.Verify(d => d.UpdateCounterparty(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
}
[Fact]
public async Task Delete_CallsDalWithEnvironmentCode()
{
var m = Mocks();
m.dal.Setup(d => d.DeleteCounterparty("DEVSYS", It.IsAny(), It.IsAny()))
.ReturnsAsync(1);
var bll = NewBll(m);
await bll.DeletePromotionCounterparty("DEVSYS", Login(), CancellationToken.None);
m.dal.Verify(d => d.DeleteCounterparty("DEVSYS", It.IsAny(), It.IsAny()), Times.Once);
}
}