4b594ec525
DriverTestConnectButton always sent a fixed 10 s, and the Galaxy/OpcUaClient probes run under min(caller, configured), so ProbeTimeoutSeconds - documented on every driver as the Test Connect timeout - could shorten a probe but never lengthen it (Galaxy's 30 s default was silently 10 s). TestConnectBudget reads the root ProbeTimeoutSeconds from the merged config at click time, clamped to the actor's [1, 60] s, falling back to TimeoutSeconds. DeviceModal (the only host) needs no change. The Galaxy/OpcUaClient form hints are reworded. The 30 s result-clear timer moves from System.Timers.Timer + an async void Elapsed lambda to System.Threading.Timer, drained in DisposeAsync, with a generation so a superseded fire cannot clear a newer result. Refs #745 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
56 lines
2.3 KiB
C#
56 lines
2.3 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Drivers;
|
|
|
|
/// <summary>
|
|
/// G1-F5 / F-FU2: Test Connect honours the driver's configured <c>ProbeTimeoutSeconds</c> (so it can
|
|
/// lengthen the budget, not only shorten it), aligned across Galaxy and OpcUaClient, and the button
|
|
/// drains its clear timer (R04 L2).
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class TestConnectBudgetTests
|
|
{
|
|
[Theory]
|
|
[InlineData("{\"ProbeTimeoutSeconds\":30}", 30)] // Galaxy default — was silently 10 s
|
|
[InlineData("{\"probeTimeoutSeconds\":15}", 15)] // OpcUaClient default, camelCase
|
|
[InlineData("{\"ProbeTimeoutSeconds\":\"25\"}", 25)]
|
|
[InlineData("{\"ProbeTimeoutSeconds\":2.5}", 3)]
|
|
[InlineData("{\"ProbeTimeoutSeconds\":600}", 60)] // clamped to the actor's max
|
|
public void A_configured_timeout_is_the_budget(string json, int expected)
|
|
=> TestConnectBudget.Resolve(json).ShouldBe(expected);
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData("{}")]
|
|
[InlineData("{\"ProbeTimeoutSeconds\":0}")]
|
|
[InlineData("{\"ProbeTimeoutSeconds\":-5}")]
|
|
[InlineData("{\"ProbeTimeoutSeconds\":null}")]
|
|
[InlineData("not json")]
|
|
[InlineData("[1,2]")]
|
|
[InlineData("{\"Nested\":{\"ProbeTimeoutSeconds\":40}}")] // only the root key counts
|
|
public void No_valid_configured_timeout_falls_back(string? json)
|
|
=> TestConnectBudget.Resolve(json, fallbackSeconds: 10).ShouldBe(10);
|
|
|
|
[Fact]
|
|
public void The_fallback_is_clamped_too()
|
|
{
|
|
TestConnectBudget.Resolve("{}", fallbackSeconds: 0).ShouldBe(TestConnectBudget.MinSeconds);
|
|
TestConnectBudget.Resolve("{}", fallbackSeconds: 999).ShouldBe(TestConnectBudget.MaxSeconds);
|
|
}
|
|
|
|
[Fact]
|
|
public void Button_resolves_its_budget_from_the_config_and_drains_a_threading_timer()
|
|
{
|
|
var src = AdminUiSource.Read("Components", "Shared", "Drivers", "DriverTestConnectButton.razor");
|
|
|
|
src.ShouldContain("TestConnectBudget.Resolve(json, TimeoutSeconds)");
|
|
src.ShouldContain("@implements IAsyncDisposable");
|
|
src.ShouldContain("System.Threading.Timer");
|
|
src.ShouldNotContain("System.Timers.Timer");
|
|
src.ShouldNotContain("async (_, _)");
|
|
}
|
|
}
|