635 lines
18 KiB
C#
635 lines
18 KiB
C#
using adas_core.Application.Services.Caching;
|
|
using adas_core.Domain.Enums;
|
|
using adas_core.Domain.Models.AppSettings;
|
|
using adas_core.Domain.Utils;
|
|
using MongoDB.Bson;
|
|
using Moq;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
namespace adas_core.Test.Utilities;
|
|
|
|
[TestFixture]
|
|
public class CacheKeyClassifierTest
|
|
{
|
|
#region TC-01
|
|
[Test]
|
|
public void Classify_ReturnsPatients_ForKeyStartingWithPatients()
|
|
{
|
|
|
|
// Arrange
|
|
var key = "patients:latestObs:abc123:HR:0";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Patients));
|
|
}
|
|
|
|
[Test]
|
|
public void Classify_ReturnsPatients_ForKeyStartingWithPatientsXYZ()
|
|
{
|
|
// Arrange
|
|
var key = "patients:xyz:any:key";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Patients));
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
#region TC-02
|
|
[Test]
|
|
public void Classify_ReturnsDisplays_ForKeyStartingWithConfigDisplaysBase()
|
|
{
|
|
// Arrange
|
|
//var key2 = "configDisplays:display:id:base";
|
|
var key = "configDisplays:display:690b341c4a349b5898898241:base ";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Displays));
|
|
|
|
}
|
|
|
|
[Test]
|
|
public void Classify_ReturnsDisplays_ForKeyStartingWithConfigDisplaysConfig()
|
|
{
|
|
// Arrange
|
|
var key = "configDisplays:display:id:config";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Displays));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-03
|
|
[Test]
|
|
public void Classify_ReturnsPumpObservations_ForKeyStartingWithPumpObs()
|
|
{
|
|
// Arrange
|
|
var key = "pumpObs:latest:patientId:10";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.PumpObservations));
|
|
}
|
|
#endregion
|
|
|
|
|
|
#region TC-04
|
|
[Test]
|
|
public void Classify_ReturnsAppointments_ForKeyStartingWithAppointmentsPatient()
|
|
{
|
|
// Arrange
|
|
var key = "appointments:patient:id:20260224";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Appointments));
|
|
}
|
|
|
|
[Test]
|
|
public void Classify_ReturnsAppointments_ForKeyStartingWithAppointmentsPoc()
|
|
{
|
|
// Arrange
|
|
var key = "appointments:PoC:pocId:20260224";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Appointments));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-05
|
|
[Test]
|
|
public void Classify_ReturnsGroupedObservations_ForKeyStartingWithGroupedObs()
|
|
{
|
|
// Arrange
|
|
var key = "groupedObs:HR:patient:abc123";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.GroupedObservations));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-06
|
|
[Test]
|
|
public void Classify_ReturnsConfigObservations_ForKeyStartingWithConfigObservations()
|
|
{
|
|
// Arrange
|
|
var key = "configObservations:all";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.ConfigObservations));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-07
|
|
[Test]
|
|
public void Classify_ReturnsUnknown_ForUnrecognizedPrefixDiagnostics()
|
|
{
|
|
// Arrange
|
|
var key = "diagnostics:patient:abc";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Unknown));
|
|
}
|
|
|
|
[Test]
|
|
public void Classify_ReturnsUnknown_ForUnrecognizedPrefixRandom()
|
|
{
|
|
// Arrange
|
|
var key = "random:key";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Unknown));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-08
|
|
[Test]
|
|
public void Classify_ReturnsPatients_ForKeyInUpperCase()
|
|
{
|
|
// Arrange
|
|
var key = "PATIENTS:latestObs:abc";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Patients));
|
|
}
|
|
|
|
[Test]
|
|
public void Classify_ReturnsAppointments_ForKeyInMixedCase()
|
|
{
|
|
// Arrange
|
|
var key = "Appointments:PoC:id:date";
|
|
|
|
// Act
|
|
var result = CacheKeyClassifier.Classify(key);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(CacheEnum.EntityType.Appointments));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-09
|
|
[Test]
|
|
public void ResolveForEntity_ReturnsNull_WhenGlobalCacheDisabled()
|
|
{
|
|
// Arrange
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = false,
|
|
Patients = CacheEnum.Mode.Redis,
|
|
Redis = new RedisSettings
|
|
{
|
|
Ttl = new TtlSettings { PatientsSeconds = 600 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, CacheEnum.EntityType.Patients);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
|
|
[Test]
|
|
public void ResolveForEntity_ReturnsNull_WhenGlobalDisabled_ForAllEntities()
|
|
{
|
|
// Arrange
|
|
var entities = new[]
|
|
{
|
|
CacheEnum.EntityType.Patients,
|
|
CacheEnum.EntityType.Displays,
|
|
CacheEnum.EntityType.PumpObservations,
|
|
CacheEnum.EntityType.Appointments,
|
|
CacheEnum.EntityType.PontOfCare,
|
|
CacheEnum.EntityType.GroupedObservations,
|
|
CacheEnum.EntityType.PatientObservations,
|
|
CacheEnum.EntityType.ConfigObservations
|
|
};
|
|
|
|
var settings = new CacheSettings { IsEnabled = false };
|
|
|
|
// Act & Assert
|
|
foreach (var entity in entities)
|
|
{
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, entity);
|
|
Assert.That(result, Is.Null, $"Expected null for entity {entity}");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region TC-10
|
|
[Test]
|
|
public void ResolveForEntity_ReturnsNull_WhenEntityModeIsNone()
|
|
{
|
|
// Arrange
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
PumpObservations = CacheEnum.Mode.None,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings { PumpObservationsSeconds = 300 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, CacheEnum.EntityType.PumpObservations);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
#endregion
|
|
|
|
#region TC-11
|
|
[Test]
|
|
public void ResolveForEntity_UsesEntitySpecificTtl_WhenModeIsCache()
|
|
{
|
|
// Arrange
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
Patients = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings
|
|
{
|
|
GlobalSeconds = 3600,
|
|
PatientsSeconds = 600
|
|
}
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, CacheEnum.EntityType.Patients);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(TimeSpan.FromSeconds(600)));
|
|
Assert.That(result, Is.Not.EqualTo(TimeSpan.FromSeconds(3600)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-12
|
|
[Test]
|
|
public void ResolveForEntity_UsesRedisTtl_WhenModeIsRedis()
|
|
{
|
|
// Arrange
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
Appointments = CacheEnum.Mode.Redis,
|
|
Redis = new RedisSettings
|
|
{
|
|
Ttl = new TtlSettings
|
|
{
|
|
GlobalSeconds = 3600,
|
|
AppointmentsSeconds = 900
|
|
}
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, CacheEnum.EntityType.Appointments);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(TimeSpan.FromSeconds(900)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-13
|
|
[Test]
|
|
public void ResolveForEntity_UsesGlobalSeconds_WhenNoEntitySpecificTtl()
|
|
{
|
|
// Arrange
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
Patients = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings
|
|
{
|
|
GlobalSeconds = 3600
|
|
// No PatientsSeconds defined
|
|
}
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, CacheEnum.EntityType.Patients);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.EqualTo(TimeSpan.FromSeconds(3600)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-14
|
|
[Test]
|
|
public void ResolveForEntity_ReturnsNull_WhenNoTtlConfigured()
|
|
{
|
|
// Arrange
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
Patients = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings()
|
|
// No GlobalSeconds, no PatientsSeconds
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = CacheKeyTtl.ResolveForEntity(settings, CacheEnum.EntityType.Patients);
|
|
|
|
// Assert
|
|
Assert.That(result, Is.Null);
|
|
}
|
|
#endregion
|
|
|
|
#region TC-15
|
|
[Test]
|
|
public void PatientAppointmentsTodayKeyWithTtl_GeneratesCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var patientId = ObjectId.GenerateNewId();
|
|
var today = DateTime.UtcNow.ToString("yyyyMMdd");
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
Appointments = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings { AppointmentsSeconds = 120 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var (key, ttl) = CacheKeys.PatientAppointmentsTodayKeyWithTtl(settings, patientId);
|
|
|
|
// Assert
|
|
Assert.That(key, Is.EqualTo($"appointments:patient:{patientId}:{today}"));
|
|
Assert.That(ttl, Is.EqualTo(TimeSpan.FromSeconds(120)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-16
|
|
[Test]
|
|
public void PocAppointmentsTodayKeyWithTtl_GeneratesCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var pocId = ObjectId.GenerateNewId();
|
|
var today = DateTime.UtcNow.ToString("yyyyMMdd");
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
PointOfCares = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings { PointOfCaresSeconds = 180 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var (key, ttl) = CacheKeys.PocAppointmentsTodayKeyWithTtl(settings, pocId);
|
|
|
|
// Assert
|
|
Assert.That(key, Is.EqualTo($"appointments:PoC:{pocId}:{today}"));
|
|
Assert.That(ttl, Is.EqualTo(TimeSpan.FromSeconds(180)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-17
|
|
[Test]
|
|
public void LatestObservationsKeyWithTtl_SortsFieldNamesAlphabetically()
|
|
{
|
|
// Arrange
|
|
var patientId = ObjectId.GenerateNewId();
|
|
var settings = new CacheSettings();
|
|
|
|
// Act
|
|
var (key1, _) = CacheKeys.LatestObservationsKeyWithTtl(
|
|
settings, patientId, new[] { "Systolic", "HR", "Diastolic" });
|
|
|
|
var (key2, _) = CacheKeys.LatestObservationsKeyWithTtl(
|
|
settings, patientId, new[] { "HR", "Diastolic", "Systolic" });
|
|
|
|
// Assert
|
|
Assert.That(key1, Is.EqualTo(key2));
|
|
Assert.That(key1, Does.Contain("Diastolic|HR|Systolic"));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-18
|
|
[Test]
|
|
public void LatestObservationsKeyWithTtl_ExcludesEmptyFields()
|
|
{
|
|
// Arrange
|
|
var patientId = ObjectId.GenerateNewId();
|
|
var settings = new CacheSettings();
|
|
|
|
// Act
|
|
var (key, _) = CacheKeys.LatestObservationsKeyWithTtl(
|
|
settings, patientId, new[] { "HR", "", "Diastolic", null! });
|
|
|
|
// Assert
|
|
Assert.That(key.Contains("||"), Is.False);
|
|
Assert.That(key, Does.Contain("Diastolic|HR"));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-19
|
|
[Test]
|
|
public void DisplayBaseKeyWithTtl_GeneratesCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var displayId = ObjectId.GenerateNewId();
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
Displays = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings { DisplaysSeconds = 300 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var (key, ttl) = CacheKeys.DisplayBaseKeyWithTtl(settings, displayId);
|
|
|
|
// Assert
|
|
Assert.That(key, Is.EqualTo($"configDisplays:display:{displayId}:base"));
|
|
Assert.That(ttl, Is.EqualTo(TimeSpan.FromSeconds(300)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-20
|
|
[Test]
|
|
public void DisplayWithConfigKeyWithTtl_GeneratesCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var displayId = ObjectId.GenerateNewId();
|
|
var settings = new CacheSettings();
|
|
|
|
// Act
|
|
var (keyBase, _) = CacheKeys.DisplayBaseKeyWithTtl(settings, displayId);
|
|
var (keyConfig, _) = CacheKeys.DisplayWithConfigKeyWithTtl(settings, displayId);
|
|
|
|
// Assert
|
|
Assert.That(keyConfig, Is.EqualTo($"configDisplays:display:{displayId}:config"));
|
|
Assert.That(keyBase, Is.Not.EqualTo(keyConfig));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-21
|
|
[Test]
|
|
public void PointOfCareBaseKeyWithTtl_GeneratesCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var pocId = ObjectId.GenerateNewId();
|
|
var settings = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
PointOfCares = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings { PointOfCaresSeconds = 240 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var (key, ttl) = CacheKeys.PointOfCareBaseKeyWithTtl(settings, pocId);
|
|
|
|
// Assert
|
|
Assert.That(key, Is.EqualTo($"pointOfCare:{pocId}:base"));
|
|
Assert.That(ttl, Is.EqualTo(TimeSpan.FromSeconds(240)));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-22
|
|
[Test]
|
|
public void ConfigObservationsAllKeyWithTtl_GeneratesCorrectFormat()
|
|
{
|
|
// Arrange
|
|
var settings1 = new CacheSettings();
|
|
var settings2 = new CacheSettings
|
|
{
|
|
IsEnabled = true,
|
|
ConfigObservations = CacheEnum.Mode.Cache,
|
|
InMemory = new InMemorySettings
|
|
{
|
|
Ttl = new TtlSettings { ConfigObservationsSeconds = 600 }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var (key1, _) = CacheKeys.ConfigObservationsAllKeyWithTtl(settings1);
|
|
var (key2, _) = CacheKeys.ConfigObservationsAllKeyWithTtl(settings2);
|
|
|
|
// Assert
|
|
Assert.That(key1, Is.EqualTo("configObservations:all"));
|
|
Assert.That(key2, Is.EqualTo("configObservations:all"));
|
|
Assert.That(key1, Is.EqualTo(key2));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-59
|
|
[TestCase(CacheEnum.EntityType.Patients, "patients:*")]
|
|
[TestCase(CacheEnum.EntityType.Displays, "displays:*")]
|
|
[TestCase(CacheEnum.EntityType.PumpObservations, "pumpObs:*")]
|
|
[TestCase(CacheEnum.EntityType.Appointments, "appointments:*")]
|
|
[TestCase(CacheEnum.EntityType.GroupedObservations, "groupedObs:*")]
|
|
[TestCase(CacheEnum.EntityType.ConfigObservations, "configObservation:*")]
|
|
[TestCase(CacheEnum.EntityType.Unknown, "*")]
|
|
public void ForEntity_ReturnsCorrectPatternForEachEntityType(
|
|
CacheEnum.EntityType entity, string expectedPattern)
|
|
{
|
|
var result = CacheKeyPatterns.ForEntity(entity);
|
|
|
|
Assert.That(result, Is.EqualTo(expectedPattern));
|
|
}
|
|
#endregion
|
|
|
|
#region TC-60
|
|
[Test]
|
|
public void ByPatient_ReturnsPatternContainingPrefixAndPatientId()
|
|
{
|
|
const string prefix = "patients:latestObs";
|
|
const string patientId = "abc123";
|
|
|
|
var pattern = CacheKeyPatterns.ByPatient(prefix, patientId);
|
|
|
|
Assert.That(pattern, Is.EqualTo($"{prefix}:*:{patientId}*"));
|
|
Assert.That(pattern, Does.Contain(prefix));
|
|
Assert.That(pattern, Does.Contain(patientId));
|
|
}
|
|
|
|
[Test]
|
|
public async Task ByPatient_DeleteByPatternAsync_OnlyRemovesMatchingPatientKeys()
|
|
{
|
|
var lockMgr = new LockManagerService(
|
|
new Mock<ILogger<LockManagerService>>().Object,
|
|
new InMemoryLockProvider());
|
|
var svc = new CacheService(lockMgr);
|
|
|
|
const string prefix = "patients:latestObs";
|
|
const string patientId = "abc123";
|
|
const string otherId = "def456";
|
|
|
|
var pattern = CacheKeyPatterns.ByPatient(prefix, patientId);
|
|
var effectiveSubstring = pattern.Replace("*", "");
|
|
|
|
var matchingKey = $"{effectiveSubstring}:HR:0";
|
|
var nonMatchingKey = $"patients:latestObs::{otherId}:HR:0";
|
|
var differentEntityKey = $"appointments::{patientId}:data";
|
|
|
|
await svc.SetObjectAsync(matchingKey, "val1");
|
|
await svc.SetObjectAsync(nonMatchingKey, "val2");
|
|
await svc.SetObjectAsync(differentEntityKey, "val3");
|
|
|
|
var deleted = await svc.DeleteByPatternAsync(pattern);
|
|
|
|
Assert.That(deleted, Is.EqualTo(1));
|
|
Assert.That(await svc.GetObjectAsync<string>(matchingKey), Is.Null);
|
|
Assert.That(await svc.GetObjectAsync<string>(nonMatchingKey), Is.EqualTo("val2"));
|
|
Assert.That(await svc.GetObjectAsync<string>(differentEntityKey), Is.EqualTo("val3"));
|
|
}
|
|
#endregion
|
|
|
|
}
|