using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Runtime.Drivers;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;
///
/// lmxopcua#777: every write bound derives from the one write timeout, and each outer bound covers the worst case
/// of everything inside it — whatever the timeout. (The fixed 8 s Ask failed this from a 7 s timeout up.)
///
public sealed class DriverWriteBoundsTests
{
[Theory]
[InlineData(0.5)]
[InlineData(2)]
[InlineData(5)]
[InlineData(9)]
[InlineData(10)]
[InlineData(30)]
[InlineData(300)]
public void Each_outer_bound_covers_everything_inside_it(double timeoutSeconds)
{
var b = new DriverWriteBounds(TimeSpan.FromSeconds(timeoutSeconds));
b.AbandonGrace.ShouldBeGreaterThan(TimeSpan.Zero);
b.RunBound.ShouldBe(b.Timeout + b.AbandonGrace);
// The child answers a write by queue budget + run bound after it was issued; the Asks sit strictly outside.
b.ReplyBound.ShouldBe(b.QueueBudget + b.RunBound);
b.HostAsk.ShouldBeGreaterThanOrEqualTo(b.ReplyBound + DriverWriteBounds.ReplyMargin);
b.GatewayAsk.ShouldBeGreaterThanOrEqualTo(b.HostAsk + DriverWriteBounds.ReplyMargin);
}
[Fact]
public void The_default_is_10s_and_the_grace_is_half_the_timeout_capped_at_2s()
{
DriverWriteBounds.Default.Timeout.ShouldBe(TimeSpan.FromSeconds(10));
new DriverWriteBounds(TimeSpan.FromSeconds(3)).AbandonGrace.ShouldBe(TimeSpan.FromSeconds(1.5));
new DriverWriteBounds(TimeSpan.FromSeconds(9)).AbandonGrace.ShouldBe(TimeSpan.FromSeconds(2));
DriverInstanceActor.DefaultWriteTimeout.ShouldBe(DriverWriteBounds.Default.Timeout);
DriverInstanceActor.DefaultQueuedWriteBudget.ShouldBe(DriverWriteBounds.Default.QueueBudget);
}
[Theory]
[InlineData(0)]
[InlineData(-1)]
public void A_non_positive_timeout_is_rejected(double seconds) =>
Should.Throw(() => new DriverWriteBounds(TimeSpan.FromSeconds(seconds)));
}