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
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
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
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
api-governance / spectral-lint (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using StellaOps.Policy.Engine.AdvisoryAI;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class AdvisoryAiKnobsServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Get_ReturnsDefaultsWithHash()
|
||||
{
|
||||
var service = new AdvisoryAiKnobsService(TimeProvider.System);
|
||||
var profile = service.Get();
|
||||
|
||||
Assert.NotEmpty(profile.Knobs);
|
||||
Assert.False(string.IsNullOrWhiteSpace(profile.ProfileHash));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_NormalizesOrdering()
|
||||
{
|
||||
var service = new AdvisoryAiKnobsService(TimeProvider.System);
|
||||
|
||||
var profile = service.Set(new[]
|
||||
{
|
||||
new AdvisoryAiKnob("Time_Decay_Half_Life_Days", 20m, 1m, 365m, 1m, "decay"),
|
||||
new AdvisoryAiKnob("ai_signal_weight", 1.5m, 0m, 2m, 0.1m, "weight")
|
||||
});
|
||||
|
||||
Assert.Equal("ai_signal_weight", profile.Knobs[0].Name);
|
||||
Assert.Equal("time_decay_half_life_days", profile.Knobs[1].Name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Policy.Engine.Ledger;
|
||||
using StellaOps.Policy.Engine.Orchestration;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class LedgerExportServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task BuildAsync_ProducesOrderedNdjson()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T15:00:00Z"));
|
||||
var jobStore = new InMemoryOrchestratorJobStore();
|
||||
var resultStore = new InMemoryWorkerResultStore(jobStore);
|
||||
var exportStore = new InMemoryLedgerExportStore();
|
||||
var service = new LedgerExportService(clock, jobStore, resultStore, exportStore);
|
||||
|
||||
var job = new OrchestratorJob(
|
||||
JobId: "job-1",
|
||||
TenantId: "acme",
|
||||
ContextId: "ctx",
|
||||
PolicyProfileHash: "hash",
|
||||
RequestedAt: clock.GetUtcNow(),
|
||||
Priority: "normal",
|
||||
BatchItems: new[]
|
||||
{
|
||||
new OrchestratorJobItem("pkg:b", "ADV-2"),
|
||||
new OrchestratorJobItem("pkg:a", "ADV-1")
|
||||
},
|
||||
Callbacks: null,
|
||||
TraceRef: "trace",
|
||||
Status: "completed",
|
||||
DeterminismHash: "hash",
|
||||
CompletedAt: clock.GetUtcNow(),
|
||||
ResultHash: "res");
|
||||
|
||||
await jobStore.SaveAsync(job);
|
||||
|
||||
var result = new WorkerRunResult(
|
||||
job.JobId,
|
||||
"worker",
|
||||
clock.GetUtcNow(),
|
||||
clock.GetUtcNow(),
|
||||
new[]
|
||||
{
|
||||
new WorkerResultItem("pkg:b", "ADV-2", "ok", "trace-b"),
|
||||
new WorkerResultItem("pkg:a", "ADV-1", "violation", "trace-a")
|
||||
},
|
||||
"hash");
|
||||
|
||||
await resultStore.SaveAsync(result);
|
||||
|
||||
var export = await service.BuildAsync(new LedgerExportRequest("acme"));
|
||||
|
||||
Assert.Equal(2, export.Manifest.RecordCount);
|
||||
Assert.Equal("policy-ledger-export-v1", export.Manifest.SchemaVersion);
|
||||
Assert.Equal(3, export.Lines.Count); // manifest + 2 records
|
||||
Assert.Contains(export.Records, r => r.ComponentPurl == "pkg:a");
|
||||
Assert.Equal("pkg:a", export.Records[0].ComponentPurl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Policy.Engine.Orchestration;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class OrchestratorJobServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SubmitAsync_NormalizesOrderingAndHashes()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T10:00:00Z"));
|
||||
var store = new InMemoryOrchestratorJobStore();
|
||||
var service = new OrchestratorJobService(clock, store);
|
||||
|
||||
var job = await service.SubmitAsync(
|
||||
new OrchestratorJobRequest(
|
||||
TenantId: "acme",
|
||||
ContextId: "ctx-123",
|
||||
PolicyProfileHash: "overlay-hash",
|
||||
BatchItems: new[]
|
||||
{
|
||||
new OrchestratorJobItem("pkg:npm/zeta@1.0.0", "ADV-2"),
|
||||
new OrchestratorJobItem("pkg:npm/alpha@1.0.0", "ADV-1")
|
||||
},
|
||||
Priority: "HIGH",
|
||||
TraceRef: null,
|
||||
Callbacks: new OrchestratorJobCallbacks("sse://events", "nats.subject"),
|
||||
RequestedAt: null));
|
||||
|
||||
Assert.Equal("acme", job.TenantId);
|
||||
Assert.Equal("ctx-123", job.ContextId);
|
||||
Assert.Equal("high", job.Priority);
|
||||
Assert.Equal(clock.GetUtcNow(), job.RequestedAt);
|
||||
Assert.Equal("queued", job.Status);
|
||||
Assert.Equal(2, job.BatchItems.Count);
|
||||
Assert.Equal("pkg:npm/alpha@1.0.0", job.BatchItems[0].ComponentPurl);
|
||||
Assert.False(string.IsNullOrWhiteSpace(job.JobId));
|
||||
Assert.False(string.IsNullOrWhiteSpace(job.DeterminismHash));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SubmitAsync_IsDeterministicAcrossOrdering()
|
||||
{
|
||||
var requestedAt = DateTimeOffset.Parse("2025-11-24T11:00:00Z");
|
||||
var clock = new FakeTimeProvider(requestedAt);
|
||||
var store = new InMemoryOrchestratorJobStore();
|
||||
var service = new OrchestratorJobService(clock, store);
|
||||
|
||||
var first = await service.SubmitAsync(
|
||||
new OrchestratorJobRequest(
|
||||
"tenant",
|
||||
"ctx",
|
||||
"hash",
|
||||
new[]
|
||||
{
|
||||
new OrchestratorJobItem("pkg:a", "ADV-1"),
|
||||
new OrchestratorJobItem("pkg:b", "ADV-2")
|
||||
},
|
||||
RequestedAt: requestedAt));
|
||||
|
||||
var second = await service.SubmitAsync(
|
||||
new OrchestratorJobRequest(
|
||||
"tenant",
|
||||
"ctx",
|
||||
"hash",
|
||||
new[]
|
||||
{
|
||||
new OrchestratorJobItem("pkg:b", "ADV-2"),
|
||||
new OrchestratorJobItem("pkg:a", "ADV-1")
|
||||
},
|
||||
RequestedAt: requestedAt));
|
||||
|
||||
Assert.Equal(first.JobId, second.JobId);
|
||||
Assert.Equal(first.DeterminismHash, second.DeterminismHash);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Preview_DoesNotPersist()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T12:00:00Z"));
|
||||
var store = new InMemoryOrchestratorJobStore();
|
||||
var service = new OrchestratorJobService(clock, store);
|
||||
|
||||
var preview = await service.PreviewAsync(
|
||||
new OrchestratorJobRequest(
|
||||
"tenant",
|
||||
"ctx",
|
||||
"hash",
|
||||
new[] { new OrchestratorJobItem("pkg:a", "ADV-1") }));
|
||||
|
||||
Assert.Equal("preview", preview.Status);
|
||||
|
||||
var fetched = await store.GetAsync(preview.JobId);
|
||||
Assert.Null(fetched);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Policy.Engine.Overlay;
|
||||
using StellaOps.Policy.Engine.Tests.Fakes;
|
||||
@@ -8,6 +9,8 @@ namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class PathScopeSimulationBridgeServiceTests
|
||||
{
|
||||
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
|
||||
|
||||
[Fact]
|
||||
public async Task SimulateAsync_OrdersByInputAndProducesMetrics()
|
||||
{
|
||||
@@ -28,8 +31,8 @@ public sealed class PathScopeSimulationBridgeServiceTests
|
||||
var result = await bridge.SimulateAsync(request);
|
||||
|
||||
Assert.Equal(2, result.Decisions.Count);
|
||||
Assert.Contains("\"filePath\":\"b/file.js\"", JsonSerializer.Serialize(result.Decisions[0].PathScope));
|
||||
Assert.Contains("\"filePath\":\"a/file.js\"", JsonSerializer.Serialize(result.Decisions[1].PathScope));
|
||||
Assert.Contains("\"filePath\":\"b/file.js\"", JsonSerializer.Serialize(result.Decisions[0].PathScope, SerializerOptions));
|
||||
Assert.Contains("\"filePath\":\"a/file.js\"", JsonSerializer.Serialize(result.Decisions[1].PathScope, SerializerOptions));
|
||||
Assert.Equal(2, result.Metrics.Evaluated);
|
||||
}
|
||||
|
||||
@@ -49,7 +52,8 @@ public sealed class PathScopeSimulationBridgeServiceTests
|
||||
var result = await bridge.SimulateAsync(request);
|
||||
|
||||
Assert.Single(result.Decisions);
|
||||
Assert.Single(result.Deltas);
|
||||
Assert.NotNull(result.Deltas);
|
||||
Assert.Single(result.Deltas!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Policy.Engine.Orchestration;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class PolicyWorkerServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_ReturnsDeterministicResults()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T13:00:00Z"));
|
||||
var jobStore = new InMemoryOrchestratorJobStore();
|
||||
var resultStore = new InMemoryWorkerResultStore(jobStore);
|
||||
var service = new PolicyWorkerService(clock, jobStore, resultStore);
|
||||
|
||||
var job = new OrchestratorJob(
|
||||
JobId: "01HZX1QJP6Z3MNA0Q2T3VCPV5K",
|
||||
TenantId: "tenant",
|
||||
ContextId: "ctx",
|
||||
PolicyProfileHash: "hash",
|
||||
RequestedAt: clock.GetUtcNow(),
|
||||
Priority: "normal",
|
||||
BatchItems: new[]
|
||||
{
|
||||
new OrchestratorJobItem("pkg:npm/alpha@1.0.0", "ADV-1"),
|
||||
new OrchestratorJobItem("pkg:npm/zeta@1.0.0", "ADV-2")
|
||||
},
|
||||
Callbacks: null,
|
||||
TraceRef: "trace",
|
||||
Status: "queued",
|
||||
DeterminismHash: "hash-determinism");
|
||||
|
||||
await jobStore.SaveAsync(job);
|
||||
|
||||
var result = await service.ExecuteAsync(new WorkerRunRequest(job.JobId), CancellationToken.None);
|
||||
|
||||
Assert.Equal(job.JobId, result.JobId);
|
||||
Assert.Equal("worker-stub", result.WorkerId);
|
||||
Assert.Equal(2, result.Results.Count);
|
||||
Assert.True(result.Results.All(r => !string.IsNullOrWhiteSpace(r.Status)));
|
||||
|
||||
var fetched = await resultStore.GetByJobIdAsync(job.JobId);
|
||||
Assert.NotNull(fetched);
|
||||
Assert.Equal(result.ResultHash, fetched!.ResultHash);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_IsIdempotentOnRetry()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T14:00:00Z"));
|
||||
var jobStore = new InMemoryOrchestratorJobStore();
|
||||
var resultStore = new InMemoryWorkerResultStore(jobStore);
|
||||
var service = new PolicyWorkerService(clock, jobStore, resultStore);
|
||||
|
||||
var job = new OrchestratorJob(
|
||||
JobId: "job-id",
|
||||
TenantId: "tenant",
|
||||
ContextId: "ctx",
|
||||
PolicyProfileHash: "hash",
|
||||
RequestedAt: clock.GetUtcNow(),
|
||||
Priority: "normal",
|
||||
BatchItems: new[] { new OrchestratorJobItem("pkg:a", "ADV-1") },
|
||||
Callbacks: null,
|
||||
TraceRef: "trace",
|
||||
Status: "queued",
|
||||
DeterminismHash: "hash");
|
||||
|
||||
await jobStore.SaveAsync(job);
|
||||
|
||||
var first = await service.ExecuteAsync(new WorkerRunRequest(job.JobId));
|
||||
var second = await service.ExecuteAsync(new WorkerRunRequest(job.JobId));
|
||||
|
||||
Assert.Equal(first.ResultHash, second.ResultHash);
|
||||
Assert.Equal(first.CompletedAt, second.CompletedAt);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Policy.Engine.Ledger;
|
||||
using StellaOps.Policy.Engine.Orchestration;
|
||||
using StellaOps.Policy.Engine.Snapshots;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class SnapshotServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateAsync_ProducesSnapshotFromLedger()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T16: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 service = new SnapshotService(clock, ledger, snapshotStore);
|
||||
|
||||
var job = new OrchestratorJob(
|
||||
JobId: "job-xyz",
|
||||
TenantId: "acme",
|
||||
ContextId: "ctx",
|
||||
PolicyProfileHash: "hash",
|
||||
RequestedAt: clock.GetUtcNow(),
|
||||
Priority: "normal",
|
||||
BatchItems: new[] { new OrchestratorJobItem("pkg:a", "ADV-1") },
|
||||
Callbacks: null,
|
||||
TraceRef: "trace",
|
||||
Status: "completed",
|
||||
DeterminismHash: "hash",
|
||||
CompletedAt: clock.GetUtcNow(),
|
||||
ResultHash: "res");
|
||||
|
||||
await jobStore.SaveAsync(job);
|
||||
await resultStore.SaveAsync(new WorkerRunResult(
|
||||
job.JobId,
|
||||
"worker",
|
||||
clock.GetUtcNow(),
|
||||
clock.GetUtcNow(),
|
||||
new[] { new WorkerResultItem("pkg:a", "ADV-1", "violation", "trace-ref") },
|
||||
"hash"));
|
||||
|
||||
await ledger.BuildAsync(new LedgerExportRequest("acme"));
|
||||
|
||||
var snapshot = await service.CreateAsync(new SnapshotRequest("acme", "overlay-1"));
|
||||
|
||||
Assert.Equal("acme", snapshot.TenantId);
|
||||
Assert.Equal("overlay-1", snapshot.OverlayHash);
|
||||
Assert.Single(snapshot.Records);
|
||||
Assert.Contains("violation", snapshot.StatusCounts.Keys);
|
||||
|
||||
var list = await service.ListAsync("acme");
|
||||
Assert.Single(list.Items);
|
||||
}
|
||||
}
|
||||
@@ -11,4 +11,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../StellaOps.Policy.Engine/StellaOps.Policy.Engine.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using StellaOps.Policy.Engine.TrustWeighting;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class TrustWeightingServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Get_ReturnsDefaultsWithHash()
|
||||
{
|
||||
var service = new TrustWeightingService(TimeProvider.System);
|
||||
|
||||
var profile = service.Get();
|
||||
|
||||
Assert.NotEmpty(profile.Weights);
|
||||
Assert.False(string.IsNullOrWhiteSpace(profile.ProfileHash));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Set_NormalizesOrderingAndScale()
|
||||
{
|
||||
var service = new TrustWeightingService(TimeProvider.System);
|
||||
var now = TimeProvider.System.GetUtcNow().ToString("O");
|
||||
|
||||
var profile = service.Set(new[]
|
||||
{
|
||||
new TrustWeightingEntry("Scanner", 1.2345m, " hi ", now),
|
||||
new TrustWeightingEntry("cartographer", 0.9999m, null, now)
|
||||
});
|
||||
|
||||
Assert.Equal(2, profile.Weights.Count);
|
||||
Assert.Equal("cartographer", profile.Weights[0].Source);
|
||||
Assert.Equal(0.999m, profile.Weights[0].Weight);
|
||||
Assert.Equal(1.234m, profile.Weights[1].Weight);
|
||||
Assert.False(string.IsNullOrWhiteSpace(profile.ProfileHash));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Policy.Engine.Ledger;
|
||||
using StellaOps.Policy.Engine.Orchestration;
|
||||
using StellaOps.Policy.Engine.Snapshots;
|
||||
using StellaOps.Policy.Engine.TrustWeighting;
|
||||
using StellaOps.Policy.Engine.Violations;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests;
|
||||
|
||||
public sealed class ViolationServicesTests
|
||||
{
|
||||
private static (ViolationEventService events, SeverityFusionService fusion, ConflictHandlingService conflicts, string snapshotId) BuildPipeline()
|
||||
{
|
||||
var clock = new FakeTimeProvider(DateTimeOffset.Parse("2025-11-24T17: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 job = new OrchestratorJob(
|
||||
JobId: "job-viol",
|
||||
TenantId: "acme",
|
||||
ContextId: "ctx",
|
||||
PolicyProfileHash: "hash",
|
||||
RequestedAt: clock.GetUtcNow(),
|
||||
Priority: "normal",
|
||||
BatchItems: new[] { new OrchestratorJobItem("pkg:a", "ADV-1"), new OrchestratorJobItem("pkg:b", "ADV-2") },
|
||||
Callbacks: null,
|
||||
TraceRef: "trace",
|
||||
Status: "completed",
|
||||
DeterminismHash: "hash",
|
||||
CompletedAt: clock.GetUtcNow(),
|
||||
ResultHash: "res");
|
||||
|
||||
jobStore.SaveAsync(job).GetAwaiter().GetResult();
|
||||
|
||||
resultStore.SaveAsync(new WorkerRunResult(
|
||||
job.JobId,
|
||||
"worker",
|
||||
clock.GetUtcNow(),
|
||||
clock.GetUtcNow(),
|
||||
new[]
|
||||
{
|
||||
new WorkerResultItem("pkg:a", "ADV-1", "violation", "trace-a"),
|
||||
new WorkerResultItem("pkg:b", "ADV-2", "warn", "trace-b")
|
||||
},
|
||||
"hash")).GetAwaiter().GetResult();
|
||||
|
||||
ledger.BuildAsync(new LedgerExportRequest("acme")).GetAwaiter().GetResult();
|
||||
var snapshot = snapshotService.CreateAsync(new SnapshotRequest("acme", "overlay-1")).GetAwaiter().GetResult();
|
||||
|
||||
return (eventService, fusionService, conflictService, snapshot.SnapshotId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmitAsync_BuildsEvents()
|
||||
{
|
||||
var (eventService, _, _, snapshotId) = BuildPipeline();
|
||||
|
||||
var events = await eventService.EmitAsync(new ViolationEventRequest(snapshotId));
|
||||
|
||||
Assert.Equal(2, events.Count);
|
||||
Assert.All(events, e => Assert.Equal("policy.violation.detected", e.ViolationCode));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FuseAsync_ProducesWeightedSeverity()
|
||||
{
|
||||
var (eventService, fusionService, _, snapshotId) = BuildPipeline();
|
||||
|
||||
await eventService.EmitAsync(new ViolationEventRequest(snapshotId));
|
||||
var fused = await fusionService.FuseAsync(snapshotId);
|
||||
|
||||
Assert.Equal(2, fused.Count);
|
||||
Assert.All(fused, f => Assert.False(string.IsNullOrWhiteSpace(f.SeverityFused)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConflictsAsync_DetectsDivergentSeverities()
|
||||
{
|
||||
var (eventService, fusionService, conflictService, snapshotId) = BuildPipeline();
|
||||
await eventService.EmitAsync(new ViolationEventRequest(snapshotId));
|
||||
var fused = await fusionService.FuseAsync(snapshotId);
|
||||
|
||||
var conflicts = await conflictService.ComputeAsync(snapshotId, fused);
|
||||
|
||||
// Only triggers when severities differ; in this stub they do, so expect at least one.
|
||||
Assert.NotNull(conflicts);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user