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; }
}