up
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
This commit is contained in:
@@ -8,7 +8,6 @@ using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MongoDB.Bson;
|
||||
using StellaOps.Signals.Models;
|
||||
using StellaOps.Signals.Options;
|
||||
using StellaOps.Signals.Parsing;
|
||||
@@ -47,15 +46,31 @@ public sealed class ReachabilityScoringTests
|
||||
public async Task RecomputedFactsMatchTruthFixtures(string caseId, string variant)
|
||||
{
|
||||
var casePath = Path.Combine(FixtureRoot, caseId);
|
||||
var variantPath = Path.Combine(casePath, "images", variant);
|
||||
var truth = JsonDocument.Parse(File.ReadAllText(Path.Combine(variantPath, "reachgraph.truth.json"))).RootElement;
|
||||
var sinks = truth.GetProperty("sinks").EnumerateArray().Select(x => x.GetProperty("sid").GetString()!).ToList();
|
||||
var entryPoints = truth.GetProperty("paths").EnumerateArray()
|
||||
.Select(path => path[0].GetString()!)
|
||||
var caseJson = JsonDocument.Parse(File.ReadAllText(Path.Combine(casePath, "case.json"))).RootElement;
|
||||
var reachablePathsNode = caseJson
|
||||
.GetProperty("ground_truth")
|
||||
.GetProperty("reachable_variant")
|
||||
.GetProperty("evidence")
|
||||
.GetProperty("paths");
|
||||
|
||||
var paths = reachablePathsNode.EnumerateArray()
|
||||
.Select(path => path.EnumerateArray().Select(x => x.GetString()!).Where(x => !string.IsNullOrWhiteSpace(x)).ToList())
|
||||
.Where(path => path.Count > 0)
|
||||
.ToList();
|
||||
|
||||
var entryPoints = paths
|
||||
.Select(path => path[0])
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
var callgraph = await LoadCallgraphAsync(caseId, variant, variantPath);
|
||||
var sinks = paths
|
||||
.Select(path => path[^1])
|
||||
.Where(p => !string.IsNullOrWhiteSpace(p))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
var callgraph = BuildCallgraphFromPaths(caseId, paths);
|
||||
var callgraphRepo = new InMemoryCallgraphRepository(callgraph);
|
||||
var factRepo = new InMemoryReachabilityFactRepository();
|
||||
var options = new SignalsOptions();
|
||||
@@ -66,7 +81,7 @@ public sealed class ReachabilityScoringTests
|
||||
callgraphRepo,
|
||||
factRepo,
|
||||
TimeProvider.System,
|
||||
Options.Create(options),
|
||||
Microsoft.Extensions.Options.Options.Create(options),
|
||||
cache,
|
||||
unknowns,
|
||||
eventsPublisher,
|
||||
@@ -149,41 +164,46 @@ public sealed class ReachabilityScoringTests
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<CallgraphDocument> LoadCallgraphAsync(string caseId, string variant, string variantPath)
|
||||
private static CallgraphDocument BuildCallgraphFromPaths(string caseId, IReadOnlyList<IReadOnlyList<string>> paths)
|
||||
{
|
||||
var parser = new SimpleJsonCallgraphParser("fixture");
|
||||
var nodes = new Dictionary<string, CallgraphNode>(StringComparer.Ordinal);
|
||||
var edges = new List<CallgraphEdge>();
|
||||
|
||||
foreach (var fileName in new[] { "callgraph.static.json", "callgraph.framework.json" })
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var path = Path.Combine(variantPath, fileName);
|
||||
if (!File.Exists(path))
|
||||
if (path.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
await using var stream = File.OpenRead(path);
|
||||
var result = await parser.ParseAsync(stream, CancellationToken.None);
|
||||
foreach (var node in result.Nodes)
|
||||
foreach (var nodeId in path)
|
||||
{
|
||||
nodes[node.Id] = node;
|
||||
if (!nodes.ContainsKey(nodeId))
|
||||
{
|
||||
nodes[nodeId] = new CallgraphNode(nodeId, nodeId, "function", null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
edges.AddRange(result.Edges);
|
||||
for (var i = 0; i < path.Count - 1; i++)
|
||||
{
|
||||
edges.Add(new CallgraphEdge(path[i], path[i + 1], "call"));
|
||||
}
|
||||
}
|
||||
|
||||
return new CallgraphDocument
|
||||
{
|
||||
Id = ObjectId.GenerateNewId().ToString(),
|
||||
Id = caseId,
|
||||
Language = "fixture",
|
||||
Component = caseId,
|
||||
Version = variant,
|
||||
Nodes = nodes.Values.ToList(),
|
||||
Edges = edges,
|
||||
Version = "truth",
|
||||
Nodes = nodes.Values.OrderBy(n => n.Id, StringComparer.Ordinal).ToList(),
|
||||
Edges = edges
|
||||
.OrderBy(e => e.SourceId, StringComparer.Ordinal)
|
||||
.ThenBy(e => e.TargetId, StringComparer.Ordinal)
|
||||
.ToList(),
|
||||
Artifact = new CallgraphArtifactMetadata
|
||||
{
|
||||
Path = $"cas://fixtures/{caseId}/{variant}",
|
||||
Path = $"cas://fixtures/{caseId}",
|
||||
Hash = "stub",
|
||||
ContentType = "application/json",
|
||||
Length = 0
|
||||
|
||||
@@ -15,12 +15,23 @@ namespace StellaOps.Signals.Reachability.Tests;
|
||||
public sealed class RuntimeFactsIngestionServiceTests
|
||||
{
|
||||
private readonly FakeReachabilityFactRepository repository = new();
|
||||
private readonly FakeReachabilityCache cache = new();
|
||||
private readonly FakeEventsPublisher eventsPublisher = new();
|
||||
private readonly FakeScoringService scoringService = new();
|
||||
private readonly FakeProvenanceNormalizer provenanceNormalizer = new();
|
||||
private readonly FakeTimeProvider timeProvider = new(DateTimeOffset.Parse("2025-11-09T10:15:00Z", null, System.Globalization.DateTimeStyles.AssumeUniversal));
|
||||
private readonly RuntimeFactsIngestionService sut;
|
||||
|
||||
public RuntimeFactsIngestionServiceTests()
|
||||
{
|
||||
sut = new RuntimeFactsIngestionService(repository, timeProvider, NullLogger<RuntimeFactsIngestionService>.Instance);
|
||||
sut = new RuntimeFactsIngestionService(
|
||||
repository,
|
||||
timeProvider,
|
||||
cache,
|
||||
eventsPublisher,
|
||||
scoringService,
|
||||
provenanceNormalizer,
|
||||
NullLogger<RuntimeFactsIngestionService>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -145,4 +156,83 @@ public sealed class RuntimeFactsIngestionServiceTests
|
||||
public Task<ReachabilityFactDocument?> GetBySubjectAsync(string subjectKey, CancellationToken cancellationToken) =>
|
||||
Task.FromResult(LastUpsert is { SubjectKey: not null } doc && doc.SubjectKey == subjectKey ? doc : null);
|
||||
}
|
||||
|
||||
private sealed class FakeReachabilityCache : IReachabilityCache
|
||||
{
|
||||
private readonly Dictionary<string, ReachabilityFactDocument> storage = new(StringComparer.Ordinal);
|
||||
|
||||
public Task<ReachabilityFactDocument?> GetAsync(string subjectKey, CancellationToken cancellationToken)
|
||||
{
|
||||
storage.TryGetValue(subjectKey, out var document);
|
||||
return Task.FromResult(document);
|
||||
}
|
||||
|
||||
public Task SetAsync(ReachabilityFactDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
storage[document.SubjectKey] = document;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task InvalidateAsync(string subjectKey, CancellationToken cancellationToken)
|
||||
{
|
||||
storage.Remove(subjectKey);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeEventsPublisher : IEventsPublisher
|
||||
{
|
||||
public List<ReachabilityFactDocument> Published { get; } = new();
|
||||
|
||||
public Task PublishFactUpdatedAsync(ReachabilityFactDocument fact, CancellationToken cancellationToken)
|
||||
{
|
||||
Published.Add(fact);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeScoringService : IReachabilityScoringService
|
||||
{
|
||||
public List<ReachabilityRecomputeRequest> Requests { get; } = new();
|
||||
|
||||
public Task<ReachabilityFactDocument> RecomputeAsync(ReachabilityRecomputeRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
Requests.Add(request);
|
||||
return Task.FromResult(new ReachabilityFactDocument
|
||||
{
|
||||
Subject = request.Subject,
|
||||
SubjectKey = request.Subject.ToSubjectKey(),
|
||||
CallgraphId = request.CallgraphId,
|
||||
ComputedAt = TimeProvider.System.GetUtcNow()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeProvenanceNormalizer : IRuntimeFactsProvenanceNormalizer
|
||||
{
|
||||
public ProvenanceFeed NormalizeToFeed(
|
||||
IEnumerable<RuntimeFactEvent> events,
|
||||
ReachabilitySubject subject,
|
||||
string callgraphId,
|
||||
Dictionary<string, string?>? metadata,
|
||||
DateTimeOffset generatedAt) => new()
|
||||
{
|
||||
FeedId = "fixture",
|
||||
GeneratedAt = generatedAt,
|
||||
CorrelationId = callgraphId,
|
||||
Records = new List<ProvenanceRecord>()
|
||||
};
|
||||
|
||||
public ContextFacts CreateContextFacts(
|
||||
IEnumerable<RuntimeFactEvent> events,
|
||||
ReachabilitySubject subject,
|
||||
string callgraphId,
|
||||
Dictionary<string, string?>? metadata,
|
||||
DateTimeOffset timestamp) => new()
|
||||
{
|
||||
Provenance = NormalizeToFeed(events, subject, callgraphId, metadata, timestamp),
|
||||
LastUpdatedAt = timestamp,
|
||||
RecordCount = events is ICollection<RuntimeFactEvent> collection ? collection.Count : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user