Add integration test harness: OpcUaServiceBuilder + OpcUaServerFixture
OpcUaServiceBuilder provides fluent API for constructing OpcUaService with dependency overrides (IMxProxy, IGalaxyRepository, IMxAccessClient). WithMxAccessClient skips the STA thread and COM interop entirely. OpcUaServerFixture wraps the service lifecycle with automatic port allocation (atomic counter starting at 16000), guaranteed cleanup via IAsyncLifetime, and factory methods for common test scenarios: - WithFakes() — FakeMxProxy + FakeGalaxyRepository with standard data - WithFakeMxAccessClient() — bypasses COM, fastest for most tests Also adds TestData helper with reusable hierarchy/attributes matching gr/layout.md, and 5 fixture tests verifying startup, shutdown, port isolation, and address space building. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,11 +22,14 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
private readonly AppConfiguration _config;
|
||||
private readonly IMxProxy? _mxProxy;
|
||||
private readonly IGalaxyRepository? _galaxyRepository;
|
||||
private readonly IMxAccessClient? _mxAccessClientOverride;
|
||||
private readonly bool _hasMxAccessClientOverride;
|
||||
|
||||
private CancellationTokenSource? _cts;
|
||||
private PerformanceMetrics? _metrics;
|
||||
private StaComThread? _staThread;
|
||||
private MxAccessClient? _mxAccessClient;
|
||||
private IMxAccessClient? _mxAccessClientForWiring;
|
||||
private ChangeDetectionService? _changeDetection;
|
||||
private OpcUaServerHost? _serverHost;
|
||||
private LmxNodeManager? _nodeManager;
|
||||
@@ -59,11 +62,14 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
/// <summary>
|
||||
/// Test constructor. Accepts injected dependencies.
|
||||
/// </summary>
|
||||
internal OpcUaService(AppConfiguration config, IMxProxy? mxProxy, IGalaxyRepository? galaxyRepository)
|
||||
internal OpcUaService(AppConfiguration config, IMxProxy? mxProxy, IGalaxyRepository? galaxyRepository,
|
||||
IMxAccessClient? mxAccessClientOverride = null, bool hasMxAccessClientOverride = false)
|
||||
{
|
||||
_config = config;
|
||||
_mxProxy = mxProxy;
|
||||
_galaxyRepository = galaxyRepository;
|
||||
_mxAccessClientOverride = mxAccessClientOverride;
|
||||
_hasMxAccessClientOverride = hasMxAccessClientOverride;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@@ -87,7 +93,16 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
_metrics = new PerformanceMetrics();
|
||||
|
||||
// Step 5: Create MxAccessClient → Connect
|
||||
if (_mxProxy != null)
|
||||
if (_hasMxAccessClientOverride)
|
||||
{
|
||||
// Test path: use injected IMxAccessClient directly (skips STA thread + COM)
|
||||
_mxAccessClientForWiring = _mxAccessClientOverride;
|
||||
if (_mxAccessClientForWiring != null && _mxAccessClientForWiring.State != ConnectionState.Connected)
|
||||
{
|
||||
_mxAccessClientForWiring.ConnectAsync(_cts.Token).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
else if (_mxProxy != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -121,8 +136,8 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
}
|
||||
|
||||
// Step 8: Create OPC UA server host + node manager
|
||||
IMxAccessClient mxClient = _mxAccessClient ?? (IMxAccessClient)new NullMxAccessClient();
|
||||
_serverHost = new OpcUaServerHost(_config.OpcUa, mxClient, _metrics);
|
||||
var effectiveMxClient = (IMxAccessClient?)_mxAccessClient ?? _mxAccessClientForWiring ?? new NullMxAccessClient();
|
||||
_serverHost = new OpcUaServerHost(_config.OpcUa, effectiveMxClient, _metrics);
|
||||
|
||||
// Step 9-10: Query hierarchy, start server, build address space
|
||||
if (_galaxyRepository != null && _galaxyStats.DbConnected)
|
||||
@@ -170,7 +185,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
// Step 13: Dashboard
|
||||
_healthCheck = new HealthCheckService();
|
||||
_statusReport = new StatusReportService(_healthCheck, _config.Dashboard.RefreshIntervalSeconds);
|
||||
_statusReport.SetComponents(_mxAccessClient, _metrics, _galaxyStats, _serverHost);
|
||||
_statusReport.SetComponents(effectiveMxClient, _metrics, _galaxyStats, _serverHost);
|
||||
|
||||
if (_config.Dashboard.Enabled)
|
||||
{
|
||||
@@ -253,7 +268,7 @@ namespace ZB.MOM.WW.LmxOpcUa.Host
|
||||
}
|
||||
|
||||
// Accessors for testing
|
||||
internal IMxAccessClient? MxClient => _mxAccessClient;
|
||||
internal IMxAccessClient? MxClient => (IMxAccessClient?)_mxAccessClient ?? _mxAccessClientForWiring;
|
||||
internal PerformanceMetrics? Metrics => _metrics;
|
||||
internal OpcUaServerHost? ServerHost => _serverHost;
|
||||
internal LmxNodeManager? NodeManagerInstance => _nodeManager;
|
||||
|
||||
Reference in New Issue
Block a user