fix(r2-01): RED connect-timeout regression tests T1+T3 (task 1)
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"subject": "RED: connect-timeout regression tests T1+T3 (read/write degrade instead of throwing OCE) — must FAIL at f6eaa267",
|
"subject": "RED: connect-timeout regression tests T1+T3 (read/write degrade instead of throwing OCE) — must FAIL at f6eaa267",
|
||||||
"status": "pending",
|
"status": "completed",
|
||||||
"blockedBy": [0]
|
"blockedBy": [0]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -133,6 +133,56 @@ public sealed class S7DriverReconnectTests
|
|||||||
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
|
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- STAB-14: connect-timeout must degrade, not escape as OCE ----
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// T1 (STAB-14 regression). When the reopen HANGS and is cut by the internal
|
||||||
|
/// <c>CancelAfter(_options.Timeout)</c> (the unreachable-host outage — SYNs dropped, not
|
||||||
|
/// refused), the read batch must degrade to a communication error with Degraded health,
|
||||||
|
/// NOT throw the timeout-born <see cref="TaskCanceledException"/> up to the caller.
|
||||||
|
/// Before the fix the OCE escaped <see cref="S7Driver.ReadAsync"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task Read_degrades_batch_on_connect_timeout_instead_of_throwing()
|
||||||
|
{
|
||||||
|
var factory = new FakeS7PlcFactory();
|
||||||
|
using var drv = new S7Driver(Options(), "s7-read-timeout", factory);
|
||||||
|
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
|
// Drop connection #1 so the next read reopens; every subsequent open hangs until the
|
||||||
|
// internal connect-timeout CTS fires.
|
||||||
|
factory.Created[0].NextReadThrows = new PlcException(ErrorCode.ConnectionError, "dropped");
|
||||||
|
await drv.ReadAsync(["W0"], TestContext.Current.CancellationToken); // marks dead
|
||||||
|
factory.OpenHangsUntilCancelledCount = int.MaxValue;
|
||||||
|
|
||||||
|
// Must NOT throw — the connect timeout degrades the batch.
|
||||||
|
var timedOut = await drv.ReadAsync(["W0"], TestContext.Current.CancellationToken);
|
||||||
|
timedOut[0].StatusCode.ShouldNotBe(0u);
|
||||||
|
drv.GetHealth().State.ShouldBe(DriverState.Degraded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// T3 (STAB-14 regression). The same connect-timeout degrade through
|
||||||
|
/// <see cref="S7Driver.WriteAsync"/> — <c>EnsureConnectedAsync</c> is called before the
|
||||||
|
/// per-item loop, so a hanging reopen must degrade the whole write batch, not throw OCE.
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task Write_degrades_batch_on_connect_timeout_instead_of_throwing()
|
||||||
|
{
|
||||||
|
var factory = new FakeS7PlcFactory();
|
||||||
|
using var drv = new S7Driver(Options(), "s7-write-timeout", factory);
|
||||||
|
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
|
factory.Created[0].NextReadThrows = new PlcException(ErrorCode.ConnectionError, "dropped");
|
||||||
|
await drv.ReadAsync(["W0"], TestContext.Current.CancellationToken); // marks dead
|
||||||
|
factory.OpenHangsUntilCancelledCount = int.MaxValue;
|
||||||
|
|
||||||
|
var timedOut = await drv.WriteAsync(
|
||||||
|
[new WriteRequest("W0", (ushort)1)], TestContext.Current.CancellationToken);
|
||||||
|
timedOut[0].StatusCode.ShouldNotBe(0u);
|
||||||
|
drv.GetHealth().State.ShouldBe(DriverState.Degraded);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- fakes ----
|
// ---- fakes ----
|
||||||
|
|
||||||
private sealed class FakeS7PlcFactory : IS7PlcFactory
|
private sealed class FakeS7PlcFactory : IS7PlcFactory
|
||||||
|
|||||||
Reference in New Issue
Block a user