using System.Security.Claims; using adas_core.Application.Exceptions; using adas_core.Application.Repositories.Interfaces; using adas_core.Application.Services; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.MongoModels; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using MongoDB.Bson; using Moq; using static NUnit.Framework.Assert; using Options = Microsoft.Extensions.Options.Options; namespace adas_core.Test.Services; [TestFixture] public class DisplayServiceTest { private Mock _mockCacheService = null!; private Mock _mockDisplayRepository = null!; private Mock _mockPointOfCareService = null!; private Mock _mockDisplayConfigService = null!; private Mock _mockUserRepository = null!; private Mock _authServiceMock = null!; private Mock _mockSubscribersService = null!; private Mock _mockClientMessageService = null!; private Mock> _mockUnitService = null!; private Mock _mockPermissionService = null!; private Lazy _lazyMockPermission = null!; private Mock> _mockLogger = null!; private readonly Mock _httpContextAccessorMock = new(); private readonly Mock _auditServiceMock = new(); private DisplayService _displayService = null!; /// /// Initializes mocked dependencies and configuration required to construct a instance for unit tests. /// Sets up the HTTP context with a test user claim, creates default cache settings, and wires all collaborators (repositories, services, logger, permissions) into the service under test. /// [SetUp] public void SetUp() { _mockCacheService = new Mock(); _mockDisplayRepository = new Mock(); _mockPointOfCareService = new Mock(); _mockDisplayConfigService = new Mock(); _mockUserRepository = new Mock(); _authServiceMock = new Mock(); _mockSubscribersService = new Mock(); _mockClientMessageService = new Mock(); _mockUnitService = new Mock>(); _mockPermissionService = new Mock(); _lazyMockPermission = new Lazy(() => _mockPermissionService.Object); _mockLogger = new Mock>(); var claims = new ClaimsPrincipal( new ClaimsIdentity([new Claim(ClaimTypes.Name, "TestUser")], "mock")); _httpContextAccessorMock.Setup(a => a.HttpContext).Returns(new DefaultHttpContext { User = claims }); var cacheSettings = Options.Create(new CacheSettings()); _displayService = new DisplayService( _mockDisplayRepository.Object, _mockPointOfCareService.Object, _mockUnitService.Object, _mockDisplayConfigService.Object, _mockSubscribersService.Object, _mockClientMessageService.Object, _mockUserRepository.Object, _authServiceMock.Object, _mockLogger.Object, _httpContextAccessorMock.Object, _auditServiceMock.Object, _lazyMockPermission, _mockCacheService.Object, cacheSettings ); } // ----------------------------------------------------------- // InsertOne // ----------------------------------------------------------- /// /// Verifies that inserts a display by resolving the default configuration for its type and persisting it through the repository's insert method. /// [Test] public async Task InsertOne_ShouldInsertDisplay() { var display = new Display { Name = "Test Display", Type = DisplayConfigEnums.DisplayType.DisplayNurse }; var defaultCfg = new DisplayConfig { Id = ObjectId.GenerateNewId(), Type = display.Type }; _mockDisplayConfigService.Setup(s => s.GetDefaultConfig(display.Type)).ReturnsAsync(defaultCfg); _mockDisplayRepository.Setup(r => r.InsertOneAsync(display)) .Returns(Task.CompletedTask); var result = await _displayService.InsertOne(display); That(result, Is.EqualTo(display)); _mockDisplayRepository.Verify(r => r.InsertOneAsync(display), Times.Once); } // ----------------------------------------------------------- // GetById // ----------------------------------------------------------- /// /// Verifies that the returns the expected instance when a matching id is provided through the repository. /// [Test] public async Task GetById_ShouldReturnDisplay() { var id = ObjectId.GenerateNewId(); var d = new Display { Id = id, Name = "D1" }; _mockDisplayRepository.Setup(r => r.GetById(id)).ReturnsAsync(d); var result = await _displayService.GetById(id); That(result, Is.EqualTo(d)); } // ----------------------------------------------------------- // GetAllByUser - userName null // ----------------------------------------------------------- /// /// Verifies that _displayService.GetAllByUser returns an empty collection when the user name is null. /// [Test] public async Task GetAllByUser_ShouldReturnEmpty_WhenUserNameNull() { var result = await _displayService.GetAllByUser(null); That(result, Is.Empty); } // ----------------------------------------------------------- // GetAllByUser - Admin case // ----------------------------------------------------------- [Test] public async Task GetAllByUser_ShouldReturnDisplays_WhenAdmin() { // Arrange const string userName = "admin"; var adminId = ObjectId.GenerateNewId(); var unitId = ObjectId.GenerateNewId(); var user = new User { Id = adminId, UserName = userName, Authorization = [ new Authorization { UnitId = unitId.ToString(), Rol = nameof(PermissionEnum.RolesType.AuthAdmin) } ] }; // 1) Usuario + authorities _mockUserRepository.Setup(r => r.GetByUserAndAuthoritesName(userName)) .ReturnsAsync(user); _authServiceMock.Setup(a => a.GetUserAuthorities(adminId)) .ReturnsAsync(user.Authorization); // 2) Displays por UnitId var d1 = new Display { Id = ObjectId.GenerateNewId(), Name = "D1", UnitId = unitId }; var d2 = new Display { Id = ObjectId.GenerateNewId(), Name = "D2", UnitId = unitId }; var displays = new List { d1, d2 }; _mockDisplayRepository.Setup(r => r.GetByUnitId(unitId)) .ReturnsAsync(displays); // 3) GetInfo (rama base) → caché debe devolver el display haciendo el factory _mockDisplayRepository.Setup(r => r.GetById(d1.Id)).ReturnsAsync(d1); _mockDisplayRepository.Setup(r => r.GetById(d2.Id)).ReturnsAsync(d2); _mockCacheService .Setup(c => c.GetOrSetObjectAsync( It.IsAny(), It.IsAny>>(), It.IsAny(), It.IsAny())) .Returns((string _, Func> factory, TimeSpan? _, CancellationToken _) => factory()); // 4) Permisos (si es null, el servicio NO añade el display) _mockPermissionService .Setup(p => p.GetPermissionsForUnit(unitId.ToString(), user)) .ReturnsAsync(new DisplayPermissionTypes( new UserActions(true, true, true, true, true), new UserActions(true, true, true, true, true), new UserActions(true, true, true, true, true), new UserActions(true, true, true, true, true), new UserActions(true, true, true, true, true), new UserActions(true, true, true, true, true), new UserActions(true, true, true, true, true), true )); // Act var result = await _displayService.GetAllByUser(userName); // Assert That(result, Has.Count.EqualTo(2)); using (Assert.EnterMultipleScope()) { That(result.Any(r => r.Display != null && r.Display.Id == d1.Id), Is.True); That(result.Any(r => r.Display != null && r.Display.Id == d2.Id), Is.True); } } // ----------------------------------------------------------- // GetByType // ----------------------------------------------------------- /// /// Verifies that retrieving display configurations by type returns the associated displays /// from the display repository, ensuring the service correctly resolves configurations and /// their linked displays for the given display type. /// [Test] public async Task GetByType_ShouldReturnDisplays() { var cfgId = ObjectId.GenerateNewId(); var cfg = new DisplayConfig { Id = cfgId, Type = DisplayConfigEnums.DisplayType.DisplayNurse }; var display = new Display { Id = ObjectId.GenerateNewId(), DisplayConfigId = cfgId, Name = "D" }; _mockDisplayConfigService.Setup(s => s.GetByType(cfg.Type)) .ReturnsAsync([cfg]); _mockDisplayRepository.Setup(r => r.GetByConfigId(cfgId)) .ReturnsAsync([display]); var result = await _displayService.GetByType(cfg.Type); That(result.Count, Is.EqualTo(1)); That(result[0], Is.EqualTo(display)); } /// /// Verifies that returns an empty collection when no display configurations are found for the specified display type. /// /// A task that completes when the assertion confirming the empty result has been executed. [Test] public async Task GetByType_ShouldReturnEmpty_WhenNoConfigsFound() { _mockDisplayConfigService.Setup(s => s.GetByType(It.IsAny())) .ReturnsAsync([]); var result = await _displayService.GetByType(DisplayConfigEnums.DisplayType.DisplayNurse); That(result, Is.Empty); } // ----------------------------------------------------------- // GetByPointOfCare // ----------------------------------------------------------- /// /// Tests that GetByPointOfCare returns the displays associated with the specified point of care. /// /// The point of care used to look up associated displays. /// A task representing the asynchronous test execution. [Test] public async Task GetByPointOfCare_ShouldReturnDisplays() { var poc = new PointOfCare { Id = ObjectId.GenerateNewId() }; var d = new Display { Id = ObjectId.GenerateNewId(), Name = "Display" }; _mockDisplayRepository.Setup(r => r.GetByPointOfCare(poc)) .ReturnsAsync([d]); var result = await _displayService.GetByPointOfCare(poc); That(result, Has.Count.EqualTo(1)); } // ----------------------------------------------------------- // GetByConfigId // ----------------------------------------------------------- /// /// Verifies that returns the displays associated with the specified configuration ID retrieved from the repository. /// /// A task that completes when the assertion confirms the returned collection contains the expected number of displays. [Test] public async Task GetByConfigId_ShouldReturnDisplays() { var cfgId = ObjectId.GenerateNewId(); var d = new Display { Id = ObjectId.GenerateNewId(), DisplayConfigId = cfgId, Name = "display1" }; _mockDisplayRepository.Setup(r => r.GetByConfigId(cfgId)) .ReturnsAsync([d]); var result = await _displayService.GetByConfigId(cfgId); That(result, Has.Count.EqualTo(1)); } // ----------------------------------------------------------- // GetByName // ----------------------------------------------------------- /// /// Verifies that the method throws a when no display matching the specified name is found in the repository. /// [Test] public void GetByName_ShouldThrow_WhenNotFound() { _mockDisplayRepository.Setup(r => r.GetByName("X")) .ReturnsAsync((Display?)null); Func act = () => _displayService.GetByName("X"); Assert.ThrowsAsync(act); } // ----------------------------------------------------------- // GetInfo // ----------------------------------------------------------- /// /// Verifies that GetInfo returns a with its associated PointOfCare entries populated when invoked with the "with POC" flag enabled and a valid display identifier. /// [Test] public async Task GetInfo_ShouldReturnDisplayWithPoc() { var id = ObjectId.GenerateNewId(); var cfgId = ObjectId.GenerateNewId(); var pocId = ObjectId.GenerateNewId(); var display = new Display { Id = id, Name = "Display", DisplayConfigId = cfgId, PointOfCareIdList = [pocId] }; _mockCacheService .Setup(c => c.GetOrSetObjectAsync( It.IsAny(), It.IsAny>>(), It.IsAny(), It.IsAny())) .Returns((string _, Func> factory, TimeSpan? _, CancellationToken _) => factory()); _mockDisplayRepository.Setup(r => r.GetById(id)).ReturnsAsync(display); _mockDisplayConfigService.Setup(s => s.GetById(cfgId)) .ReturnsAsync(new DisplayConfig { Id = cfgId }); var poc = new PointOfCare { Id = pocId }; _mockPointOfCareService.Setup(s => s.GetInfo(pocId, null, true, It.IsAny())) .ReturnsAsync(poc); var result = await _displayService.GetInfo(id, "user", [], null, true, true, false); That(result, Is.Not.Null); That(result!.PointOfCares.First(), Is.EqualTo(poc)); } // ----------------------------------------------------------- // GetByUnitId // ----------------------------------------------------------- /// /// Verifies that returns the displays associated with the specified unit identifier when the repository contains matching records. /// [Test] public async Task GetByUnitId_ShouldReturnDisplays() { var id = ObjectId.GenerateNewId(); var d = new Display { Id = ObjectId.GenerateNewId(), UnitId = id, Name = "display1" }; _mockDisplayRepository.Setup(r => r.GetByUnitId(id)) .ReturnsAsync([d]); var result = await _displayService.GetByUnitId(id); That(result, Has.Count.EqualTo(1)); } // ----------------------------------------------------------- // UpdatePointOfCareList // ----------------------------------------------------------- /// /// Verifies that UpdatePointOfCareList throws a when the display with the specified identifier does not exist. /// /// Thrown when no display is found for the given id. [Test] public void UpdatePointOfCareList_ShouldThrow_WhenDisplayNotFound() { var id = ObjectId.GenerateNewId(); _mockDisplayRepository.Setup(r => r.GetById(id)) .ReturnsAsync((Display?)null); Func act = () => _displayService.UpdatePointOfCareList(id, []); Assert.ThrowsAsync(act); } // ----------------------------------------------------------- // UpdateConfigPreset // ----------------------------------------------------------- /// /// Verifies that throws a when the repository update operation returns a null result, indicating the display or configuration preset could not be found. /// /// The unique identifier of the display whose configuration preset is being updated. /// The unique identifier of the configuration preset to associate with the display. [Test] public void UpdateConfigPreset_ShouldThrow_WhenUpdateFails() { var id = ObjectId.GenerateNewId(); var cfgId = ObjectId.GenerateNewId(); _mockDisplayRepository.Setup(r => r.UpdateConfigPreset(id, cfgId)) .ReturnsAsync((Display?)null); Func act = () => _displayService.UpdateConfigPreset(id, cfgId); Assert.ThrowsAsync(act); } }