Files
git.stella-ops.org/src/Policy/__Tests/StellaOps.Policy.Engine.Tests/Adapters/ExceptionEffectRegistryTests.cs
StellaOps Bot ba2f015184 Implement Exception Effect Registry and Evaluation Service
- Added IExceptionEffectRegistry interface and its implementation ExceptionEffectRegistry to manage exception effects based on type and reason.
- Created ExceptionAwareEvaluationService for evaluating policies with automatic exception loading from the repository.
- Developed unit tests for ExceptionAdapter and ExceptionEffectRegistry to ensure correct behavior and mappings of exceptions and effects.
- Enhanced exception loading logic to filter expired and non-active exceptions, and to respect maximum exceptions limit.
- Implemented caching mechanism in ExceptionAdapter to optimize repeated exception loading.
2025-12-21 08:29:51 +02:00

224 lines
7.4 KiB
C#

using FluentAssertions;
using StellaOps.Policy.Engine.Adapters;
using StellaOps.Policy.Exceptions.Models;
using Xunit;
namespace StellaOps.Policy.Engine.Tests.Adapters;
/// <summary>
/// Unit tests for ExceptionEffectRegistry.
/// </summary>
public sealed class ExceptionEffectRegistryTests
{
private readonly IExceptionEffectRegistry _registry;
public ExceptionEffectRegistryTests()
{
_registry = new ExceptionEffectRegistry();
}
[Theory]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.FalsePositive, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.AcceptedRisk, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.CompensatingControl, PolicyExceptionEffectType.RequireControl)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.TestOnly, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.VendorNotAffected, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.ScheduledFix, PolicyExceptionEffectType.Defer)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.RuntimeMitigation, PolicyExceptionEffectType.Downgrade)]
[InlineData(ExceptionType.Vulnerability, ExceptionReason.NetworkIsolation, PolicyExceptionEffectType.Downgrade)]
public void GetEffect_ReturnsCorrectEffect_ForVulnerabilityType(
ExceptionType type,
ExceptionReason reason,
PolicyExceptionEffectType expectedEffect)
{
// Act
var effect = _registry.GetEffect(type, reason);
// Assert
effect.Should().NotBeNull();
effect.Effect.Should().Be(expectedEffect);
}
[Theory]
[InlineData(ExceptionType.Policy, ExceptionReason.FalsePositive, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Policy, ExceptionReason.AcceptedRisk, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Policy, ExceptionReason.CompensatingControl, PolicyExceptionEffectType.RequireControl)]
[InlineData(ExceptionType.Policy, ExceptionReason.ScheduledFix, PolicyExceptionEffectType.Defer)]
public void GetEffect_ReturnsCorrectEffect_ForPolicyType(
ExceptionType type,
ExceptionReason reason,
PolicyExceptionEffectType expectedEffect)
{
// Act
var effect = _registry.GetEffect(type, reason);
// Assert
effect.Should().NotBeNull();
effect.Effect.Should().Be(expectedEffect);
}
[Theory]
[InlineData(ExceptionType.Unknown, ExceptionReason.FalsePositive, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Unknown, ExceptionReason.ScheduledFix, PolicyExceptionEffectType.Defer)]
public void GetEffect_ReturnsCorrectEffect_ForUnknownType(
ExceptionType type,
ExceptionReason reason,
PolicyExceptionEffectType expectedEffect)
{
// Act
var effect = _registry.GetEffect(type, reason);
// Assert
effect.Should().NotBeNull();
effect.Effect.Should().Be(expectedEffect);
}
[Theory]
[InlineData(ExceptionType.Component, ExceptionReason.DeprecationInProgress, PolicyExceptionEffectType.Suppress)]
[InlineData(ExceptionType.Component, ExceptionReason.Other, PolicyExceptionEffectType.Suppress)] // License waiver
public void GetEffect_ReturnsCorrectEffect_ForComponentType(
ExceptionType type,
ExceptionReason reason,
PolicyExceptionEffectType expectedEffect)
{
// Act
var effect = _registry.GetEffect(type, reason);
// Assert
effect.Should().NotBeNull();
effect.Effect.Should().Be(expectedEffect);
}
[Fact]
public void GetEffect_ReturnsDefaultDeferral_ForUnmappedCombination()
{
// Note: All combinations are mapped, so we test a hypothetical case
// by checking that the registry handles all known combinations
var allTypes = Enum.GetValues<ExceptionType>();
var allReasons = Enum.GetValues<ExceptionReason>();
foreach (var type in allTypes)
{
foreach (var reason in allReasons)
{
// Act
var effect = _registry.GetEffect(type, reason);
// Assert - should never be null
effect.Should().NotBeNull();
effect.Id.Should().NotBeNullOrEmpty();
effect.Effect.Should().BeOneOf(
PolicyExceptionEffectType.Suppress,
PolicyExceptionEffectType.Defer,
PolicyExceptionEffectType.Downgrade,
PolicyExceptionEffectType.RequireControl);
}
}
}
[Fact]
public void GetAllEffects_ReturnsDistinctEffects()
{
// Act
var allEffects = _registry.GetAllEffects();
// Assert
allEffects.Should().NotBeEmpty();
allEffects.Should().OnlyHaveUniqueItems(e => e.Id);
}
[Fact]
public void GetEffectById_ReturnsEffect_WhenExists()
{
// Act
var effect = _registry.GetEffectById("suppress");
// Assert
effect.Should().NotBeNull();
effect!.Id.Should().Be("suppress");
effect.Effect.Should().Be(PolicyExceptionEffectType.Suppress);
}
[Fact]
public void GetEffectById_ReturnsNull_WhenNotExists()
{
// Act
var effect = _registry.GetEffectById("non-existent-effect-id");
// Assert
effect.Should().BeNull();
}
[Fact]
public void GetEffectById_IsCaseInsensitive()
{
// Act
var effect1 = _registry.GetEffectById("SUPPRESS");
var effect2 = _registry.GetEffectById("suppress");
var effect3 = _registry.GetEffectById("Suppress");
// Assert
effect1.Should().Be(effect2);
effect2.Should().Be(effect3);
}
[Fact]
public void Effects_HaveValidProperties()
{
// Act
var allEffects = _registry.GetAllEffects();
// Assert
foreach (var effect in allEffects)
{
effect.Id.Should().NotBeNullOrWhiteSpace();
effect.Name.Should().NotBeNullOrWhiteSpace();
effect.Description.Should().NotBeNullOrWhiteSpace();
effect.MaxDurationDays.Should().BeGreaterThan(0);
}
}
[Fact]
public void DowngradeEffects_HaveValidSeverity()
{
// Act
var downgradeEffects = _registry.GetAllEffects()
.Where(e => e.Effect == PolicyExceptionEffectType.Downgrade);
// Assert
foreach (var effect in downgradeEffects)
{
effect.DowngradeSeverity.Should().NotBeNull();
}
}
[Fact]
public void RequireControlEffects_HaveControlId()
{
// Act
var requireControlEffects = _registry.GetAllEffects()
.Where(e => e.Effect == PolicyExceptionEffectType.RequireControl);
// Assert
foreach (var effect in requireControlEffects)
{
effect.RequiredControlId.Should().NotBeNullOrWhiteSpace();
}
}
[Fact]
public void SuppressEffects_DoNotRequireControl()
{
// Act
var suppressEffects = _registry.GetAllEffects()
.Where(e => e.Effect == PolicyExceptionEffectType.Suppress);
// Assert
foreach (var effect in suppressEffects)
{
// Suppress effects should not require controls
effect.RequiredControlId.Should().BeNull();
}
}
}