using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using StellaOps.Scanner.CallGraph; using StellaOps.Scanner.CallGraph.Caching; using StellaOps.Scanner.Contracts; using Xunit; using StellaOps.TestKit; namespace StellaOps.Scanner.CallGraph.Tests; /// /// Integration tests for Valkey/Redis call graph caching. /// These tests require a Redis-compatible server to be running. /// Set STELLA_VALKEY_TESTS=1 to enable when Valkey is available. /// public class ValkeyCallGraphCacheServiceTests : IAsyncLifetime { private ValkeyCallGraphCacheService? _cache; private static readonly bool ValkeyTestsEnabled = Environment.GetEnvironmentVariable("STELLA_VALKEY_TESTS") == "1"; public async ValueTask InitializeAsync() { if (!ValkeyTestsEnabled) { return; } var options = Options.Create(new CallGraphCacheConfig { Enabled = true, ConnectionString = Environment.GetEnvironmentVariable("STELLA_VALKEY_CONNECTION") ?? "localhost:6379", KeyPrefix = $"test:callgraph:{Guid.NewGuid():N}:", TtlSeconds = 60, EnableGzip = true, CircuitBreaker = new CircuitBreakerConfig { FailureThreshold = 3, TimeoutSeconds = 30, HalfOpenTimeout = 10 } }); try { _cache = new ValkeyCallGraphCacheService( options, NullLogger.Instance); } catch { _cache = null; } await ValueTask.CompletedTask; } public async ValueTask DisposeAsync() { if (_cache is not null) { await _cache.DisposeAsync(); } } [Trait("Category", TestCategories.Integration)] [Fact] public async Task SetThenGet_CallGraph_RoundTrips() { if (!ValkeyTestsEnabled || _cache is null) { Assert.True(true, "Valkey integration tests disabled. Set STELLA_VALKEY_TESTS=1 to enable."); return; } var nodeId = CallGraphNodeIds.Compute("dotnet:test:entry"); var snapshot = new CallGraphSnapshot( ScanId: "scan-cache-1", GraphDigest: "sha256:cg", Language: "dotnet", ExtractedAt: DateTimeOffset.UtcNow, Nodes: [new CallGraphNode(nodeId, "Entry", "file.cs", 1, "app", Visibility.Public, true, EntrypointType.HttpHandler, false, null)], Edges: [], EntrypointIds: [nodeId], SinkIds: []); await _cache.SetCallGraphAsync(snapshot, cancellationToken: TestContext.Current.CancellationToken); var loaded = await _cache.TryGetCallGraphAsync("scan-cache-1", "dotnet", cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(loaded); Assert.Equal(snapshot.ScanId, loaded!.ScanId); Assert.Equal(snapshot.Language, loaded.Language); Assert.Equal(snapshot.GraphDigest, loaded.GraphDigest); } [Trait("Category", TestCategories.Integration)] [Fact] public async Task SetThenGet_ReachabilityResult_RoundTrips() { if (!ValkeyTestsEnabled || _cache is null) { Assert.True(true, "Valkey integration tests disabled. Set STELLA_VALKEY_TESTS=1 to enable."); return; } var result = new ReachabilityAnalysisResult( ScanId: "scan-cache-2", GraphDigest: "sha256:cg", Language: "dotnet", ComputedAt: DateTimeOffset.UtcNow, ReachableNodeIds: [], ReachableSinkIds: [], Paths: [], ResultDigest: "sha256:r"); await _cache.SetReachabilityResultAsync(result, cancellationToken: TestContext.Current.CancellationToken); var loaded = await _cache.TryGetReachabilityResultAsync("scan-cache-2", "dotnet", cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(loaded); Assert.Equal(result.ResultDigest, loaded!.ResultDigest); } }