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;
///
/// Load-bearing regression guard for 01/S-6 — an operator-authorable out-of-range
/// ResilienceConfig value must NEVER brick a healthy driver. Before the R2-02 hardening pass,
/// "timeoutSeconds": 0 (→ TimeSpan.Zero, outside Polly's [10 ms, 24 h] window) and
/// "breakerFailureThreshold": 1 (below Polly's MinimumThroughput floor of 2) each made
/// throw ValidationException inside the
/// memoizing GetOrAdd factory on EVERY capability call for that (instance, host,
/// capability) — a permanent brick while the driver is healthy. The parser now clamps and the
/// builder belt-and-braces clamps, so a pipeline built from a parsed hostile config executes.
///
[Trait("Category", "Unit")]
public sealed class ResilienceConfigBrickTests
{
/// A parsed timeoutSeconds:0 override must not brick a capability call.
[Fact]
public async Task ParsedConfig_WithZeroTimeout_DoesNotBrickCapabilityCalls()
{
var options = DriverResilienceOptionsParser.ParseOrDefaults(
DriverTier.A, "{\"capabilityPolicies\":{\"Read\":{\"timeoutSeconds\":0}}}", out _);
var pipeline = new DriverResiliencePipelineBuilder()
.GetOrCreate("i1", "h1", DriverCapability.Read, options);
var result = await pipeline.ExecuteAsync(_ => ValueTask.FromResult(42), CancellationToken.None);
result.ShouldBe(42);
}
/// A parsed breakerFailureThreshold:1 override must not brick a capability call.
[Fact]
public async Task ParsedConfig_WithBreakerThresholdOne_DoesNotBrickCapabilityCalls()
{
var options = DriverResilienceOptionsParser.ParseOrDefaults(
DriverTier.A, "{\"capabilityPolicies\":{\"Read\":{\"breakerFailureThreshold\":1}}}", out _);
var pipeline = new DriverResiliencePipelineBuilder()
.GetOrCreate("i1", "h1", DriverCapability.Read, options);
var result = await pipeline.ExecuteAsync(_ => ValueTask.FromResult(42), CancellationToken.None);
result.ShouldBe(42);
}
}