using Microsoft.Extensions.DependencyInjection; using StellaOps.Policy.Registry.Contracts; using StellaOps.Policy.Registry.Services; using StellaOps.Policy.Registry.Storage; namespace StellaOps.Policy.Registry.Testing; /// /// Test harness for Policy Registry integration testing. /// Implements REGISTRY-API-27-010: Test suites + fixtures. /// public sealed class PolicyRegistryTestHarness : IDisposable { private readonly ServiceProvider _serviceProvider; private readonly TimeProvider _timeProvider; public IPolicyPackStore PackStore { get; } public IVerificationPolicyStore VerificationPolicyStore { get; } public ISnapshotStore SnapshotStore { get; } public IViolationStore ViolationStore { get; } public IOverrideStore OverrideStore { get; } public IPolicyPackCompiler Compiler { get; } public IPolicySimulationService SimulationService { get; } public IBatchSimulationOrchestrator BatchOrchestrator { get; } public IReviewWorkflowService ReviewService { get; } public IPublishPipelineService PublishService { get; } public IPromotionService PromotionService { get; } public PolicyRegistryTestHarness(TimeProvider? timeProvider = null) { _timeProvider = timeProvider ?? TimeProvider.System; var services = new ServiceCollection(); services.AddSingleton(_timeProvider); services.AddPolicyRegistryInMemoryStorage(); _serviceProvider = services.BuildServiceProvider(); PackStore = _serviceProvider.GetRequiredService(); VerificationPolicyStore = _serviceProvider.GetRequiredService(); SnapshotStore = _serviceProvider.GetRequiredService(); ViolationStore = _serviceProvider.GetRequiredService(); OverrideStore = _serviceProvider.GetRequiredService(); Compiler = _serviceProvider.GetRequiredService(); SimulationService = _serviceProvider.GetRequiredService(); BatchOrchestrator = _serviceProvider.GetRequiredService(); ReviewService = _serviceProvider.GetRequiredService(); PublishService = _serviceProvider.GetRequiredService(); PromotionService = _serviceProvider.GetRequiredService(); } /// /// Creates a test tenant ID. /// public static Guid CreateTestTenantId() => Guid.NewGuid(); /// /// Creates a policy pack with test data. /// public async Task CreateTestPackAsync( Guid tenantId, string? name = null, string? version = null, IReadOnlyList? rules = null, CancellationToken cancellationToken = default) { var request = new CreatePolicyPackRequest { Name = name ?? $"test-pack-{Guid.NewGuid():N}", Version = version ?? "1.0.0", Description = "Test policy pack", Rules = rules ?? PolicyRegistryTestFixtures.CreateBasicRules() }; return await PackStore.CreateAsync(tenantId, request, "test-user", cancellationToken); } /// /// Creates and publishes a policy pack through the full workflow. /// public async Task CreateAndPublishPackAsync( Guid tenantId, string? name = null, CancellationToken cancellationToken = default) { // Create pack var pack = await CreateTestPackAsync(tenantId, name, cancellationToken: cancellationToken); // Submit for review var review = await ReviewService.SubmitForReviewAsync(tenantId, pack.PackId, new SubmitReviewRequest { Description = "Test review" }, cancellationToken); // Approve review await ReviewService.ApproveAsync(tenantId, review.ReviewId, new ApproveReviewRequest { ApprovedBy = "test-approver" }, cancellationToken); // Publish return await PublishService.PublishAsync(tenantId, pack.PackId, new PublishPackRequest { PublishedBy = "test-publisher" }, cancellationToken); } /// /// Runs a determinism test to verify consistent outputs. /// public async Task RunDeterminismTestAsync( Guid tenantId, int iterations = 3, CancellationToken cancellationToken = default) { var results = new List(); var pack = await CreateTestPackAsync(tenantId, cancellationToken: cancellationToken); for (int i = 0; i < iterations; i++) { var compilationResult = await Compiler.CompileAsync(tenantId, pack.PackId, cancellationToken); if (compilationResult.Success && compilationResult.Digest is not null) { results.Add(compilationResult.Digest); } } var allSame = results.Distinct().Count() == 1; return new DeterminismTestResult { Passed = allSame && results.Count == iterations, Iterations = iterations, UniqueResults = results.Distinct().Count(), Digests = results }; } public void Dispose() { (_serviceProvider as IDisposable)?.Dispose(); } } /// /// Result of a determinism test. /// public sealed record DeterminismTestResult { public required bool Passed { get; init; } public required int Iterations { get; init; } public required int UniqueResults { get; init; } public required IReadOnlyList Digests { get; init; } }