up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-28 09:40:40 +02:00
parent 1c6730a1d2
commit 05da719048
206 changed files with 34741 additions and 1751 deletions

View File

@@ -0,0 +1,301 @@
using System.Collections.Immutable;
using FluentAssertions;
using Microsoft.Extensions.Time.Testing;
using StellaOps.Policy.Engine.Telemetry;
using Xunit;
namespace StellaOps.Policy.Engine.Tests.Telemetry;
public sealed class TelemetryTests
{
#region RuleHitTrace Tests
[Fact]
public void RuleHitTrace_GetOrCreateTraceId_ReturnsValidId()
{
var traceId = RuleHitTrace.GetOrCreateTraceId();
traceId.Should().NotBeNullOrEmpty();
traceId.Should().HaveLength(32); // 16 bytes = 32 hex chars
}
[Fact]
public void RuleHitTrace_GetOrCreateSpanId_ReturnsValidId()
{
var spanId = RuleHitTrace.GetOrCreateSpanId();
spanId.Should().NotBeNullOrEmpty();
spanId.Should().HaveLength(16); // 8 bytes = 16 hex chars
}
[Fact]
public void RuleHitTrace_GetOrCreateTraceId_GeneratesUniqueIds()
{
var ids = Enumerable.Range(0, 100)
.Select(_ => RuleHitTrace.GetOrCreateTraceId())
.ToList();
ids.Distinct().Should().HaveCount(100);
}
#endregion
#region RuleHitTraceFactory Tests
[Fact]
public void Create_ProducesValidTrace()
{
var timestamp = DateTimeOffset.UtcNow;
var timeProvider = new FakeTimeProvider(timestamp);
var trace = RuleHitTraceFactory.Create(
tenantId: "TENANT-1",
policyId: "policy-1",
policyVersion: 2,
runId: "run-123",
ruleName: "block-critical",
rulePriority: 10,
outcome: "deny",
evaluationTimestamp: timestamp,
timeProvider: timeProvider,
ruleCategory: "severity",
assignedSeverity: "Critical",
componentPurl: "pkg:npm/lodash@4.17.21",
advisoryId: "GHSA-test-001",
vulnerabilityId: "CVE-2021-12345");
trace.TenantId.Should().Be("tenant-1"); // Normalized to lowercase
trace.PolicyId.Should().Be("policy-1");
trace.PolicyVersion.Should().Be(2);
trace.RunId.Should().Be("run-123");
trace.RuleName.Should().Be("block-critical");
trace.RulePriority.Should().Be(10);
trace.Outcome.Should().Be("deny");
trace.RuleCategory.Should().Be("severity");
trace.AssignedSeverity.Should().Be("Critical");
trace.ComponentPurl.Should().Be("pkg:npm/lodash@4.17.21");
trace.EvaluationTimestamp.Should().Be(timestamp);
trace.RecordedAt.Should().Be(timestamp);
trace.TraceId.Should().NotBeNullOrEmpty();
trace.SpanId.Should().NotBeNullOrEmpty();
}
[Fact]
public void Create_TracksVexOverride()
{
var timestamp = DateTimeOffset.UtcNow;
var trace = RuleHitTraceFactory.Create(
tenantId: "tenant-1",
policyId: "policy-1",
policyVersion: 1,
runId: "run-123",
ruleName: "vex-override",
rulePriority: 1,
outcome: "suppress",
evaluationTimestamp: timestamp,
vexStatus: "not_affected",
vexJustification: "vulnerable_code_not_in_execute_path",
vexVendor: "vendor-1",
isVexOverride: true);
trace.VexStatus.Should().Be("not_affected");
trace.VexJustification.Should().Be("vulnerable_code_not_in_execute_path");
trace.VexVendor.Should().Be("vendor-1");
trace.IsVexOverride.Should().BeTrue();
}
[Fact]
public void Create_TracksReachability()
{
var timestamp = DateTimeOffset.UtcNow;
var trace = RuleHitTraceFactory.Create(
tenantId: "tenant-1",
policyId: "policy-1",
policyVersion: 1,
runId: "run-123",
ruleName: "reachability-rule",
rulePriority: 5,
outcome: "allow",
evaluationTimestamp: timestamp,
reachabilityState: "reachable",
reachabilityConfidence: 0.95);
trace.ReachabilityState.Should().Be("reachable");
trace.ReachabilityConfidence.Should().Be(0.95);
}
[Fact]
public void Create_IncludesCustomAttributes()
{
var timestamp = DateTimeOffset.UtcNow;
var attributes = ImmutableDictionary<string, string>.Empty
.Add("custom_key", "custom_value")
.Add("another_key", "another_value");
var trace = RuleHitTraceFactory.Create(
tenantId: "tenant-1",
policyId: "policy-1",
policyVersion: 1,
runId: "run-123",
ruleName: "test-rule",
rulePriority: 1,
outcome: "allow",
evaluationTimestamp: timestamp,
attributes: attributes);
trace.Attributes.Should().ContainKey("custom_key");
trace.Attributes["custom_key"].Should().Be("custom_value");
}
[Fact]
public void ToJson_ProducesValidJson()
{
var trace = RuleHitTraceFactory.Create(
tenantId: "tenant-1",
policyId: "policy-1",
policyVersion: 1,
runId: "run-123",
ruleName: "test-rule",
rulePriority: 1,
outcome: "allow",
evaluationTimestamp: DateTimeOffset.UtcNow);
var json = RuleHitTraceFactory.ToJson(trace);
json.Should().Contain("\"tenant_id\":\"tenant-1\"");
json.Should().Contain("\"policy_id\":\"policy-1\"");
json.Should().Contain("\"rule_name\":\"test-rule\"");
json.Should().NotContain("\n"); // Single line
}
[Fact]
public void ToNdjson_ProducesMultipleLines()
{
var timestamp = DateTimeOffset.UtcNow;
var traces = new[]
{
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-1", 1, "allow", timestamp),
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-2", 2, "deny", timestamp),
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-3", 3, "suppress", timestamp)
};
var ndjson = RuleHitTraceFactory.ToNdjson(traces);
var lines = ndjson.Split('\n', StringSplitOptions.RemoveEmptyEntries);
lines.Should().HaveCount(3);
lines[0].Should().Contain("rule-1");
lines[1].Should().Contain("rule-2");
lines[2].Should().Contain("rule-3");
}
#endregion
#region RuleHitStatistics Tests
[Fact]
public void CreateStatistics_AggregatesCorrectly()
{
var timestamp = DateTimeOffset.UtcNow;
var traces = new[]
{
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-1", 1, "allow", timestamp,
ruleCategory: "severity"),
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-2", 2, "deny", timestamp,
ruleCategory: "severity"),
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-3", 3, "suppress", timestamp,
ruleCategory: "vex", isVexOverride: true, vexVendor: "vendor-1", vexStatus: "not_affected"),
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", "rule-4", 4, "suppress", timestamp,
ruleCategory: "vex", isVexOverride: true, vexVendor: "vendor-2", vexStatus: "fixed")
};
var stats = RuleHitTraceFactory.CreateStatistics(
runId: "run-1",
policyId: "policy-1",
traces: traces,
totalRulesEvaluated: 10,
totalEvaluationMs: 50);
stats.RunId.Should().Be("run-1");
stats.PolicyId.Should().Be("policy-1");
stats.TotalRulesEvaluated.Should().Be(10);
stats.TotalRulesFired.Should().Be(4);
stats.TotalVexOverrides.Should().Be(2);
stats.RulesFiredByCategory.Should().ContainKey("severity");
stats.RulesFiredByCategory["severity"].Should().Be(2);
stats.RulesFiredByCategory["vex"].Should().Be(2);
stats.RulesFiredByOutcome.Should().ContainKey("allow");
stats.RulesFiredByOutcome["allow"].Should().Be(1);
stats.RulesFiredByOutcome["deny"].Should().Be(1);
stats.RulesFiredByOutcome["suppress"].Should().Be(2);
stats.VexOverridesByVendor.Should().HaveCount(2);
stats.VexOverridesByStatus.Should().ContainKey("not_affected");
stats.VexOverridesByStatus.Should().ContainKey("fixed");
}
[Fact]
public void CreateStatistics_ComputesAverageEvaluationTime()
{
var traces = Array.Empty<RuleHitTrace>();
var stats = RuleHitTraceFactory.CreateStatistics(
runId: "run-1",
policyId: "policy-1",
traces: traces,
totalRulesEvaluated: 100,
totalEvaluationMs: 50);
stats.TotalEvaluationMs.Should().Be(50);
stats.AverageRuleEvaluationMicroseconds.Should().Be(500); // 50ms * 1000 / 100 rules
}
[Fact]
public void CreateStatistics_HandlesZeroRules()
{
var traces = Array.Empty<RuleHitTrace>();
var stats = RuleHitTraceFactory.CreateStatistics(
runId: "run-1",
policyId: "policy-1",
traces: traces,
totalRulesEvaluated: 0,
totalEvaluationMs: 0);
stats.TotalRulesEvaluated.Should().Be(0);
stats.AverageRuleEvaluationMicroseconds.Should().Be(0);
}
[Fact]
public void CreateStatistics_GeneratesTopRules()
{
var timestamp = DateTimeOffset.UtcNow;
var traces = Enumerable.Range(0, 20)
.SelectMany(i => Enumerable.Range(0, i + 1).Select(_ =>
RuleHitTraceFactory.Create("tenant-1", "policy-1", 1, "run-1", $"rule-{i}", i, "allow", timestamp)))
.ToArray();
var stats = RuleHitTraceFactory.CreateStatistics("run-1", "policy-1", traces, 100, 50);
stats.TopRulesByHitCount.Should().HaveCount(10);
stats.TopRulesByHitCount[0].RuleName.Should().Be("rule-19"); // Highest count
stats.TopRulesByHitCount[0].HitCount.Should().Be(20);
}
#endregion
#region RuleHitCount Tests
[Fact]
public void RuleHitCount_RecordWorks()
{
var hitCount = new RuleHitCount("severity-rule", 42, "deny");
hitCount.RuleName.Should().Be("severity-rule");
hitCount.HitCount.Should().Be(42);
hitCount.Outcome.Should().Be("deny");
}
#endregion
}