- Server-004: pass the role-derived display name to UserIdentity's base ctor (the SDK's DisplayName has no public setter) and drop the dead Display property; make RoleBasedIdentity internal sealed. - Server-006: derive a bounded CancellationToken from the SDK's OperationContext.OperationDeadline in OnReadValue / OnWriteValue so a stalled driver call can no longer pin the request thread. - Server-008: mark handled slots via CallMethodRequest.Processed = true in RouteScriptedAlarmMethodCalls (the SDK skips on Processed, not on a Good error slot). - Server-012: PeerHttpProbeLoop.ProbeAsync stops mutating client.Timeout per call; uses a per-request CancellationTokenSource linked to the shutdown token instead. - Server-014: wire SealedBootstrap into Program.cs via AddSealedBootstrap + OpcUaServerService so the generation-sealed cache + stale-config flag + resilient reader actually run; /healthz now reflects cache-fallback state. - Server-015: replace the stale 'PR 16 / PR 17 minimum-viable scope' class summaries on OtOpcUaServer and OpcUaServerOptions with the shipped LDAP + anonymous-role + configurable security-profile prose. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
53 lines
2.4 KiB
C#
53 lines
2.4 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration.LocalCache;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Server.Tests;
|
|
|
|
/// <summary>
|
|
/// Regression for Server-014 — <see cref="SealedBootstrap"/> exists in the source tree and
|
|
/// is referenced by <c>docs/v2/v2-release-readiness.md</c> as the closed release blocker for
|
|
/// generation-sealed config plumbing, but it was never registered in the production DI
|
|
/// container. The release blocker remained de-facto open. This test asserts the DI
|
|
/// registrations (which <c>Program.cs</c> performs at startup) actually compose: every
|
|
/// dependency <see cref="SealedBootstrap"/> needs — <see cref="GenerationSealedCache"/>,
|
|
/// <see cref="ResilientConfigReader"/>, <see cref="StaleConfigFlag"/> — must be resolvable
|
|
/// so the production wire-up doesn't fail with a missing-service exception at startup.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class SealedBootstrapWiringTests
|
|
{
|
|
[Fact]
|
|
public void SealedBootstrap_and_its_dependencies_are_registered_in_DI()
|
|
{
|
|
var tempRoot = Path.Combine(Path.GetTempPath(), $"otopcua-sealed-bootstrap-wiring-{Guid.NewGuid():N}");
|
|
try
|
|
{
|
|
// Mirror Program.cs's registrations of NodeOptions + the SealedBootstrap chain.
|
|
var services = new ServiceCollection();
|
|
ZB.MOM.WW.OtOpcUa.Server.ServerWiring.AddSealedBootstrap(services, new NodeOptions
|
|
{
|
|
NodeId = "test-node",
|
|
ClusterId = "test-cluster",
|
|
ConfigDbConnectionString = "Server=fake;Database=fake;Integrated Security=true;",
|
|
LocalCachePath = tempRoot,
|
|
});
|
|
services.AddSingleton(NullLoggerFactory.Instance);
|
|
services.AddLogging();
|
|
|
|
using var sp = services.BuildServiceProvider();
|
|
|
|
sp.GetRequiredService<GenerationSealedCache>().ShouldNotBeNull();
|
|
sp.GetRequiredService<ResilientConfigReader>().ShouldNotBeNull();
|
|
sp.GetRequiredService<StaleConfigFlag>().ShouldNotBeNull();
|
|
sp.GetRequiredService<SealedBootstrap>().ShouldNotBeNull();
|
|
}
|
|
finally
|
|
{
|
|
try { if (Directory.Exists(tempRoot)) Directory.Delete(tempRoot, recursive: true); } catch { }
|
|
}
|
|
}
|
|
}
|