using System; using Microsoft.Extensions.Options; using StellaOps.Policy; using StellaOps.PolicyDsl; using StellaOps.Policy.Engine.Options; using StellaOps.Policy.Engine.Services; using Xunit; namespace StellaOps.Policy.Engine.Tests; public sealed class PolicyCompilationServiceTests { private const string SimplePolicy = """ policy "Sample" syntax "stella-dsl@1" { rule block_high priority 10 { when severity.normalized >= "High" then status := "blocked" because "Block high severity findings" } rule warn_medium priority 20 { when severity.normalized >= "Medium" then status := "warn" because "Warn on medium severity findings" } } """; [Fact] public void Compile_ReturnsComplexityReport_WhenWithinLimits() { var service = CreateService(maxComplexityScore: 1000, maxDurationMilliseconds: 1000, simulatedDurationMilliseconds: 12.3); var request = new PolicyCompileRequest(new PolicyDslPayload("stella-dsl@1", SimplePolicy)); var result = service.Compile(request); Assert.True(result.Success); Assert.NotNull(result.Digest); Assert.NotNull(result.Complexity); Assert.True(result.Complexity!.Score > 0); Assert.True(result.Complexity.RuleCount >= 2); Assert.Equal(13, result.DurationMilliseconds); Assert.True(result.Diagnostics.IsDefaultOrEmpty); } [Fact] public void Compile_Fails_WhenComplexityExceedsThreshold() { var service = CreateService(maxComplexityScore: 1, maxDurationMilliseconds: 1000, simulatedDurationMilliseconds: 2); var request = new PolicyCompileRequest(new PolicyDslPayload("stella-dsl@1", SimplePolicy)); var result = service.Compile(request); Assert.False(result.Success); Assert.NotNull(result.Complexity); Assert.Equal(2, result.DurationMilliseconds); var diagnostic = Assert.Single(result.Diagnostics); Assert.Equal(PolicyEngineDiagnosticCodes.CompilationComplexityExceeded, diagnostic.Code); Assert.Equal(PolicyIssueSeverity.Error, diagnostic.Severity); } [Fact] public void Compile_Fails_WhenDurationExceedsThreshold() { var service = CreateService(maxComplexityScore: 1000, maxDurationMilliseconds: 1, simulatedDurationMilliseconds: 5.2); var request = new PolicyCompileRequest(new PolicyDslPayload("stella-dsl@1", SimplePolicy)); var result = service.Compile(request); Assert.False(result.Success); Assert.NotNull(result.Complexity); Assert.Equal(6, result.DurationMilliseconds); var diagnostic = Assert.Single(result.Diagnostics); Assert.Equal(PolicyEngineDiagnosticCodes.CompilationComplexityExceeded, diagnostic.Code); } private static PolicyCompilationService CreateService(double maxComplexityScore, int maxDurationMilliseconds, double simulatedDurationMilliseconds) { var compiler = new PolicyCompiler(); var analyzer = new PolicyComplexityAnalyzer(); var options = new PolicyEngineOptions(); options.Compilation.MaxComplexityScore = maxComplexityScore; options.Compilation.MaxDurationMilliseconds = maxDurationMilliseconds; var optionsMonitor = new StaticOptionsMonitor(options); var timeProvider = new FakeTimeProvider(simulatedDurationMilliseconds); return new PolicyCompilationService(compiler, analyzer, optionsMonitor, timeProvider); } private sealed class StaticOptionsMonitor : IOptionsMonitor where T : class { public StaticOptionsMonitor(T value) { CurrentValue = value ?? throw new ArgumentNullException(nameof(value)); } public T CurrentValue { get; } public T Get(string? name) => CurrentValue; public IDisposable OnChange(Action listener) => Disposable.Instance; private sealed class Disposable : IDisposable { public static readonly Disposable Instance = new(); public void Dispose() { } } } private sealed class FakeTimeProvider : TimeProvider { private readonly long elapsedCounts; private readonly long frequency = 1_000_000; private bool firstCall = true; public FakeTimeProvider(double milliseconds) { elapsedCounts = (long)Math.Round(milliseconds * frequency / 1000d); } public override long GetTimestamp() { if (firstCall) { firstCall = false; return 0; } return elapsedCounts; } public override long TimestampFrequency => frequency; } }