feat(dcl): BrowseNext continuation paging + StubOpcUaClient canned browse (T15)
This commit is contained in:
+4
-4
@@ -105,7 +105,7 @@ public class DataConnectionManagerBrowseHandlerTests : TestKit
|
||||
new BrowseNode("ns=2;s=B", "B", BrowseNodeClass.Object, HasChildren: true),
|
||||
};
|
||||
((IBrowsableDataConnection)adapter)
|
||||
.BrowseChildrenAsync(null, Arg.Any<CancellationToken>())
|
||||
.BrowseChildrenAsync(null, Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(new BrowseChildrenResult(children, Truncated: false));
|
||||
|
||||
_factory.Create("OpcUa", Arg.Any<IDictionary<string, string>>())
|
||||
@@ -139,7 +139,7 @@ public class DataConnectionManagerBrowseHandlerTests : TestKit
|
||||
((IDataConnection)adapter).Status.Returns(ConnectionHealth.Connected);
|
||||
|
||||
((IBrowsableDataConnection)adapter)
|
||||
.BrowseChildrenAsync(Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.BrowseChildrenAsync(Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromException<BrowseChildrenResult>(
|
||||
new ConnectionNotConnectedException("OPC UA session is not connected.")));
|
||||
|
||||
@@ -183,7 +183,7 @@ public class DataConnectionManagerBrowseHandlerTests : TestKit
|
||||
((IDataConnection)adapter).Status.Returns(ConnectionHealth.Connected);
|
||||
|
||||
((IBrowsableDataConnection)adapter)
|
||||
.BrowseChildrenAsync(Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.BrowseChildrenAsync(Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromException<BrowseChildrenResult>(new NotSupportedException(reason)));
|
||||
|
||||
_factory.Create("MxGateway", Arg.Any<IDictionary<string, string>>())
|
||||
@@ -226,7 +226,7 @@ public class DataConnectionManagerBrowseHandlerTests : TestKit
|
||||
.Returns(Task.CompletedTask);
|
||||
((IDataConnection)adapter).Status.Returns(ConnectionHealth.Connected);
|
||||
((IBrowsableDataConnection)adapter)
|
||||
.BrowseChildrenAsync(null, Arg.Any<CancellationToken>())
|
||||
.BrowseChildrenAsync(null, Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(new BrowseChildrenResult(bigList, Truncated: false));
|
||||
|
||||
_factory.Create("OpcUa", Arg.Any<IDictionary<string, string>>())
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ public class OpcUaDataConnectionBrowseTests
|
||||
var expected = new BrowseChildrenResult(
|
||||
new[] { new BrowseNode("ns=2;s=X", "X", BrowseNodeClass.Variable, false) },
|
||||
Truncated: false);
|
||||
client.BrowseChildrenAsync("ns=2;s=Parent", Arg.Any<CancellationToken>())
|
||||
client.BrowseChildrenAsync("ns=2;s=Parent", Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(expected);
|
||||
|
||||
var adapter = new OpcUaDataConnection(factory, NullLogger<OpcUaDataConnection>.Instance);
|
||||
@@ -36,6 +36,6 @@ public class OpcUaDataConnectionBrowseTests
|
||||
var actual = await adapter.BrowseChildrenAsync("ns=2;s=Parent");
|
||||
|
||||
Assert.Same(expected, actual);
|
||||
await client.Received(1).BrowseChildrenAsync("ns=2;s=Parent", Arg.Any<CancellationToken>());
|
||||
await client.Received(1).BrowseChildrenAsync("ns=2;s=Parent", Arg.Any<string?>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
|
||||
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// M7-B2 (T15): <see cref="StubOpcUaClient"/> serves a small canned address-space
|
||||
/// tree so DCL browse/paging flows can be exercised without a live OPC UA server.
|
||||
/// The stub also fakes BrowseNext continuation paging on <c>Folder1</c> so the
|
||||
/// continuation-token round-trip is testable end-to-end against the contract.
|
||||
/// </summary>
|
||||
public class StubOpcUaClientBrowseTests
|
||||
{
|
||||
// StubOpcUaClient is internal; reached here via InternalsVisibleTo on the
|
||||
// DataConnectionLayer assembly (the test project is already a friend, since
|
||||
// other tests in this project new up internal adapter types).
|
||||
private static async Task<IOpcUaClient> ConnectedStubAsync()
|
||||
{
|
||||
var client = new StubOpcUaClient();
|
||||
await client.ConnectAsync("opc.tcp://stub", null, CancellationToken.None);
|
||||
return client;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BrowseRoot_ReturnsTwoFolders_NoContinuation()
|
||||
{
|
||||
await using var client = (StubOpcUaClient)await ConnectedStubAsync();
|
||||
|
||||
var result = await client.BrowseChildrenAsync(parentNodeId: null);
|
||||
|
||||
Assert.False(result.Truncated);
|
||||
Assert.Null(result.ContinuationToken);
|
||||
Assert.Collection(
|
||||
result.Children,
|
||||
n =>
|
||||
{
|
||||
Assert.Equal("Folder1", n.DisplayName);
|
||||
Assert.Equal(BrowseNodeClass.Object, n.NodeClass);
|
||||
Assert.True(n.HasChildren);
|
||||
},
|
||||
n =>
|
||||
{
|
||||
Assert.Equal("Folder2", n.DisplayName);
|
||||
Assert.Equal(BrowseNodeClass.Object, n.NodeClass);
|
||||
Assert.False(n.HasChildren);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BrowseFolder1_NoToken_ReturnsTag1_AndContinuationToken()
|
||||
{
|
||||
await using var client = (StubOpcUaClient)await ConnectedStubAsync();
|
||||
|
||||
var result = await client.BrowseChildrenAsync(parentNodeId: "Folder1");
|
||||
|
||||
Assert.True(result.Truncated);
|
||||
Assert.Equal("STUB_PAGE2", result.ContinuationToken);
|
||||
var only = Assert.Single(result.Children);
|
||||
Assert.Equal("Tag1", only.DisplayName);
|
||||
Assert.Equal(BrowseNodeClass.Variable, only.NodeClass);
|
||||
Assert.False(only.HasChildren);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BrowseFolder1_WithContinuationToken_ReturnsTag2_AndNoFurtherContinuation()
|
||||
{
|
||||
await using var client = (StubOpcUaClient)await ConnectedStubAsync();
|
||||
|
||||
var result = await client.BrowseChildrenAsync(
|
||||
parentNodeId: "Folder1",
|
||||
continuationToken: "STUB_PAGE2");
|
||||
|
||||
Assert.False(result.Truncated);
|
||||
Assert.Null(result.ContinuationToken);
|
||||
var only = Assert.Single(result.Children);
|
||||
Assert.Equal("Tag2", only.DisplayName);
|
||||
Assert.Equal(BrowseNodeClass.Variable, only.NodeClass);
|
||||
Assert.False(only.HasChildren);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BrowseLeaf_ReturnsEmpty_NoContinuation()
|
||||
{
|
||||
await using var client = (StubOpcUaClient)await ConnectedStubAsync();
|
||||
|
||||
var result = await client.BrowseChildrenAsync(parentNodeId: "Folder2");
|
||||
|
||||
Assert.False(result.Truncated);
|
||||
Assert.Null(result.ContinuationToken);
|
||||
Assert.Empty(result.Children);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user