Documentation modifications

This commit is contained in:
julian
2026-06-27 15:23:26 -07:00
parent a633fe6c06
commit a19fb90902
218 changed files with 2882 additions and 0 deletions
@@ -13,9 +13,20 @@ using Options = Microsoft.Extensions.Options.Options;
namespace adas_core.Test.Customizations.HRYC;
/// <summary>
/// Provides an NUnit test fixture that verifies the behavior of calculated observations.
/// </summary>
/// <remarks>
/// Decorated with <see cref="TestFixtureAttribute"/> to mark the class as a container for related NUnit test methods that exercise calculated observation logic.
/// </remarks>
/// <!-- aidoc:v1 sig=ce3e368 -->
[TestFixture]
public class CalculatedObservationsTest
{
/// <summary>
/// Initializes the test environment for <see cref="CalculatedObservations"/> by mocking <see cref="IObservationService"/>, <see cref="IPatientService"/>, <see cref="ILightBeaconService"/>, and <see cref="IConfigObservationService"/>, configuring the latter to look up NEWS alert configurations by <see cref="PatientObservation.Name"/> and return <see langword="null"/> when no entry matches, registering all dependencies through a <see cref="ServiceCollection"/>, and providing a sample <see cref="Patient"/> resolved by its <see cref="Patient.Id"/>.
/// </summary>
/// <!-- aidoc:v1 sig=dee8bf2 body=439c7b9 -->
[SetUp]
public void Setup()
{
@@ -113,6 +124,10 @@ public class CalculatedObservationsTest
private ObjectId _patientUnitId = ObjectId.GenerateNewId();
private Patient _patient;
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> with <c>NEWS</c> coding, when the resolved <see cref="ConfigObservation"/> enables a beacon alarm with <see cref="AlarmEnum.BeaconColor.None"/>, triggers the light beacon service to send <see cref="LightBeaconColor.Off"/> to the patient's <see cref="BasePatientObservation.PointOfCareId"/>.
/// </summary>
/// <!-- aidoc:v1 sig=e823ca6 body=65dc365 -->
[Test]
public async Task Calculate_CheckBeaconOnNEWS_Should_Send_PowerOff()
{
@@ -148,6 +163,10 @@ public class CalculatedObservationsTest
Times.Once);
}
/// <summary>
/// Verifies that when a <see cref="PatientObservation"/> with the "NEWS" name, "ADAS" coding system, and a value of 6 is processed, and the resolved <see cref="ConfigObservation"/> enables a yellow beacon alarm, the <see cref="LightBeaconService"/> is invoked exactly once to send <see cref="LightBeaconColor.Yellow"/> to the patient's point of care.
/// </summary>
/// <!-- aidoc:v1 sig=aa7e929 body=e6fe577 -->
[Test]
public async Task Calculate_CheckBeaconOnNEWS_Should_Send_Yellow()
{
@@ -183,6 +202,10 @@ public class CalculatedObservationsTest
LightBeaconColor.Yellow), Times.Once);
}
/// <summary>
/// Verifies that when a <see cref="PatientObservation"/> using the NEWS coding system is mapped and the resolved configuration defines an enabled red beacon alarm, the light beacon service is invoked exactly once to send the red color to the patient's point of care.
/// </summary>
/// <!-- aidoc:v1 sig=843f672 body=55f19b1 -->
[Test]
public async Task Calculate_CheckBeaconOnNEWS_Should_Send_Red()
{
@@ -219,6 +242,10 @@ public class CalculatedObservationsTest
Times.Once);
}
/// <summary>
/// Verifies that <see cref="_calculatedObservations"/>.Map correctly produces a <see cref="PatientObservation"/> with name <c>NEWS_EXTR_HI_Resp_Rate</c>, coding system <c>ADAS</c>, and the same numeric value as the supplied respiratory rate (<c>FR</c>) when the latest ventilator rate, <c>SpO2</c> and <c>FiO2</c> observations are available.
/// </summary>
/// <!-- aidoc:v1 sig=49f263a body=aad4daf -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_FR_Returns_NEWS_EXTR_HI_Resp_Rate()
@@ -270,6 +297,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> named <c>FR</c> with an invalid (non-numeric) value does not produce a NEWS_EXTR_HI respiratory rate result and instead raises a <see cref="FormatException"/>, even when valid <c>Vent_Rat</c>, <c>SpO2</c>, and <c>FiO2</c> observations are available for the same patient.
/// </summary>
/// <!-- aidoc:v1 sig=80e9384 body=260f9f8 -->
[Test]
public async Task Calculate_FR_Does_Not_Returns_NEWS_EXTR_HI_Resp_Rate_When_Invalid_Value()
{
@@ -321,6 +352,15 @@ public class CalculatedObservationsTest
}
/// <summary>
/// Verifies that mapping a respiratory rate (<c>FR</c>) observation through
/// <see cref="ICalculatedObservations.Map(PatientObservation, bool)"/> produces a
/// <c>NEWS_HI_Resp_Rate</c> observation with the expected value (<c>23</c>) and coding system
/// (<c>ADAS</c>), resolving the ventilator rate (<c>Vent_Rat</c>), <c>SpO2</c>, and <c>FiO2</c>
/// inputs via the mocked <see cref="IObservationService.FindLastObservations"/> calls and inserting
/// the result through <see cref="IObservationService.InsertObservation"/>.
/// </summary>
/// <!-- aidoc:v1 sig=36eec84 body=c350cc3 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_FR_Returns_NEWS_HI_Resp_Rate()
@@ -377,6 +417,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies (under the legacy NEWS calculation path, currently ignored via <see cref="IgnoreAttribute"/>) that mapping a respiratory rate (<c>FR</c>) <see cref="PatientObservation"/> produces an inserted <c>NEWS_EXTR_LO_Resp_Rate</c> observation tagged with the <c>ADAS</c> coding system and carrying the same numeric value, using mocked supporting observations for ventilation rate (<c>Vent_Rate</c>) and oxygenation (<c>SpO2</c>, <c>FiO2</c>).
/// </summary>
/// <!-- aidoc:v1 sig=4fa8ea2 body=f8aaeaa -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_FR_Returns_NEWS_EXTR_LO_Resp_Rate()
@@ -433,6 +477,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> with an SpO2 value of 90 produces an inserted <see cref="PatientObservation"/> named <c>NEWS_EXTR_LO_SpO2</c> using the <c>ADAS</c> coding system, by mocking the lookup of the last <c>FiO2</c> and <c>FR</c> observations for the same <see cref="PatientObservation.PatientId"/>. The test is annotated with <c>[Ignore]</c> because it targets the legacy NEWS calculation logic.
/// </summary>
/// <!-- aidoc:v1 sig=937a891 body=deafefb -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_SpO2_Returns_NEWS_EXTR_LO_SpO2()
@@ -471,6 +519,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a SpO2 <see cref="PatientObservation"/> produces a NEWS Low SpO2 score <see cref="PatientObservation"/> with name "NEWS_LO_SpO2", coding system "ADAS", and a value reflecting the source SpO2 reading (93), while relying on the last available respiratory rate (FR) observation for the calculation.
/// </summary>
/// <!-- aidoc:v1 sig=686d725 body=e5019f8 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_SpO2_Returns_NEWS_LO_SpO2()
@@ -509,6 +561,11 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> whose <see cref="BasePatientObservation.Name"/> is <c>Temperature</c> results in a derived <see cref="PatientObservation"/> named <c>NEWS_EXTR_LO_Temperature</c>, coded under the <c>ADAS</c> system, and carrying the original numeric <see cref="BasePatientObservation.Value"/>.
/// Marked with <see cref="IgnoreAttribute"/> because it covers the legacy NEWS calculation logic.
/// </summary>
/// <!-- aidoc:v1 sig=34bdb7c body=0b6d2c3 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_Temperature_Returns_NEWS_EXTR_LO_Temperature()
@@ -534,6 +591,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a high <see cref="PatientObservation"/> with name "Temperature" and value 40 produces a calculated NEWS high-temperature observation (<c>NEWS_HI_Temperature</c>) under the <c>ADAS</c> coding system.
/// </summary>
/// <!-- aidoc:v1 sig=2cfd40e body=7a7791e -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_Temperature_Returns_NEWS_HI_Temperature()
@@ -559,6 +620,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> with the name "TAs" produces an associated low NEWS extra observation named "NEWS_EXTR_LO_TAs" in the ADAS coding system, carrying the same numeric value as the source observation.
/// </summary>
/// <!-- aidoc:v1 sig=32954ae body=a546520 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_TAs_Returns_NEWS_EXTR_LO_TAs()
@@ -584,6 +649,11 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that when a <c>TAs</c> (systolic blood pressure) <see cref="PatientObservation"/> is mapped, a derived <c>NEWS_LO_TAs</c> observation is inserted with the expected <see cref="PatientObservation.CodingSystem"/> and <see cref="PatientObservation.Value"/>.
/// This test is currently ignored because it targets the old NEWS calculation logic.
/// </summary>
/// <!-- aidoc:v1 sig=a6cacee body=6089652 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_TAs_Returns_NEWS_LO_TAs()
@@ -609,6 +679,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> named "TAs" with a value of 240 produces an inserted observation named "NEWS_EXTR_HI_TAs" under the "ADAS" coding system through <see cref="_calculatedObservations"/>. The test is currently ignored as it covers the old NEWS calculation logic.
/// </summary>
/// <!-- aidoc:v1 sig=1b56dd6 body=7a377a8 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_TAs_Returns_NEWS_EXTR_HI_TAs()
@@ -634,6 +708,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> with the name "FC" and a value of 20 produces an inserted calculated observation named "NEWS_EXTR_LO_FC" using the "ADAS" coding system, where the inserted value matches the original value of 20.
/// </summary>
/// <!-- aidoc:v1 sig=58742a8 body=f2f71c1 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_FC_Returns_NEWS_EXTR_LO_FC()
@@ -659,6 +737,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> with a heart rate (FC) value of 140 through the calculation pipeline produces a derived NEWS extra-high heart rate (<c>NEWS_EXTR_HI_FC</c>) observation carrying the same value and tagged with the ADAS coding system.
/// </summary>
/// <!-- aidoc:v1 sig=1eb9206 body=e721300 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_FC_Returns_NEWS_EXTR_HI_FC()
@@ -684,6 +766,11 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> named "FC" with value 120 produces a derived "NEWS_HI_FC" observation that is inserted through the observation service under the "ADAS" coding system with the original value preserved.
/// The test is marked as ignored because it targets the legacy NEWS calculation flow.
/// </summary>
/// <!-- aidoc:v1 sig=2bf5811 body=3387162 -->
[Ignore("old NEWS calc")]
[Test]
public async Task Calculate_FC_Returns_NEWS_HI_FC()
@@ -709,6 +796,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that during the pre-mapping of a list of <see cref="PatientObservation"/> items, any observation named <c>MDC_VENT_RESP_RATE</c> (code <c>151586</c>) is removed from the resulting list and inserted as a separate observation via <see cref="IObservationService.InsertObservation"/>.
/// </summary>
/// <!-- aidoc:v1 sig=fb56de1 body=f339d1c -->
[Test]
public async Task Check_MDC_VENT_RESP_RATE_IsInsertedBefore_And_RemoveFromList()
{
@@ -769,6 +860,10 @@ public class CalculatedObservationsTest
obs.Code == "151586"), true, true), Times.Once);
}
/// <summary>
/// Verifies that when <see cref="PatientObservation"/> mapping processes a respiratory-rate observation alongside a preceding vent-rate observation returned by <see cref="IObservationService.FindLastObservations"/>, the calculated respiratory-rate observation is inserted with its <see cref="PatientObservation.Time"/> offset by one second from the source observation and with its <see cref="PatientObservation.ParentData"/> populated from the vent-rate observation.
/// </summary>
/// <!-- aidoc:v1 sig=c5af3a7 body=2299b03 -->
[Test]
public async Task
Check_Insert_Resp_Rate_Calculated_HasTimePlusOneSecond_And_Resp_Rate_Calculated_HasVentRateAsParent()
@@ -816,6 +911,14 @@ public class CalculatedObservationsTest
obs.Time == nowPlusOneSecond), true, true), Times.Once);
}
/// <summary>
/// Verifies that mapping a patient observation with the name <c>Resp_Mode</c> and value <c>PC-AC</c>
/// through <see cref="_calculatedObservations"/>.Map produces a calculated observation named
/// <c>Resp_Type</c> using the <c>ADAS</c> coding system with the value
/// <see cref="RespirationType.Invasive"/>.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test execution.</returns>
/// <!-- aidoc:v1 sig=480e248 body=e8d6f79 -->
[Test]
public async Task Calculate_Ventilation_Mode_Should_Return_INVASIVE()
{
@@ -838,6 +941,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> for respiratory mode with a non-invasive ventilation value produces a calculated <c>Resp_Type</c> observation classified as <see cref="RespirationType.NonInvasive"/>.
/// </summary>
/// <!-- aidoc:v1 sig=11ecdaa body=7ef6d7b -->
[Test]
public async Task Calculate_Ventilation_Mode_Should_Return_NON_INVASIVE()
{
@@ -861,6 +968,10 @@ public class CalculatedObservationsTest
}
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> whose <see cref="PatientObservation.Name"/> is "Resp_Mode" and <see cref="PatientObservation.Code"/> is "HNF" causes the calculation service to insert an observation with the <see cref="PatientObservation.Name"/> "Resp_Type", the <see cref="PatientObservation.CodingSystem"/> "ADAS", and the <see cref="PatientObservation.Value"/> set to <see cref="RespirationType.HighFrequencyVentilation"/>.
/// </summary>
/// <!-- aidoc:v1 sig=64e7ba4 body=1553933 -->
[Test]
public async Task Calculate_Ventilation_Mode_Should_Return_HIGH_FREQUENCY()
{
@@ -985,6 +1096,10 @@ public class CalculatedObservationsTest
//}
/// <summary>
/// Verifies that the mapping logic computes a diuresis-to-weight ratio of 43.6 from a diuresis observation of 3924 and a weight observation of 90 for the same patient, inserting the result as a new <see cref="PatientObservation"/> named "Diuresis_Weight".
/// </summary>
/// <!-- aidoc:v1 sig=5a47059 body=df4b18b -->
[Test]
public async Task CalculateDiuresis_Weight_Received_D_3924_W_90_Return_43_6()
{
@@ -1018,6 +1133,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that <see cref="_calculatedObservations"/>.<see cref="ICalculatedObservations.Map(PatientObservation, bool)"/> correctly computes the diuresis-to-weight ratio as 43.6 when the latest diuresis observation value is 3924 and the current weight observation value is 90, and persists a derived <see cref="PatientObservation"/> named "Diuresis_Weight" for the same <paramref name="patientId"/>.
/// </summary>
/// <!-- aidoc:v1 sig=b46e7fe body=50dd2ac -->
[Test]
public async Task CalculateDiuresis_Weight_D_3924_Receive_W_90_Return_43_6()
{
@@ -1051,6 +1170,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that when a <see cref="PatientObservation"/> with the name <c>Weight_Current</c> is received but no prior diuresis observations exist for the patient, the mapping does not insert any calculated observation.
/// </summary>
/// <!-- aidoc:v1 sig=7c61653 body=30e7115 -->
[Test]
public async Task CalculateDiuresis_Weight_Received_W_90_D_Null_Return_Nothing()
{
@@ -1073,6 +1196,10 @@ public class CalculatedObservationsTest
Times.Never);
}
/// <summary>
/// Verifies that when <see cref="PatientObservation.Map"/> receives a Diuresis observation with value 3924 and the lookup for the last observation returns no results, the calculated "Diuresis_Weight" observation is not inserted.
/// </summary>
/// <!-- aidoc:v1 sig=7539e9b body=c0f4109 -->
[Test]
public async Task CalculateDiuresis_Weight_Received_D_3924_W_Null_Return_Nothing()
{
@@ -1097,6 +1224,10 @@ public class CalculatedObservationsTest
), true, true), Times.Never);
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> named "AllergiesObs" containing an empty list of <see cref="PatientAllergiesValue"/> produces a calculated <see cref="PatientObservation"/> named "Allergies" with an empty string value for the given patient.
/// </summary>
/// <!-- aidoc:v1 sig=aabc273 body=af29e9f -->
[Test]
public async Task CalculateAllergiesObservation_Received_AllergiesObs_Emty_Return_Allergies_Emty()
{
@@ -1121,6 +1252,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> named "AllergiesObs" that contains non-pharmacological allergies (such as "Latex" and "Alergia ambiental") results in the insertion of an "Allergies" observation whose value is the uppercase, comma-separated concatenation of the allergy types (e.g., "LATEX, AMBIENTAL").
/// </summary>
/// <!-- aidoc:v1 sig=87f619d body=739c72a -->
[Test]
public async Task CalculateAllergiesObservation_Received_AllergiesObs_Without_Farmacos_Return_Allergies()
{
@@ -1161,6 +1296,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that <see cref="CalculatedObservations.Map(PatientObservation, bool)"/> consolidates a received <c>AllergiesObs</c> <see cref="PatientObservation"/> containing one drug allergy (fármacos), a latex allergy, and an environmental allergy into a single <see cref="PatientObservation"/> named <c>Allergies</c> for the same <see cref="PatientObservation.PatientId"/>, with the values merged into the formatted string <c>LATEX, AMBIENTAL, FÁRMACOS (METILPREDNISOLONA)</c>.
/// </summary>
/// <!-- aidoc:v1 sig=6202c3a body=ca57051 -->
[Test]
public async Task CalculateAllergiesObservation_Received_AllergiesObs_With_1_Farmacos_Return_Allergies()
{
@@ -1208,6 +1347,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that <see cref="CalculateAllergiesObservation"/> aggregates a received <c>AllergiesObs</c> observation containing multiple allergy types — including two drug allergies (<c>METILPREDNISOLONA</c> and <c>Penicilina/cefalosporinas</c>), a latex allergy and an environmental allergy — into a single <see cref="PatientObservation"/> named <c>Allergies</c>, whose value combines the allergy categories and the drug allergens into the expected grouped string.
/// </summary>
/// <!-- aidoc:v1 sig=661f407 body=8eae86e -->
[Test]
public async Task CalculateAllergiesObservation_Received_AllergiesObs_With_2_Farmacos_Return_Allergies()
{
@@ -1261,6 +1404,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that when a <see cref="PatientObservation"/> carrying a <see cref="PatientDrainagesValue"/> of type "Drenaje ventricular" with volume 60 and height 8 is processed by the calculator, two derived observations are inserted: one named "DVE" carrying the volume value and another named "Drainage_Height" carrying the height value, both under the "ADAS" coding system.
/// </summary>
/// <!-- aidoc:v1 sig=0c97fe5 body=b098a53 -->
[Test]
public async Task Calculate_Drainages_Received_volume_60_height_8_Return_DVE_height()
{
@@ -1298,6 +1445,10 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Verifies that mapping a <see cref="PatientObservation"/> carrying a <see cref="PatientDrainagesValue"/> of type "Drenaje ventricular" with a volume of 60 and no height produces a "DVE" observation while suppressing the "Drainage_Height" observation.
/// </summary>
/// <!-- aidoc:v1 sig=b24ca90 body=c070bf1 -->
[Test]
public async Task Calculate_Drainages_Received_volume_60_height_null_Return_DVE()
{
@@ -1332,6 +1483,12 @@ public class CalculatedObservationsTest
), true, true), Times.Never);
}
/// <summary>
/// Verifies that when a <see cref="PatientObservation"/> containing a <see cref="PatientDrainagesValue"/>
/// has a null received volume but a defined height (8), the mapping inserts a new observation named "Drainage_Height"
/// with the height value while not creating a "DVE" observation.
/// </summary>
/// <!-- aidoc:v1 sig=9f6bb92 body=73e12d9 -->
[Test]
public async Task Calculate_Drainages_Received_volume_null_height_8_Return_height()
{
@@ -1366,6 +1523,11 @@ public class CalculatedObservationsTest
), true, true));
}
/// <summary>
/// Tests that mapping a <see cref="PatientObservation"/> of drainages type with null volume and null height values
/// does not generate any calculated <c>DVE</c> or <c>Drainage_Height</c> observations.
/// </summary>
/// <!-- aidoc:v1 sig=ff5bb6d body=bf2607d -->
[Test]
public async Task Calculate_Drainages_Received_volume_null_height_null_Return_nothing()
{