using Microsoft.Extensions.Logging.Abstractions; using StellaOps.Vexer.Core; using StellaOps.Vexer.Export; using StellaOps.Vexer.Storage.Mongo; namespace StellaOps.Vexer.Export.Tests; public sealed class VexExportCacheServiceTests { [Fact] public async Task InvalidateAsync_RemovesEntry() { var cacheIndex = new RecordingIndex(); var maintenance = new StubMaintenance(); var service = new VexExportCacheService(cacheIndex, maintenance, NullLogger.Instance); var signature = new VexQuerySignature("format=json|provider=vendor"); await service.InvalidateAsync(signature, VexExportFormat.Json, CancellationToken.None); Assert.Equal(signature.Value, cacheIndex.LastSignature?.Value); Assert.Equal(VexExportFormat.Json, cacheIndex.LastFormat); Assert.Equal(1, cacheIndex.RemoveCalls); } [Fact] public async Task PruneExpiredAsync_ReturnsCount() { var cacheIndex = new RecordingIndex(); var maintenance = new StubMaintenance { ExpiredCount = 3 }; var service = new VexExportCacheService(cacheIndex, maintenance, NullLogger.Instance); var removed = await service.PruneExpiredAsync(DateTimeOffset.UtcNow, CancellationToken.None); Assert.Equal(3, removed); } [Fact] public async Task PruneDanglingAsync_ReturnsCount() { var cacheIndex = new RecordingIndex(); var maintenance = new StubMaintenance { DanglingCount = 2 }; var service = new VexExportCacheService(cacheIndex, maintenance, NullLogger.Instance); var removed = await service.PruneDanglingAsync(CancellationToken.None); Assert.Equal(2, removed); } private sealed class RecordingIndex : IVexCacheIndex { public VexQuerySignature? LastSignature { get; private set; } public VexExportFormat LastFormat { get; private set; } public int RemoveCalls { get; private set; } public ValueTask FindAsync(VexQuerySignature signature, VexExportFormat format, CancellationToken cancellationToken) => ValueTask.FromResult(null); public ValueTask SaveAsync(VexCacheEntry entry, CancellationToken cancellationToken) => ValueTask.CompletedTask; public ValueTask RemoveAsync(VexQuerySignature signature, VexExportFormat format, CancellationToken cancellationToken) { LastSignature = signature; LastFormat = format; RemoveCalls++; return ValueTask.CompletedTask; } } private sealed class StubMaintenance : IVexCacheMaintenance { public int ExpiredCount { get; set; } public int DanglingCount { get; set; } public ValueTask RemoveExpiredAsync(DateTimeOffset asOf, CancellationToken cancellationToken) => ValueTask.FromResult(ExpiredCount); public ValueTask RemoveMissingManifestReferencesAsync(CancellationToken cancellationToken) => ValueTask.FromResult(DanglingCount); } }