Files
git.stella-ops.org/src/__Libraries/StellaOps.Resolver/ResolutionVerifier.cs
StellaOps Bot b9f71fc7e9 sprints work
2025-12-24 21:46:08 +02:00

126 lines
4.0 KiB
C#

/**
* 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;
/// <summary>
/// Result of verifying two resolution results.
/// </summary>
/// <param name="Match">True if FinalDigests match.</param>
/// <param name="ExpectedDigest">Expected FinalDigest.</param>
/// <param name="ActualDigest">Actual FinalDigest.</param>
/// <param name="Differences">List of differences if not matching.</param>
public sealed record VerificationResult(
bool Match,
string ExpectedDigest,
string ActualDigest,
ImmutableArray<string> Differences)
{
public static VerificationResult Success(string digest) => new(
true,
digest,
digest,
ImmutableArray<string>.Empty);
}
/// <summary>
/// Interface for verifying resolution results.
/// </summary>
public interface IResolutionVerifier
{
/// <summary>
/// Verifies that actual matches expected.
/// </summary>
VerificationResult Verify(ResolutionResult expected, ResolutionResult actual);
/// <summary>
/// Verifies that actual matches expected digest.
/// </summary>
VerificationResult Verify(string expectedDigest, ResolutionResult actual);
}
/// <summary>
/// Default resolution verifier.
/// </summary>
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<string>();
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]}..."));
}
}