Expand XML docs across bridge and test code
This commit is contained in:
@@ -6,40 +6,88 @@ using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// In-memory Galaxy repository used by tests to control hierarchy rows, attribute rows, and deploy metadata without SQL access.
|
||||
/// </summary>
|
||||
public class FakeGalaxyRepository : IGalaxyRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when the fake repository simulates a Galaxy deploy change.
|
||||
/// </summary>
|
||||
public event Action? OnGalaxyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the hierarchy rows returned to address-space construction logic.
|
||||
/// </summary>
|
||||
public List<GalaxyObjectInfo> Hierarchy { get; set; } = new List<GalaxyObjectInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the attribute rows returned to address-space construction logic.
|
||||
/// </summary>
|
||||
public List<GalaxyAttributeInfo> Attributes { get; set; } = new List<GalaxyAttributeInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the deploy timestamp returned to change-detection logic.
|
||||
/// </summary>
|
||||
public DateTime? LastDeployTime { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether connection checks should report success.
|
||||
/// </summary>
|
||||
public bool ConnectionSucceeds { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether repository calls should throw to simulate database failures.
|
||||
/// </summary>
|
||||
public bool ShouldThrow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the configured hierarchy rows or throws to simulate a repository failure.
|
||||
/// </summary>
|
||||
/// <param name="ct">A cancellation token ignored by the in-memory fake.</param>
|
||||
/// <returns>The configured hierarchy rows.</returns>
|
||||
public Task<List<GalaxyObjectInfo>> GetHierarchyAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (ShouldThrow) throw new Exception("Simulated DB failure");
|
||||
return Task.FromResult(Hierarchy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the configured attribute rows or throws to simulate a repository failure.
|
||||
/// </summary>
|
||||
/// <param name="ct">A cancellation token ignored by the in-memory fake.</param>
|
||||
/// <returns>The configured attribute rows.</returns>
|
||||
public Task<List<GalaxyAttributeInfo>> GetAttributesAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (ShouldThrow) throw new Exception("Simulated DB failure");
|
||||
return Task.FromResult(Attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the configured deploy timestamp or throws to simulate a repository failure.
|
||||
/// </summary>
|
||||
/// <param name="ct">A cancellation token ignored by the in-memory fake.</param>
|
||||
/// <returns>The configured deploy timestamp.</returns>
|
||||
public Task<DateTime?> GetLastDeployTimeAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (ShouldThrow) throw new Exception("Simulated DB failure");
|
||||
return Task.FromResult(LastDeployTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the configured connection result or throws to simulate a repository failure.
|
||||
/// </summary>
|
||||
/// <param name="ct">A cancellation token ignored by the in-memory fake.</param>
|
||||
/// <returns>The configured connection result.</returns>
|
||||
public Task<bool> TestConnectionAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (ShouldThrow) throw new Exception("Simulated DB failure");
|
||||
return Task.FromResult(ConnectionSucceeds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the deploy-change event so tests can trigger rebuild logic.
|
||||
/// </summary>
|
||||
public void RaiseGalaxyChanged() => OnGalaxyChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,44 +7,99 @@ using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// In-memory IMxAccessClient used by tests to drive connection, read, write, and subscription scenarios without COM runtime dependencies.
|
||||
/// </summary>
|
||||
public class FakeMxAccessClient : IMxAccessClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the connection state returned to the system under test.
|
||||
/// </summary>
|
||||
public ConnectionState State { get; set; } = ConnectionState.Connected;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of active subscriptions currently stored by the fake client.
|
||||
/// </summary>
|
||||
public int ActiveSubscriptionCount => _subscriptions.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reconnect count exposed to health and dashboard tests.
|
||||
/// </summary>
|
||||
public int ReconnectCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when tests explicitly simulate a connection-state transition.
|
||||
/// </summary>
|
||||
public event EventHandler<ConnectionStateChangedEventArgs>? ConnectionStateChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when tests publish a simulated runtime value change.
|
||||
/// </summary>
|
||||
public event Action<string, Vtq>? OnTagValueChanged;
|
||||
|
||||
private readonly ConcurrentDictionary<string, Action<string, Vtq>> _subscriptions = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the in-memory tag-value table returned by fake reads.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, Vtq> TagValues { get; } = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values written through the fake client so tests can assert write behavior.
|
||||
/// </summary>
|
||||
public List<(string Tag, object Value)> WrittenValues { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the result returned by fake writes to simulate success or failure.
|
||||
/// </summary>
|
||||
public bool WriteResult { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Simulates establishing a healthy runtime connection.
|
||||
/// </summary>
|
||||
/// <param name="ct">A cancellation token that is ignored by the in-memory fake.</param>
|
||||
public Task ConnectAsync(CancellationToken ct = default)
|
||||
{
|
||||
State = ConnectionState.Connected;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates disconnecting from the runtime.
|
||||
/// </summary>
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
State = ConnectionState.Disconnected;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores a subscription callback so later simulated data changes can target it.
|
||||
/// </summary>
|
||||
/// <param name="fullTagReference">The Galaxy attribute reference to monitor.</param>
|
||||
/// <param name="callback">The callback that should receive simulated value changes.</param>
|
||||
public Task SubscribeAsync(string fullTagReference, Action<string, Vtq> callback)
|
||||
{
|
||||
_subscriptions[fullTagReference] = callback;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a stored subscription callback for the specified tag reference.
|
||||
/// </summary>
|
||||
/// <param name="fullTagReference">The Galaxy attribute reference to stop monitoring.</param>
|
||||
public Task UnsubscribeAsync(string fullTagReference)
|
||||
{
|
||||
_subscriptions.TryRemove(fullTagReference, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current in-memory VTQ for a tag reference or a bad-quality placeholder when none has been seeded.
|
||||
/// </summary>
|
||||
/// <param name="fullTagReference">The Galaxy attribute reference to read.</param>
|
||||
/// <param name="ct">A cancellation token that is ignored by the in-memory fake.</param>
|
||||
/// <returns>The seeded VTQ value or a bad not-connected VTQ when the tag was not populated.</returns>
|
||||
public Task<Vtq> ReadAsync(string fullTagReference, CancellationToken ct = default)
|
||||
{
|
||||
if (TagValues.TryGetValue(fullTagReference, out var vtq))
|
||||
@@ -52,6 +107,13 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return Task.FromResult(Vtq.Bad(Quality.BadNotConnected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records a write request, optionally updates the in-memory tag table, and returns the configured write result.
|
||||
/// </summary>
|
||||
/// <param name="fullTagReference">The Galaxy attribute reference being written.</param>
|
||||
/// <param name="value">The value supplied by the code under test.</param>
|
||||
/// <param name="ct">A cancellation token that is ignored by the in-memory fake.</param>
|
||||
/// <returns>A completed task returning the configured write outcome.</returns>
|
||||
public Task<bool> WriteAsync(string fullTagReference, object value, CancellationToken ct = default)
|
||||
{
|
||||
WrittenValues.Add((fullTagReference, value));
|
||||
@@ -60,6 +122,11 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return Task.FromResult(WriteResult);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a simulated tag-value change to both the event stream and any stored subscription callback.
|
||||
/// </summary>
|
||||
/// <param name="address">The Galaxy attribute reference whose value changed.</param>
|
||||
/// <param name="vtq">The value, timestamp, and quality payload to publish.</param>
|
||||
public void SimulateDataChange(string address, Vtq vtq)
|
||||
{
|
||||
OnTagValueChanged?.Invoke(address, vtq);
|
||||
@@ -67,12 +134,20 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
callback(address, vtq);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises a simulated connection-state transition for health and reconnect tests.
|
||||
/// </summary>
|
||||
/// <param name="prev">The previous connection state.</param>
|
||||
/// <param name="curr">The new connection state.</param>
|
||||
public void RaiseConnectionStateChanged(ConnectionState prev, ConnectionState curr)
|
||||
{
|
||||
State = curr;
|
||||
ConnectionStateChanged?.Invoke(this, new ConnectionStateChangedEventArgs(prev, curr));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the fake client. No unmanaged resources are held.
|
||||
/// </summary>
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,21 +17,71 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
private int _connectionHandle;
|
||||
private bool _registered;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the fake proxy publishes a simulated runtime data-change callback to the system under test.
|
||||
/// </summary>
|
||||
public event MxDataChangeHandler? OnDataChange;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the fake proxy publishes a simulated write-complete callback to the system under test.
|
||||
/// </summary>
|
||||
public event MxWriteCompleteHandler? OnWriteComplete;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item-handle to tag-reference map built by the test as attributes are registered with the fake runtime.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<int, string> Items { get; } = new ConcurrentDictionary<int, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item handles currently marked as advised so tests can assert subscription behavior.
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<int, bool> AdvisedItems { get; } = new ConcurrentDictionary<int, bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values written through the fake runtime so write scenarios can assert the final payload.
|
||||
/// </summary>
|
||||
public List<(string Address, object Value)> WrittenValues { get; } = new List<(string, object)>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the fake runtime is currently considered registered.
|
||||
/// </summary>
|
||||
public bool IsRegistered => _registered;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times the system under test attempted to register with the fake runtime.
|
||||
/// </summary>
|
||||
public int RegisterCallCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of times the system under test attempted to unregister from the fake runtime.
|
||||
/// </summary>
|
||||
public int UnregisterCallCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether registration should fail to exercise connection-error paths.
|
||||
/// </summary>
|
||||
public bool ShouldFailRegister { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether writes should fail to exercise runtime write-error paths.
|
||||
/// </summary>
|
||||
public bool ShouldFailWrite { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the fake should suppress the write-complete callback for timeout scenarios.
|
||||
/// </summary>
|
||||
public bool SkipWriteCompleteCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status code returned in the simulated write-complete callback.
|
||||
/// </summary>
|
||||
public int WriteCompleteStatus { get; set; } = 0; // 0 = success
|
||||
|
||||
/// <summary>
|
||||
/// Simulates the MXAccess registration handshake and returns a synthetic connection handle.
|
||||
/// </summary>
|
||||
/// <param name="clientName">The client name supplied by the code under test.</param>
|
||||
/// <returns>A synthetic connection handle for subsequent fake operations.</returns>
|
||||
public int Register(string clientName)
|
||||
{
|
||||
RegisterCallCount++;
|
||||
@@ -41,6 +91,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return _connectionHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates tearing down the fake MXAccess connection.
|
||||
/// </summary>
|
||||
/// <param name="handle">The connection handle supplied by the code under test.</param>
|
||||
public void Unregister(int handle)
|
||||
{
|
||||
UnregisterCallCount++;
|
||||
@@ -48,6 +102,12 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
_connectionHandle = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates resolving a tag reference into a fake runtime item handle.
|
||||
/// </summary>
|
||||
/// <param name="handle">The synthetic connection handle.</param>
|
||||
/// <param name="address">The Galaxy attribute reference being registered.</param>
|
||||
/// <returns>A synthetic item handle.</returns>
|
||||
public int AddItem(int handle, string address)
|
||||
{
|
||||
var itemHandle = Interlocked.Increment(ref _nextHandle);
|
||||
@@ -55,21 +115,43 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return itemHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates removing an item from the fake runtime session.
|
||||
/// </summary>
|
||||
/// <param name="handle">The synthetic connection handle.</param>
|
||||
/// <param name="itemHandle">The synthetic item handle to remove.</param>
|
||||
public void RemoveItem(int handle, int itemHandle)
|
||||
{
|
||||
Items.TryRemove(itemHandle, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks an item as actively advised so tests can assert subscription activation.
|
||||
/// </summary>
|
||||
/// <param name="handle">The synthetic connection handle.</param>
|
||||
/// <param name="itemHandle">The synthetic item handle being monitored.</param>
|
||||
public void AdviseSupervisory(int handle, int itemHandle)
|
||||
{
|
||||
AdvisedItems[itemHandle] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks an item as no longer advised so tests can assert subscription teardown.
|
||||
/// </summary>
|
||||
/// <param name="handle">The synthetic connection handle.</param>
|
||||
/// <param name="itemHandle">The synthetic item handle no longer being monitored.</param>
|
||||
public void UnAdviseSupervisory(int handle, int itemHandle)
|
||||
{
|
||||
AdvisedItems.TryRemove(itemHandle, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simulates a runtime write, records the written value, and optionally raises the write-complete callback.
|
||||
/// </summary>
|
||||
/// <param name="handle">The synthetic connection handle.</param>
|
||||
/// <param name="itemHandle">The synthetic item handle to write.</param>
|
||||
/// <param name="value">The value supplied by the system under test.</param>
|
||||
/// <param name="securityClassification">The security classification supplied with the write request.</param>
|
||||
public void Write(int handle, int itemHandle, object value, int securityClassification)
|
||||
{
|
||||
if (ShouldFailWrite) throw new InvalidOperationException("Write failed (simulated)");
|
||||
@@ -95,6 +177,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <summary>
|
||||
/// Simulates an MXAccess data change event for a specific item handle.
|
||||
/// </summary>
|
||||
/// <param name="itemHandle">The synthetic item handle that should receive the new value.</param>
|
||||
/// <param name="value">The value to publish to the system under test.</param>
|
||||
/// <param name="quality">The runtime quality code to send with the value.</param>
|
||||
/// <param name="timestamp">The optional timestamp to send with the value; defaults to the current UTC time.</param>
|
||||
public void SimulateDataChange(int itemHandle, object value, int quality = 192, DateTime? timestamp = null)
|
||||
{
|
||||
var status = new MXSTATUS_PROXY[1];
|
||||
@@ -106,6 +192,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <summary>
|
||||
/// Simulates data change for a specific address (finds handle by address).
|
||||
/// </summary>
|
||||
/// <param name="address">The Galaxy attribute reference whose registered handle should receive the new value.</param>
|
||||
/// <param name="value">The value to publish to the system under test.</param>
|
||||
/// <param name="quality">The runtime quality code to send with the value.</param>
|
||||
/// <param name="timestamp">The optional timestamp to send with the value; defaults to the current UTC time.</param>
|
||||
public void SimulateDataChangeByAddress(string address, object value, int quality = 192, DateTime? timestamp = null)
|
||||
{
|
||||
foreach (var kvp in Items)
|
||||
|
||||
@@ -21,8 +21,19 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
{
|
||||
private static int _nextPort = 16000;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the started service instance managed by the fixture.
|
||||
/// </summary>
|
||||
public OpcUaService Service { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the OPC UA port assigned to this fixture instance.
|
||||
/// </summary>
|
||||
public int OpcUaPort { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the OPC UA endpoint URL exposed by the fixture.
|
||||
/// </summary>
|
||||
public string EndpointUrl => $"opc.tcp://localhost:{OpcUaPort}/LmxOpcUa";
|
||||
|
||||
/// <summary>
|
||||
@@ -44,6 +55,13 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
private readonly OpcUaServiceBuilder _builder;
|
||||
private bool _started;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a fixture around a prepared service builder and optional fake dependencies.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder used to construct the service under test.</param>
|
||||
/// <param name="repo">The optional fake Galaxy repository exposed to tests.</param>
|
||||
/// <param name="mxClient">The optional fake MXAccess client exposed to tests.</param>
|
||||
/// <param name="mxProxy">The optional fake MXAccess proxy exposed to tests.</param>
|
||||
private OpcUaServerFixture(OpcUaServiceBuilder builder,
|
||||
FakeGalaxyRepository? repo = null,
|
||||
FakeMxAccessClient? mxClient = null,
|
||||
@@ -62,6 +80,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// Creates fixture with FakeMxProxy + FakeGalaxyRepository (standard test data).
|
||||
/// The STA thread and COM interop run against FakeMxProxy.
|
||||
/// </summary>
|
||||
/// <param name="proxy">An optional fake proxy to inject; otherwise a default fake is created.</param>
|
||||
/// <param name="repo">An optional fake repository to inject; otherwise standard test data is used.</param>
|
||||
/// <returns>A fixture configured to exercise the COM-style runtime path.</returns>
|
||||
public static OpcUaServerFixture WithFakes(
|
||||
FakeMxProxy? proxy = null,
|
||||
FakeGalaxyRepository? repo = null)
|
||||
@@ -85,6 +106,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// Creates fixture using FakeMxAccessClient directly — skips STA thread + COM entirely.
|
||||
/// Fastest option for tests that don't need real COM interop.
|
||||
/// </summary>
|
||||
/// <param name="mxClient">An optional fake MXAccess client to inject; otherwise a default fake is created.</param>
|
||||
/// <param name="repo">An optional fake repository to inject; otherwise standard test data is used.</param>
|
||||
/// <returns>A fixture configured to exercise the direct fake-client path.</returns>
|
||||
public static OpcUaServerFixture WithFakeMxAccessClient(
|
||||
FakeMxAccessClient? mxClient = null,
|
||||
FakeGalaxyRepository? repo = null)
|
||||
@@ -104,6 +128,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return new OpcUaServerFixture(builder, repo: r, mxClient: client);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds and starts the OPC UA service for the current fixture.
|
||||
/// </summary>
|
||||
public Task InitializeAsync()
|
||||
{
|
||||
Service = _builder.Build();
|
||||
@@ -112,6 +139,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the OPC UA service when the fixture had previously been started.
|
||||
/// </summary>
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
if (_started)
|
||||
|
||||
@@ -6,8 +6,14 @@ using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
||||
|
||||
namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies the reusable OPC UA server fixture used by integration and wiring tests.
|
||||
/// </summary>
|
||||
public class OpcUaServerFixtureTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Confirms that the standard fake-backed fixture starts the bridge and tears it down cleanly.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task WithFakes_StartsAndStops()
|
||||
{
|
||||
@@ -25,6 +31,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
await fixture.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirms that the fake-client fixture bypasses COM wiring and uses the provided fake runtime client.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task WithFakeMxAccessClient_SkipsCom()
|
||||
{
|
||||
@@ -38,6 +47,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
await fixture.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirms that separate fixture instances automatically allocate unique OPC UA ports.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task MultipleFixtures_GetUniquePortsAutomatically()
|
||||
{
|
||||
@@ -57,6 +69,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
await fixture2.DisposeAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirms that fixture shutdown completes quickly enough for the integration test suite.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Shutdown_CompletesWithin30Seconds()
|
||||
{
|
||||
@@ -70,6 +85,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
sw.Elapsed.TotalSeconds.ShouldBeLessThan(30);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Confirms that the fake-backed fixture builds the seeded address space and Galaxy statistics.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task WithFakes_BuildsAddressSpace()
|
||||
{
|
||||
|
||||
@@ -16,11 +16,16 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
{
|
||||
private Session? _session;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active OPC UA session used by integration tests once the helper has connected to the bridge.
|
||||
/// </summary>
|
||||
public Session Session => _session ?? throw new InvalidOperationException("Not connected");
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the namespace index for a given namespace URI (e.g., "urn:TestGalaxy:LmxOpcUa").
|
||||
/// </summary>
|
||||
/// <param name="galaxyName">The Galaxy name whose OPC UA namespace should be resolved on the test server.</param>
|
||||
/// <returns>The namespace index assigned by the server for the requested Galaxy namespace.</returns>
|
||||
public ushort GetNamespaceIndex(string galaxyName = "TestGalaxy")
|
||||
{
|
||||
var nsUri = $"urn:{galaxyName}:LmxOpcUa";
|
||||
@@ -32,11 +37,18 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <summary>
|
||||
/// Creates a NodeId in the LmxOpcUa namespace using the server's actual namespace index.
|
||||
/// </summary>
|
||||
/// <param name="identifier">The string identifier for the node inside the Galaxy namespace.</param>
|
||||
/// <param name="galaxyName">The Galaxy name whose namespace should be used for the node identifier.</param>
|
||||
/// <returns>A node identifier that targets the requested node on the test server.</returns>
|
||||
public NodeId MakeNodeId(string identifier, string galaxyName = "TestGalaxy")
|
||||
{
|
||||
return new NodeId(identifier, GetNamespaceIndex(galaxyName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects the helper to an OPC UA endpoint exposed by the test bridge.
|
||||
/// </summary>
|
||||
/// <param name="endpointUrl">The OPC UA endpoint URL to connect to.</param>
|
||||
public async Task ConnectAsync(string endpointUrl)
|
||||
{
|
||||
var config = new ApplicationConfiguration
|
||||
@@ -87,6 +99,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <summary>
|
||||
/// Browse children of a node. Returns list of (DisplayName, NodeId, NodeClass).
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node whose hierarchical children should be browsed.</param>
|
||||
/// <returns>The child nodes exposed beneath the requested node.</returns>
|
||||
public async Task<List<(string Name, NodeId NodeId, NodeClass NodeClass)>> BrowseAsync(NodeId nodeId)
|
||||
{
|
||||
var results = new List<(string, NodeId, NodeClass)>();
|
||||
@@ -109,6 +123,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// <summary>
|
||||
/// Read a node's value.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node whose current value should be read from the server.</param>
|
||||
/// <returns>The OPC UA data value returned by the server.</returns>
|
||||
public DataValue Read(NodeId nodeId)
|
||||
{
|
||||
return Session.ReadValue(nodeId);
|
||||
@@ -118,6 +134,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// Write a node's value, optionally using an OPC UA index range for array element writes.
|
||||
/// Returns the server status code for the write.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node whose value should be written.</param>
|
||||
/// <param name="value">The value to send to the server.</param>
|
||||
/// <param name="indexRange">An optional OPC UA index range used for array element writes.</param>
|
||||
/// <returns>The server status code returned for the write request.</returns>
|
||||
public StatusCode Write(NodeId nodeId, object value, string? indexRange = null)
|
||||
{
|
||||
var nodesToWrite = new WriteValueCollection
|
||||
@@ -139,6 +159,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// Create a subscription with a monitored item on the given node.
|
||||
/// Returns the subscription and monitored item for inspection.
|
||||
/// </summary>
|
||||
/// <param name="nodeId">The node whose value changes should be monitored.</param>
|
||||
/// <param name="intervalMs">The publishing and sampling interval, in milliseconds, for the test subscription.</param>
|
||||
/// <returns>The created subscription and monitored item pair for later assertions and cleanup.</returns>
|
||||
public async Task<(Subscription Sub, MonitoredItem Item)> SubscribeAsync(
|
||||
NodeId nodeId, int intervalMs = 250)
|
||||
{
|
||||
@@ -162,6 +185,9 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
return (subscription, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the test session and releases OPC UA client resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_session != null)
|
||||
|
||||
@@ -8,6 +8,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
/// </summary>
|
||||
public static class TestData
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the standard Galaxy hierarchy used by integration and wiring tests.
|
||||
/// </summary>
|
||||
/// <returns>The standard hierarchy rows for the fake repository.</returns>
|
||||
public static List<GalaxyObjectInfo> CreateStandardHierarchy()
|
||||
{
|
||||
return new List<GalaxyObjectInfo>
|
||||
@@ -20,6 +24,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the standard attribute set used by integration and wiring tests.
|
||||
/// </summary>
|
||||
/// <returns>The standard attribute rows for the fake repository.</returns>
|
||||
public static List<GalaxyAttributeInfo> CreateStandardAttributes()
|
||||
{
|
||||
return new List<GalaxyAttributeInfo>
|
||||
@@ -33,6 +41,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a minimal hierarchy containing a single object for focused unit tests.
|
||||
/// </summary>
|
||||
/// <returns>A minimal hierarchy row set.</returns>
|
||||
public static List<GalaxyObjectInfo> CreateMinimalHierarchy()
|
||||
{
|
||||
return new List<GalaxyObjectInfo>
|
||||
@@ -41,6 +53,10 @@ namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a minimal attribute set containing a single scalar attribute for focused unit tests.
|
||||
/// </summary>
|
||||
/// <returns>A minimal attribute row set.</returns>
|
||||
public static List<GalaxyAttributeInfo> CreateMinimalAttributes()
|
||||
{
|
||||
return new List<GalaxyAttributeInfo>
|
||||
|
||||
Reference in New Issue
Block a user