Files
UA-.NETStandard/tests/Opc.Ua.Aot.Tests/AotTestFixture.cs

238 lines
10 KiB
C#

/* ========================================================================
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
using Microsoft.Extensions.Logging;
using Opc.Ua.Client;
using Quickstarts.ReferenceServer;
using TUnit.Core.Interfaces;
namespace Opc.Ua.Aot.Tests
{
/// <summary>
/// Shared fixture that starts a ReferenceServer in-process and
/// provides a connected <see cref="ISession"/> for tests.
/// This fixture is created once and shared across all tests.
/// </summary>
public sealed class AotTestFixture : IAsyncInitializer, IAsyncDisposable
{
public AotServerFixture<ReferenceServer> ServerFixture { get; private set; }
public ISession Session { get; private set; }
public string ServerUrl { get; private set; }
public ITelemetryContext Telemetry { get; private set; }
private ApplicationConfiguration m_clientConfiguration;
private string m_pkiRoot;
public async Task InitializeAsync()
{
Telemetry = DefaultTelemetry.Create(builder =>
builder.SetMinimumLevel(LogLevel.Warning));
// Start server
ServerFixture = new AotServerFixture<ReferenceServer>(
t => new ReferenceServer(t), Telemetry)
{
AutoAccept = true,
SecurityNone = true,
AllNodeManagers = true
};
await ServerFixture.LoadConfigurationAsync(
Path.Combine(Directory.GetCurrentDirectory(), "pki")).ConfigureAwait(false);
await ServerFixture.StartAsync().ConfigureAwait(false);
ServerUrl = $"opc.tcp://localhost:{ServerFixture.Port}" +
"/Quickstarts/ReferenceServer";
m_pkiRoot = Path.Combine(
Path.GetTempPath(), "OpcUaAotTests", "pki");
// Create a client config programmatically
m_clientConfiguration = new ApplicationConfiguration(Telemetry)
{
ApplicationName = "AotTestClient",
ApplicationUri = "urn:localhost:OPCFoundation:AotTestClient",
ApplicationType = ApplicationType.Client,
SecurityConfiguration = new SecurityConfiguration
{
ApplicationCertificate = new CertificateIdentifier
{
StoreType = CertificateStoreType.Directory,
StorePath = Path.Combine(m_pkiRoot, "own"),
SubjectName = "CN=AotTestClient, O=OPC Foundation"
},
TrustedIssuerCertificates = new CertificateTrustList
{
StoreType = CertificateStoreType.Directory,
StorePath = Path.Combine(m_pkiRoot, "issuer")
},
TrustedPeerCertificates = new CertificateTrustList
{
StoreType = CertificateStoreType.Directory,
StorePath = Path.Combine(m_pkiRoot, "trusted")
},
RejectedCertificateStore = new CertificateTrustList
{
StoreType = CertificateStoreType.Directory,
StorePath = Path.Combine(m_pkiRoot, "rejected")
},
AutoAcceptUntrustedCertificates = true
},
TransportQuotas = new TransportQuotas
{
MaxMessageSize = 4 * 1024 * 1024
},
ClientConfiguration = new ClientConfiguration(),
ServerConfiguration = new ServerConfiguration()
};
await m_clientConfiguration.ValidateAsync(
ApplicationType.Client).ConfigureAwait(false);
m_clientConfiguration.CertificateManager ??= CertificateManagerFactory.Create(
m_clientConfiguration.SecurityConfiguration, Telemetry);
m_clientConfiguration.CertificateManager.AcceptError = static (cert, err) => true;
// Connect session
EndpointDescription endpointDescription = await CoreClientUtils.SelectEndpointAsync(
m_clientConfiguration, ServerUrl, useSecurity: false,
Telemetry, CancellationToken.None).ConfigureAwait(false);
var configuredEndpoint = new ConfiguredEndpoint(
null, endpointDescription,
EndpointConfiguration.Create(m_clientConfiguration));
var sessionFactory = new DefaultSessionFactory(Telemetry);
#pragma warning disable CA2000 // Dispose objects before losing scope
Session = await sessionFactory.CreateAsync(
m_clientConfiguration,
configuredEndpoint,
updateBeforeConnect: false,
sessionName: "AotTest",
sessionTimeout: 60000,
identity: new UserIdentity(new AnonymousIdentityToken()),
preferredLocales: default,
ct: CancellationToken.None).ConfigureAwait(false);
#pragma warning restore CA2000 // Dispose objects before losing scope
}
/// <summary>
/// Creates a new independent <see cref="ISession"/> connected to the
/// same server. Callers are responsible for closing and disposing.
/// </summary>
public async Task<ISession> CreateSessionAsync(
string sessionName = "AotTestNewSession")
{
EndpointDescription endpointDescription = await CoreClientUtils.SelectEndpointAsync(
m_clientConfiguration, ServerUrl, useSecurity: false,
Telemetry, CancellationToken.None).ConfigureAwait(false);
var configuredEndpoint = new ConfiguredEndpoint(
null, endpointDescription,
EndpointConfiguration.Create(m_clientConfiguration));
var sessionFactory = new DefaultSessionFactory(Telemetry);
#pragma warning disable CA2000 // Dispose objects before losing scope
return await sessionFactory.CreateAsync(
m_clientConfiguration,
configuredEndpoint,
updateBeforeConnect: false,
sessionName: sessionName,
sessionTimeout: 60000,
identity: new UserIdentity(new AnonymousIdentityToken()),
preferredLocales: default,
ct: CancellationToken.None).ConfigureAwait(false);
#pragma warning restore CA2000 // Dispose objects before losing scope
}
/// <summary>
/// Creates a new <see cref="ManagedSession"/> (V2 engine)
/// connected to the same server. Used by AOT tests that
/// exercise V2-only surfaces such as
/// <see cref="Client.Subscriptions.ISubscriptionManager"/>
/// or <see cref="Client.Subscriptions.IPartitionedSubscription"/>.
/// Callers are responsible for closing and disposing.
/// </summary>
public async Task<ManagedSession> CreateManagedSessionAsync(
string sessionName = "AotTestManagedSession")
{
EndpointDescription endpointDescription = await CoreClientUtils.SelectEndpointAsync(
m_clientConfiguration, ServerUrl, useSecurity: false,
Telemetry, CancellationToken.None).ConfigureAwait(false);
var configuredEndpoint = new ConfiguredEndpoint(
null, endpointDescription,
EndpointConfiguration.Create(m_clientConfiguration));
#pragma warning disable CA2000 // Ownership transfers to caller via returned ManagedSession.
return await new ManagedSessionBuilder(m_clientConfiguration, Telemetry)
.UseEndpoint(configuredEndpoint)
.WithSessionName(sessionName)
.WithSessionTimeout(TimeSpan.FromSeconds(60))
.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
#pragma warning restore CA2000
}
public async ValueTask DisposeAsync()
{
if (Session != null)
{
Session.DeleteSubscriptionsOnClose = true;
await Session.CloseAsync(CancellationToken.None)
.ConfigureAwait(false);
await Session.DisposeAsync().ConfigureAwait(false);
Session = null;
}
if (m_clientConfiguration?.CertificateManager is IDisposable disposableManager)
{
disposableManager.Dispose();
m_clientConfiguration.CertificateManager = null;
}
if (ServerFixture != null)
{
await ServerFixture.StopAsync().ConfigureAwait(false);
}
// Clean up pki folders
foreach (string dir in new[] {
m_pkiRoot,
Path.Combine(Directory.GetCurrentDirectory(), "pki") })
{
if (dir != null && Directory.Exists(dir))
{
try
{
Directory.Delete(dir, true);
}
catch
{
// ignore cleanup errors
}
}
}
GC.SuppressFinalize(this);
}
}
}