/**
* Resolution Verification
* Sprint: SPRINT_9100_0002_0001 (FinalDigest Implementation)
* Tasks: DIGEST-9100-011 through DIGEST-9100-014
*
* Provides verification of resolution results.
*/
using System.Collections.Immutable;
namespace StellaOps.Resolver;
///
/// Result of verifying two resolution results.
///
/// True if FinalDigests match.
/// Expected FinalDigest.
/// Actual FinalDigest.
/// List of differences if not matching.
public sealed record VerificationResult(
bool Match,
string ExpectedDigest,
string ActualDigest,
ImmutableArray Differences)
{
public static VerificationResult Success(string digest) => new(
true,
digest,
digest,
ImmutableArray.Empty);
}
///
/// Interface for verifying resolution results.
///
public interface IResolutionVerifier
{
///
/// Verifies that actual matches expected.
///
VerificationResult Verify(ResolutionResult expected, ResolutionResult actual);
///
/// Verifies that actual matches expected digest.
///
VerificationResult Verify(string expectedDigest, ResolutionResult actual);
}
///
/// Default resolution verifier.
///
public sealed class DefaultResolutionVerifier : IResolutionVerifier
{
private readonly IVerdictDeltaDetector _deltaDetector;
public DefaultResolutionVerifier(IVerdictDeltaDetector? deltaDetector = null)
{
_deltaDetector = deltaDetector ?? new DefaultVerdictDeltaDetector();
}
public VerificationResult Verify(ResolutionResult expected, ResolutionResult actual)
{
ArgumentNullException.ThrowIfNull(expected);
ArgumentNullException.ThrowIfNull(actual);
if (expected.FinalDigest == actual.FinalDigest)
{
return VerificationResult.Success(expected.FinalDigest);
}
// Drill down to find differences
var differences = new List();
if (expected.GraphDigest != actual.GraphDigest)
{
differences.Add($"GraphDigest mismatch: expected {expected.GraphDigest[..16]}..., got {actual.GraphDigest[..16]}...");
}
if (expected.PolicyDigest != actual.PolicyDigest)
{
differences.Add($"PolicyDigest mismatch: expected {expected.PolicyDigest[..16]}..., got {actual.PolicyDigest[..16]}...");
}
// Check verdict-level differences
var delta = _deltaDetector.Detect(expected, actual);
if (!delta.IsEmpty)
{
foreach (var (old, @new) in delta.ChangedVerdicts)
{
differences.Add($"Verdict changed for node {old.Node.Value[..16]}...: {old.Status} -> {@new.Status}");
}
foreach (var added in delta.AddedVerdicts)
{
differences.Add($"Verdict added for node {added.Node.Value[..16]}...: {added.Status}");
}
foreach (var removed in delta.RemovedVerdicts)
{
differences.Add($"Verdict removed for node {removed.Node.Value[..16]}...: {removed.Status}");
}
}
return new VerificationResult(
false,
expected.FinalDigest,
actual.FinalDigest,
differences.ToImmutableArray());
}
public VerificationResult Verify(string expectedDigest, ResolutionResult actual)
{
ArgumentException.ThrowIfNullOrWhiteSpace(expectedDigest);
ArgumentNullException.ThrowIfNull(actual);
if (expectedDigest == actual.FinalDigest)
{
return VerificationResult.Success(expectedDigest);
}
return new VerificationResult(
false,
expectedDigest,
actual.FinalDigest,
ImmutableArray.Create($"FinalDigest mismatch: expected {expectedDigest[..16]}..., got {actual.FinalDigest[..16]}..."));
}
}