161 lines
5.5 KiB
C#
161 lines
5.5 KiB
C#
/**
|
|
* 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
|
|
}
|
|
}
|