up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (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-27 23:44:42 +02:00
parent ef6e4b2067
commit 3b96b2e3ea
298 changed files with 47516 additions and 1168 deletions

View File

@@ -1,11 +1,12 @@
using Xunit;
using System.Collections.Immutable;
using System.Collections.Immutable;
using Microsoft.Extensions.Options;
using StellaOps.Policy;
using StellaOps.Policy.Engine.Compilation;
using StellaOps.Policy.Engine.Domain;
using StellaOps.Policy.Engine.Options;
using StellaOps.Policy.Engine.Services;
using StellaOps.PolicyDsl;
namespace StellaOps.Policy.Engine.Tests;
@@ -21,7 +22,7 @@ public sealed class PolicyBundleServiceTests
public async Task CompileAndStoreAsync_SucceedsAndStoresBundle()
{
var services = CreateServices();
var request = new PolicyBundleRequest(new PolicyDslPayload("stella-dsl@1", BaselineDsl), signingKeyId: "test-key");
var request = new PolicyBundleRequest(new PolicyDslPayload("stella-dsl@1", BaselineDsl), SigningKeyId: "test-key");
var response = await services.BundleService.CompileAndStoreAsync("pack-1", 1, request, CancellationToken.None);
@@ -35,7 +36,7 @@ public sealed class PolicyBundleServiceTests
public async Task CompileAndStoreAsync_FailsWithBadSyntax()
{
var services = CreateServices();
var request = new PolicyBundleRequest(new PolicyDslPayload("unknown", "policy bad"), signingKeyId: null);
var request = new PolicyBundleRequest(new PolicyDslPayload("unknown", "policy bad"), SigningKeyId: null);
var response = await services.BundleService.CompileAndStoreAsync("pack-1", 1, request, CancellationToken.None);
@@ -48,7 +49,7 @@ public sealed class PolicyBundleServiceTests
{
var compiler = new PolicyCompiler();
var complexity = new PolicyComplexityAnalyzer();
var options = Options.Create(new PolicyEngineOptions());
var options = Microsoft.Extensions.Options.Options.Create(new PolicyEngineOptions());
var compilationService = new PolicyCompilationService(compiler, complexity, new StaticOptionsMonitor(options.Value), TimeProvider.System);
var repo = new InMemoryPolicyPackRepository();
return new ServiceHarness(

View File

@@ -1,9 +1,10 @@
using System;
using Microsoft.Extensions.Options;
using StellaOps.Policy;
using StellaOps.PolicyDsl;
using StellaOps.Policy.Engine.Compilation;
using StellaOps.Policy.Engine.Options;
using StellaOps.Policy.Engine.Services;
using StellaOps.PolicyDsl;
using Xunit;
namespace StellaOps.Policy.Engine.Tests;

View File

@@ -0,0 +1,208 @@
using Xunit;
using Microsoft.Extensions.Time.Testing;
using StellaOps.Policy.Engine.Domain;
using StellaOps.Policy.Engine.Ledger;
using StellaOps.Policy.Engine.Orchestration;
using StellaOps.Policy.Engine.Services;
using StellaOps.Policy.Engine.Snapshots;
using StellaOps.Policy.Engine.TrustWeighting;
using StellaOps.Policy.Engine.Violations;
namespace StellaOps.Policy.Engine.Tests;
public sealed class PolicyDecisionServiceTests
{
private static (PolicyDecisionService service, string snapshotId) BuildService()
{
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-27T10:00:00Z"));
var jobStore = new InMemoryOrchestratorJobStore();
var resultStore = new InMemoryWorkerResultStore(jobStore);
var exportStore = new InMemoryLedgerExportStore();
var ledger = new LedgerExportService(clock, jobStore, resultStore, exportStore);
var snapshotStore = new InMemorySnapshotStore();
var violationStore = new InMemoryViolationEventStore();
var trust = new TrustWeightingService(clock);
var snapshotService = new SnapshotService(clock, ledger, snapshotStore);
var eventService = new ViolationEventService(snapshotStore, jobStore, violationStore);
var fusionService = new SeverityFusionService(violationStore, trust);
var conflictService = new ConflictHandlingService(violationStore);
var evidenceService = new EvidenceSummaryService(clock);
var decisionService = new PolicyDecisionService(
eventService,
fusionService,
conflictService,
evidenceService);
// Setup test data
var job = new OrchestratorJob(
JobId: "job-decision-test",
TenantId: "acme",
ContextId: "ctx",
PolicyProfileHash: "hash",
RequestedAt: clock.GetUtcNow(),
Priority: "normal",
BatchItems: new[]
{
new OrchestratorJobItem("pkg:npm/lodash@4.17.21", "CVE-2021-23337"),
new OrchestratorJobItem("pkg:npm/axios@0.21.1", "CVE-2021-3749"),
new OrchestratorJobItem("pkg:maven/log4j@2.14.1", "CVE-2021-44228")
},
Callbacks: null,
TraceRef: "trace-decision",
Status: "completed",
DeterminismHash: "hash",
CompletedAt: clock.GetUtcNow(),
ResultHash: "res");
jobStore.SaveAsync(job).GetAwaiter().GetResult();
resultStore.SaveAsync(new WorkerRunResult(
job.JobId,
"worker-decision",
clock.GetUtcNow(),
clock.GetUtcNow(),
new[]
{
new WorkerResultItem("pkg:npm/lodash@4.17.21", "CVE-2021-23337", "violation", "trace-lodash"),
new WorkerResultItem("pkg:npm/axios@0.21.1", "CVE-2021-3749", "warn", "trace-axios"),
new WorkerResultItem("pkg:maven/log4j@2.14.1", "CVE-2021-44228", "violation", "trace-log4j")
},
"hash")).GetAwaiter().GetResult();
ledger.BuildAsync(new LedgerExportRequest("acme")).GetAwaiter().GetResult();
var snapshot = snapshotService.CreateAsync(new SnapshotRequest("acme", "overlay-decision")).GetAwaiter().GetResult();
return (decisionService, snapshot.SnapshotId);
}
[Fact]
public async Task GetDecisionsAsync_ReturnsDecisionsWithEvidence()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(snapshotId);
var response = await service.GetDecisionsAsync(request);
Assert.Equal(snapshotId, response.SnapshotId);
Assert.Equal(3, response.Decisions.Count);
Assert.All(response.Decisions, d =>
{
Assert.False(string.IsNullOrWhiteSpace(d.SeverityFused));
Assert.NotNull(d.Evidence);
Assert.NotNull(d.TopSources);
Assert.True(d.TopSources.Count > 0);
});
}
[Fact]
public async Task GetDecisionsAsync_BuildsSummaryStatistics()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(snapshotId);
var response = await service.GetDecisionsAsync(request);
Assert.Equal(3, response.Summary.TotalDecisions);
Assert.NotEmpty(response.Summary.SeverityCounts);
Assert.NotEmpty(response.Summary.TopSeveritySources);
}
[Fact]
public async Task GetDecisionsAsync_FiltersById()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(
SnapshotId: snapshotId,
AdvisoryId: "CVE-2021-44228");
var response = await service.GetDecisionsAsync(request);
Assert.Single(response.Decisions);
Assert.Equal("CVE-2021-44228", response.Decisions[0].AdvisoryId);
}
[Fact]
public async Task GetDecisionsAsync_FiltersByTenant()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(
SnapshotId: snapshotId,
TenantId: "acme");
var response = await service.GetDecisionsAsync(request);
Assert.All(response.Decisions, d => Assert.Equal("acme", d.TenantId));
}
[Fact]
public async Task GetDecisionsAsync_LimitsTopSources()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(
SnapshotId: snapshotId,
MaxSources: 1);
var response = await service.GetDecisionsAsync(request);
Assert.All(response.Decisions, d =>
{
Assert.True(d.TopSources.Count <= 1);
});
}
[Fact]
public async Task GetDecisionsAsync_ExcludesEvidenceWhenNotRequested()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(
SnapshotId: snapshotId,
IncludeEvidence: false);
var response = await service.GetDecisionsAsync(request);
Assert.All(response.Decisions, d => Assert.Null(d.Evidence));
}
[Fact]
public async Task GetDecisionsAsync_ReturnsDeterministicOrder()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(snapshotId);
var response1 = await service.GetDecisionsAsync(request);
var response2 = await service.GetDecisionsAsync(request);
Assert.Equal(
response1.Decisions.Select(d => d.ComponentPurl),
response2.Decisions.Select(d => d.ComponentPurl));
}
[Fact]
public async Task GetDecisionsAsync_ThrowsOnEmptySnapshotId()
{
var (service, _) = BuildService();
var request = new PolicyDecisionRequest(string.Empty);
await Assert.ThrowsAsync<ArgumentException>(() => service.GetDecisionsAsync(request));
}
[Fact]
public async Task GetDecisionsAsync_TopSourcesHaveRanks()
{
var (service, snapshotId) = BuildService();
var request = new PolicyDecisionRequest(snapshotId);
var response = await service.GetDecisionsAsync(request);
foreach (var decision in response.Decisions)
{
for (var i = 0; i < decision.TopSources.Count; i++)
{
Assert.Equal(i + 1, decision.TopSources[i].Rank);
}
}
}
}

View File

@@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.0.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>