Creado apartir del commit b888de6c92056de294456f581a56e25e734d4ab7 de develop
This commit is contained in:
@@ -0,0 +1,461 @@
|
||||
using System.Security.Claims;
|
||||
using adas_core.Application.Repositories.Interfaces;
|
||||
using adas_core.Application.Services;
|
||||
using adas_core.Application.Services.Interfaces;
|
||||
using adas_core.Application.Subscriptions;
|
||||
using adas_core.Domain.Enums;
|
||||
using adas_core.Domain.Models;
|
||||
using adas_core.Domain.Models.AppSettings;
|
||||
using adas_core.Domain.Models.Filter;
|
||||
using adas_core.Domain.Models.MongoModels;
|
||||
using adas_core.Domain.Models.Pumps;
|
||||
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 PumpServiceTest
|
||||
{
|
||||
private PumpService _service = null!;
|
||||
private Mock<IPumpObservationRepository> _obsRepo = null!;
|
||||
private Mock<IPumpStateRepository> _stateRepo = null!;
|
||||
private Mock<IPumpAlarmEventRepository> _alarmEventRepo = null!;
|
||||
private Mock<IPumpAlarmStateRepository> _alarmStateRepo = null!;
|
||||
private Mock<IPumpArchiveRepository> _archiveRepo = null!;
|
||||
private Mock<IPatientService> _patientSvc = null!;
|
||||
private Mock<IConfigPumpsService> _configPumps = null!;
|
||||
private Mock<ISubscribersService> _subs = null!;
|
||||
private Mock<IClientMessageService> _clientMsg = null!;
|
||||
private Mock<IConfigUnitsService> _configUnits = null!;
|
||||
private Mock<ICalculatedObservationsService> _calcObs = null!;
|
||||
private Lazy<ICalculatedObservationsService> _lazyCalc = null!;
|
||||
private Mock<IHttpContextAccessor> _http = null!;
|
||||
private Mock<ILocalAuditService> _audit = null!;
|
||||
private Mock<ILogger<PumpService>> _logger = null!;
|
||||
|
||||
private static readonly ObjectId PatientId = ObjectId.GenerateNewId();
|
||||
private static readonly DateTime Now = DateTime.UtcNow;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_obsRepo = new Mock<IPumpObservationRepository>();
|
||||
_stateRepo = new Mock<IPumpStateRepository>();
|
||||
_alarmEventRepo = new Mock<IPumpAlarmEventRepository>();
|
||||
_alarmStateRepo = new Mock<IPumpAlarmStateRepository>();
|
||||
_archiveRepo = new Mock<IPumpArchiveRepository>();
|
||||
_patientSvc = new Mock<IPatientService>();
|
||||
_configPumps = new Mock<IConfigPumpsService>();
|
||||
_subs = new Mock<ISubscribersService>();
|
||||
_clientMsg = new Mock<IClientMessageService>();
|
||||
_calcObs = new Mock<ICalculatedObservationsService>();
|
||||
_lazyCalc = new Lazy<ICalculatedObservationsService>(() => _calcObs.Object);
|
||||
_http = new Mock<IHttpContextAccessor>();
|
||||
_audit = new Mock<ILocalAuditService>();
|
||||
_logger = new Mock<ILogger<PumpService>>();
|
||||
_configUnits = new Mock<IConfigUnitsService>();
|
||||
|
||||
var principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity([new Claim(ClaimTypes.Name, "test")], "mock"));
|
||||
|
||||
_http.Setup(x => x.HttpContext)
|
||||
.Returns(new DefaultHttpContext { User = principal });
|
||||
|
||||
_calcObs.Setup(x => x.Map(It.IsAny<PumpObservation>()))
|
||||
.ReturnsAsync((PumpObservation o) => o);
|
||||
|
||||
var api = Options.Create(new ApiSettings
|
||||
{
|
||||
PumpExpiresSeconds = 5,
|
||||
SendPumpsZero = false
|
||||
});
|
||||
|
||||
_service = new PumpService(
|
||||
_obsRepo.Object,
|
||||
_stateRepo.Object,
|
||||
_alarmEventRepo.Object,
|
||||
_alarmStateRepo.Object,
|
||||
_archiveRepo.Object,
|
||||
_patientSvc.Object,
|
||||
_configPumps.Object,
|
||||
api,
|
||||
_logger.Object,
|
||||
_subs.Object,
|
||||
_clientMsg.Object,
|
||||
_lazyCalc,
|
||||
_http.Object,
|
||||
_audit.Object,
|
||||
_configUnits.Object
|
||||
);
|
||||
|
||||
_configPumps.Setup(x => x.Map(It.IsAny<PumpObservation>()))
|
||||
.ReturnsAsync((PumpObservation o) => o);
|
||||
_configUnits.Setup(x => x.Map(It.IsAny<PumpObservation>()))
|
||||
.ReturnsAsync((PumpObservation o) => o);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// SAVE REQUEST — casos básicos
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task SaveRequest_Returns_When_No_Observations()
|
||||
{
|
||||
var req = new ApiRequest { Type = "ORU_R01" };
|
||||
await _service.SaveRequest(req);
|
||||
_obsRepo.Verify(r => r.InsertAsync(It.IsAny<PumpObservation>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveRequest_UnknownType_DoesNotInsert()
|
||||
{
|
||||
var req = new ApiRequest
|
||||
{
|
||||
Type = "UNKNOWN",
|
||||
PumpObservation = new PumpObservation { Time = Now }
|
||||
};
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
_obsRepo.Verify(r => r.InsertAsync(It.IsAny<PumpObservation>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveRequest_Converts_SingleObservation_ToList()
|
||||
{
|
||||
var obs = new PumpObservation { Time = Now };
|
||||
var req = new ApiRequest
|
||||
{
|
||||
Type = "ORU_R01",
|
||||
PumpObservation = obs,
|
||||
PatientNumber = "123"
|
||||
};
|
||||
|
||||
_patientSvc.Setup(p => p.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(new Patient { Id = PatientId });
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
Assert.That(req.PumpObservations, Has.Count.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveRequest_SetsExpires_BeforeInsert()
|
||||
{
|
||||
var obs = new PumpObservation
|
||||
{
|
||||
Time = Now,
|
||||
DeviceId = "D1"
|
||||
};
|
||||
|
||||
var req = new ApiRequest
|
||||
{
|
||||
Type = "ORU_R01",
|
||||
PumpObservation = obs,
|
||||
PatientNumber = "1"
|
||||
};
|
||||
|
||||
_patientSvc.Setup(x => x.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(new Patient { Id = PatientId });
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
_obsRepo.Verify(r => r.InsertAsync(
|
||||
It.Is<PumpObservation>(o => o.Expires == 5)), Times.Once);
|
||||
}
|
||||
// --------------------------------------------------------------
|
||||
// PROCESS ALARM
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task SaveRequest_ORU_R40_CreatesAlarmEvent()
|
||||
{
|
||||
var obs = new PumpObservation
|
||||
{
|
||||
Time = Now,
|
||||
DeviceId = "D1",
|
||||
AlarmType = PumpEnum.AlarmType.Occlusion,
|
||||
EventPhase = PumpEnum.EventPhase.Start
|
||||
};
|
||||
|
||||
var req = new ApiRequest { Type = "ORU_R40", PumpObservation = obs };
|
||||
_patientSvc.Setup(x => x.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(new Patient { Id = PatientId });
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
_alarmEventRepo.Verify(r => r.InsertAsync(It.IsAny<PumpAlarmEvent>()), Times.Once);
|
||||
_obsRepo.Verify(r => r.InsertAsync(It.IsAny<PumpObservation>()), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ProcessAlarm_End_RemovesAlarmState()
|
||||
{
|
||||
var obs = new PumpObservation
|
||||
{
|
||||
Time = Now,
|
||||
DeviceId = "DX",
|
||||
AlarmType = PumpEnum.AlarmType.Occlusion,
|
||||
AlarmTypeMdc = "H1",
|
||||
EventPhase = PumpEnum.EventPhase.End
|
||||
};
|
||||
|
||||
var req = new ApiRequest { Type = "ORU_R40", PumpObservation = obs };
|
||||
_patientSvc.Setup(x => x.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(new Patient { Id = PatientId });
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
_alarmStateRepo.Verify(r => r.RemoveAsync("DX", PumpEnum.AlarmType.Occlusion, "H1"), Times.Once);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// SNAPSHOT DE BOMBA
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task SaveRequest_CreatesPumpState_IfNotExists()
|
||||
{
|
||||
var obs = new PumpObservation { Time = Now, DeviceId = "P1" };
|
||||
var req = new ApiRequest { Type = "ORU_R01", PumpObservation = obs, PatientNumber = "1" };
|
||||
|
||||
_patientSvc.Setup(x => x.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(new Patient { Id = PatientId });
|
||||
|
||||
_stateRepo.Setup(r => r.FindByDeviceIdAsync("P1"))
|
||||
.ReturnsAsync((PumpState?)null);
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
_stateRepo.Verify(r => r.UpsertAsync(It.Is<PumpState>(s => s.DeviceId == "P1")), Times.Once);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// BROADCAST
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task SaveRequest_NoSubscribers_NoBroadcast()
|
||||
{
|
||||
var obs = new PumpObservation { Time = Now, DeviceId = "BR1", PatientId = PatientId };
|
||||
var patient = new Patient
|
||||
{
|
||||
Id = PatientId,
|
||||
Location = new PatientLocation("U1", "B1")
|
||||
};
|
||||
|
||||
var req = new ApiRequest
|
||||
{
|
||||
Type = "ORU_R01",
|
||||
PumpObservation = obs,
|
||||
PatientNumber = "1"
|
||||
};
|
||||
|
||||
_subs.Setup(x => x.GetSubscribers()).Returns([]);
|
||||
_patientSvc.Setup(x => x.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(patient);
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
_clientMsg.Verify(r => r.SendAsync(It.IsAny<string>(), It.IsAny<OperationType>(), It.IsAny<object>()),
|
||||
Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SaveRequest_WithSubscribers_UsesReqLocation_AndSendsBroadcast()
|
||||
{
|
||||
// Arrange
|
||||
var obs = new PumpObservation
|
||||
{
|
||||
Time = Now,
|
||||
DeviceId = "D22",
|
||||
PatientId = null // <- clave para que se use req.Location
|
||||
};
|
||||
|
||||
var req = new ApiRequest
|
||||
{
|
||||
Type = "ORU_R01",
|
||||
PumpObservation = obs,
|
||||
Location = new PatientLocation("UCI5C", "Box4", "Room1"),
|
||||
PatientNumber = "437537"
|
||||
};
|
||||
|
||||
var subscriber = new WsSubscriber("sub1")
|
||||
{
|
||||
Locations = [ new PatientLocation("UCI5C", "Box4", "Room1") ]
|
||||
};
|
||||
|
||||
// 1) NO devolver paciente en FindPatientByApiRequest -> fuerza uso de req. Location
|
||||
_patientSvc.Setup(p => p.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync((Patient?)null);
|
||||
|
||||
// 2) Suscriptores con la misma ubicación que req.Location
|
||||
_subs.Setup(x => x.GetSubscribers())
|
||||
.Returns([subscriber]);
|
||||
|
||||
// 3) Evitar null en alarmas activas al enumerar (foreach)
|
||||
_alarmStateRepo.Setup(r => r.FindAllActiveByDeviceAsync("D22"))
|
||||
.ReturnsAsync(Enumerable.Empty<PumpAlarmState>());
|
||||
|
||||
// 4) Snapshot: por claridad, fuerza que se cree uno nuevo
|
||||
_stateRepo.Setup(r => r.FindByDeviceIdAsync("D22"))
|
||||
.ReturnsAsync((PumpState?)null);
|
||||
|
||||
// Map mínimo para que avance el flujo
|
||||
_configPumps.Setup(m => m.Map(It.IsAny<PumpObservation>()))
|
||||
.ReturnsAsync((PumpObservation o) => o);
|
||||
_configUnits.Setup(m => m.Map(It.IsAny<PumpObservation>()))
|
||||
.ReturnsAsync((PumpObservation o) => o);
|
||||
|
||||
// Act
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
// Assert
|
||||
_clientMsg.Verify(x =>
|
||||
x.SendAsync("sub1", OperationType.Pump, It.IsAny<PumpState>()),
|
||||
Times.Once);
|
||||
|
||||
// No se envían PumpAlarm si no hay activas
|
||||
_clientMsg.Verify(x =>
|
||||
x.SendAsync("sub1", OperationType.PumpAlarm, It.IsAny<object>()),
|
||||
Times.Never);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// RETENCIÓN — DeleteOlderDays
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task SaveRequest_Retention_DeleteOlderDays_CallsRepo()
|
||||
{
|
||||
var obs = new PumpObservation { Time = Now, DeviceId = "R1", PatientId = PatientId };
|
||||
var req = new ApiRequest { Type = "ORU_R01", PumpObservation = obs, PatientNumber = "1" };
|
||||
|
||||
_patientSvc.Setup(x => x.FindPatientByApiRequest(req))
|
||||
.ReturnsAsync(new Patient { Id = PatientId });
|
||||
|
||||
_configPumps.Setup(x => x.RetentionActions(It.IsAny<PumpObservation>()))
|
||||
.ReturnsAsync(new ObservatitonRetentionResult
|
||||
{
|
||||
RetentionPolicy = RetentionPolicy.DeleteOlderDays,
|
||||
RetentionPolicyValue = 7
|
||||
});
|
||||
|
||||
await _service.SaveRequest(req);
|
||||
|
||||
_obsRepo.Verify(r => r.DeleteOlderThanDaysAsync(7, null), Times.Once);
|
||||
}
|
||||
// --------------------------------------------------------------
|
||||
// PAGINACIÓN
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task GetPaginatedPump_ByPatient_Works()
|
||||
{
|
||||
var obs = new List<PumpObservation>
|
||||
{
|
||||
new() { Time = Now.AddMinutes(-1), PatientId = PatientId },
|
||||
new() { Time = Now.AddMinutes(-5), PatientId = PatientId }
|
||||
};
|
||||
|
||||
_obsRepo.Setup(r => r.FindByPatientId(PatientId))
|
||||
.ReturnsAsync(obs);
|
||||
|
||||
var fileredRequest = new FilteredRequest
|
||||
{
|
||||
PatientId = PatientId.ToString()
|
||||
};
|
||||
|
||||
var filter = new PaginationFilter(1, 1, fileredRequest);
|
||||
|
||||
var result = await _service.GetPaginatedPump(filter);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(result!.Data, Has.Count.EqualTo(1));
|
||||
Assert.That(result.Data[0].Time, Is.EqualTo(Now.AddMinutes(-1)).Within(TimeSpan.FromMilliseconds(2)));
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// ARCHIVO
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task ArchiveByPatientId_InsertsArchive_ThenDeletes()
|
||||
{
|
||||
var list = new List<PumpObservation>
|
||||
{
|
||||
new() { Id = ObjectId.GenerateNewId(), PatientId = PatientId }
|
||||
};
|
||||
|
||||
_obsRepo.Setup(r => r.FindByPatientId(PatientId)).ReturnsAsync(list);
|
||||
|
||||
await _service.ArchiveByPatientId(PatientId);
|
||||
|
||||
_archiveRepo.Verify(r => r.InsertManyAsync(list), Times.Once);
|
||||
_obsRepo.Verify(r => r.DeleteByPatientId(PatientId), Times.Once);
|
||||
_alarmEventRepo.Verify(r => r.DeleteByPatientId(PatientId), Times.Once);
|
||||
_alarmStateRepo.Verify(r => r.DeleteByPatientId(PatientId), Times.Once);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// UPDATE MANY
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task UpdateManyObjectId_UpdatesObs_Alarms_States()
|
||||
{
|
||||
var oldId = ObjectId.GenerateNewId();
|
||||
var newId = ObjectId.GenerateNewId();
|
||||
|
||||
_obsRepo.Setup(r => r.UpdateManyObjectIdByFieldAsync("patientId", newId, oldId))
|
||||
.ReturnsAsync(3);
|
||||
_alarmEventRepo.Setup(r => r.UpdateManyObjectIdByFiledNameAsync("patientId", newId, oldId))
|
||||
.ReturnsAsync(1);
|
||||
_alarmStateRepo.Setup(r => r.UpdateManyObjectIdByFieldNameAsync("patientId", newId, oldId))
|
||||
.ReturnsAsync(2);
|
||||
|
||||
await _service.UpdateManyObjectId("patientId", newId, oldId);
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// FIND LAST OBSERVATIONS
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task FindLastPumpObservations_ReturnsOrdered()
|
||||
{
|
||||
var items = new List<PumpObservation>
|
||||
{
|
||||
new() { Time = Now.AddMinutes(-10) },
|
||||
new() { Time = Now.AddMinutes(-1) }
|
||||
};
|
||||
|
||||
_obsRepo.Setup(r => r.FindByPatientId(PatientId))
|
||||
.ReturnsAsync(items);
|
||||
|
||||
var result = await _service.FindLastPumpObservations(PatientId, 1);
|
||||
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(result[0].Time, Is.EqualTo(items[1].Time));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// INSERT MANUAL
|
||||
// --------------------------------------------------------------
|
||||
[Test]
|
||||
public async Task InsertPumpObservation_Inserts_AndBroadcasts()
|
||||
{
|
||||
var obs = new PumpObservation
|
||||
{
|
||||
DeviceId = "D11",
|
||||
PatientId = PatientId,
|
||||
Time = Now
|
||||
};
|
||||
|
||||
_stateRepo.Setup(r => r.FindByDeviceIdAsync("D11"))
|
||||
.ReturnsAsync((PumpState?)null);
|
||||
|
||||
_subs.Setup(x => x.GetSubscribers()).Returns([]);
|
||||
|
||||
await _service.InsertPumpObservation(obs);
|
||||
|
||||
_obsRepo.Verify(r => r.InsertAsync(It.IsAny<PumpObservation>()), Times.Once);
|
||||
_stateRepo.Verify(r => r.UpsertAsync(It.IsAny<PumpState>()), Times.Once);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user