using Microsoft.Extensions.Logging.Abstractions; using Opc.Ua.Server; using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// /// Verifies the F13a cert auto-creation slice of : the SDK /// self-signs a certificate into {PkiStoreRoot}/own/certs/ on first boot against a /// fresh PKI tree, and the file persists for reuse on the next boot. /// public sealed class OpcUaApplicationHostTests : IDisposable { private static CancellationToken Ct => TestContext.Current.CancellationToken; private readonly string _pkiRoot = Path.Combine( Path.GetTempPath(), $"otopcua-pki-{Guid.NewGuid():N}"); [Fact] public async Task StartAsync_creates_application_certificate_in_pki_own() { await using var host = new OpcUaApplicationHost( new OpcUaApplicationHostOptions { ApplicationName = "OtOpcUa.Test", ApplicationUri = $"urn:OtOpcUa.Test:{Guid.NewGuid():N}", OpcUaPort = AllocateFreePort(), PublicHostname = "localhost", PkiStoreRoot = _pkiRoot, }, NullLogger.Instance); await host.StartAsync(new StandardServer(), Ct); var ownCerts = Path.Combine(_pkiRoot, "own", "certs"); Directory.Exists(ownCerts).ShouldBeTrue($"expected SDK to create {ownCerts}"); Directory.EnumerateFiles(ownCerts).ShouldNotBeEmpty("expected a self-signed cert file in the own store"); } [Fact] public async Task StartAsync_reuses_existing_certificate_on_second_boot() { var port1 = AllocateFreePort(); await using (var first = new OpcUaApplicationHost( new OpcUaApplicationHostOptions { ApplicationName = "OtOpcUa.Reuse", ApplicationUri = "urn:OtOpcUa.Reuse", OpcUaPort = port1, PublicHostname = "localhost", PkiStoreRoot = _pkiRoot, }, NullLogger.Instance)) { await first.StartAsync(new StandardServer(), Ct); } var ownCerts = Path.Combine(_pkiRoot, "own", "certs"); var firstFiles = Directory.GetFiles(ownCerts).OrderBy(f => f).ToArray(); firstFiles.ShouldNotBeEmpty(); var port2 = AllocateFreePort(); await using (var second = new OpcUaApplicationHost( new OpcUaApplicationHostOptions { ApplicationName = "OtOpcUa.Reuse", ApplicationUri = "urn:OtOpcUa.Reuse", OpcUaPort = port2, PublicHostname = "localhost", PkiStoreRoot = _pkiRoot, }, NullLogger.Instance)) { await second.StartAsync(new StandardServer(), Ct); } var secondFiles = Directory.GetFiles(ownCerts).OrderBy(f => f).ToArray(); secondFiles.ShouldBe(firstFiles, "expected the second boot to reuse the cert from the first, not create a new one"); } private static int AllocateFreePort() { using var listener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0); listener.Start(); var port = ((System.Net.IPEndPoint)listener.LocalEndpoint).Port; listener.Stop(); return port; } public void Dispose() { if (Directory.Exists(_pkiRoot)) { try { Directory.Delete(_pkiRoot, recursive: true); } catch { /* best-effort cleanup */ } } } }