using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; namespace ZB.MOM.WW.ScadaBridge.IntegrationTests; /// /// WP-22: Startup validation — missing required config fails with clear error. /// Tests the StartupValidator that runs on boot. /// /// Note: These tests temporarily set environment variables because Program.cs reads /// configuration from env vars in the initial ConfigurationBuilder (before WebApplicationFactory /// can inject settings). Each test saves/restores env vars to avoid interference. /// public class StartupValidationTests { [Fact] public void MissingRole_ThrowsInvalidOperationException() { // Set all required config EXCEPT Role using var env = new TempEnvironment(new Dictionary { ["DOTNET_ENVIRONMENT"] = "Development", ["ScadaBridge__Node__NodeHostname"] = "localhost", ["ScadaBridge__Node__RemotingPort"] = "8081", ["ScadaBridge__Cluster__SeedNodes__0"] = "akka.tcp://scadabridge@localhost:8081", ["ScadaBridge__Cluster__SeedNodes__1"] = "akka.tcp://scadabridge@localhost:8082", }); var factory = new WebApplicationFactory(); var ex = Assert.Throws(() => factory.CreateClient()); Assert.Contains("Role", ex.Message, StringComparison.OrdinalIgnoreCase); factory.Dispose(); } [Fact] public void MissingJwtSigningKey_ForCentral_ThrowsInvalidOperationException() { using var env = new TempEnvironment(new Dictionary { ["DOTNET_ENVIRONMENT"] = "Development", ["ScadaBridge__Node__Role"] = "Central", ["ScadaBridge__Node__NodeHostname"] = "localhost", ["ScadaBridge__Node__RemotingPort"] = "8081", ["ScadaBridge__Cluster__SeedNodes__0"] = "akka.tcp://scadabridge@localhost:8081", ["ScadaBridge__Cluster__SeedNodes__1"] = "akka.tcp://scadabridge@localhost:8082", ["ScadaBridge__Database__ConfigurationDb"] = "Server=x;Database=x", ["ScadaBridge__Database__MachineDataDb"] = "Server=x;Database=x", ["ScadaBridge__Security__Ldap__Server"] = "localhost", // Deliberately missing JwtSigningKey }); var factory = new WebApplicationFactory(); var ex = Assert.Throws(() => factory.CreateClient()); Assert.Contains("JwtSigningKey", ex.Message, StringComparison.OrdinalIgnoreCase); factory.Dispose(); } [Fact] public void CentralRole_StartsSuccessfully_WithValidConfig() { using var factory = new ScadaBridgeWebApplicationFactory(); using var client = factory.CreateClient(); Assert.NotNull(client); } /// /// Helper to temporarily set environment variables and restore them on dispose. /// Clears all ScadaBridge__ vars first to ensure a clean slate. /// private sealed class TempEnvironment : IDisposable { private readonly Dictionary _previousValues = new(); /// /// All ScadaBridge env vars that might be set by other tests/factories. /// private static readonly string[] KnownKeys = { "DOTNET_ENVIRONMENT", "ScadaBridge__Node__Role", "ScadaBridge__Node__NodeHostname", "ScadaBridge__Node__RemotingPort", "ScadaBridge__Node__SiteId", "ScadaBridge__Cluster__SeedNodes__0", "ScadaBridge__Cluster__SeedNodes__1", "ScadaBridge__Database__ConfigurationDb", "ScadaBridge__Database__MachineDataDb", "ScadaBridge__Database__SkipMigrations", "ScadaBridge__Security__JwtSigningKey", "ScadaBridge__Security__Ldap__Server", "ScadaBridge__Security__Ldap__Port", "ScadaBridge__Security__Ldap__Transport", "ScadaBridge__Security__Ldap__AllowInsecure", "ScadaBridge__Security__Ldap__SearchBase", "ScadaBridge__Security__Ldap__ServiceAccountDn", "ScadaBridge__Security__Ldap__ServiceAccountPassword", }; public TempEnvironment(Dictionary varsToSet) { // Save and clear all known keys foreach (var key in KnownKeys) { _previousValues[key] = Environment.GetEnvironmentVariable(key); Environment.SetEnvironmentVariable(key, null); } // Set the requested vars foreach (var (key, value) in varsToSet) { if (!_previousValues.ContainsKey(key)) _previousValues[key] = Environment.GetEnvironmentVariable(key); Environment.SetEnvironmentVariable(key, value); } } public void Dispose() { foreach (var (key, previousValue) in _previousValues) { Environment.SetEnvironmentVariable(key, previousValue); } } } }