64e3fbe035
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public members surfaced by commentchecker — resolves 5,847 of 5,869 issues (99.6%) across three /fixdocs passes.
139 lines
5.0 KiB
C#
139 lines
5.0 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Resilience;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Resilience;
|
|
|
|
[Trait("Category", "Unit")]
|
|
public sealed class InFlightCounterTests
|
|
{
|
|
/// <summary>Verifies that starting and completing a call nets to zero.</summary>
|
|
[Fact]
|
|
public void StartThenComplete_NetsToZero()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
tracker.RecordCallStart("drv", "host-a");
|
|
tracker.RecordCallComplete("drv", "host-a");
|
|
|
|
tracker.TryGet("drv", "host-a")!.CurrentInFlight.ShouldBe(0);
|
|
}
|
|
|
|
/// <summary>Verifies that nested starts sum the depth.</summary>
|
|
[Fact]
|
|
public void NestedStarts_SumDepth()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
tracker.RecordCallStart("drv", "host-a");
|
|
tracker.RecordCallStart("drv", "host-a");
|
|
tracker.RecordCallStart("drv", "host-a");
|
|
|
|
tracker.TryGet("drv", "host-a")!.CurrentInFlight.ShouldBe(3);
|
|
|
|
tracker.RecordCallComplete("drv", "host-a");
|
|
tracker.TryGet("drv", "host-a")!.CurrentInFlight.ShouldBe(2);
|
|
}
|
|
|
|
/// <summary>Verifies that completing before start is clamped to zero.</summary>
|
|
[Fact]
|
|
public void CompleteBeforeStart_ClampedToZero()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
tracker.RecordCallComplete("drv", "host-a");
|
|
|
|
// A stray Complete without a matching Start shouldn't drive the counter negative.
|
|
tracker.TryGet("drv", "host-a")!.CurrentInFlight.ShouldBe(0);
|
|
}
|
|
|
|
/// <summary>Verifies that different hosts track independently.</summary>
|
|
[Fact]
|
|
public void DifferentHosts_TrackIndependently()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
tracker.RecordCallStart("drv", "host-a");
|
|
tracker.RecordCallStart("drv", "host-a");
|
|
tracker.RecordCallStart("drv", "host-b");
|
|
|
|
tracker.TryGet("drv", "host-a")!.CurrentInFlight.ShouldBe(2);
|
|
tracker.TryGet("drv", "host-b")!.CurrentInFlight.ShouldBe(1);
|
|
}
|
|
|
|
/// <summary>Verifies that concurrent starts do not lose count.</summary>
|
|
[Fact]
|
|
public void ConcurrentStarts_DoNotLose_Count()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
Parallel.For(0, 500, _ => tracker.RecordCallStart("drv", "host-a"));
|
|
|
|
tracker.TryGet("drv", "host-a")!.CurrentInFlight.ShouldBe(500);
|
|
}
|
|
|
|
/// <summary>Verifies that CapabilityInvoker increments the tracker during execution.</summary>
|
|
[Fact]
|
|
public async Task CapabilityInvoker_IncrementsTracker_DuringExecution()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
var invoker = new CapabilityInvoker(
|
|
new DriverResiliencePipelineBuilder(),
|
|
"drv-live",
|
|
() => new DriverResilienceOptions { Tier = DriverTier.A },
|
|
driverType: "Modbus",
|
|
statusTracker: tracker);
|
|
|
|
var observedMidCall = -1;
|
|
await invoker.ExecuteAsync(
|
|
DriverCapability.Read,
|
|
"plc-1",
|
|
async _ =>
|
|
{
|
|
observedMidCall = tracker.TryGet("drv-live", "plc-1")?.CurrentInFlight ?? -1;
|
|
await Task.Yield();
|
|
return 42;
|
|
},
|
|
CancellationToken.None);
|
|
|
|
observedMidCall.ShouldBe(1, "during call, in-flight == 1");
|
|
tracker.TryGet("drv-live", "plc-1")!.CurrentInFlight.ShouldBe(0, "post-call, counter decremented");
|
|
}
|
|
|
|
/// <summary>Verifies that CapabilityInvoker decrements the counter on exception.</summary>
|
|
[Fact]
|
|
public async Task CapabilityInvoker_ExceptionPath_DecrementsCounter()
|
|
{
|
|
var tracker = new DriverResilienceStatusTracker();
|
|
var invoker = new CapabilityInvoker(
|
|
new DriverResiliencePipelineBuilder(),
|
|
"drv-live",
|
|
() => new DriverResilienceOptions { Tier = DriverTier.A },
|
|
statusTracker: tracker);
|
|
|
|
await Should.ThrowAsync<InvalidOperationException>(async () =>
|
|
await invoker.ExecuteAsync<int>(
|
|
DriverCapability.Write,
|
|
"plc-1",
|
|
_ => throw new InvalidOperationException("boom"),
|
|
CancellationToken.None));
|
|
|
|
tracker.TryGet("drv-live", "plc-1")!.CurrentInFlight.ShouldBe(0,
|
|
"finally-block must decrement even when call-site throws");
|
|
}
|
|
|
|
/// <summary>Verifies that CapabilityInvoker without a tracker does not throw.</summary>
|
|
[Fact]
|
|
public async Task CapabilityInvoker_WithoutTracker_DoesNotThrow()
|
|
{
|
|
var invoker = new CapabilityInvoker(
|
|
new DriverResiliencePipelineBuilder(),
|
|
"drv-live",
|
|
() => new DriverResilienceOptions { Tier = DriverTier.A },
|
|
statusTracker: null);
|
|
|
|
var result = await invoker.ExecuteAsync(
|
|
DriverCapability.Read, "host-1",
|
|
_ => ValueTask.FromResult(7),
|
|
CancellationToken.None);
|
|
|
|
result.ShouldBe(7);
|
|
}
|
|
}
|