< Summary

Information
Class: AsiBackbone.Testing.Contracts.AsiBackboneDecisionContract
Assembly: AsiBackbone.Testing
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Testing/Contracts/AsiBackboneDecisionContract.cs
Line coverage
100%
Covered lines: 58
Uncovered lines: 0
Coverable lines: 58
Total lines: 183
Line coverage: 100%
Branch coverage
100%
Covered branches: 50
Total branches: 50
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
VerifySafeDecision(...)100%1414100%
VerifyTelemetryFromContext(...)100%11100%
VerifyInvalidCapabilityGrantDoesNotAllow(...)100%22100%
VerifyAuditResidue(...)100%1010100%
VerifySupportedOutcome(...)100%1212100%
RequiresReasonCodes(...)100%22100%
VerifyCorrelationTelemetryValue(...)100%44100%
VerifyTelemetryPresence(...)100%44100%
VerifyRequiredString(...)100%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Testing/Contracts/AsiBackboneDecisionContract.cs

#LineLine coverage
 1using AsiBackbone.Core.Audit;
 2using AsiBackbone.Core.Constraints;
 3using AsiBackbone.Core.Decisions;
 4using AsiBackbone.Core.Results;
 5
 6namespace AsiBackbone.Testing.Contracts;
 7
 8/// <summary>
 9/// Provides reusable safe-collapse assertions for governance decisions and audit residue.
 10/// </summary>
 11public static class AsiBackboneDecisionContract
 12{
 13    /// <summary>
 14    /// Verifies that a governance decision is present and carries the minimum shape required for safe downstream handli
 15    /// </summary>
 16    /// <param name="decision">The decision returned by an implementation under test.</param>
 17    /// <param name="contractName">The human-readable contract name used in failure messages.</param>
 18    /// <returns>The verified decision.</returns>
 19    public static GovernanceDecision VerifySafeDecision(
 20        GovernanceDecision? decision,
 21        string contractName = "Governance decision")
 22    {
 5423        if (decision is null)
 24        {
 425            throw new AsiBackboneContractViolationException($"{contractName} must return a decision and must never retur
 26        }
 27
 5028        VerifySupportedOutcome(decision, contractName);
 29
 4830        if (RequiresReasonCodes(decision) && decision.Reasons.Count == 0)
 31        {
 1032            throw new AsiBackboneContractViolationException($"{contractName} outcome '{decision.Outcome}' must include a
 33        }
 34
 10835        for (int index = 0; index < decision.Reasons.Count; index++)
 36        {
 2237            OperationReason reason = decision.Reasons[index] ?? throw new AsiBackboneContractViolationException($"{contr
 38
 2039            if (string.IsNullOrWhiteSpace(reason.Code))
 40            {
 241                throw new AsiBackboneContractViolationException($"{contractName} contains a reason with an empty code at
 42            }
 43
 1844            if (string.IsNullOrWhiteSpace(reason.Message))
 45            {
 246                throw new AsiBackboneContractViolationException($"{contractName} contains a reason with an empty message
 47            }
 48        }
 49
 3250        return decision;
 51    }
 52
 53    /// <summary>
 54    /// Verifies decision shape, context correlation propagation, and policy telemetry presence when supplied.
 55    /// </summary>
 56    /// <typeparam name="TContext">The framework-neutral evaluation context type.</typeparam>
 57    /// <param name="decision">The decision returned by an implementation under test.</param>
 58    /// <param name="context">The context supplied to the implementation under test.</param>
 59    /// <param name="contractName">The human-readable contract name used in failure messages.</param>
 60    /// <returns>The verified decision.</returns>
 61    public static GovernanceDecision VerifyTelemetryFromContext<TContext>(
 62        GovernanceDecision? decision,
 63        TContext context,
 64        string contractName = "Governance decision")
 65        where TContext : IAsiBackboneConstraintEvaluationContext
 66    {
 1667        ArgumentNullException.ThrowIfNull(context);
 68
 1469        GovernanceDecision verifiedDecision = VerifySafeDecision(decision, contractName);
 1270        VerifyCorrelationTelemetryValue(context.CorrelationId, verifiedDecision.CorrelationId, contractName);
 1071        VerifyTelemetryPresence(context.PolicyVersion, verifiedDecision.PolicyVersion, "policy version", contractName);
 872        VerifyTelemetryPresence(context.PolicyHash, verifiedDecision.PolicyHash, "policy hash", contractName);
 673        return verifiedDecision;
 74    }
 75
 76    /// <summary>
 77    /// Verifies that a known invalid capability-grant scenario does not produce an allow decision.
 78    /// </summary>
 79    /// <param name="decision">The decision returned by the capability validator for a known invalid scenario.</param>
 80    /// <param name="contractName">The human-readable contract name used in failure messages.</param>
 81    /// <returns>The verified decision.</returns>
 82    public static GovernanceDecision VerifyInvalidCapabilityGrantDoesNotAllow(
 83        GovernanceDecision? decision,
 84        string contractName = "Invalid capability grant")
 85    {
 886        GovernanceDecision verifiedDecision = VerifySafeDecision(decision, contractName);
 87
 888        return verifiedDecision.IsAllowed
 889            ? throw new AsiBackboneContractViolationException($"{contractName} must not return Allow for an invalid or u
 890            : verifiedDecision;
 91    }
 92
 93    /// <summary>
 94    /// Verifies that audit residue contains the minimum identity, operation, outcome, and policy telemetry shape.
 95    /// </summary>
 96    /// <param name="residue">The audit residue to verify.</param>
 97    /// <param name="contractName">The human-readable contract name used in failure messages.</param>
 98    /// <returns>The verified audit residue.</returns>
 99    public static IAsiBackboneAuditResidue VerifyAuditResidue(
 100        IAsiBackboneAuditResidue? residue,
 101        string contractName = "Audit residue")
 102    {
 28103        if (residue is null)
 104        {
 2105            throw new AsiBackboneContractViolationException($"{contractName} must not be null.");
 106        }
 107
 26108        VerifyRequiredString(residue.EventId, "event ID", contractName);
 22109        VerifyRequiredString(residue.ActorId, "actor ID", contractName);
 20110        VerifyRequiredString(residue.OperationName, "operation name", contractName);
 18111        VerifyRequiredString(residue.Outcome, "outcome", contractName);
 112
 16113        if (residue.ReasonCodes is null)
 114        {
 2115            throw new AsiBackboneContractViolationException($"{contractName} reason-code collection must not be null.");
 116        }
 117
 32118        for (int index = 0; index < residue.ReasonCodes.Count; index++)
 119        {
 4120            if (string.IsNullOrWhiteSpace(residue.ReasonCodes[index]))
 121            {
 2122                throw new AsiBackboneContractViolationException($"{contractName} contains an empty reason code at index 
 123            }
 124        }
 125
 12126        return residue.Metadata is null
 12127            ? throw new AsiBackboneContractViolationException($"{contractName} metadata collection must not be null.")
 12128            : residue;
 129    }
 130
 131    private static void VerifySupportedOutcome(GovernanceDecision decision, string contractName)
 132    {
 50133        if (decision.Outcome is not GovernanceDecisionOutcome.Allowed
 50134            and not GovernanceDecisionOutcome.Warning
 50135            and not GovernanceDecisionOutcome.Denied
 50136            and not GovernanceDecisionOutcome.Deferred
 50137            and not GovernanceDecisionOutcome.AcknowledgmentRequired
 50138            and not GovernanceDecisionOutcome.EscalationRecommended)
 139        {
 2140            throw new AsiBackboneContractViolationException($"{contractName} returned unsupported outcome '{decision.Out
 141        }
 48142    }
 143
 144    private static bool RequiresReasonCodes(GovernanceDecision decision)
 145    {
 48146        return decision.Outcome is GovernanceDecisionOutcome.Warning
 48147            or GovernanceDecisionOutcome.Denied
 48148            or GovernanceDecisionOutcome.Deferred
 48149            or GovernanceDecisionOutcome.AcknowledgmentRequired
 48150            or GovernanceDecisionOutcome.EscalationRecommended;
 151    }
 152
 153    private static void VerifyCorrelationTelemetryValue(
 154        string? expected,
 155        string? actual,
 156        string contractName)
 157    {
 12158        if (!string.IsNullOrWhiteSpace(expected) && !string.Equals(expected, actual, StringComparison.Ordinal))
 159        {
 2160            throw new AsiBackboneContractViolationException($"{contractName} must preserve the supplied correlation ID w
 161        }
 10162    }
 163
 164    private static void VerifyTelemetryPresence(
 165        string? expected,
 166        string? actual,
 167        string telemetryName,
 168        string contractName)
 169    {
 18170        if (!string.IsNullOrWhiteSpace(expected) && string.IsNullOrWhiteSpace(actual))
 171        {
 4172            throw new AsiBackboneContractViolationException($"{contractName} must include a {telemetryName} when one is 
 173        }
 14174    }
 175
 176    private static void VerifyRequiredString(string? value, string name, string contractName)
 177    {
 86178        if (string.IsNullOrWhiteSpace(value))
 179        {
 10180            throw new AsiBackboneContractViolationException($"{contractName} must include a non-empty {name}.");
 181        }
 76182    }
 183}