Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.IntegrationTests/ScadaBridgeWebApplicationFactory.cs
T
Joseph Doherty 513fa1aa16 docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
2026-10-07 15:05:49 -04:00

123 lines
6.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();
/// <summary>Initializes the factory and sets the environment variables that configure a Central node for tests.</summary>
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",
// NodeName is eagerly validated at boot:
// an empty node name stamps the SourceNode audit column NULL, so the validator now
// fails host boot without it. The integration harness must supply one.
["ScadaBridge__Node__NodeName"] = "central-a",
["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",
// The inbound API-key pepper is a REQUIRED Central config value (StartupValidator
// enforces a >=16-char floor; it backs the peppered-HMAC verifier). Supply a fixed
// test pepper so host boot passes validation in the test environment.
["ScadaBridge__InboundApi__ApiKeyPepper"] = "integration-test-api-key-pepper-0123456789",
// 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",
// Search-then-bind needs an account holding GLAuth's `search` capability, which
// the current directory grants ONLY to `serviceaccount` (binding as `admin`
// succeeds but its searches return `50 Insufficient access`, i.e. auth reads as
// "service unavailable"). Matches the local redundant pair
// (scadaproj/infra/glauth-redundant), whose serviceaccount password is the
// well-known dev value — the shared 10.100.0.35 instance's has been rotated.
["ScadaBridge__Security__Ldap__ServiceAccountDn"] = "cn=serviceaccount,dc=zb,dc=local",
["ScadaBridge__Security__Ldap__ServiceAccountPassword"] = "serviceaccount123",
};
foreach (var (key, value) in envVars)
{
_previousEnvVars[key] = Environment.GetEnvironmentVariable(key);
Environment.SetEnvironmentVariable(key, value);
}
}
/// <inheritdoc />
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);
});
}
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
foreach (var (key, previousValue) in _previousEnvVars)
{
Environment.SetEnvironmentVariable(key, previousValue);
}
}
}
}