using adas_core.Application.Services.Caching;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Enums;
using adas_core.Domain.Models.AppSettings;
using adas_core.Domain.Models.GroupedObservations;
using adas_core.Domain.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using Moq;
namespace adas_core.Test.Services;
///
/// Provides a fake implementation of the interface, typically used as a test double or non-functional placeholder.
///
public class FakeLockProvider : ILockProvider
{
///
/// Asynchronously attempts to acquire a resource identified by the specified key within the given timeout.
/// This implementation always reports a successful acquisition by returning true.
///
/// The identifier of the resource to acquire.
/// The maximum time to wait for the resource to become available.
/// A that always completes with true, indicating the resource was acquired.
public Task AcquireAsync(string key, TimeSpan timeout) => Task.FromResult(true);
///
/// Releases the resource associated with the specified key. This implementation completes immediately without performing any additional operation.
///
/// The identifier of the resource to release.
public Task ReleaseAsync(string key) => Task.CompletedTask;
}
///
/// Represents a fake implementation of , typically used as a test double to provide controlled or simplified behavior in unit tests.
///
///
/// Inherits all members from and is intended to be used in place of the real service when actual locking functionality is not required.
///
public class FakeLockManagerService : LockManagerService
{
public FakeLockManagerService() : base(
Mock.Of>(),
new FakeLockProvider())
{
}
}
///
/// Represents a fake or test double implementation of that also implements , typically used to simulate Redis caching behavior in testing scenarios.
///
///
/// This class combines the inheritance of with the contract of , allowing it to stand in for a real Redis-backed cache during unit tests or development.
///
public class FakeRedisService : RedisService, ICacheService
{
public bool WasCalled { get; private set; }
public string? LastKey { get; private set; }
public FakeRedisService() : base(
Options.Create(new CacheSettings()),
Mock.Of>(),
new FakeLockManagerService())
{
}
///
/// Retrieves a cached object associated with the specified key, or creates and stores one using the provided factory if no cached entry exists.
/// This implementation bypasses caching and directly invokes the factory, recording the invocation for verification purposes while ignoring the TTL and cancellation token.
///
/// The cache key used to identify the stored object.
/// A delegate that asynchronously produces the object to cache when no entry exists for the specified key.
/// An optional time-to-live duration for the cached entry. Not used in this implementation.
/// A token to observe for cancellation requests. Not used in this implementation.
/// A task that represents the asynchronous operation, containing the object produced by the factory.
Task ICacheService.GetOrSetObjectAsync(
string key,
Func> factory,
TimeSpan? ttl,
CancellationToken cancellationToken)
{
WasCalled = true;
LastKey = key;
return factory();
}
///
/// Retrieves or sets a cached object associated with the specified grouped field and patient identifier.
/// Marks the call as executed via the WasCalled flag and returns the value produced by the supplied factory delegate.
///
/// The grouped field used to identify the cached object.
/// The identifier of the patient associated with the cached object.
/// The asynchronous factory delegate invoked to produce the value when no cached entry exists.
/// An optional time-to-live duration for the cached entry.
/// The token used to cancel the asynchronous operation.
/// The value of type produced by the delegate.
Task ICacheService.GetOrSetObjectAsync(
GroupedField groupedField,
ObjectId patientId,
Func> factory,
TimeSpan? ttl,
CancellationToken cancellationToken)
{
WasCalled = true;
return factory();
}
}
///
/// Represents a fake implementation of that also implements the interface, typically used for testing or stubbing scenarios.
///
///
/// This class combines inheritance from the concrete base class with the contract, allowing it to be used wherever an is required.
///
public class FakeCacheService : CacheService, ICacheService
{
public bool WasCalled { get; private set; }
public string? LastKey { get; private set; }
public FakeCacheService() : base(new FakeLockManagerService())
{
}
Task ICacheService.GetOrSetObjectAsync(
string key,
Func> factory,
TimeSpan? ttl,
CancellationToken cancellationToken)
{
WasCalled = true;
LastKey = key;
return factory();
}
Task ICacheService.GetOrSetObjectAsync(
GroupedField groupedField,
ObjectId patientId,
Func> factory,
TimeSpan? ttl,
CancellationToken cancellationToken)
{
WasCalled = true;
return factory();
}
}
///
/// Represents a fake implementation of the cache service, used for testing or scenarios where a no-op cache behavior is required.
///
///
/// This class inherits from and implements the interface, providing a non-functional cache suitable for unit tests or environments where caching should be bypassed.
///
public class FakeNoCacheService : NoCacheService, ICacheService
{
public bool WasCalled { get; private set; }
public string? LastKey { get; private set; }
Task ICacheService.GetOrSetObjectAsync(
string key,
Func> factory,
TimeSpan? ttl,
CancellationToken cancellationToken)
{
WasCalled = true;
LastKey = key;
return factory();
}
Task ICacheService.GetOrSetObjectAsync(
GroupedField groupedField,
ObjectId patientId,
Func> factory,
TimeSpan? ttl,
CancellationToken cancellationToken)
{
WasCalled = true;
return factory();
}
}
[TestFixture]
public class CacheDispatcherTest
{
private FakeRedisService _redisServiceFake = null!;
private FakeCacheService _memoryServiceFake = null!;
private FakeNoCacheService _noopServiceFake = null!;
private CacheSettings _cacheSettings = null!;
private CacheDispatcher _cacheDispatcher = null!;
///
/// Initializes the test environment by instantiating fake implementations of the Redis, in-memory, and no-op cache services, along with default , and constructs a under test using these dependencies.
///
[SetUp]
public void SetUp()
{
_redisServiceFake = new FakeRedisService();
_memoryServiceFake = new FakeCacheService();
_noopServiceFake = new FakeNoCacheService();
_cacheSettings = new CacheSettings();
_cacheDispatcher = new CacheDispatcher(
_redisServiceFake,
_memoryServiceFake,
_noopServiceFake,
_cacheSettings);
}
#region TC-23
[Test]
public async Task GetOrSetObjectAsync_UsesRedisService_WhenPatientsModeIsRedis()
{
// Arrange
var key = "patients:latestObs:abc";
var expectedResult = new { Name = "TestPatient" };
Func> factory = () => Task.FromResult