feat(host): StartupValidator warns when a clustered central keeps per-node inbound API keys; rejects an unknown ApiKeyStore:Provider at boot; Program.cs logs CollectWarnings once Serilog is up
Claude-Session: https://claude.ai/code/session_013L3XdmVii9i5sY4FChU8pB
This commit is contained in:
@@ -694,6 +694,91 @@ public class StartupValidatorTests
|
||||
Assert.Null(ex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectWarnings_WarnsForTwoSeedCentralOnSqlite()
|
||||
{
|
||||
// Design 2026-09-02: the inbound API key store is per-node SQLite by default, so a
|
||||
// 2+-node central cluster is one failover away from rejecting every inbound client.
|
||||
var config = BuildConfig(ValidCentralConfig());
|
||||
|
||||
var warnings = StartupValidator.CollectWarnings(config);
|
||||
|
||||
var warning = Assert.Single(warnings);
|
||||
Assert.Contains("2-node", warning);
|
||||
Assert.Contains("SQLite", warning);
|
||||
Assert.Contains("Provider=SqlServer", warning);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectWarnings_WarnsWhenProviderExplicitlySqlite()
|
||||
{
|
||||
var values = ValidCentralConfig();
|
||||
values["ScadaBridge:InboundApi:ApiKeyStore:Provider"] = "sqlite";
|
||||
var config = BuildConfig(values);
|
||||
|
||||
var warnings = StartupValidator.CollectWarnings(config);
|
||||
|
||||
Assert.Single(warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectWarnings_SilentForSqlServer()
|
||||
{
|
||||
var values = ValidCentralConfig();
|
||||
values["ScadaBridge:InboundApi:ApiKeyStore:Provider"] = "SqlServer";
|
||||
var config = BuildConfig(values);
|
||||
|
||||
var warnings = StartupValidator.CollectWarnings(config);
|
||||
|
||||
Assert.Empty(warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectWarnings_SilentForSingleSeedCentral()
|
||||
{
|
||||
// CollectWarnings is independent of Validate's own >=2-seeds requirement — it should
|
||||
// simply not fire the failover warning when there is nothing to fail over to.
|
||||
var values = ValidCentralConfig();
|
||||
values.Remove("ScadaBridge:Cluster:SeedNodes:1");
|
||||
var config = BuildConfig(values);
|
||||
|
||||
var warnings = StartupValidator.CollectWarnings(config);
|
||||
|
||||
Assert.Empty(warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectWarnings_SilentForSite()
|
||||
{
|
||||
var config = BuildConfig(ValidSiteConfig());
|
||||
|
||||
var warnings = StartupValidator.CollectWarnings(config);
|
||||
|
||||
Assert.Empty(warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_RejectsUnknownApiKeyStoreProvider()
|
||||
{
|
||||
var values = ValidCentralConfig();
|
||||
values["ScadaBridge:InboundApi:ApiKeyStore:Provider"] = "Postgres";
|
||||
var config = BuildConfig(values);
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => StartupValidator.Validate(config));
|
||||
Assert.Contains("ScadaBridge:InboundApi:ApiKeyStore:Provider", ex.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Validate_AcceptsProviderCaseInsensitively()
|
||||
{
|
||||
var values = ValidCentralConfig();
|
||||
values["ScadaBridge:InboundApi:ApiKeyStore:Provider"] = "SQLSERVER";
|
||||
var config = BuildConfig(values);
|
||||
|
||||
var ex = Record.Exception(() => StartupValidator.Validate(config));
|
||||
Assert.Null(ex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleErrors_AllReported()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user