d98f4a5bf1
The knob was authored by the AdminUI and parsed, but consumed by nothing. It now caps the probe deadline (shorter of the caller's budget and the config; never extends it; 1 s floor), the same rule as the Galaxy probe. Refs #743 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
55 lines
2.4 KiB
C#
55 lines
2.4 KiB
C#
using System.Diagnostics;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
|
|
|
|
/// <summary>
|
|
/// Review 2026-10-21 R06:O-4 — <c>ProbeTimeoutSeconds</c> was authored by the AdminUI and parsed, but consumed
|
|
/// by nothing: the Test Connect probe ran on the caller's budget alone. It now caps the probe deadline (it can
|
|
/// shorten the caller's budget, never extend it) — the same rule as the Galaxy probe.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class OpcUaClientProbeTimeoutTests
|
|
{
|
|
/// <summary>The effective deadline is the shorter of the caller's budget and the configured cap.</summary>
|
|
[Theory]
|
|
[InlineData(10, 30, 10)] // caller shorter → caller wins
|
|
[InlineData(10, 2, 2)] // config shorter → config caps
|
|
[InlineData(10, 0, 10)] // unset config → caller
|
|
[InlineData(10, -1, 10)] // nonsense config → caller
|
|
[InlineData(0, 5, 1)] // no caller budget → 1 s floor beats a longer config
|
|
public void EffectiveTimeout_is_the_shorter_of_caller_and_config(int callerSeconds, int configSeconds, int expectedSeconds)
|
|
=> OpcUaClientDriverProbe.EffectiveTimeout(TimeSpan.FromSeconds(callerSeconds), configSeconds)
|
|
.ShouldBe(TimeSpan.FromSeconds(expectedSeconds));
|
|
|
|
/// <summary>
|
|
/// Against a port that accepts TCP but never speaks OPC UA, a 1 s configured cap returns long before the
|
|
/// caller's 30 s budget — the discovery handshake does the waiting, so this exercises the real deadline path.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Configured_cap_shortens_the_probe_against_a_silent_listener()
|
|
{
|
|
var listener = new TcpListener(IPAddress.Loopback, 0);
|
|
listener.Start();
|
|
try
|
|
{
|
|
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
|
var config = $$"""{ "endpointUrl": "opc.tcp://127.0.0.1:{{port}}", "probeTimeoutSeconds": 1 }""";
|
|
|
|
var sw = Stopwatch.StartNew();
|
|
var result = await new OpcUaClientDriverProbe().ProbeAsync(config, TimeSpan.FromSeconds(30), TestContext.Current.CancellationToken);
|
|
sw.Stop();
|
|
|
|
result.Ok.ShouldBeFalse();
|
|
sw.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(15), $"the 1 s configured cap must bound the probe (message: {result.Message})");
|
|
}
|
|
finally
|
|
{
|
|
listener.Stop();
|
|
}
|
|
}
|
|
}
|