using System; using System.IO; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using StellaOps.Scanner.Surface.Env; using StellaOps.Scanner.Surface.Validation; using StellaOps.TestKit; namespace StellaOps.Scanner.Surface.Validation.Tests; public sealed class SurfaceValidatorRunnerTests { [Trait("Category", TestCategories.Unit)] [Fact] public async Task EnsureAsync_Throws_WhenValidationFails() { var services = CreateServices(services => { services.Configure(options => { options.ThrowOnFailure = true; options.ContinueOnError = false; }); }); var runner = services.GetRequiredService(); var environment = new SurfaceEnvironmentSettings( new Uri("https://surface.invalid"), string.Empty, null, new DirectoryInfo(Path.Combine(Path.GetTempPath(), "stellaops-tests", Guid.NewGuid().ToString())), 0, false, Array.Empty(), new SurfaceSecretsConfiguration("kubernetes", "", null, null, null, false), string.Empty, new SurfaceTlsConfiguration(null, null, null)) { CreatedAtUtc = DateTimeOffset.UtcNow }; var context = SurfaceValidationContext.Create(services, "TestComponent", environment); await Assert.ThrowsAsync(() => runner.EnsureAsync(context).AsTask()); } [Trait("Category", TestCategories.Unit)] [Fact] public async Task RunAllAsync_ReturnsSuccess_ForValidConfiguration() { var directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "stellaops-tests", Guid.NewGuid().ToString())); directory.Create(); var environment = new SurfaceEnvironmentSettings( new Uri("https://surface.example.com"), "surface-cache", null, directory, 1024, false, Array.Empty(), new SurfaceSecretsConfiguration("kubernetes", "tenant-a", null, "stellaops", null, false), "tenant-a", new SurfaceTlsConfiguration(null, null, null)) { CreatedAtUtc = DateTimeOffset.UtcNow }; var services = CreateServices(); var runner = services.GetRequiredService(); var context = SurfaceValidationContext.Create(services, "TestComponent", environment); var result = await runner.RunAllAsync(context); Assert.True(result.IsSuccess); } [Trait("Category", TestCategories.Unit)] [Fact] public async Task RunAllAsync_Fails_WhenInlineProviderDisallowed() { var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "stellaops-tests", Guid.NewGuid().ToString())); var environment = new SurfaceEnvironmentSettings( new Uri("https://surface.example.com"), "surface-cache", null, directory, 1024, false, Array.Empty(), new SurfaceSecretsConfiguration("inline", "tenant-a", Root: null, Namespace: null, FallbackProvider: null, AllowInline: false), "tenant-a", new SurfaceTlsConfiguration(null, null, null)) { CreatedAtUtc = DateTimeOffset.UtcNow }; var services = CreateServices(); var runner = services.GetRequiredService(); var context = SurfaceValidationContext.Create(services, "TestComponent", environment); var result = await runner.RunAllAsync(context); Assert.False(result.IsSuccess); Assert.Contains(result.Issues, i => i.Code == SurfaceValidationIssueCodes.SecretsConfigurationInvalid); } [Trait("Category", TestCategories.Unit)] [Fact] public async Task RunAllAsync_Fails_WhenFileRootMissing() { var missingRoot = Path.Combine(Path.GetTempPath(), "stellaops-tests", "missing-root", Guid.NewGuid().ToString()); var directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "stellaops-tests", Guid.NewGuid().ToString())); directory.Create(); var environment = new SurfaceEnvironmentSettings( new Uri("https://surface.example.com"), "surface-cache", null, directory, 1024, false, Array.Empty(), new SurfaceSecretsConfiguration("file", "tenant-a", Root: missingRoot, Namespace: null, FallbackProvider: null, AllowInline: false), "tenant-a", new SurfaceTlsConfiguration(null, null, null)) { CreatedAtUtc = DateTimeOffset.UtcNow }; var services = CreateServices(); var runner = services.GetRequiredService(); var context = SurfaceValidationContext.Create(services, "TestComponent", environment); var result = await runner.RunAllAsync(context); Assert.False(result.IsSuccess); Assert.Contains(result.Issues, i => i.Code == SurfaceValidationIssueCodes.SecretsConfigurationInvalid); } private static ServiceProvider CreateServices(Action? configure = null) { var services = new ServiceCollection(); services.AddLogging(builder => builder.ClearProviders()); services.AddOptions(); services.AddSurfaceValidation(); configure?.Invoke(services); return services.BuildServiceProvider(); } }