using SwBLL.ServerConfig; using SwDAL.CustomCode.ServerConfig; using SwDAL.DTO.ServerConfig; using GB5Shared.Connection; using GB5Shared.DTO.Framework.AutoNumber; using GB5Shared.DTO.Framework.CommonConfig; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using GB5Shared.QueryExecutor; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using Xunit; namespace SwTests; /// /// Covers ServerConfigBLL — the write path ClientDatabaseProvisioner calls after creating a /// physical database, so ReportConnectionResolver/ServerConfigCache can route to it at runtime. /// All DAL/encryption dependencies are mocked; no real gb5system database is touched. /// public class ServerConfigBLLTests { private static (ServerConfigBLL Svc, Mock Dal, Mock AppConnection) BuildService() { var dal = new Mock(); var appConnection = new Mock(); appConnection.Setup(c => c.Encrypt(It.IsAny(), It.IsAny())).Returns("ENCRYPTED"); // AutoNumber is a concrete class (not an interface) — build a real instance backed by a // mocked IQueryExecutor so RegisterDatabaseAsync's now-real MSERVER/MSERVERCONFIG // AutoNumber allocations resolve without throwing. The exact allocated value doesn't // matter to any assertion here (InsertServerAsync/InsertServerConfigAsync are themselves // mocked to return fixed IDs) — only that GetNumberAsync completes successfully. var queryExecutor = new Mock(); queryExecutor .Setup(q => q.QueryAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, false, It.IsAny())) .ReturnsAsync(new List { 1 }); var dbConfig = new Mock>(); dbConfig.Setup(o => o.Value).Returns(new Gb5SystemDTO { DataBaseType = 0 }); var autoNumber = new AutoNumber(queryExecutor.Object, dbConfig.Object); var svc = new ServerConfigBLL(dal.Object, appConnection.Object, autoNumber, NullLogger.Instance); return (svc, dal, appConnection); } private static LoginDTO TestLogin() => new() { ClientId = 42, UserId = -1 }; [Fact] public async Task RegisterDatabaseAsync_CreatesNewMServerRow_WhenMachineNameNotFound() { var (svc, dal, _) = BuildService(); dal.Setup(d => d.FindServerIdByMachineNameAsync("sql01", It.IsAny())) .ReturnsAsync((int?)null); dal.Setup(d => d.InsertServerAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(101); dal.Setup(d => d.InsertServerConfigAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(555); var serverConfigId = await svc.RegisterDatabaseAsync(new RegisterServerConfigRequest { ConnectionName = "ACME01-MAIN", ServerMachineName = "sql01", DatabaseType = 0, DatabaseName = "acme01", DatabaseUserName = "acme01_app", DatabasePassword = "plaintext-pw", DatabasePort = "1433", DbInstanceType = 0, ClientId = 42 }, TestLogin(), CancellationToken.None); Assert.Equal(555, serverConfigId); dal.Verify(d => d.InsertServerAsync( It.Is(s => s.ServerMachineName == "sql01"), It.IsAny()), Times.Once); dal.Verify(d => d.InsertServerConfigAsync( It.Is(c => c.ServerId == 101 && c.DatabasePassword == "ENCRYPTED"), It.IsAny()), Times.Once); } [Fact] public async Task RegisterDatabaseAsync_ReusesExistingMServerRow_WhenMachineNameFound() { var (svc, dal, _) = BuildService(); dal.Setup(d => d.FindServerIdByMachineNameAsync("sql01", It.IsAny())) .ReturnsAsync(202); dal.Setup(d => d.InsertServerConfigAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(556); await svc.RegisterDatabaseAsync(new RegisterServerConfigRequest { ConnectionName = "ACME01-MAIN", ServerMachineName = "sql01", DatabaseType = 0, DatabaseName = "acme01", DatabaseUserName = "acme01_app", DatabasePassword = "plaintext-pw", DatabasePort = "1433", DbInstanceType = 0, ClientId = 42 }, TestLogin(), CancellationToken.None); dal.Verify(d => d.InsertServerAsync(It.IsAny(), It.IsAny()), Times.Never); dal.Verify(d => d.InsertServerConfigAsync( It.Is(c => c.ServerId == 202), It.IsAny()), Times.Once); } [Fact] public async Task LinkReportOrArchiveAsync_UpdatesArchiveColumn_WhenIsArchiveTrue() { var (svc, dal, _) = BuildService(); await svc.LinkReportOrArchiveAsync(mainServerConfigId: 555, linkedServerConfigId: 700, isArchive: true, CancellationToken.None); dal.Verify(d => d.UpdateArchiveServerConfigIdAsync(555, 700, It.IsAny()), Times.Once); dal.Verify(d => d.UpdateReportServerConfigIdAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [Fact] public async Task LinkReportOrArchiveAsync_UpdatesReportColumn_WhenIsArchiveFalse() { var (svc, dal, _) = BuildService(); await svc.LinkReportOrArchiveAsync(mainServerConfigId: 555, linkedServerConfigId: 701, isArchive: false, CancellationToken.None); dal.Verify(d => d.UpdateReportServerConfigIdAsync(555, 701, It.IsAny()), Times.Once); dal.Verify(d => d.UpdateArchiveServerConfigIdAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } }