feat(historian): backend registry, ServerHistorian:Backend, Host wiring via provider
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+36
@@ -33,6 +33,42 @@ public sealed class ServerHistorianOptionsValidatorTests
|
||||
}
|
||||
|
||||
/// <summary>Enabled historian with an empty endpoint fails (would crash-loop in the factory).</summary>
|
||||
/// <summary>An unknown backend always fails, even when nothing consumes the section.</summary>
|
||||
[Fact]
|
||||
public void Unknown_backend_fails_even_when_not_consumed()
|
||||
{
|
||||
var result = Sut().Validate(null, new ServerHistorianOptions { Backend = "Nope" });
|
||||
|
||||
result.Failed.ShouldBeTrue();
|
||||
result.Failures.ShouldContain(f => f.Contains("ServerHistorian:Backend") && f.Contains("Nope") && f.Contains("Gateway"));
|
||||
}
|
||||
|
||||
/// <summary>Backend names are case-insensitive; the gateway checks still apply (control: no ApiKey fails).</summary>
|
||||
[Fact]
|
||||
public void Lowercase_gateway_backend_still_enforces_gateway_checks()
|
||||
{
|
||||
var result = Sut().Validate(null, new ServerHistorianOptions
|
||||
{
|
||||
Backend = "gateway", Enabled = true, Endpoint = "https://host:5222", ApiKey = "",
|
||||
});
|
||||
|
||||
result.Failed.ShouldBeTrue();
|
||||
result.Failures.ShouldContain(f => f.Contains("ServerHistorian:ApiKey"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The gateway-only checks (Endpoint, ApiKey, UseTls) do not apply to a non-gateway backend. Needs a
|
||||
/// registered non-gateway backend, which does not exist yet, so this asserts the gate through
|
||||
/// <see cref="ServerHistorianOptions.IsGatewayBackend"/> and the registry's unknown-name failure only.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Gateway_checks_are_gated_on_backend_being_gateway()
|
||||
{
|
||||
new ServerHistorianOptions { Backend = "SqlServer" }.IsGatewayBackend.ShouldBeFalse();
|
||||
new ServerHistorianOptions { Backend = "gateway" }.IsGatewayBackend.ShouldBeTrue();
|
||||
new ServerHistorianOptions { Backend = "SqlServer", Enabled = true }.Validate().ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Enabled_with_empty_endpoint_fails()
|
||||
{
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Recorder;
|
||||
using ZB.MOM.WW.OtOpcUa.Host.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.Historian;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Host.Tests.Configuration;
|
||||
|
||||
public sealed class HistorianBackendRegistryTests
|
||||
{
|
||||
private static readonly IConfiguration Cfg = new ConfigurationBuilder().Build();
|
||||
|
||||
private static ServerHistorianOptions Gateway(string backend = "Gateway") => new()
|
||||
{
|
||||
Backend = backend, Enabled = true, Endpoint = "http://localhost:5222", ApiKey = "histgw_id_secret", UseTls = false,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Default_backend_is_Gateway_and_resolves_the_gateway_provider()
|
||||
{
|
||||
new ServerHistorianOptions().Backend.ShouldBe("Gateway");
|
||||
HistorianBackendRegistry.Resolve(new ServerHistorianOptions(), Cfg).ShouldBeOfType<GatewayHistorianProvider>();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("gateway")]
|
||||
[InlineData("GATEWAY")]
|
||||
[InlineData("Gateway")]
|
||||
public void Resolve_is_case_insensitive(string name) =>
|
||||
HistorianBackendRegistry.Resolve(Gateway(name), Cfg).Name.ShouldBe("Gateway");
|
||||
|
||||
[Fact]
|
||||
public void Unknown_backend_throws_naming_the_value_and_the_allowed_names()
|
||||
{
|
||||
var ex = Should.Throw<OptionsValidationException>(
|
||||
() => HistorianBackendRegistry.Resolve(Gateway("Nope"), Cfg));
|
||||
var msg = string.Join(" ", ex.Failures);
|
||||
msg.ShouldContain("Nope");
|
||||
msg.ShouldContain("Gateway");
|
||||
msg.ShouldContain("ServerHistorian:Backend");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KnownBackends_lists_Gateway() => HistorianBackendRegistry.KnownBackends.ShouldContain("Gateway");
|
||||
|
||||
[Fact]
|
||||
public void Gateway_provider_builds_the_same_concrete_seams_Program_used_to_build()
|
||||
{
|
||||
var sp = new ServiceCollection().BuildServiceProvider();
|
||||
var p = HistorianBackendRegistry.Resolve(Gateway(), Cfg);
|
||||
|
||||
p.CreateDataSource(sp).ShouldBeOfType<GatewayHistorianDataSource>();
|
||||
p.CreateValueWriter(sp).ShouldBeOfType<GatewayHistorianValueWriter>();
|
||||
p.CreateAlarmWriter(sp).ShouldBeOfType<GatewayAlarmHistorianWriter>();
|
||||
p.CreateProvisioning(sp).ShouldBeOfType<GatewayTagProvisioner>();
|
||||
p.CreateUpdater(sp).ShouldBeNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user