324 lines
14 KiB
C#
324 lines
14 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 Options = Microsoft.Extensions.Options.Options;
|
|
|
|
namespace adas_core.Test.Services;
|
|
|
|
[TestFixture]
|
|
public class PointOfCareServiceTests
|
|
{
|
|
/// <summary>
|
|
/// Initializes the mock dependencies and test environment required for unit testing the <see cref="PointOfCareService"/>.
|
|
/// Configures repository, service, cache, admission, unit, and HTTP context mocks, including a simulated authenticated user
|
|
/// and cache behavior that invokes the supplied factory directly to return the produced <see cref="PointOfCare"/> instance.
|
|
/// </summary>
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
_pointOfCareRepositoryMock = new Mock<IPointOfCareRepository>();
|
|
_unitServiceMock = new Mock<IUnitService>();
|
|
_admissionServiceMock = new Mock<IAdmissionService>();
|
|
_cacheServiceMock = new Mock<ICacheService>();
|
|
_cacheServiceMock
|
|
.Setup(c => c.GetOrSetObjectAsync(
|
|
It.IsAny<string>(),
|
|
It.IsAny<Func<Task<PointOfCare?>>>(),
|
|
It.IsAny<TimeSpan?>(),
|
|
It.IsAny<CancellationToken>()))
|
|
.Returns((string _, Func<Task<PointOfCare?>> factory, TimeSpan? _, CancellationToken _) => factory());
|
|
|
|
var loggerMock = new Mock<ILogger<PointOfCareService>>();
|
|
var userClaims = new ClaimsPrincipal(new ClaimsIdentity([
|
|
new Claim(ClaimTypes.Name, "TestUser")
|
|
], "mock"));
|
|
|
|
var httpContextMock = new DefaultHttpContext
|
|
{
|
|
User = userClaims
|
|
};
|
|
|
|
_httpContextAccessorMock.Setup(accessor => accessor.HttpContext)
|
|
.Returns(httpContextMock);
|
|
_pointOfCareService = new PointOfCareService(
|
|
loggerMock.Object,
|
|
_pointOfCareRepositoryMock.Object,
|
|
new Lazy<IPatientService>(Mock.Of<IPatientService>),
|
|
new Lazy<IUnitService>(() => _unitServiceMock.Object),
|
|
Mock.Of<ISubscribersService>(),
|
|
Mock.Of<Lazy<IClientMessageService>>(),
|
|
new Lazy<IAdmissionService>(() => _admissionServiceMock.Object),
|
|
_httpContextAccessorMock.Object,
|
|
_auditServiceMock.Object,
|
|
_cacheServiceMock.Object,
|
|
Options.Create(new CacheSettings())
|
|
);
|
|
}
|
|
|
|
private PointOfCareService _pointOfCareService = null!;
|
|
private Mock<IPointOfCareRepository> _pointOfCareRepositoryMock = null!;
|
|
private Mock<IUnitService> _unitServiceMock = null!;
|
|
private Mock<ICacheService> _cacheServiceMock = null!;
|
|
private Mock<IAdmissionService> _admissionServiceMock = null!;
|
|
private readonly Mock<IHttpContextAccessor> _httpContextAccessorMock = new();
|
|
private readonly Mock<ILocalAuditService> _auditServiceMock = new();
|
|
|
|
/// <summary>
|
|
/// Verifies that the <see cref="PointOfCareService.Delete"/> method successfully deletes a <c>PointOfCare</c>
|
|
/// when invoked with a valid <see cref="ObjectId"/> whose associated admission is <c>null</c>, ensuring
|
|
/// the underlying repository's delete operation is invoked.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task Delete_ValidObjectId_DeletesPointOfCare()
|
|
{
|
|
// Arrange
|
|
var id = ObjectId.GenerateNewId();
|
|
_pointOfCareRepositoryMock.Setup(m => m.FindByIdAllConfig(id))
|
|
.ReturnsAsync(new PointOfCare { Id = id, AdmissionId = null });
|
|
_pointOfCareRepositoryMock.Setup(m => m.Delete(id)).Verifiable();
|
|
|
|
// Act
|
|
await _pointOfCareService.Delete(id);
|
|
|
|
// Assert
|
|
_pointOfCareRepositoryMock.Verify();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that the <see cref="PointOfCareService.Update"/> method successfully updates a valid
|
|
/// <see cref="PointOfCare"/> instance by delegating the operation to the underlying repository.
|
|
/// </summary>
|
|
/// <returns>A <see cref="Task"/> representing the asynchronous test execution.</returns>
|
|
[Test]
|
|
public async Task Update_ValidPointOfCare_UpdatesPointOfCare()
|
|
{
|
|
// Arrange
|
|
var pointOfCare = new PointOfCare { Id = ObjectId.GenerateNewId(), UnitName = "TestUnit" };
|
|
_pointOfCareRepositoryMock.Setup(m => m.Update(pointOfCare)).Verifiable();
|
|
|
|
// Act
|
|
await _pointOfCareService.Update(pointOfCare);
|
|
|
|
// Assert
|
|
_pointOfCareRepositoryMock.Verify();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that <see cref="PointOfCareService.GetAll"/> returns the complete list of point of care records provided by the repository when invoked with no arguments.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task GetAll_NoArguments_ReturnsListOfPointOfCare()
|
|
{
|
|
// Arrange
|
|
var pointOfCareList = new List<PointOfCare> { new(), new() };
|
|
_pointOfCareRepositoryMock.Setup(m => m.GetAll()).ReturnsAsync(pointOfCareList);
|
|
|
|
// Act
|
|
var result = await _pointOfCareService.GetAll();
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(pointOfCareList));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that <see cref="PointOfCareService.UpdateConfiguration"/> throws a <see cref="ConflictException"/> when invoked with a valid id and configuration, ensuring the service surfaces conflict conditions during the update operation.
|
|
/// </summary>
|
|
[Test]
|
|
public void UpdateConfiguration_ValidIdAndConfiguration_InvokesRepositoryUpdateConfiguration()
|
|
{
|
|
// Arrange
|
|
var id = ObjectId.GenerateNewId();
|
|
var configuration = new PointOfCareConfiguration();
|
|
|
|
// Act & Assert
|
|
Assert.That(_pointOfCareService, Is.Not.Null);
|
|
Assert.That(_pointOfCareRepositoryMock, Is.Not.Null);
|
|
|
|
Func<Task> act = async () => await _pointOfCareService.UpdateConfiguration(id, configuration);
|
|
Assert.ThrowsAsync<ConflictException>(act);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that <c>FindById</c> returns the expected <see cref="PointOfCare"/> when a valid identifier is provided.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task FindById_ValidId_ReturnsPointOfCare()
|
|
{
|
|
// Arrange
|
|
var id = ObjectId.GenerateNewId();
|
|
var expectedPointOfCare = new PointOfCare { Id = id };
|
|
_pointOfCareRepositoryMock.Setup(m => m.FindByIdAllConfig(id)).ReturnsAsync(expectedPointOfCare);
|
|
|
|
// Act
|
|
var result = await _pointOfCareService.FindById(id);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(expectedPointOfCare));
|
|
}
|
|
|
|
// [Test]
|
|
// public async Task FindByUnit_ValidUnit_ReturnsListOfPointOfCare()
|
|
// {
|
|
// // Arrange
|
|
// var id = ObjectId.GenerateNewId();
|
|
// var pointOfCare = new PointOfCare { Id = id, UnitId = ObjectId.GenerateNewId() };
|
|
// var unit = new Unit { Id = pointOfCare.UnitId, Name = "TestUnit" };
|
|
// var expectedPointOfCareList = new List<PointOfCare>
|
|
// {
|
|
// new PointOfCare { Unit = unit },
|
|
// new PointOfCare { Unit = unit },
|
|
// };
|
|
// _pointOfCareRepositoryMock.Setup(m => m.FindByUnit(unit)).ReturnsAsync(expectedPointOfCareList);
|
|
//
|
|
// // Act
|
|
// var result = await _pointOfCareService.FindByUnit(unit);
|
|
//
|
|
// // Assert
|
|
// Assert.That(result, Is.EqualTo(expectedPointOfCareList));
|
|
// }
|
|
|
|
/// <summary>
|
|
/// Verifies that <see cref="PointOfCareService.FindByUnitAndStatus"/> returns the expected list of point-of-care records when invoked with a valid unit identifier and status.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task FindByUnitAndStatus_ValidUnitIdAndStatus_ReturnsListOfPointOfCare()
|
|
{
|
|
// Arrange
|
|
var unitId = ObjectId.GenerateNewId();
|
|
var pocStatus = StatusEnum.PointOfCare.Available;
|
|
var expectedPointOfCareList = new List<PointOfCare>
|
|
{
|
|
new(),
|
|
new()
|
|
};
|
|
_pointOfCareRepositoryMock.Setup(m => m.FindByUnitAndStatus(unitId, pocStatus, false))
|
|
.ReturnsAsync(expectedPointOfCareList);
|
|
|
|
// Act
|
|
var result = await _pointOfCareService.FindByUnitAndStatus(unitId, pocStatus);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(expectedPointOfCareList));
|
|
}
|
|
|
|
[Test]
|
|
public void CheckNextAdmission_ValidPatientLocation_UpdatesPoCStatus_AndCallsUpdate()
|
|
{
|
|
// Arrange
|
|
var patientLocationId = ObjectId.GenerateNewId();
|
|
|
|
var pocToCheck = new PointOfCare
|
|
{
|
|
Id = patientLocationId,
|
|
AdmissionId = null, // Forzamos la rama que busca la siguiente admisión
|
|
Status = StatusEnum.PointOfCare.Locked // Locked → no debe actualizar. Cambia para probar actualización:
|
|
// Usa Available o Reserved para que se ejecute el update.
|
|
};
|
|
|
|
// Para que ejecute la lógica de actualización, cambiamos el estado inicial:
|
|
pocToCheck.Status = StatusEnum.PointOfCare.Available;
|
|
|
|
var nextAdmission = new Admission { Id = ObjectId.GenerateNewId() };
|
|
|
|
// 1) Cache: debe ejecutar el factory y devolver el PoC
|
|
_cacheServiceMock
|
|
.Setup(c => c.GetOrSetObjectAsync(
|
|
It.IsAny<string>(),
|
|
It.IsAny<Func<Task<PointOfCare?>>>(),
|
|
It.IsAny<TimeSpan?>(),
|
|
It.IsAny<CancellationToken>()))
|
|
.Returns((string _, Func<Task<PointOfCare?>> factory, TimeSpan? _, CancellationToken _) => factory());
|
|
|
|
// 2) Repo: FindById (usado por el factory del caché)
|
|
_pointOfCareRepositoryMock
|
|
.Setup(m => m.FindById(patientLocationId))
|
|
.ReturnsAsync(pocToCheck);
|
|
|
|
// 3) AdmissionService (ruta AdmissionId == null → buscar siguiente)
|
|
_admissionServiceMock
|
|
.Setup(m => m.GetAdmissionByPointOfCareId(pocToCheck.Id))
|
|
.ReturnsAsync([nextAdmission]);
|
|
|
|
// 4) Update(poc) → queremos esperar a que se invoque
|
|
var updatedSignal = new ManualResetEventSlim(false);
|
|
_pointOfCareRepositoryMock
|
|
.Setup(m => m.Update(It.IsAny<PointOfCare>()))
|
|
.Callback<PointOfCare>(_ => updatedSignal.Set())
|
|
.Returns(Task.CompletedTask);
|
|
|
|
// Update llama internamente a FindById otra vez (en Update)
|
|
_pointOfCareRepositoryMock
|
|
.Setup(m => m.FindById(pocToCheck.Id))
|
|
.ReturnsAsync(pocToCheck);
|
|
|
|
// 5) Ejecutar (async void) y esperar a que se dispare el update
|
|
_pointOfCareService.CheckNextAdmission(patientLocationId);
|
|
|
|
// Espera razonable a que termine la operación asíncrona interna
|
|
var completed = updatedSignal.Wait(TimeSpan.FromSeconds(1));
|
|
Assert.That(completed, Is.True, "CheckNextAdmission no disparó Update a tiempo.");
|
|
|
|
// Assert
|
|
// a) Se llamó a FindById (vía caché)
|
|
_pointOfCareRepositoryMock.Verify(m => m.FindById(patientLocationId), Times.AtLeastOnce);
|
|
|
|
// b) Se llamó a Update con el PoC actualizado: Status Reserved + Admission asignada
|
|
_pointOfCareRepositoryMock.Verify(m => m.Update(
|
|
It.Is<PointOfCare>(p =>
|
|
p.Id == pocToCheck.Id &&
|
|
p.Status == StatusEnum.PointOfCare.Reserved &&
|
|
p.AdmissionId == nextAdmission.Id &&
|
|
p.Admission == nextAdmission)
|
|
), Times.Once);
|
|
|
|
// c) NO verifiques FindByIdAllConfig: en CheckNextAdmission no se usa esa ruta
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests that the service's FindByRoom method, when called with a valid room, returns the collection provided by the repository and invokes the repository's FindByRoom exactly once.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task FindByRoom_ValidRoom_CallsRepositoryFindByRoom()
|
|
{
|
|
// Arrange
|
|
const string room = "TestRoom";
|
|
var expectedPointOfCares = new List<PointOfCare>();
|
|
_pointOfCareRepositoryMock.Setup(m => m.FindByRoom(room)).ReturnsAsync(expectedPointOfCares);
|
|
|
|
// Act
|
|
var result = await _pointOfCareService.FindByRoom(room);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(expectedPointOfCares));
|
|
_pointOfCareRepositoryMock.Verify(m => m.FindByRoom(room), Times.Once);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that the <see cref="PointOfCareService.FindByBed"/> method correctly delegates to the repository's FindByBed method when a valid bed identifier is provided, returning the expected collection of point of care records.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task FindByBed_ValidBed_CallsRepositoryFindByBed()
|
|
{
|
|
// Arrange
|
|
const string bed = "TestBed";
|
|
var expectedPointOfCares = new List<PointOfCare>();
|
|
_pointOfCareRepositoryMock.Setup(m => m.FindByBed(bed)).ReturnsAsync(expectedPointOfCares);
|
|
|
|
// Act
|
|
var result = await _pointOfCareService.FindByBed(bed);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(expectedPointOfCares));
|
|
_pointOfCareRepositoryMock.Verify(m => m.FindByBed(bed), Times.Once);
|
|
}
|
|
|
|
} |