using Xunit; using Microsoft.Extensions.Time.Testing; using StellaOps.Policy.Engine.Ledger; using StellaOps.Policy.Engine.Orchestration; using StellaOps.TestKit; namespace StellaOps.Policy.Engine.Tests; public sealed class LedgerExportServiceTests { [Trait("Category", TestCategories.Unit)] [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); } }