Files
adas-core/adas-core.Test/Repositories/PumpAlarmEventRepositoryTest.cs
T
2026-06-26 10:29:23 +02:00

450 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.Pumps;
using adas_core.Infrastructure.Repositories;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
namespace adas_core.Test.Repositories;
[TestFixture]
[Category("Integration")]
public class PumpAlarmEventRepositoryTest
{
private PumpAlarmEventRepository _repo;
private IOptions<ApiSettings> _apiSettings;
private static readonly DateTime Now = DateTime.UtcNow;
private const string DeviceA = "Device-A";
private const string DeviceB = "Device-B";
private static readonly ObjectId PatientId = ObjectId.GenerateNewId();
// -------------------------------------------------------------------
// INIT Setup inicial de colección con índices y datos de prueba
// -------------------------------------------------------------------
/// <summary>
/// Performs one-time integration test setup for the pump alarm event repository by configuring API settings,
/// resetting the dedicated MongoDB collection, creating required indexes, and seeding it with three initial
/// alarm events covering both Device and DeviceB across attention and occlusion
/// alarm types. Verifies that exactly three documents are present after seeding.
/// </summary>
[OneTimeSetUp]
public async Task Init()
{
_apiSettings = Options.Create(new ApiSettings
{
PumpAlarmEvent = "pump_alarm_event"
});
await IntegrationDb.Database.DropCollectionAsync("pump_alarm_event");
await IntegrationDb.Database.CreateCollectionAsync("pump_alarm_event");
_repo = new PumpAlarmEventRepository(
_apiSettings,
IntegrationDb.Database
);
await _repo.CreateIndexes();
var initialEvents = new List<PumpAlarmEvent>
{
new()
{
Id = ObjectId.GenerateNewId(),
DeviceId = DeviceA,
PatientId = PatientId,
AlarmType = PumpEnum.AlarmType.Attention,
Time = Now.AddSeconds(-1)
},
new()
{
Id = ObjectId.GenerateNewId(),
DeviceId = DeviceA,
PatientId = PatientId,
AlarmType = PumpEnum.AlarmType.Occlusion,
Time = Now.AddSeconds(-3)
},
new()
{
Id = ObjectId.GenerateNewId(),
DeviceId = DeviceB,
PatientId = PatientId,
AlarmType = PumpEnum.AlarmType.Occlusion,
Time = Now.AddSeconds(-2)
}
};
await IntegrationDb.Database
.GetCollection<PumpAlarmEvent>("pump_alarm_event")
.InsertManyAsync(initialEvents);
var count = await _repo.Collection.CountDocumentsAsync(_ => true);
Assert.That(count, Is.EqualTo(3));
}
// -------------------------------------------------------------------
// INSERT
// -------------------------------------------------------------------
/// <summary>
/// Verifies that InsertAsync correctly persists a <see cref="PumpAlarmEvent"/> to the repository by inserting an event and confirming it can be retrieved by its identifier.
/// </summary>
[Test]
public async Task InsertAsync_Works()
{
var evt = new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-C",
PatientId = PatientId,
AlarmType = PumpEnum.AlarmType.Occlusion,
Time = Now
};
await _repo.InsertAsync(evt);
var found = await _repo.Collection.FindAsync(x => x.Id == evt.Id);
Assert.That(found.FirstOrDefault(), Is.Not.Null);
}
// -------------------------------------------------------------------
// FIND BY DEVICE ID
// -------------------------------------------------------------------
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> returns a non-empty collection of records for the specified device, ordered by time so that the most recent entry appears first.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_ReturnsOrdered()
{
var result = await _repo.FindByDeviceIdAsync(DeviceA);
var list = result.ToList();
using (Assert.EnterMultipleScope())
{
Assert.That(list, Is.Not.Empty);
Assert.That(list.First().Time, Is.GreaterThanOrEqualTo(list.Last().Time));
}
}
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> returns only pump alarm events whose timestamps fall within the specified date range.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_WithDateRange_Works()
{
var from = Now.AddSeconds(-2);
var to = Now;
var result = await _repo.FindByDeviceIdAsync(DeviceA, from, to);
using (Assert.EnterMultipleScope())
{
var pumpAlarmEvents = result.ToList();
Assert.That(pumpAlarmEvents, Is.Not.Empty);
Assert.That(pumpAlarmEvents.All(x => x.Time >= from && x.Time <= to), Is.True);
}
}
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> returns at most the specified number of results when a limit is provided.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_WithLimit_Works()
{
var result = await _repo.FindByDeviceIdAsync(DeviceA, limit: 1);
var list = result.ToList();
Assert.That(list, Has.Count.EqualTo(1));
}
// -------------------------------------------------------------------
// FIND LAST BY DEVICE
// -------------------------------------------------------------------
[Test]
public async Task FindLastByDeviceIdAsync_Works()
{
// Arrange: dataset ya insertado en Init()
var events = await _repo.FindByDeviceIdAsync("Device-A");
var expected = events.OrderByDescending(e => e.Time).First();
// Act
var evt = await _repo.FindLastByDeviceIdAsync("Device-A");
// Assert
Assert.That(evt, Is.Not.Null);
Assert.That(evt!.Id, Is.EqualTo(expected.Id)); // comparación robusta
}
// -------------------------------------------------------------------
// DELETE BY PATIENT ID
// -------------------------------------------------------------------
/// <summary>
/// Verifies that DeleteByPatientId correctly removes all alarm events associated with a given patient.
/// Inserts a sample <c>PumpAlarmEvent</c> for a specific patient, deletes it by patient ID, and asserts that no matching events remain in the collection.
/// </summary>
[Test]
public async Task DeleteByPatientId_Works()
{
var patient2 = ObjectId.GenerateNewId();
var evt = new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-X",
PatientId = patient2,
AlarmType = PumpEnum.AlarmType.Occlusion,
Time = Now
};
await _repo.InsertAsync(evt);
await _repo.DeleteByPatientId(patient2);
var found = await _repo.Collection.FindAsync(x => x.PatientId == patient2);
Assert.That(await found.AnyAsync(), Is.False);
}
// -------------------------------------------------------------------
// UPDATE MANY (CAMBIO DE PATIENTID O SIMILAR)
// -------------------------------------------------------------------
/// <summary>
/// Verifies that <c>UpdateManyObjectIdByFiledNameAsync</c> correctly updates documents matching the specified field name and old ObjectId value, replacing it with the new ObjectId, and that the changes are persisted and queryable.
/// </summary>
[Test]
public async Task UpdateManyObjectIdByFiledNameAsync_Works()
{
var oldId = ObjectId.GenerateNewId();
var newId = ObjectId.GenerateNewId();
var evt = new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-U",
PatientId = oldId,
AlarmType = PumpEnum.AlarmType.Attention,
Time = Now
};
await _repo.InsertAsync(evt);
var updated = await _repo.UpdateManyObjectIdByFiledNameAsync("PatientId", newId, oldId);
Assert.That(updated, Is.EqualTo(1));
var found = await _repo.Collection.FindAsync(x => x.PatientId == newId);
Assert.That(await found.AnyAsync(), Is.True);
}
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> returns an empty result when the queried date range falls outside of the available data window.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_ReturnsEmpty_WhenOutOfRange()
{
var from = Now.AddYears(-2);
var to = Now.AddYears(-2).AddDays(1);
var result = await _repo.FindByDeviceIdAsync("Device-A", from, to);
Assert.That(result, Is.Empty);
}
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> correctly applies the requested limit and orders results so that the most recent alarm event for the given device is returned first.
/// Seeds multiple events for the target device, requests a single record, and asserts that the returned entry matches the latest event retrieved by <c>FindLastByDeviceIdAsync</c>.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_RespectsLimitAndOrder()
{
// Semilla adicional para asegurar > 1
var events = new[]
{
new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = DeviceA, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.AirInLine, Time = Now.AddMilliseconds(-10) },
new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = DeviceA, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.Occlusion, Time = Now.AddMilliseconds(-5) }
};
await _repo.Collection.InsertManyAsync(events);
var result = await _repo.FindByDeviceIdAsync(DeviceA, limit: 1);
var list = result.ToList();
using (Assert.EnterMultipleScope())
{
Assert.That(list, Has.Count.EqualTo(1));
var last = await _repo.FindLastByDeviceIdAsync(DeviceA);
Assert.That(list[0].Id, Is.EqualTo(last!.Id));
}
}
/// <summary>
/// Verifies that InsertAsync successfully persists a <see cref="PumpAlarmEvent"/> when its optional fields (<c>PatientId</c> and <c>AlarmType</c>) are <c>null</c>, and that the record can be retrieved afterwards.
/// </summary>
[Test]
public async Task InsertAsync_AllowsNullOptionalFields()
{
var evt = new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-Nulls",
PatientId = null, // opcional
AlarmType = null, // opcional
Time = Now
};
await _repo.InsertAsync(evt);
var found = await _repo.Collection.FindAsync(x => x.Id == evt.Id);
Assert.That(found.FirstOrDefault(), Is.Not.Null);
}
/// <summary>
/// Verifies that <c>DeleteByPatientId</c> removes all alarm events associated with the given patient and can be invoked repeatedly without throwing once the records no longer exist.
/// </summary>
[Test]
public async Task DeleteByPatientId_DeletesMany_AndIsIdempotent()
{
var pid = ObjectId.GenerateNewId();
var docs = Enumerable.Range(0, 3).Select(i => new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-Del",
PatientId = pid,
AlarmType = PumpEnum.AlarmType.Attention,
Time = Now.AddSeconds(-i)
});
await _repo.Collection.InsertManyAsync(docs);
// Primera vez: borra 3
await _repo.DeleteByPatientId(pid);
var left = await _repo.Collection.FindAsync(x => x.PatientId == pid);
Assert.That(left.ToList(), Is.Empty);
Func<Task> act = async () => await _repo.DeleteByPatientId(pid);
Assert.That(act, Throws.Nothing);
}
/// <summary>
/// Verifies that <c>UpdateManyObjectIdByFiledNameAsync</c> updates the <c>PatientId</c> field from an old <see cref="ObjectId"/> to a new one across all matching documents, returning the modified count and persisting the new identifier in the collection.
/// </summary>
[Test]
public async Task UpdateManyObjectIdByFiledNameAsync_UpdatesMultiple()
{
var oldId = ObjectId.GenerateNewId();
var newId = ObjectId.GenerateNewId();
var events = Enumerable.Range(0, 4).Select(i => new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-U",
PatientId = oldId,
AlarmType = PumpEnum.AlarmType.AirInLine,
Time = Now.AddMilliseconds(-i)
});
await _repo.Collection.InsertManyAsync(events);
var modified = await _repo.UpdateManyObjectIdByFiledNameAsync("PatientId", newId, oldId);
using (Assert.EnterMultipleScope())
{
Assert.That(modified, Is.EqualTo(4));
var foundNew = await _repo.Collection.FindAsync(x => x.PatientId == newId);
Assert.That(foundNew.ToList(), Has.Count.EqualTo(4));
}
}
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> treats the <c>from</c> and <c>to</c> time bounds as inclusive,
/// ensuring events whose timestamp matches the upper bound exactly are returned and that all returned
/// events fall within the supplied range.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_InclusiveRange_BoundsRespected()
{
var start = Now.AddMinutes(-30);
var end = Now.AddMinutes(-29);
var exactEvt = new PumpAlarmEvent
{
Id = ObjectId.GenerateNewId(),
DeviceId = "Device-Range",
PatientId = PatientId,
AlarmType = PumpEnum.AlarmType.Attention,
Time = end // exactamente igual al to
};
await _repo.InsertAsync(exactEvt);
var result = await _repo.FindByDeviceIdAsync("Device-Range", from: start, to: end);
using (Assert.EnterMultipleScope())
{
var pumpAlarmEvents = result as PumpAlarmEvent[] ?? result.ToArray();
Assert.That(pumpAlarmEvents, Is.Not.Empty);
Assert.That(pumpAlarmEvents.All(r => r.Time >= start && r.Time <= end), Is.True);
}
}
/// <summary>
/// Verifies that FindLastByDeviceIdAsync returns the most recent alarm event for a given device and tracks newly inserted events as the latest entry.
/// </summary>
[Test]
public async Task FindLastByDeviceIdAsync_TracksNewest()
{
const string dev = "Device-LastCheck";
var e1 = new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = dev, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.AirInLine, Time = Now.AddSeconds(-10) };
var e2 = new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = dev, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.Occlusion, Time = Now.AddSeconds(-5) };
await _repo.Collection.InsertManyAsync([e1, e2]);
var last1 = await _repo.FindLastByDeviceIdAsync(dev);
Assert.That(last1!.Id, Is.EqualTo(e2.Id));
var e3 = new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = dev, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.Attention, Time = Now };
await _repo.InsertAsync(e3);
var last2 = await _repo.FindLastByDeviceIdAsync(dev);
Assert.That(last2!.Id, Is.EqualTo(e3.Id));
}
/// <summary>
/// Verifies that <c>FindByDeviceIdAsync</c> correctly filters pump alarm events by device identifier
/// and time range when multiple queries are issued against overlapping windows, ensuring each
/// range returns only the event that falls within its boundaries.
/// </summary>
[Test]
public async Task FindByDeviceIdAsync_MultipleOverlappingRanges()
{
const string dev = "Device-Timeline";
var batch = new[]
{
new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = dev, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.AirInLine, Time = Now.AddMinutes(-20) },
new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = dev, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.Occlusion, Time = Now.AddMinutes(-10) },
new PumpAlarmEvent { Id = ObjectId.GenerateNewId(), DeviceId = dev, PatientId = PatientId, AlarmType = PumpEnum.AlarmType.Attention, Time = Now.AddMinutes(-5) }
};
await _repo.Collection.InsertManyAsync(batch);
var r1 = await _repo.FindByDeviceIdAsync(dev, from: Now.AddMinutes(-30), to: Now.AddMinutes(-15));
var r2 = await _repo.FindByDeviceIdAsync(dev, from: Now.AddMinutes(-12), to: Now.AddMinutes(-8));
var r3 = await _repo.FindByDeviceIdAsync(dev, from: Now.AddMinutes(-6), to: Now);
using (Assert.EnterMultipleScope())
{
var r1List = r1.ToList();
Assert.That(r1List, Has.Count.EqualTo(1)); // -20
var r2List = r2.ToList();
Assert.That(r2List, Has.Count.EqualTo(1)); // -10
var r3List = r3.ToList();
Assert.That(r3List, Has.Count.EqualTo(1)); // -5
}
}
}