using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using ZB.MOM.WW.OtOpcUa.Host.Configuration; using ZB.MOM.WW.OtOpcUa.Host.Domain; namespace ZB.MOM.WW.OtOpcUa.Host { /// /// Fluent builder for constructing OpcUaService with dependency overrides. /// Used by integration tests to substitute fakes for COM/DB components. /// internal class OpcUaServiceBuilder { private IUserAuthenticationProvider? _authProvider; private bool _authProviderSet; private AppConfiguration _config = new(); private IGalaxyRepository? _galaxyRepository; private bool _galaxyRepositorySet; private IMxAccessClient? _mxAccessClient; private bool _mxAccessClientSet; private IMxProxy? _mxProxy; private bool _mxProxySet; /// /// Replaces the default service configuration used by the test host. /// /// The full configuration snapshot to inject into the service under test. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithConfig(AppConfiguration config) { _config = config; return this; } /// /// Sets the OPC UA port used by the test host so multiple integration runs can coexist. /// /// The TCP port to expose for the test server. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithOpcUaPort(int port) { _config.OpcUa.Port = port; return this; } /// /// Sets the Galaxy name represented by the test address space. /// /// The Galaxy name to expose through OPC UA and diagnostics. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithGalaxyName(string name) { _config.OpcUa.GalaxyName = name; return this; } /// /// Injects an MXAccess proxy substitute for tests that exercise the proxy-driven runtime path. /// /// The proxy fake or stub to supply to the service. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithMxProxy(IMxProxy? proxy) { _mxProxy = proxy; _mxProxySet = true; return this; } /// /// Injects a repository substitute for tests that control Galaxy hierarchy and deploy metadata. /// /// The repository fake or stub to supply to the service. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithGalaxyRepository(IGalaxyRepository? repository) { _galaxyRepository = repository; _galaxyRepositorySet = true; return this; } /// /// Override the MxAccessClient directly, skipping STA thread and COM interop entirely. /// When set, the service will use this client instead of creating one from IMxProxy. /// /// The direct MXAccess client substitute to inject into the service. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithMxAccessClient(IMxAccessClient? client) { _mxAccessClient = client; _mxAccessClientSet = true; return this; } /// /// Seeds a convenience fake repository with Galaxy hierarchy and attribute rows for address-space tests. /// /// The object hierarchy to expose through the test OPC UA namespace. /// The attribute rows to attach to the hierarchy. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithHierarchy(List hierarchy, List attributes) { if (!_galaxyRepositorySet) { var fake = new FakeBuilderGalaxyRepository(); _galaxyRepository = fake; _galaxyRepositorySet = true; } if (_galaxyRepository is FakeBuilderGalaxyRepository fakeRepo) { fakeRepo.Hierarchy = hierarchy; fakeRepo.Attributes = attributes; } return this; } /// /// Disables the embedded dashboard so tests can focus on the runtime bridge without binding the HTTP listener. /// /// The current builder so additional overrides can be chained. /// /// Injects a custom authentication provider for tests that need deterministic role resolution. /// public OpcUaServiceBuilder WithAuthProvider(IUserAuthenticationProvider? provider) { _authProvider = provider; _authProviderSet = true; return this; } /// /// Sets the authentication configuration for the test host. /// public OpcUaServiceBuilder WithAuthentication(AuthenticationConfiguration authConfig) { _config.Authentication = authConfig; return this; } public OpcUaServiceBuilder DisableDashboard() { _config.Dashboard.Enabled = false; return this; } /// /// Sets the redundancy configuration for the test host. /// /// The redundancy configuration to inject. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithRedundancy(RedundancyConfiguration redundancy) { _config.Redundancy = redundancy; return this; } /// /// Sets the application URI for the test host, distinct from the namespace URI. /// /// The unique application URI for this server instance. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithApplicationUri(string applicationUri) { _config.OpcUa.ApplicationUri = applicationUri; return this; } /// /// Sets the security profile configuration for the test host. /// /// The security profile configuration to inject. /// The current builder so additional overrides can be chained. /// /// Enables alarm condition tracking on the test host so integration tests can exercise the alarm-creation path. /// /// Whether alarm tracking should be enabled. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithAlarmTracking(bool enabled) { _config.OpcUa.AlarmTrackingEnabled = enabled; return this; } /// /// Configures the template-based alarm object filter for integration tests. /// /// Zero or more wildcard patterns. Empty → filter disabled. /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder WithAlarmFilter(params string[] filters) { _config.OpcUa.AlarmFilter = new AlarmFilterConfiguration { ObjectFilters = filters.ToList() }; return this; } public OpcUaServiceBuilder WithSecurity(SecurityProfileConfiguration security) { _config.Security = security; return this; } /// /// Effectively disables Galaxy change detection by pushing the polling interval beyond realistic test durations. /// /// The current builder so additional overrides can be chained. public OpcUaServiceBuilder DisableChangeDetection() { _config.GalaxyRepository.ChangeDetectionIntervalSeconds = int.MaxValue; return this; } /// /// Creates an using the accumulated test doubles and configuration overrides. /// /// A service instance ready for integration-style testing. public OpcUaService Build() { return new OpcUaService( _config, _mxProxySet ? _mxProxy : null, _galaxyRepositorySet ? _galaxyRepository : null, _mxAccessClientSet ? _mxAccessClient : null, _mxAccessClientSet, _authProviderSet ? _authProvider : null, _authProviderSet); } /// /// Internal fake repository used by WithHierarchy for convenience. /// private class FakeBuilderGalaxyRepository : IGalaxyRepository { /// /// Gets or sets the hierarchy rows that the fake repository returns to the service. /// public List Hierarchy { get; set; } = new(); /// /// Gets or sets the attribute rows that the fake repository returns to the service. /// public List Attributes { get; set; } = new(); /// /// Occurs when the fake repository wants to simulate a Galaxy deploy change. /// public event Action? OnGalaxyChanged; /// /// Returns the seeded hierarchy rows for address-space construction. /// /// A cancellation token that is ignored by the in-memory fake. /// The configured hierarchy rows. public Task> GetHierarchyAsync(CancellationToken ct = default) { return Task.FromResult(Hierarchy); } /// /// Returns the seeded attribute rows for address-space construction. /// /// A cancellation token that is ignored by the in-memory fake. /// The configured attribute rows. public Task> GetAttributesAsync(CancellationToken ct = default) { return Task.FromResult(Attributes); } /// /// Returns the current UTC time so change-detection tests have a deploy timestamp to compare against. /// /// A cancellation token that is ignored by the in-memory fake. /// The current UTC time. public Task GetLastDeployTimeAsync(CancellationToken ct = default) { return Task.FromResult(DateTime.UtcNow); } /// /// Reports a healthy repository connection for builder-based test setups. /// /// A cancellation token that is ignored by the in-memory fake. /// A completed task returning . public Task TestConnectionAsync(CancellationToken ct = default) { return Task.FromResult(true); } } } }