Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.IntegrationTests/ScadaBridgeWebApplicationFactory.cs
T
Joseph Doherty 6ae605160c chore(auth): ScadaBridge unify dev LDAP base DN to dc=zb,dc=local (Task 1.6)
Replace dc=scadabridge,dc=local with dc=zb,dc=local in all dev/test LDAP
references — app config, docker test-cluster node configs (docker/ and
docker-env2/), GLAuth fixture, dev tooling, Host.Tests fixtures,
IntegrationTests factory, and operational test_infra docs. OU structure
(ou=SCADA-Admins,ou=users,etc.) preserved throughout. Email domains
(@scadabridge.local), hostnames, and container names are untouched.
Historical plan docs (2026-05-24-second-environment.md,
2026-05-31-folder-repo-rename-scadabridge-design.md) excluded as
point-in-time records. No synthetic dc=example,dc=com placeholders touched.
2026-06-02 06:54:14 -04:00

109 lines
5.4 KiB
C#

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
using ZB.MOM.WW.ScadaBridge.Host.Actors;
namespace ZB.MOM.WW.ScadaBridge.IntegrationTests;
/// <summary>
/// Shared WebApplicationFactory for integration tests.
/// Replaces SQL Server with an in-memory database and skips migrations.
/// Removes AkkaHostedService to avoid DNS resolution issues in test environments.
/// Uses environment variables for config since Program.cs reads them in the initial ConfigurationBuilder
/// before WebApplicationFactory can inject settings.
/// </summary>
public class ScadaBridgeWebApplicationFactory : WebApplicationFactory<Program>
{
/// <summary>
/// Environment variables that were set by this factory, to be cleaned up on dispose.
/// </summary>
private readonly Dictionary<string, string?> _previousEnvVars = new();
public ScadaBridgeWebApplicationFactory()
{
// The initial ConfigurationBuilder in Program.cs reads env vars with AddEnvironmentVariables().
// The env var format uses __ as section separator.
var envVars = new Dictionary<string, string>
{
["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=localhost;Database=ScadaBridge_Test;TrustServerCertificate=True",
["ScadaBridge__Database__MachineDataDb"] = "Server=localhost;Database=ScadaBridge_MachineData_Test;TrustServerCertificate=True",
["ScadaBridge__Database__SkipMigrations"] = "true",
["ScadaBridge__Security__JwtSigningKey"] = "integration-test-signing-key-must-be-at-least-32-chars-long",
// Task 1.4: LDAP settings nest under Security:Ldap (shared LdapOptions) and use
// the renamed keys (Transport replaces LdapUseTls; None == plaintext for the
// GLAuth dev directory, paired with AllowInsecure=true).
["ScadaBridge__Security__Ldap__Server"] = "localhost",
["ScadaBridge__Security__Ldap__Port"] = "3893",
["ScadaBridge__Security__Ldap__Transport"] = "None",
["ScadaBridge__Security__Ldap__AllowInsecure"] = "true",
["ScadaBridge__Security__Ldap__SearchBase"] = "dc=zb,dc=local",
// GLAuth places users at cn=<name>,ou=<group>,ou=users,dc=... — a service
// account is configured to enable the shared service's search-then-bind:
// resolve the user's real DN by (UserNameAttribute=<name>) lookup, then bind it.
["ScadaBridge__Security__Ldap__ServiceAccountDn"] = "cn=admin,ou=SCADA-Admins,ou=users,dc=zb,dc=local",
["ScadaBridge__Security__Ldap__ServiceAccountPassword"] = "password",
};
foreach (var (key, value) in envVars)
{
_previousEnvVars[key] = Environment.GetEnvironmentVariable(key);
Environment.SetEnvironmentVariable(key, value);
}
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development");
builder.ConfigureServices(services =>
{
// Remove ALL DbContext and EF-related service registrations to avoid dual-provider conflict.
// AddDbContext<> with UseSqlServer registers many internal services. We must remove them all.
var descriptorsToRemove = services
.Where(d =>
d.ServiceType == typeof(DbContextOptions<ScadaBridgeDbContext>) ||
d.ServiceType == typeof(DbContextOptions) ||
d.ServiceType == typeof(ScadaBridgeDbContext) ||
d.ServiceType.FullName?.Contains("EntityFrameworkCore") == true)
.ToList();
foreach (var d in descriptorsToRemove)
services.Remove(d);
// Add in-memory database as sole provider
services.AddDbContext<ScadaBridgeDbContext>(options =>
options.UseInMemoryDatabase($"ScadaBridge_IntegrationTests_{Guid.NewGuid()}"));
// Remove the factory-registered IHostedService registrations so
// Akka.NET remoting / DNS resolution never starts in tests — but
// keep the AkkaHostedService SINGLETON resolvable: IClusterNodeProvider
// (and other services) depend on it via GetRequiredService.
var hostedServiceDescriptors = services
.Where(d => d.ServiceType == typeof(IHostedService) && d.ImplementationFactory != null)
.ToList();
foreach (var d in hostedServiceDescriptors)
services.Remove(d);
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
foreach (var (key, previousValue) in _previousEnvVars)
{
Environment.SetEnvironmentVariable(key, previousValue);
}
}
}
}