feat: thread BrowseNext continuation token through actor + BrowseService (T15)
This commit is contained in:
+53
@@ -253,4 +253,57 @@ public class DataConnectionManagerBrowseHandlerTests : TestKit
|
||||
var keptBytes = reply.Children.Sum(n => 64 + n.NodeId.Length + n.DisplayName.Length);
|
||||
Assert.True(keptBytes <= byteBudget, $"kept estimate {keptBytes} exceeds budget {byteBudget}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ContinuationToken_round_trips_through_the_browse_handler()
|
||||
{
|
||||
// Task B3 (T15): the BrowseNext continuation token must thread through the
|
||||
// actor plumbing in both directions — the inbound command's token must reach
|
||||
// the adapter, and the adapter's returned token must be surfaced on the reply
|
||||
// so the UI can request the next page. We capture the token the adapter is
|
||||
// called with and return a fresh one, then assert both halves.
|
||||
const string inboundToken = "page-1-cursor";
|
||||
const string outboundToken = "page-2-cursor";
|
||||
string? receivedToken = "NOT-CALLED";
|
||||
|
||||
var adapter = Substitute.For<IDataConnection, IBrowsableDataConnection>();
|
||||
((IDataConnection)adapter).ConnectAsync(Arg.Any<IDictionary<string, string>>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.CompletedTask);
|
||||
((IDataConnection)adapter).Status.Returns(ConnectionHealth.Connected);
|
||||
|
||||
var children = new[]
|
||||
{
|
||||
new BrowseNode("ns=2;s=Next", "Next", BrowseNodeClass.Variable, HasChildren: false),
|
||||
};
|
||||
((IBrowsableDataConnection)adapter)
|
||||
.BrowseChildrenAsync(Arg.Any<string?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
|
||||
.Returns(ci =>
|
||||
{
|
||||
receivedToken = ci.ArgAt<string?>(1);
|
||||
return new BrowseChildrenResult(children, Truncated: true, ContinuationToken: outboundToken);
|
||||
});
|
||||
|
||||
_factory.Create("OpcUa", Arg.Any<IDictionary<string, string>>())
|
||||
.Returns((IDataConnection)adapter);
|
||||
|
||||
var manager = Sys.ActorOf(Props.Create(() =>
|
||||
new DataConnectionManagerActor(_factory, _options, _healthCollector, null)));
|
||||
manager.Tell(new CreateConnectionCommand(
|
||||
"conn-paged", "OpcUa", new Dictionary<string, string>(), null, 3));
|
||||
|
||||
AwaitCondition(
|
||||
() => _factory.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Create"),
|
||||
TimeSpan.FromSeconds(2));
|
||||
|
||||
manager.Tell(new BrowseNodeCommand("conn-paged", ParentNodeId: "ns=2;s=Folder", ContinuationToken: inboundToken));
|
||||
|
||||
var reply = ExpectMsg<BrowseNodeResult>(TimeSpan.FromSeconds(3));
|
||||
Assert.Null(reply.Failure);
|
||||
// Inbound token reached the adapter verbatim.
|
||||
Assert.Equal(inboundToken, receivedToken);
|
||||
// Outbound token from the adapter is surfaced on the reply for the next page.
|
||||
Assert.Equal(outboundToken, reply.ContinuationToken);
|
||||
Assert.True(reply.Truncated);
|
||||
Assert.Single(reply.Children);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user