using adas_core.Application.Customizations.HUVH.UCIA; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using adas_core.Domain.Models; using adas_core.Domain.Models.AppSettings; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; using Moq; using Options = Microsoft.Extensions.Options.Options; namespace adas_core.Test.Customizations.HUVH.UCIA; [TestFixture] [Order(2)] public class CalculatedObservationsTest { /// /// Initializes the test environment by creating mock instances of the observation, medicine, and treatment services, /// configuring a service provider with these dependencies, and instantiating the /// class under test. /// [SetUp] public void Setup() { _observationServiceMock = new Mock(); _medicineServiceMock = new Mock(); _treatmentServiceMock = new Mock(); _logger = new Mock>(); _optionsApiSettings = Options.Create(_apiSettings); var serviceCollection = new ServiceCollection(); //serviceCollection.AddSingleton(_observationServiceMock.Object); serviceCollection.AddSingleton(new Lazy(() => _observationServiceMock.Object)); serviceCollection.AddSingleton(new Lazy(() => _medicineServiceMock.Object)); serviceCollection.AddSingleton(new Lazy(() => _treatmentServiceMock.Object)); serviceCollection.AddSingleton(_optionsApiSettings); serviceCollection.AddSingleton(_logger.Object); var serviceProvider = serviceCollection.BuildServiceProvider(); _calculatedObservations = new CalculatedObservations(serviceProvider); } private CalculatedObservations _calculatedObservations; private Mock _medicineServiceMock; private Mock _observationServiceMock; private Mock _treatmentServiceMock; private Mock> _logger; private readonly ApiSettings _apiSettings = new() { ConfigObservation = new ConfigObservationSettings { IgnoreUnknownObservation = false } }; private IOptions _optionsApiSettings; //Mandar observación 'SOBRESEDACIÓN' //cuando: //(RASS =-4 o RASS =-5) y //PSI < 25 y //TS > 5 y //(Propofol > 3 ó Midazolam > 0,05 ó Isoflorano > 10) /// /// Verifies that an over-sedation observation is inserted when the patient meets the clinical criteria: /// a RASS observation with a sufficiently low score, recent PSI and TS observations, and an active treatment /// (e.g., Propofol) with a minimum requested give amount that triggers the over-sedation condition. /// [Test] public async Task CalculateOverSedation_Should_Insert_Observation_When_Conditions_Met() { var patientId = ObjectId.GenerateNewId(); var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 }; var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 }; var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "PSI", "TS" })) .ReturnsAsync([psiObservation, tsObservation]); _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId)) .ReturnsAsync(new List { new() { RequestedGiveCodes = [new Code { Text = "Propofol" }], RequestedGiveAmountMinimum = 4 } }); await _calculatedObservations.Map(rassObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Once); } /// /// Verifies that no over-sedation observation is inserted when the patient's RASS score is not -4 or -5, ensuring the over-sedation calculation only applies for those specific values. /// [Test] public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_RASS_Is_Not_Negative4_Or_Negative5() { var patientId = ObjectId.GenerateNewId(); var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -3 }; await _calculatedObservations.Map(rassObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Never); } /// /// Verifies that no observation is inserted when calculating over-sedation and the patient's PSI (Sedation Intensity Index) is 25 or higher, which indicates the threshold above which the over-sedation mapping should not produce an observation. /// [Test] public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_PSI_Is_25_Or_Higher() { var patientId = ObjectId.GenerateNewId(); var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 }; var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 25 }; var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "PSI", "TS" })) .ReturnsAsync([psiObservation, tsObservation]); await _calculatedObservations.Map(rassObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Never); } /// /// Verifies that the over-sedation calculation does not insert an observation when the patient's TS (Tidal Spontaneous) value is 5 or lower, even with a low RASS score and an elevated PSI value. /// [Test] public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_TS_Is_5_Or_Lower() { var patientId = ObjectId.GenerateNewId(); var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 }; var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 }; var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 5 }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "PSI", "TS" })) .ReturnsAsync([psiObservation, tsObservation]); await _calculatedObservations.Map(rassObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Never); } /// /// Verifies that no observation is inserted when the patient has no active treatments that exceed the configured threshold during the over-sedation calculation. /// [Test] public async Task CalculateOverSedation_Should_Not_Insert_Observation_When_No_Treatments_Exceed_Threshold() { var patientId = ObjectId.GenerateNewId(); var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 }; var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 }; var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "PSI", "TS" })) .ReturnsAsync([psiObservation, tsObservation]); _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId)) .ReturnsAsync(new List()); // No hay tratamientos activos que superen el umbral await _calculatedObservations.Map(rassObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Never); } /// /// Verifies that an over-sedation observation is inserted when the calculation is triggered by a PSI observation /// and the patient's last RASS and TS observations, combined with active treatment data, meet the triggering criteria. /// [Test] public async Task CalculateOverSedation_Should_Insert_Observation_When_Triggered_By_PSI_Observation() { var patientId = ObjectId.GenerateNewId(); var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 }; var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 }; var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "RASS", "TS" })) .ReturnsAsync([rassObservation, tsObservation]); _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId)) .ReturnsAsync(new List { new() { RequestedGiveCodes = [new Code { Text = "Propofol" }], RequestedGiveAmountMinimum = 4 } }); await _calculatedObservations.Map(psiObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Once); } /// /// Verifies that the over-sedation calculation logic inserts a new observation when a triggering TS (Twitch Score) observation is received, given the presence of related RASS and PSI observations and an active treatment (e.g., Propofol) meeting the required minimum amount threshold. /// [Test] public async Task CalculateOverSedation_Should_Insert_Observation_When_Triggered_By_TS_Observation() { var patientId = ObjectId.GenerateNewId(); var tsObservation = new PatientObservation { PatientId = patientId, Name = "TS", Value = 6 }; var rassObservation = new PatientObservation { PatientId = patientId, Name = "RASS", Value = -4 }; var psiObservation = new PatientObservation { PatientId = patientId, Name = "PSI", Value = 20 }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "RASS", "PSI" })) .ReturnsAsync([rassObservation, psiObservation]); _treatmentServiceMock.Setup(x => x.GetActiveTreatmentsByPatient(patientId)) .ReturnsAsync(new List { new() { RequestedGiveCodes = [new Code { Text = "Propofol" }], RequestedGiveAmountMinimum = 4 } }); await _calculatedObservations.Map(tsObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Once); } //- Si llega valor de PI pero NO COMPL, el tipo de ventilación es VMNI //- Si llega valor de PS pero no llega COMPL, el tipo de ventilación es VMNI //- Si llega COMPL, el tipo de ventilación es VMI //- Si llega Flujo, leer el tipo de ventilación de CCC(CNAF o CTAF) /// /// Verifies that when an Inspiratory Pressure (PI) observation is processed and no Compliancia (COMPL) observation is found for the patient, the ventilation mode is set to "VMNI". /// [Test] public async Task CalculateVentilationMode_Should_Set_VMNI_When_PI_PS_Arrives_Without_COMPL() { var patientId = ObjectId.GenerateNewId(); var piObservation = new PatientObservation { PatientId = patientId, Name = "Inspiratory_Pressure" }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "Compliancia" })) .ReturnsAsync([]); // No hay COMPL await _calculatedObservations.Map(piObservation); _observationServiceMock.Verify( x => x.InsertObservation(It.Is(o => o.Value.ToString() == "VMNI"), true, false), Times.Once); } /// /// Verifies that when a "Compliancia" (COMPL) observation is mapped, the ventilation mode is calculated and set to "VMI". /// [Test] public async Task CalculateVentilationMode_Should_Set_VMI_When_COMPL_Arrives() { var patientId = ObjectId.GenerateNewId(); var complObservation = new PatientObservation { PatientId = patientId, Name = "Compliancia" }; await _calculatedObservations.Map(complObservation); _observationServiceMock.Verify( x => x.InsertObservation(It.Is(o => o.Value.ToString() == "VMI"), true, false), Times.Once); } /// /// Verifies that when an Air Flow observation is processed and a CCC Ventilation Mode observation exists for the patient, the calculated ventilation mode is set to the value retrieved from the CCC source (e.g., "CNAF") and persisted via an insert call. /// [Test] public async Task CalculateVentilationMode_Should_Set_CCC_Mode_When_AirFlow_Arrives_And_CCC_Has_Mode() { var patientId = ObjectId.GenerateNewId(); var airFlowObservation = new PatientObservation { PatientId = patientId, Name = "Air_Flow" }; var mockCursor = new Mock>(); mockCursor.Setup(c => c.Current).Returns(new List { new() { Value = "CNAF" } }); mockCursor.SetupSequence(c => c.MoveNext(It.IsAny())).Returns(true).Returns(false); mockCursor.SetupSequence(c => c.MoveNextAsync(It.IsAny())).ReturnsAsync(true) .ReturnsAsync(false); _observationServiceMock.Setup(x => x.FindByPatientIdAndCodingSystemAsync(patientId, "CCC", "Ventilation_Mode")) .ReturnsAsync(mockCursor.Object); await _calculatedObservations.Map(airFlowObservation); _observationServiceMock.Verify( x => x.InsertObservation(It.Is(o => o.Value.ToString() == "CNAF"), true, false), Times.Once); } [Test] public async Task CalculateVentilationMode_Should_Insert_When_AirFlow_Arrives_And_CCC_Mode() { var patientId = ObjectId.GenerateNewId(); var airFlowObservation = new PatientObservation { PatientId = patientId, Name = "Air_Flow" }; var mockCursor = new Mock>(); // Configura la lista de observaciones devueltas por el cursor var observations = new List { new() { Value = "CNAF" } }; mockCursor.Setup(c => c.Current).Returns(observations); // Configura el comportamiento de MoveNext y MoveNextAsync para simular la enumeración mockCursor.SetupSequence(c => c.MoveNext(It.IsAny())) .Returns(true) // Primera llamada: hay datos .Returns(false); // Segunda llamada: no hay más datos mockCursor.SetupSequence(c => c.MoveNextAsync(It.IsAny())) .ReturnsAsync(true) // Primera llamada asíncrona: hay datos .ReturnsAsync(false); // Segunda llamada: no hay más datos // Configura el mock del servicio para devolver el cursor simulado _observationServiceMock.Setup(x => x.FindByPatientIdAndCodingSystemAsync(patientId, "CCC", "Ventilation_Mode")) .ReturnsAsync(mockCursor.Object); await _calculatedObservations.Map(airFlowObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Once); } /// /// Verifies that when a PI observation is processed and a COMPL observation already exists for the patient, no new calculated observation is inserted. /// [Test] public async Task CalculateVentilationMode_Should_Not_Insert_When_PI_Arrives_And_COMPL_Already_Exists() { var patientId = ObjectId.GenerateNewId(); var piObservation = new PatientObservation { PatientId = patientId, Name = "PI" }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "COMPL" })) .ReturnsAsync([new PatientObservation { Name = "COMPL" }]); // Existe COMPL await _calculatedObservations.Map(piObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Never); } /// /// Verifies that no calculated observation is inserted when a PS (Pressure Support) observation is processed /// and a COMPL observation already exists for the patient. /// [Test] public async Task CalculateVentilationMode_Should_Not_Insert_When_PS_Arrives_And_COMPL_Already_Exists() { var patientId = ObjectId.GenerateNewId(); var psObservation = new PatientObservation { PatientId = patientId, Name = "PS" }; _observationServiceMock.Setup(x => x.FindLastObservations(patientId, 1, new List { "COMPL" })) .ReturnsAsync([new PatientObservation { Name = "COMPL" }]); // Existe COMPL await _calculatedObservations.Map(psObservation); _observationServiceMock.Verify(x => x.InsertObservation(It.IsAny(), true, false), Times.Never); } //Las opciones pueden ser VV, VA o ECMO. //Cuando llegue VV se muestra VV. //Cuando llegue VA se muestra VA. //Cuando llegue los siguientes parámetros, se debe mostrar ECMO: ECCO2r, VVDL, VA+V, VVDL+V, VV+V, VVA /// /// Verifies that mapping a with Name "ECMO_Location" and Value "VV" returns a non-null result whose value is "VV". /// [Test] public async Task CalculateEcmoLocation_Should_Return_VV_When_Value_Is_VV() { var observation = new PatientObservation { Name = "ECMO_Location", Value = "VV" }; var result = await _calculatedObservations.Map(observation); Assert.That(result, Is.Not.Null); Assert.That(result.Value, Is.EqualTo("VV")); } /// /// Verifies that the ECMO location calculation returns "VA" when the input observation value is "VA". /// [Test] public async Task CalculateEcmoLocation_Should_Return_VA_When_Value_Is_VA() { var observation = new PatientObservation { Name = "ECMO_Location", Value = "VA" }; var result = await _calculatedObservations.Map(observation); Assert.That(result, Is.Not.Null); Assert.That(result.Value, Is.EqualTo("VA")); } /// /// Verifies that the mapping logic returns "ECMO" when the observation value is one of the recognized ECMO location codes /// (e.g., "ECCO2r", "VVDL", "VA+V", "VVDL+V", "VV+V", "VVA"). /// /// The ECMO location code assigned to the value under test. [Test] [TestCase("ECCO2r")] [TestCase("VVDL")] [TestCase("VA+V")] [TestCase("VVDL+V")] [TestCase("VV+V")] [TestCase("VVA")] public async Task CalculateEcmoLocation_Should_Return_ECMO_When_Value_Is_In_Ecmo_List(string inputValue) { var observation = new PatientObservation { Name = "ECMO_Location", Value = inputValue }; var result = await _calculatedObservations.Map(observation); Assert.That(result, Is.Not.Null); Assert.That(result.Value, Is.EqualTo("ECMO")); } /// /// Verifies that the ECMO Location mapping does not alter the observation value when the provided value is not present in the recognized ECMO list, leaving the original value unchanged. /// [Test] public async Task CalculateEcmoLocation_Should_Not_Change_Value_When_Not_In_Ecmo_List() { var observation = new PatientObservation { Name = "ECMO_Location", Value = "OTHER" }; var result = await _calculatedObservations.Map(observation); Assert.That(result, Is.Not.Null); Assert.That(result.Value, Is.EqualTo("OTHER")); } /// /// Verifies that the mapping of a through the calculated observations mapper returns the original observation unchanged, ensuring the mapping is a no-op for patient observations. /// [Test] public async Task CalculateEcmoLocation_Should_Return_Original_Observation_When_Not_PatientObservation() { var observation = new PatientObservation(); var result = await _calculatedObservations.Map(observation); Assert.That(result, Is.Not.Null); Assert.That(result, Is.EqualTo(observation)); } // Mandar observación 'SOBREANALGESIA' // 0 <= EVN <= 3 o ESCID = 3 o ANI > 70 // y // Perfusiones de morfina, remifentanilo 0 fentanilo sostenidas > 24h. /// /// Verifies that an "Over_Analgesia" observation with value "True" is created when the patient has active analgesic treatments (Morfina, Remifentanilo) and the last ANI observation indicates a high value (80), suggesting over-analgesia. /// [Test] public async Task CalculateOverAnalgesia_Should_Create_Observation_When_Conditions_Are_Met() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "EVN", Value = "3", PatientId = patientId }; var morfina = new PatientTreatment { RequestedGiveCodes = [new Code { Text = "Morfina" }], StartTime = DateTime.UtcNow.AddHours(-25) }; var remifentanilo = new PatientTreatment { RequestedGiveCodes = [new Code { Text = "Remifentanilo" }], StartTime = DateTime.UtcNow.AddHours(-25) }; //var fentanilo = new PatientTreatment // { RequestedGiveCodes = [new Code { Text = "Fentanilo" }], StartTime = DateTime.UtcNow.AddHours(-25) }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([new PatientObservation { Name = "ANI", Value = "80" }]); _treatmentServiceMock .Setup(s => s.GetActiveTreatmentsByPatient(patientId)) .ReturnsAsync(new List { morfina, remifentanilo /*, fentanilo*/ }); // No hay tratamientos de analgesia activos _observationServiceMock .Setup(s => s.InsertObservation(It.IsAny(), true, false)) .Returns(Task.CompletedTask); // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation( It.Is(o => o.PatientId == patientId && o.Name == "Over_Analgesia" && o.Value.ToString() == "True"), true, false), Times.Once ); } [Test] public async Task CalculateOverAnalgesia_Should_Not_Create_Observation_When_Conditions_Are_Not_Met() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "EVN", Value = "5", PatientId = patientId }; // No cumple la condición _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([new PatientObservation { Name = "ANI", Value = "60" }]); _treatmentServiceMock .Setup(s => s.GetActiveTreatmentsByPatient(patientId)) .ReturnsAsync(new List()); // No hay tratamientos activos // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation(It.IsAny(), true, false), Times.Never ); } //Diferencia entre la Pmeset y la PEEP //Calculo Valor = Pmeset - PEEP /// /// Verifies that the mapping correctly calculates and inserts the driving pressure (Pmeset minus PEEP) when both source observations are available. /// [Test] public async Task CalculateDrivingPressure_Should_Calculate_Correctly_When_Both_Observations_Are_Present() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Pmeset", Value = "25", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([new PatientObservation { Name = "PEEP", Value = "10" }]); _observationServiceMock .Setup(s => s.InsertObservation(It.IsAny(), true, false)) .Returns(Task.CompletedTask); // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation( It.Is(o => o.PatientId == patientId && o.Name == "Driving_Pressure" && o.Value.ToString() == "15"), true, false), Times.Once ); } [Test] public async Task CalculateDrivingPressure_Should_Not_Calculate_If_Observation_Is_Missing() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Pmeset", Value = "25", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([]); // No hay observación complementaria // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation(It.IsAny(), true, false), Times.Never ); } // Cálculo dividiendo el ratio SpO2/FiO2 entre la FR // Valor = (SpO2_FiO2_Ratio) / FR /// /// Verifies that the Rox Index is calculated correctly by dividing the SpO2/FiO2 ratio by the respiratory rate (FR) /// when both required observations are available, and that the result is persisted as a new "Rox_Index" observation. /// [Test] public async Task CalculateRoxIndex_Should_Calculate_Correctly_When_Both_Observations_Are_Present() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "SpO2_FiO2_Ratio", Value = "200", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([new PatientObservation { Name = "FR", Value = "25" }]); _observationServiceMock .Setup(s => s.InsertObservation(It.IsAny(), true, false)) .Returns(Task.CompletedTask); // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation( It.Is(o => o.PatientId == patientId && o.Name == "Rox_Index" && o.Value.ToString() == (200 / 25).ToString()), true, false), Times.Once ); } [Test] public async Task CalculateRoxIndex_Should_Not_Calculate_If_Observation_Is_Missing() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "SpO2_FiO2_Ratio", Value = "200", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([]); // No hay observación complementaria // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation(It.IsAny(), true, false), Times.Never ); } /// /// Verifies that the ROX index calculation is not performed when the respiratory rate (FR) is zero, /// preventing a division by zero error. /// [Test] public async Task CalculateRoxIndex_Should_Not_Calculate_When_FR_Is_Zero_To_Avoid_Division_By_Zero() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "SpO2_FiO2_Ratio", Value = "200", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([new PatientObservation { Name = "FR", Value = "0" }]); // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation(It.IsAny(), true, false), Times.Never ); } // Calculado como el sumatorio de la Diuresis de las últimas 6h // entre el último peso medido del paciente y entre 6. Medido en ml/kg/h // Valor = sum(Diuresis últimas 6 horas) / Weight / 6 /// /// Verifies that the diuresis calculation produces the correct value when prior diuresis observations and a weight observation are available for the patient, resulting in a new "Calculated_Diuresis" observation being inserted. /// [Test] public async Task CalculateDiuresis_Should_Calculate_Correctly_When_Observations_Are_Available() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Diuresis", Value = "100", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 2, It.IsAny>())) .ReturnsAsync([ new PatientObservation { Name = "Diuresis", Value = "120" }, new PatientObservation { Name = "Diuresis", Value = "110" } ]); _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([new PatientObservation { Name = "Weight", Value = "70" }]); _observationServiceMock .Setup(s => s.InsertObservation(It.IsAny(), true, false)) .Returns(Task.CompletedTask); // Act await _calculatedObservations.Map(obs); // Assert const double expectedValue = (100 + 120 + 110) / 70.0 / 6; _observationServiceMock.Verify( x => x.InsertObservation( It.Is(o => o.PatientId == patientId && o.Name == "Calculated_Diuresis" && Math.Abs((double)o.Value - expectedValue) < 0.1), true, false), Times.Once ); } /// /// Verifies that diuresis is not calculated when no diuresis observations are found for the patient. /// Ensures the mapping logic skips persistence of a calculated diuresis observation when required prior observations are missing. /// [Test] public async Task CalculateDiuresis_Should_Not_Calculate_If_Diuresis_Observations_Are_Missing() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Diuresis", Value = "100", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 2, It.IsAny>())) .ReturnsAsync([]); // No hay diuresis // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation(It.IsAny(), true, false), Times.Never ); } /// /// Verifies that the diuresis calculation is not performed when the patient has no weight observation available, ensuring no new observation is inserted. /// [Test] public async Task CalculateDiuresis_Should_Not_Calculate_If_Weight_Is_Invalid() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Diuresis", Value = "100", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 2, It.IsAny>())) .ReturnsAsync([ new PatientObservation { Name = "Diuresis", Value = "120" }, new PatientObservation { Name = "Diuresis", Value = "110" } ]); _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 1, It.IsAny>())) .ReturnsAsync([]); // No hay peso // Act await _calculatedObservations.Map(obs); // Assert _observationServiceMock.Verify( x => x.InsertObservation(It.IsAny(), true, false), Times.Never ); } /// /// Verifies that the rehabilitation observation mapping sets the status to Alert when the recent rehabilitation values meet the alert condition. /// [Test] public async Task CalculateRehabilitationAlarm_Should_Set_Alert_When_Condition_Is_Met() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Rehabilitation", Value = "2", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 6, It.IsAny>())) .ReturnsAsync([ new PatientObservation { Name = "Rehabilitation", Value = "0" }, new PatientObservation { Name = "Rehabilitation", Value = "1" }, new PatientObservation { Name = "Rehabilitation", Value = "2" }, new PatientObservation { Name = "Rehabilitation", Value = "0" }, new PatientObservation { Name = "Rehabilitation", Value = "1" }, new PatientObservation { Name = "Rehabilitation", Value = "2" } ]); // Act var result = await _calculatedObservations.Map(obs); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.Status, Is.EqualTo(StatusEnum.Type.Alert)); } /// /// Verifies that the rehabilitation alarm calculation does not set the alert status when the /// configured condition is not met, even when the most recent observation values are within an /// expected range and an out-of-range value is present among the evaluated history. /// [Test] public async Task CalculateRehabilitationAlarm_Should_Not_Set_Alert_When_Condition_Is_Not_Met() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Rehabilitation", Value = "2", PatientId = patientId }; _observationServiceMock .Setup(s => s.FindLastObservations(patientId, 6, It.IsAny>())) .ReturnsAsync([ new PatientObservation { Name = "Rehabilitation", Value = "0" }, new PatientObservation { Name = "Rehabilitation", Value = "1" }, new PatientObservation { Name = "Rehabilitation", Value = "2" }, new PatientObservation { Name = "Rehabilitation", Value = "1" }, new PatientObservation { Name = "Rehabilitation", Value = "3" }, //<--fuera de rango new PatientObservation { Name = "Rehabilitation", Value = "0" } ]); // Act var result = await _calculatedObservations.Map(obs); // Assert Assert.That(result, Is.Not.Null); Assert.That(result.Status, Is.Not.EqualTo(StatusEnum.Type.Alert)); } /// /// Verifies that the Map method does not set an alert for a Rehabilitation observation when its value is greater than 2, returning the original observation unchanged. /// [Test] public async Task CalculateRehabilitationAlarm_Should_Not_Set_Alert_When_Value_Is_Greater_Than_2() { // Arrange var patientId = ObjectId.GenerateNewId(); var obs = new PatientObservation { Name = "Rehabilitation", Value = "3", PatientId = patientId }; // Act var result = await _calculatedObservations.Map(obs); // Assert Assert.That(result, Is.SameAs(obs)); } /// /// Verifies that the rehabilitation alarm calculation returns the same observation unchanged when the input observation is not a . /// This ensures that the mapping method does not perform any transformation for non-patient observations. /// [Test] public async Task CalculateRehabilitationAlarm_Should_Return_Same_Observation_If_Not_PatientObservation() { // Arrange var obs = new BasePatientObservation(); // No es un PatientObservation // Act var result = await _calculatedObservations.Map(obs); // Assert Assert.That(result, Is.SameAs(obs)); } }