Files
adas-core/adas-core.Test/Services/DisplayServiceTest.cs
T

452 lines
19 KiB
C#

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;
/// <summary>
/// Provides a NUnit test fixture that exercises the behavior of the DisplayService class.
/// </summary>
/// <remarks>
/// Marked with <see cref="TestFixtureAttribute"/> so the NUnit test runner can discover and execute the contained test methods.
/// </remarks>
/// <!-- aidoc:v1 sig=cb14af2 -->
[TestFixture]
public class DisplayServiceTest
{
private Mock<ICacheService> _mockCacheService = null!;
private Mock<IDisplayRepository> _mockDisplayRepository = null!;
private Mock<IPointOfCareService> _mockPointOfCareService = null!;
private Mock<IDisplayConfigService> _mockDisplayConfigService = null!;
private Mock<IUserRepository> _mockUserRepository = null!;
private Mock<IAuthService> _authServiceMock = null!;
private Mock<ISubscribersService> _mockSubscribersService = null!;
private Mock<IClientMessageService> _mockClientMessageService = null!;
private Mock<Lazy<IUnitService>> _mockUnitService = null!;
private Mock<IPermissionService> _mockPermissionService = null!;
private Lazy<IPermissionService> _lazyMockPermission = null!;
private Mock<ILogger<DisplayService>> _mockLogger = null!;
private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock = new();
private readonly Mock<ILocalAuditService> _auditServiceMock = new();
private DisplayService _displayService = null!;
/// <summary>
/// Initializes mocked dependencies and configuration required to construct a <see cref="DisplayService"/> 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.
/// </summary>
/// <!-- aidoc:v1 sig=d8eb19f body=d0144e9 -->
[SetUp]
public void SetUp()
{
_mockCacheService = new Mock<ICacheService>();
_mockDisplayRepository = new Mock<IDisplayRepository>();
_mockPointOfCareService = new Mock<IPointOfCareService>();
_mockDisplayConfigService = new Mock<IDisplayConfigService>();
_mockUserRepository = new Mock<IUserRepository>();
_authServiceMock = new Mock<IAuthService>();
_mockSubscribersService = new Mock<ISubscribersService>();
_mockClientMessageService = new Mock<IClientMessageService>();
_mockUnitService = new Mock<Lazy<IUnitService>>();
_mockPermissionService = new Mock<IPermissionService>();
_lazyMockPermission = new Lazy<IPermissionService>(() => _mockPermissionService.Object);
_mockLogger = new Mock<ILogger<DisplayService>>();
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
// -----------------------------------------------------------
/// <summary>
/// Verifies that <see cref="DisplayService.InsertOne"/> inserts a display by resolving the default configuration for its type and persisting it through the repository's insert method.
/// </summary>
/// <!-- aidoc:v1 sig=8846ed1 body=36d74d2 -->
[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
// -----------------------------------------------------------
/// <summary>
/// Verifies that the <see cref="DisplayService"/> returns the expected <see cref="Display"/> instance when a matching id is provided through the repository.
/// </summary>
/// <!-- aidoc:v1 sig=a7045b7 body=3124a14 -->
[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
// -----------------------------------------------------------
/// <summary>
/// Verifies that _displayService.GetAllByUser returns an empty collection when the user name is null.
/// </summary>
/// <!-- aidoc:v1 sig=67b03d0 body=cceeae1 -->
[Test]
public async Task GetAllByUser_ShouldReturnEmpty_WhenUserNameNull()
{
var result = await _displayService.GetAllByUser(null);
That(result, Is.Empty);
}
// -----------------------------------------------------------
// GetAllByUser - Admin case
// -----------------------------------------------------------
/// <summary>
/// Verifies that <see cref="DisplayService.GetAllByUser"/> returns all <see cref="Display"/> objects
/// associated with the units the user has authorization over when the user holds the
/// <see cref="PermissionEnum.RolesType.AuthAdmin"/> role.
/// </summary>
/// <!-- aidoc:v1 sig=f3916bd body=50fd0aa -->
[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<Display> { 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<string>(),
It.IsAny<Func<Task<Display?>>>(),
It.IsAny<TimeSpan?>(),
It.IsAny<CancellationToken>()))
.Returns((string _, Func<Task<Display?>> 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
// -----------------------------------------------------------
/// <summary>
/// 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.
/// </summary>
/// <!-- aidoc:v1 sig=5eccacf body=8683226 -->
[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));
}
/// <summary>
/// Verifies that <see cref="DisplayService.GetByType"/> returns an empty collection when no display configurations are found for the specified display type.
/// </summary>
/// <returns>A task that completes when the assertion confirming the empty result has been executed.</returns>
/// <!-- aidoc:v1 sig=b325f8a body=dccb49c -->
[Test]
public async Task GetByType_ShouldReturnEmpty_WhenNoConfigsFound()
{
_mockDisplayConfigService.Setup(s => s.GetByType(It.IsAny<DisplayConfigEnums.DisplayType>()))
.ReturnsAsync([]);
var result = await _displayService.GetByType(DisplayConfigEnums.DisplayType.DisplayNurse);
That(result, Is.Empty);
}
// -----------------------------------------------------------
// GetByPointOfCare
// -----------------------------------------------------------
/// <summary>
/// Tests that GetByPointOfCare returns the displays associated with the specified point of care.
/// </summary>
/// <param name="poc">The point of care used to look up associated displays.</param>
/// <returns>A task representing the asynchronous test execution.</returns>
/// <!-- aidoc-review:v1 severity=medium kind=extra_param
/// "The <param name=\"poc\"> tag documents a parameter that does not exist on the test method (which takes no arguments)." -->
[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
// -----------------------------------------------------------
/// <summary>
/// Verifies that <see cref="DisplayService.GetByConfigId"/> returns the displays associated with the specified configuration ID retrieved from the repository.
/// </summary>
/// <returns>A task that completes when the assertion confirms the returned collection contains the expected number of displays.</returns>
/// <!-- aidoc:v1 sig=0344410 body=2a414ec -->
[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
// -----------------------------------------------------------
/// <summary>
/// Verifies that the <see cref="DisplayService.GetByName"/> method throws a <see cref="NotFoundException"/> when no display matching the specified name is found in the repository.
/// </summary>
/// <!-- aidoc:v1 sig=9366b2d body=7fc914f -->
[Test]
public void GetByName_ShouldThrow_WhenNotFound()
{
_mockDisplayRepository.Setup(r => r.GetByName("X"))
.ReturnsAsync((Display?)null);
Func<Task> act = () => _displayService.GetByName("X");
Assert.ThrowsAsync<NotFoundException>(act);
}
// -----------------------------------------------------------
// GetInfo
// -----------------------------------------------------------
/// <summary>
/// Verifies that <c>GetInfo</c> returns a <see cref="Display"/> with its associated <c>PointOfCare</c> entries populated when invoked with the "with POC" flag enabled and a valid display identifier.
/// </summary>
/// <!-- aidoc:v1 sig=0097b05 body=1dea72f -->
[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<string>(),
It.IsAny<Func<Task<Display?>>>(),
It.IsAny<TimeSpan?>(),
It.IsAny<CancellationToken>()))
.Returns((string _, Func<Task<Display?>> 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<CancellationToken>()))
.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
// -----------------------------------------------------------
/// <summary>
/// Verifies that <see cref="IDisplayService.GetByUnitId"/> returns the displays associated with the specified unit identifier when the repository contains matching records.
/// </summary>
/// <!-- aidoc:v1 sig=2c9de1f body=4dae710 -->
[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
// -----------------------------------------------------------
/// <summary>
/// Verifies that UpdatePointOfCareList throws a <see cref="NotFoundException"/> when the display with the specified identifier does not exist.
/// </summary>
/// <exception cref="NotFoundException">Thrown when no display is found for the given id.</exception>
/// <!-- aidoc:v1 sig=fe05ec6 body=46f08a3 -->
[Test]
public void UpdatePointOfCareList_ShouldThrow_WhenDisplayNotFound()
{
var id = ObjectId.GenerateNewId();
_mockDisplayRepository.Setup(r => r.GetById(id))
.ReturnsAsync((Display?)null);
Func<Task> act = () => _displayService.UpdatePointOfCareList(id, []);
Assert.ThrowsAsync<NotFoundException>(act);
}
// -----------------------------------------------------------
// UpdateConfigPreset
// -----------------------------------------------------------
/// <summary>
/// Verifies that <see cref="DisplayService.UpdateConfigPreset"/> throws a <see cref="NotFoundException"/> when the repository update operation returns a null result, indicating the display or configuration preset could not be found.
/// </summary>
/// <param name="id">The unique identifier of the display whose configuration preset is being updated.</param>
/// <param name="cfgId">The unique identifier of the configuration preset to associate with the display.</param>
/// <!-- aidoc-review:v1 severity=medium kind=extra_param
/// "The method has no parameters; 'id' and 'cfgId' are local variables, not method parameters, so the <param> tags document non-existent parameters." -->
[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<Task> act = () => _displayService.UpdateConfigPreset(id, cfgId);
Assert.ThrowsAsync<NotFoundException>(act);
}
}