test(opcuaclient): HistoryRead Raw/AtTime/Processed wire tests against in-process server (#622)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -38,6 +38,7 @@ internal sealed class ServerEventRig : IAsyncDisposable
|
||||
private OpcUaApplicationHost _host = null!;
|
||||
private OtOpcUaSdkServer _server = null!;
|
||||
private int _port;
|
||||
private OpcUaApplicationHostOptions _hostOptions = null!;
|
||||
|
||||
private ServerEventRig(string pkiRoot) => _pkiRoot = pkiRoot;
|
||||
|
||||
@@ -51,28 +52,44 @@ internal sealed class ServerEventRig : IAsyncDisposable
|
||||
{
|
||||
var rig = new ServerEventRig(Path.Combine(Path.GetTempPath(), $"otopcua-{name}-{Guid.NewGuid():N}"));
|
||||
rig._port = AllocateFreePort();
|
||||
rig._server = new OtOpcUaSdkServer();
|
||||
rig._host = new OpcUaApplicationHost(
|
||||
new OpcUaApplicationHostOptions
|
||||
{
|
||||
ApplicationName = $"OtOpcUa.{name}",
|
||||
ApplicationUri = $"urn:OtOpcUa.{name}.{Guid.NewGuid():N}",
|
||||
OpcUaPort = rig._port,
|
||||
PublicHostname = "127.0.0.1",
|
||||
PkiStoreRoot = rig._pkiRoot,
|
||||
EnabledSecurityProfiles = encrypted
|
||||
? [OpcUaSecurityProfile.None, OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt]
|
||||
: [OpcUaSecurityProfile.None],
|
||||
AutoAcceptUntrustedClientCertificates = true,
|
||||
},
|
||||
NullLogger<OpcUaApplicationHost>.Instance,
|
||||
new KeyPresentingAuthenticator());
|
||||
|
||||
await rig._host.StartAsync(rig._server, ct);
|
||||
rig.Sink = new SdkAddressSpaceSink(rig.Nm);
|
||||
rig._hostOptions = new OpcUaApplicationHostOptions
|
||||
{
|
||||
ApplicationName = $"OtOpcUa.{name}",
|
||||
ApplicationUri = $"urn:OtOpcUa.{name}.{Guid.NewGuid():N}",
|
||||
OpcUaPort = rig._port,
|
||||
PublicHostname = "127.0.0.1",
|
||||
PkiStoreRoot = rig._pkiRoot,
|
||||
EnabledSecurityProfiles = encrypted
|
||||
? [OpcUaSecurityProfile.None, OpcUaSecurityProfile.Basic256Sha256SignAndEncrypt]
|
||||
: [OpcUaSecurityProfile.None],
|
||||
AutoAcceptUntrustedClientCertificates = true,
|
||||
};
|
||||
await rig.BootAsync(ct);
|
||||
return rig;
|
||||
}
|
||||
|
||||
private async Task BootAsync(CancellationToken ct)
|
||||
{
|
||||
_server = new OtOpcUaSdkServer();
|
||||
_host = new OpcUaApplicationHost(
|
||||
_hostOptions, NullLogger<OpcUaApplicationHost>.Instance, new KeyPresentingAuthenticator());
|
||||
await _host.StartAsync(_server, ct);
|
||||
Sink = new SdkAddressSpaceSink(Nm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the server and boots a fresh one on the SAME port, application URI and PKI store (a process
|
||||
/// restart as a client sees it). The address space is EMPTY afterwards: the caller re-creates its nodes and
|
||||
/// re-publishes values, as a redeploy would. <paramref name="whileDown"/> runs once the old server is gone
|
||||
/// and before the new one binds, so a test can observe the outage.
|
||||
/// </summary>
|
||||
public async Task RestartAsync(CancellationToken ct, Func<Task>? whileDown = null)
|
||||
{
|
||||
await _host.DisposeAsync();
|
||||
if (whileDown is not null) await whileDown();
|
||||
await BootAsync(ct);
|
||||
}
|
||||
|
||||
/// <summary>Wires the authorizer over the given keys and grants, enforcing or not.</summary>
|
||||
public DataPlaneAuthorityState Enforce(bool enabled, ApiKey[] keys, params NodeAcl[] grants) =>
|
||||
Enforce(enabled, keys, ScopePaths(), grants);
|
||||
@@ -148,11 +165,11 @@ internal sealed class ServerEventRig : IAsyncDisposable
|
||||
/// Adds a raw value variable under <see cref="RawDeviceFolder"/> (the folder is ensured), for tests that
|
||||
/// drive a real client against plain values rather than alarm conditions. Additive to the alarm helpers.
|
||||
/// </summary>
|
||||
public string EnsureRawVariable(string name, string dataType, bool writable)
|
||||
public string EnsureRawVariable(string name, string dataType, bool writable, string? historianTagname = null)
|
||||
{
|
||||
Sink.EnsureFolder(RawDeviceFolder, parentNodeId: null, displayName: "dev1", realm: AddressSpaceRealm.Raw);
|
||||
var id = $"{RawDeviceFolder}/{name}";
|
||||
Sink.EnsureVariable(id, RawDeviceFolder, name, dataType, writable, AddressSpaceRealm.Raw);
|
||||
Sink.EnsureVariable(id, RawDeviceFolder, name, dataType, writable, AddressSpaceRealm.Raw, historianTagname);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user