/** * VerdictDigest Tests * Sprint: SPRINT_9100_0002_0002 (Per-Node VerdictDigest) * Tasks: VDIGEST-9100-016 through VDIGEST-9100-021 */ using System.Text.Json; using Xunit; using StellaOps.TestKit; namespace StellaOps.Resolver.Tests; public class VerdictDigestTests { [Trait("Category", TestCategories.Unit)] [Fact] public void VerdictDigest_IsDeterministic() { // VDIGEST-9100-016: Same verdict → same digest var nodeId = NodeId.From("package", "test"); var evidence = JsonDocument.Parse("{\"reason\": \"test\"}").RootElement; var verdict1 = Verdict.Create(nodeId, VerdictStatus.Pass, evidence, "Test reason", 0); var verdict2 = Verdict.Create(nodeId, VerdictStatus.Pass, evidence, "Test reason", 0); Assert.Equal(verdict1.VerdictDigest, verdict2.VerdictDigest); } [Trait("Category", TestCategories.Unit)] [Fact] public void VerdictDigest_ChangesWhenStatusChanges() { // VDIGEST-9100-017: Digest changes with status var nodeId = NodeId.From("package", "test"); var evidence = JsonDocument.Parse("{\"reason\": \"test\"}").RootElement; var passVerdict = Verdict.Create(nodeId, VerdictStatus.Pass, evidence); var failVerdict = Verdict.Create(nodeId, VerdictStatus.Fail, evidence); Assert.NotEqual(passVerdict.VerdictDigest, failVerdict.VerdictDigest); } [Trait("Category", TestCategories.Unit)] [Fact] public void VerdictDigest_ChangesWhenEvidenceChanges() { // VDIGEST-9100-018: Digest changes with evidence var nodeId = NodeId.From("package", "test"); var evidence1 = JsonDocument.Parse("{\"reason\": \"reason1\"}").RootElement; var evidence2 = JsonDocument.Parse("{\"reason\": \"reason2\"}").RootElement; var verdict1 = Verdict.Create(nodeId, VerdictStatus.Pass, evidence1); var verdict2 = Verdict.Create(nodeId, VerdictStatus.Pass, evidence2); Assert.NotEqual(verdict1.VerdictDigest, verdict2.VerdictDigest); } [Trait("Category", TestCategories.Unit)] [Fact] public void VerdictDelta_CorrectlyIdentifiesChangedVerdicts() { // VDIGEST-9100-019: Delta detection identifies changed verdicts var nodeId1 = NodeId.From("package", "a"); var nodeId2 = NodeId.From("package", "b"); var oldVerdicts = new[] { Verdict.Create(nodeId1, VerdictStatus.Pass, null), Verdict.Create(nodeId2, VerdictStatus.Pass, null) }; var newVerdicts = new[] { Verdict.Create(nodeId1, VerdictStatus.Pass, null), Verdict.Create(nodeId2, VerdictStatus.Fail, null) // Changed }; var oldResult = new ResolutionResult { TraversalSequence = [nodeId1, nodeId2], Verdicts = [.. oldVerdicts], GraphDigest = "abc", PolicyDigest = "def", FinalDigest = "old" }; var newResult = new ResolutionResult { TraversalSequence = [nodeId1, nodeId2], Verdicts = [.. newVerdicts], GraphDigest = "abc", PolicyDigest = "def", FinalDigest = "new" }; var detector = new DefaultVerdictDeltaDetector(); var delta = detector.Detect(oldResult, newResult); Assert.Single(delta.ChangedVerdicts); Assert.Equal(nodeId2, delta.ChangedVerdicts[0].Old.Node); } [Trait("Category", TestCategories.Unit)] [Fact] public void VerdictDelta_HandlesAddedRemovedNodes() { // VDIGEST-9100-020: Delta handles added/removed nodes var nodeId1 = NodeId.From("package", "a"); var nodeId2 = NodeId.From("package", "b"); var nodeId3 = NodeId.From("package", "c"); var oldResult = new ResolutionResult { TraversalSequence = [nodeId1, nodeId2], Verdicts = [ Verdict.Create(nodeId1, VerdictStatus.Pass, null), Verdict.Create(nodeId2, VerdictStatus.Pass, null) ], GraphDigest = "abc", PolicyDigest = "def", FinalDigest = "old" }; var newResult = new ResolutionResult { TraversalSequence = [nodeId1, nodeId3], Verdicts = [ Verdict.Create(nodeId1, VerdictStatus.Pass, null), Verdict.Create(nodeId3, VerdictStatus.Pass, null) ], GraphDigest = "abc", PolicyDigest = "def", FinalDigest = "new" }; var detector = new DefaultVerdictDeltaDetector(); var delta = detector.Detect(oldResult, newResult); Assert.Single(delta.AddedVerdicts); Assert.Single(delta.RemovedVerdicts); Assert.Equal(nodeId3, delta.AddedVerdicts[0].Node); Assert.Equal(nodeId2, delta.RemovedVerdicts[0].Node); } [Trait("Category", TestCategories.Unit)] [Fact] public void VerdictDigest_ExcludesItselfFromComputation() { // VDIGEST-9100-021: Property test - no recursion var nodeId = NodeId.From("package", "test"); // Create two verdicts with the same input data var verdict1 = Verdict.Create(nodeId, VerdictStatus.Pass, null, "reason", 0); var verdict2 = Verdict.Create(nodeId, VerdictStatus.Pass, null, "reason", 0); // Digests should be identical and stable (not including themselves) Assert.Equal(verdict1.VerdictDigest, verdict2.VerdictDigest); Assert.Equal(64, verdict1.VerdictDigest.Length); // Valid SHA256 } }