using Omika.Core.Observability.Outbox; namespace Omika.Tests.Observability.Outbox; /// Tests du backoff borné avec jitter (§43). public class DiagnosticBackoffPolicyTests { [Fact] public void NextDelay_IsBoundedByMax() { var options = new DiagnosticUploadOptions { BackoffInitial = TimeSpan.FromSeconds(15), BackoffMax = TimeSpan.FromHours(1) }; var policy = new DiagnosticBackoffPolicy(options); // Garantie absolue : 0 < délai <= MaxDelay, pour tout attempt, y compris les // valeurs extrêmes. Le jitter (aléatoire) ne peut JAMAIS faire dépasser MaxDelay // car la borne finale est appliquée après le jitter. int[] attempts = [0, 1, 5, 9, 20, 50, 100, int.MaxValue]; foreach (int attempt in attempts) { TimeSpan delay = policy.NextDelay(attempt); Assert.True(delay > TimeSpan.Zero, $"attempt {attempt}: délai non strictement positif ({delay})"); Assert.True(delay <= options.BackoffMax, $"attempt {attempt}: délai {delay} dépasse le maximum {options.BackoffMax}"); } } [Fact] public void NextDelay_GrowsWithAttempts() { var policy = new DiagnosticBackoffPolicy(new DiagnosticUploadOptions { BackoffInitial = TimeSpan.FromSeconds(15), BackoffMax = TimeSpan.FromHours(1), BackoffJitter = 0.0 }); TimeSpan first = policy.NextDelay(1); TimeSpan second = policy.NextDelay(2); TimeSpan third = policy.NextDelay(3); Assert.True(second >= first); Assert.True(third >= second); } [Fact] public void NextDelay_WithJitter_StaysWithinJitterBounds() { var options = new DiagnosticUploadOptions { BackoffInitial = TimeSpan.FromSeconds(10), BackoffMax = TimeSpan.FromMinutes(1), BackoffJitter = 0.2 }; var policy = new DiagnosticBackoffPolicy(options); for (int i = 0; i < 100; i++) { TimeSpan delay = policy.NextDelay(1); Assert.InRange(delay.TotalMilliseconds, 8000, 12000); } } }