using System.Collections.Immutable; using System.Text.Json; using StellaOps.Scheduler.Models; namespace StellaOps.Scheduler.Models.Tests; public sealed class PolicyRunModelsTests { [Fact] public void PolicyRunInputs_NormalizesEnvironmentKeys() { var inputs = new PolicyRunInputs( sbomSet: new[] { "sbom:two", "sbom:one" }, env: new[] { new KeyValuePair("Sealed", true), new KeyValuePair("Exposure", "internet"), new KeyValuePair("region", JsonSerializer.SerializeToElement("global")) }, captureExplain: true); Assert.Equal(new[] { "sbom:one", "sbom:two" }, inputs.SbomSet); Assert.True(inputs.CaptureExplain); Assert.Equal(3, inputs.Environment.Count); Assert.True(inputs.Environment.ContainsKey("sealed")); Assert.Equal(JsonValueKind.True, inputs.Environment["sealed"].ValueKind); Assert.Equal("internet", inputs.Environment["exposure"].GetString()); Assert.Equal("global", inputs.Environment["region"].GetString()); } [Fact] public void PolicySimulationWebhookPayloadFactory_ComputesSucceeded() { var now = DateTimeOffset.UtcNow; var job = CreateJob(PolicyRunJobStatus.Completed, now); var status = PolicyRunStatusFactory.Create(job, now); var payload = PolicySimulationWebhookPayloadFactory.Create(status, now); Assert.Equal(succeeded, payload.Result); Assert.Equal(status, payload.Simulation); Assert.Null(payload.Reason); Assert.NotNull(payload.LatencySeconds); } [Fact] public void PolicySimulationWebhookPayloadFactory_ComputesFailureReason() { var now = DateTimeOffset.UtcNow; var job = CreateJob(PolicyRunJobStatus.Failed, now) with { LastError = timeout }; var status = PolicyRunStatusFactory.Create(job, now); var payload = PolicySimulationWebhookPayloadFactory.Create(status, now); Assert.Equal(failed, payload.Result); Assert.Equal(timeout, payload.Reason); } private static PolicyRunJob CreateJob(PolicyRunJobStatus status, DateTimeOffset timestamp) { return new PolicyRunJob( SchemaVersion: SchedulerSchemaVersions.PolicyRunJob, Id: job, TenantId: tenant, PolicyId: policy, PolicyVersion: 1, Mode: PolicyRunMode.Simulate, Priority: PolicyRunPriority.Normal, PriorityRank: 0, RunId: run, RequestedBy: tester, CorrelationId: corr, Metadata: null, Inputs: PolicyRunInputs.Empty, QueuedAt: timestamp, Status: status, AttemptCount: 1, LastAttemptAt: timestamp, LastError: status == PolicyRunJobStatus.Failed ? error : null, CreatedAt: timestamp, UpdatedAt: timestamp, AvailableAt: timestamp, SubmittedAt: timestamp, CompletedAt: status == PolicyRunJobStatus.Completed ? timestamp : null, LeaseOwner: null, LeaseExpiresAt: null, CancellationRequested: status == PolicyRunJobStatus.Cancelled, CancellationRequestedAt: null, CancellationReason: null, CancelledAt: status == PolicyRunJobStatus.Cancelled ? timestamp : null); } [Fact] public void PolicyRunStatus_ThrowsOnNegativeAttempts() { Assert.Throws(() => new PolicyRunStatus( runId: "run:test", tenantId: "tenant-alpha", policyId: "P-1", policyVersion: 1, mode: PolicyRunMode.Full, status: PolicyRunExecutionStatus.Queued, priority: PolicyRunPriority.Normal, queuedAt: DateTimeOffset.UtcNow, attempts: -1)); } [Fact] public void PolicyDiffSummary_NormalizesSeverityKeys() { var summary = new PolicyDiffSummary( added: 1, removed: 2, unchanged: 3, bySeverity: new[] { new KeyValuePair("critical", new PolicyDiffSeverityDelta(1, 0)), new KeyValuePair("HIGH", new PolicyDiffSeverityDelta(0, 1)) }); Assert.True(summary.BySeverity.ContainsKey("Critical")); Assert.True(summary.BySeverity.ContainsKey("High")); } [Fact] public void PolicyExplainTrace_LowercasesMetadataKeys() { var trace = new PolicyExplainTrace( findingId: "finding:alpha", policyId: "P-1", policyVersion: 1, tenantId: "tenant-alpha", runId: "run:test", verdict: new PolicyExplainVerdict(PolicyVerdictStatus.Passed, SeverityRank.Low, quiet: false, score: 0, rationale: "ok"), evaluatedAt: DateTimeOffset.UtcNow, metadata: ImmutableSortedDictionary.CreateRange(new[] { new KeyValuePair("TraceId", "trace-1"), new KeyValuePair("ComponentPurl", "pkg:npm/a@1.0.0") })); Assert.Equal("trace-1", trace.Metadata["traceid"]); Assert.Equal("pkg:npm/a@1.0.0", trace.Metadata["componentpurl"]); } }