using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
///
/// Scaffold tests for 's
/// surface that don't require a live remote server. Live-browse coverage lands in a
/// follow-up PR once the in-process OPC UA server fixture is scaffolded.
///
[Trait("Category", "Unit")]
public sealed class OpcUaClientDiscoveryTests
{
/// Verifies that DiscoverAsync throws InvalidOperationException when not initialized.
[Fact]
public async Task DiscoverAsync_without_initialize_throws_InvalidOperationException()
{
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-disco");
var builder = new NullAddressSpaceBuilder();
await Should.ThrowAsync(async () =>
await drv.DiscoverAsync(builder, TestContext.Current.CancellationToken));
}
/// Verifies that DiscoverAsync rejects null builder argument.
[Fact]
public void DiscoverAsync_rejects_null_builder()
{
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-disco");
Should.ThrowAsync(async () =>
await drv.DiscoverAsync(null!, TestContext.Current.CancellationToken));
}
/// Verifies that discovery configuration has sensible defaults.
[Fact]
public void Discovery_caps_are_sensible_defaults()
{
var opts = new OpcUaClientDriverOptions();
opts.MaxDiscoveredNodes.ShouldBe(10_000, "bounds memory on runaway servers without clipping normal models");
opts.MaxBrowseDepth.ShouldBe(10, "deep enough for realistic info models; shallow enough for cycle safety");
opts.BrowseRoot.ShouldBeNull("null = default to ObjectsFolder i=85");
}
/// Test builder that provides no-op implementations for discovery tests.
private sealed class NullAddressSpaceBuilder : IAddressSpaceBuilder
{
/// Returns this builder (no-op).
/// The browse name of the folder.
/// The display name of the folder.
public IAddressSpaceBuilder Folder(string browseName, string displayName) => this;
/// Returns a stub handle.
/// The browse name of the variable.
/// The display name of the variable.
/// The attribute information for the variable.
public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo)
=> new StubHandle();
/// No-op property addition.
/// The browse name of the property.
/// The data type of the property.
/// The property value.
public void AddProperty(string browseName, DriverDataType dataType, object? value) { }
/// No-op alarm condition attachment.
/// The source variable handle.
/// The alarm name.
/// The alarm attribute information.
public void AttachAlarmCondition(IVariableHandle sourceVariable, string alarmName, DriverAttributeInfo alarmInfo) { }
/// Stub variable handle for testing.
private sealed class StubHandle : IVariableHandle
{
/// Gets the full reference as "stub".
public string FullReference => "stub";
/// Throws NotSupportedException.
/// The alarm condition information (unused).
public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => throw new NotSupportedException();
}
}
}