using FluentAssertions; using StellaOps.Policy.Deltas; using Xunit; namespace StellaOps.Policy.Tests.Deltas; public sealed class DeltaVerdictTests { [Fact] public void Build_WithNoDrivers_ReturnsPass() { var verdict = new DeltaVerdictBuilder() .Build("delta:sha256:test"); verdict.Status.Should().Be(DeltaVerdictStatus.Pass); verdict.Explanation.Should().Contain("No blocking"); } [Fact] public void Build_WithWarningDriver_ReturnsWarn() { var driver = new DeltaDriver { Type = "new-package", Severity = DeltaDriverSeverity.Low, Description = "New package added" }; var verdict = new DeltaVerdictBuilder() .AddWarningDriver(driver) .Build("delta:sha256:test"); verdict.Status.Should().Be(DeltaVerdictStatus.Warn); verdict.WarningDrivers.Should().HaveCount(1); } [Fact] public void Build_WithBlockingDriver_ReturnsFail() { var driver = new DeltaDriver { Type = "new-reachable-cve", Severity = DeltaDriverSeverity.Critical, Description = "Critical CVE is now reachable", CveId = "CVE-2024-001" }; var verdict = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .Build("delta:sha256:test"); verdict.Status.Should().Be(DeltaVerdictStatus.Fail); verdict.BlockingDrivers.Should().HaveCount(1); verdict.RecommendedGate.Should().Be(DeltaGateLevel.G4); } [Fact] public void Build_WithBlockingDriverAndException_ReturnsPassWithExceptions() { var driver = new DeltaDriver { Type = "new-reachable-cve", Severity = DeltaDriverSeverity.Critical, Description = "Critical CVE is now reachable", CveId = "CVE-2024-001" }; var verdict = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .AddException("exception-123") .Build("delta:sha256:test"); verdict.Status.Should().Be(DeltaVerdictStatus.PassWithExceptions); verdict.AppliedExceptions.Should().Contain("exception-123"); } [Fact] public void Build_CriticalDriver_EscalatesToG4() { var driver = new DeltaDriver { Type = "critical-issue", Severity = DeltaDriverSeverity.Critical, Description = "Critical issue" }; var verdict = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .Build("delta:sha256:test"); verdict.RecommendedGate.Should().Be(DeltaGateLevel.G4); } [Fact] public void Build_HighDriver_EscalatesToG3() { var driver = new DeltaDriver { Type = "high-issue", Severity = DeltaDriverSeverity.High, Description = "High severity issue" }; var verdict = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .Build("delta:sha256:test"); verdict.RecommendedGate.Should().Be(DeltaGateLevel.G3); } [Fact] public void Build_WithRiskPoints_SetsCorrectValue() { var verdict = new DeltaVerdictBuilder() .WithRiskPoints(25) .Build("delta:sha256:test"); verdict.RiskPoints.Should().Be(25); } [Fact] public void Build_WithRecommendations_IncludesAll() { var verdict = new DeltaVerdictBuilder() .AddRecommendation("Review CVE-2024-001") .AddRecommendation("Update dependency") .Build("delta:sha256:test"); verdict.Recommendations.Should().HaveCount(2); verdict.Recommendations.Should().Contain("Review CVE-2024-001"); } [Fact] public void Build_WithCustomExplanation_UsesProvided() { var verdict = new DeltaVerdictBuilder() .WithExplanation("Custom explanation") .Build("delta:sha256:test"); verdict.Explanation.Should().Be("Custom explanation"); } [Fact] public void Build_GeneratesDeterministicVerdictId_ForIdenticalInputs() { var verdict1 = new DeltaVerdictBuilder().Build("delta:sha256:test"); var verdict2 = new DeltaVerdictBuilder().Build("delta:sha256:test"); // Content-addressed IDs are deterministic verdict1.VerdictId.Should().StartWith("verdict:sha256:"); verdict1.VerdictId.Should().Be(verdict2.VerdictId, "identical inputs must produce identical VerdictId"); } [Fact] public void Build_GeneratesDifferentVerdictId_ForDifferentInputs() { var verdict1 = new DeltaVerdictBuilder().Build("delta:sha256:test1"); var verdict2 = new DeltaVerdictBuilder().Build("delta:sha256:test2"); verdict1.VerdictId.Should().StartWith("verdict:sha256:"); verdict2.VerdictId.Should().StartWith("verdict:sha256:"); verdict1.VerdictId.Should().NotBe(verdict2.VerdictId, "different inputs must produce different VerdictId"); } [Theory] [InlineData(10)] public void Build_IsIdempotent_AcrossMultipleIterations(int iterations) { var driver = new DeltaDriver { Type = "new-reachable-cve", Severity = DeltaDriverSeverity.High, Description = "High severity CVE", CveId = "CVE-2024-999" }; var expected = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .Build("delta:sha256:determinism-test") .VerdictId; for (int i = 0; i < iterations; i++) { var verdict = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .Build("delta:sha256:determinism-test"); verdict.VerdictId.Should().Be(expected, $"iteration {i}: VerdictId must be stable"); } } [Fact] public void Build_VerdictIdIsDeterministic_RegardlessOfDriverAddOrder() { var driver1 = new DeltaDriver { Type = "aaa-first", Severity = DeltaDriverSeverity.Medium, Description = "First driver" }; var driver2 = new DeltaDriver { Type = "zzz-last", Severity = DeltaDriverSeverity.Low, Description = "Second driver" }; // Add in one order var verdict1 = new DeltaVerdictBuilder() .AddWarningDriver(driver1) .AddWarningDriver(driver2) .Build("delta:sha256:order-test"); // Add in reverse order var verdict2 = new DeltaVerdictBuilder() .AddWarningDriver(driver2) .AddWarningDriver(driver1) .Build("delta:sha256:order-test"); // Content-addressed IDs should be same because drivers are sorted by Type verdict1.VerdictId.Should().Be(verdict2.VerdictId, "drivers are sorted by Type before hashing"); } [Fact] public void VerdictIdGenerator_ComputeFromVerdict_MatchesOriginal() { var driver = new DeltaDriver { Type = "recompute-test", Severity = DeltaDriverSeverity.Critical, Description = "Test driver" }; var verdict = new DeltaVerdictBuilder() .AddBlockingDriver(driver) .AddException("EXCEPTION-001") .Build("delta:sha256:recompute-test"); var generator = new VerdictIdGenerator(); var recomputed = generator.ComputeVerdictId(verdict); recomputed.Should().Be(verdict.VerdictId, "recomputed VerdictId must match original"); } }