104 lines
3.4 KiB
C#
104 lines
3.4 KiB
C#
using System.Text.Json;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Microsoft.Extensions.Options;
|
|
using Moq;
|
|
using StackExchange.Redis;
|
|
using StellaOps.BinaryIndex.Contracts.Resolution;
|
|
|
|
namespace StellaOps.BinaryIndex.Cache.Tests;
|
|
|
|
public sealed class ResolutionCacheServiceTests
|
|
{
|
|
[Fact]
|
|
public async Task GetAsync_ExpiresEarly_WhenRandomBelowThreshold()
|
|
{
|
|
var options = new ResolutionCacheOptions
|
|
{
|
|
EnableEarlyExpiry = true,
|
|
EarlyExpiryFactor = 1.0
|
|
};
|
|
var cached = new CachedResolution
|
|
{
|
|
Status = ResolutionStatus.Fixed,
|
|
CachedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
|
Confidence = 1.0m
|
|
};
|
|
var payload = JsonSerializer.Serialize(
|
|
cached,
|
|
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
|
|
|
var db = new Mock<IDatabase>();
|
|
db.Setup(d => d.StringGetAsync(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
|
|
.ReturnsAsync(payload);
|
|
db.Setup(d => d.KeyTimeToLiveAsync(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
|
|
.ReturnsAsync(TimeSpan.FromHours(1));
|
|
|
|
var mux = new Mock<IConnectionMultiplexer>();
|
|
mux.Setup(m => m.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
|
|
.Returns(db.Object);
|
|
|
|
var service = new ResolutionCacheService(
|
|
mux.Object,
|
|
Options.Create(options),
|
|
NullLogger<ResolutionCacheService>.Instance,
|
|
new FixedRandomSource(0.1));
|
|
|
|
var result = await service.GetAsync("resolution:key", CancellationToken.None);
|
|
|
|
result.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetAsync_ReturnsCachedEntry_WhenRandomAboveThreshold()
|
|
{
|
|
var options = new ResolutionCacheOptions
|
|
{
|
|
EnableEarlyExpiry = true,
|
|
EarlyExpiryFactor = 1.0
|
|
};
|
|
var cached = new CachedResolution
|
|
{
|
|
Status = ResolutionStatus.Vulnerable,
|
|
CachedAt = new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
|
|
Confidence = 0.5m
|
|
};
|
|
var payload = JsonSerializer.Serialize(
|
|
cached,
|
|
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
|
|
|
var db = new Mock<IDatabase>();
|
|
db.Setup(d => d.StringGetAsync(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
|
|
.ReturnsAsync(payload);
|
|
db.Setup(d => d.KeyTimeToLiveAsync(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
|
|
.ReturnsAsync(TimeSpan.FromHours(1));
|
|
|
|
var mux = new Mock<IConnectionMultiplexer>();
|
|
mux.Setup(m => m.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
|
|
.Returns(db.Object);
|
|
|
|
var service = new ResolutionCacheService(
|
|
mux.Object,
|
|
Options.Create(options),
|
|
NullLogger<ResolutionCacheService>.Instance,
|
|
new FixedRandomSource(0.9));
|
|
|
|
var result = await service.GetAsync("resolution:key", CancellationToken.None);
|
|
|
|
result.Should().NotBeNull();
|
|
result!.Status.Should().Be(ResolutionStatus.Vulnerable);
|
|
}
|
|
|
|
private sealed class FixedRandomSource : IRandomSource
|
|
{
|
|
private readonly double _value;
|
|
|
|
public FixedRandomSource(double value)
|
|
{
|
|
_value = value;
|
|
}
|
|
|
|
public double NextDouble() => _value;
|
|
}
|
|
}
|