fix(client): endpoint port rewrite, Part 13 time-weighted aggregates, clean history-refusal error (#678.5, #677.5, #674.6, #678.6)

- EndpointSelector rewrites host AND port to the user-supplied URL (same scheme only)
- AggregateType/mapper/CLI/UI gain TimeAverage, TimeAverage2, Total, Total2, Interpolative
- HistoryReadCommand turns ServiceResultException into a one-line CommandException
- NodeManagerHistoryReadTests non-historized case sets IsReadModified=false
This commit is contained in:
Joseph Doherty
2026-10-11 10:45:18 -04:00
parent 825393299e
commit fe919f8b08
14 changed files with 173 additions and 17 deletions
@@ -160,4 +160,41 @@ public class EndpointSelectorTests
Should.Throw<ArgumentException>(() =>
EndpointSelector.SelectBest(Array.Empty<EndpointDescription>(), "", MessageSecurityMode.None));
}
/// <summary>#678.5: a server advertising its internal port must not redirect the session off the mapped port.</summary>
[Fact]
public void SelectBest_RewritesHostAndPort_WhenSchemeMatches()
{
var endpoints = new[] { Ep(MessageSecurityMode.None, SecurityPolicies.None, "opc.tcp://internal-host:4840/OtOpcUa") };
var best = EndpointSelector.SelectBest(endpoints, "opc.tcp://localhost:4841", MessageSecurityMode.None);
var uri = new Uri(best.EndpointUrl);
uri.Host.ShouldBe("localhost");
uri.Port.ShouldBe(4841);
uri.AbsolutePath.ShouldBe("/OtOpcUa");
}
/// <summary>A different scheme (e.g. https endpoint advertised to an opc.tcp caller) is left untouched.</summary>
[Fact]
public void SelectBest_LeavesEndpointUntouched_WhenSchemeDiffers()
{
var endpoints = new[] { Ep(MessageSecurityMode.None, SecurityPolicies.None, "https://internal-host:4843/x") };
var best = EndpointSelector.SelectBest(endpoints, "opc.tcp://localhost:4841", MessageSecurityMode.None);
new Uri(best.EndpointUrl).Host.ShouldBe("internal-host");
new Uri(best.EndpointUrl).Port.ShouldBe(4843);
}
/// <summary>Identical host, port and scheme: nothing to rewrite.</summary>
[Fact]
public void SelectBest_Unchanged_WhenHostPortAndSchemeMatch()
{
var endpoints = new[] { Ep(MessageSecurityMode.None, SecurityPolicies.None, "opc.tcp://server:4840") };
var best = EndpointSelector.SelectBest(endpoints, "opc.tcp://server:4840", MessageSecurityMode.None);
best.EndpointUrl.ShouldBe("opc.tcp://server:4840");
}
}