diff --git a/src/Polly/Bulkhead/AsyncBulkheadPolicy.cs b/src/Polly/Bulkhead/AsyncBulkheadPolicy.cs index 1bbc63710fa..db6fa2e00ad 100644 --- a/src/Polly/Bulkhead/AsyncBulkheadPolicy.cs +++ b/src/Polly/Bulkhead/AsyncBulkheadPolicy.cs @@ -19,7 +19,7 @@ internal AsyncBulkheadPolicy( Func onBulkheadRejectedAsync) { _maxQueueingActions = maxQueueingActions; - _onBulkheadRejectedAsync = onBulkheadRejectedAsync ?? throw new ArgumentNullException(nameof(onBulkheadRejectedAsync)); + _onBulkheadRejectedAsync = onBulkheadRejectedAsync; (_maxParallelizationSemaphore, _maxQueuedActionsSemaphore) = BulkheadSemaphoreFactory.CreateBulkheadSemaphores(maxParallelization, maxQueueingActions); } @@ -87,7 +87,7 @@ internal AsyncBulkheadPolicy( Func onBulkheadRejectedAsync) { _maxQueueingActions = maxQueueingActions; - _onBulkheadRejectedAsync = onBulkheadRejectedAsync ?? throw new ArgumentNullException(nameof(onBulkheadRejectedAsync)); + _onBulkheadRejectedAsync = onBulkheadRejectedAsync; (_maxParallelizationSemaphore, _maxQueuedActionsSemaphore) = BulkheadSemaphoreFactory.CreateBulkheadSemaphores(maxParallelization, maxQueueingActions); } diff --git a/src/Polly/Bulkhead/BulkheadPolicy.cs b/src/Polly/Bulkhead/BulkheadPolicy.cs index f1f8c478366..27e04e39b67 100644 --- a/src/Polly/Bulkhead/BulkheadPolicy.cs +++ b/src/Polly/Bulkhead/BulkheadPolicy.cs @@ -19,7 +19,7 @@ internal BulkheadPolicy( Action onBulkheadRejected) { _maxQueueingActions = maxQueueingActions; - _onBulkheadRejected = onBulkheadRejected ?? throw new ArgumentNullException(nameof(onBulkheadRejected)); + _onBulkheadRejected = onBulkheadRejected; (_maxParallelizationSemaphore, _maxQueuedActionsSemaphore) = BulkheadSemaphoreFactory.CreateBulkheadSemaphores(maxParallelization, maxQueueingActions); } @@ -85,7 +85,7 @@ internal BulkheadPolicy( Action onBulkheadRejected) { _maxQueueingActions = maxQueueingActions; - _onBulkheadRejected = onBulkheadRejected ?? throw new ArgumentNullException(nameof(onBulkheadRejected)); + _onBulkheadRejected = onBulkheadRejected; (_maxParallelizationSemaphore, _maxQueuedActionsSemaphore) = BulkheadSemaphoreFactory.CreateBulkheadSemaphores(maxParallelization, maxQueueingActions); } diff --git a/src/Polly/Fallback/AsyncFallbackPolicy.cs b/src/Polly/Fallback/AsyncFallbackPolicy.cs index 1c664395d8f..735fa1adaf7 100644 --- a/src/Polly/Fallback/AsyncFallbackPolicy.cs +++ b/src/Polly/Fallback/AsyncFallbackPolicy.cs @@ -9,12 +9,14 @@ public class AsyncFallbackPolicy : AsyncPolicy, IFallbackPolicy private readonly Func _onFallbackAsync; private readonly Func _fallbackAction; - internal AsyncFallbackPolicy(PolicyBuilder policyBuilder, Func onFallbackAsync, + internal AsyncFallbackPolicy( + PolicyBuilder policyBuilder, + Func onFallbackAsync, Func fallbackAction) : base(policyBuilder) { - _onFallbackAsync = onFallbackAsync ?? throw new ArgumentNullException(nameof(onFallbackAsync)); - _fallbackAction = fallbackAction ?? throw new ArgumentNullException(nameof(fallbackAction)); + _onFallbackAsync = onFallbackAsync; + _fallbackAction = fallbackAction; } /// @@ -65,8 +67,8 @@ internal AsyncFallbackPolicy( Func, Context, CancellationToken, Task> fallbackAction) : base(policyBuilder) { - _onFallbackAsync = onFallbackAsync ?? throw new ArgumentNullException(nameof(onFallbackAsync)); - _fallbackAction = fallbackAction ?? throw new ArgumentNullException(nameof(fallbackAction)); + _onFallbackAsync = onFallbackAsync; + _fallbackAction = fallbackAction; } /// diff --git a/src/Polly/Fallback/FallbackPolicy.cs b/src/Polly/Fallback/FallbackPolicy.cs index f45cfc9ddba..b291378af6e 100644 --- a/src/Polly/Fallback/FallbackPolicy.cs +++ b/src/Polly/Fallback/FallbackPolicy.cs @@ -15,14 +15,14 @@ internal FallbackPolicy( Action fallbackAction) : base(policyBuilder) { - _onFallback = onFallback ?? throw new ArgumentNullException(nameof(onFallback)); - _fallbackAction = fallbackAction ?? throw new ArgumentNullException(nameof(fallbackAction)); + _onFallback = onFallback; + _fallbackAction = fallbackAction; } /// [DebuggerStepThrough] protected override void Implementation(Action action, Context context, CancellationToken cancellationToken) => - FallbackEngine.Implementation( + FallbackEngine.Implementation( (ctx, token) => { action(ctx, token); @@ -62,8 +62,8 @@ internal FallbackPolicy( Func, Context, CancellationToken, TResult> fallbackAction) : base(policyBuilder) { - _onFallback = onFallback ?? throw new ArgumentNullException(nameof(onFallback)); - _fallbackAction = fallbackAction ?? throw new ArgumentNullException(nameof(fallbackAction)); + _onFallback = onFallback; + _fallbackAction = fallbackAction; } /// diff --git a/src/Polly/Polly.csproj b/src/Polly/Polly.csproj index 726c64b6a99..70339dbc451 100644 --- a/src/Polly/Polly.csproj +++ b/src/Polly/Polly.csproj @@ -5,7 +5,7 @@ Polly true Library - 70 + 80 true $(NoWarn);RS0037 diff --git a/src/Polly/RateLimit/AsyncRateLimitPolicy.cs b/src/Polly/RateLimit/AsyncRateLimitPolicy.cs index 19f49deb0ae..4702c07d95f 100644 --- a/src/Polly/RateLimit/AsyncRateLimitPolicy.cs +++ b/src/Polly/RateLimit/AsyncRateLimitPolicy.cs @@ -9,7 +9,7 @@ public class AsyncRateLimitPolicy : AsyncPolicy, IRateLimitPolicy private readonly IRateLimiter _rateLimiter; internal AsyncRateLimitPolicy(IRateLimiter rateLimiter) => - _rateLimiter = rateLimiter ?? throw new ArgumentNullException(nameof(rateLimiter)); + _rateLimiter = rateLimiter; /// [DebuggerStepThrough] @@ -47,7 +47,7 @@ internal AsyncRateLimitPolicy( IRateLimiter rateLimiter, Func? retryAfterFactory) { - _rateLimiter = rateLimiter ?? throw new ArgumentNullException(nameof(rateLimiter)); + _rateLimiter = rateLimiter; _retryAfterFactory = retryAfterFactory; } diff --git a/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs b/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs index 856f33f4c6f..351edf7b297 100644 --- a/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs +++ b/src/Polly/RateLimit/LockFreeTokenBucketRateLimiter.cs @@ -33,11 +33,6 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity) throw new ArgumentOutOfRangeException(nameof(onePer), onePer, $"The {nameof(LockFreeTokenBucketRateLimiter)} must specify a positive TimeSpan for how often an execution is permitted."); } - if (bucketCapacity <= 0) - { - throw new ArgumentOutOfRangeException(nameof(bucketCapacity), bucketCapacity, $"{nameof(bucketCapacity)} must be greater than or equal to 1."); - } - _addTokenTickInterval = onePer.Ticks; _bucketCapacity = bucketCapacity; @@ -82,6 +77,7 @@ public LockFreeTokenBucketRateLimiter(TimeSpan onePer, long bucketCapacity) long tokensToAdd = Math.Min(_bucketCapacity, tokensMissedAdding); // Work out when tokens would next be due to be added, if we add these tokens. + // stryker disable once all : no means to test this long newAddNextTokenAtTicks = currentAddNextTokenAtTicks + (tokensToAdd * _addTokenTickInterval); // But if we were way overdue refilling the bucket (there was inactivity for a while), that value would be out-of-date: the next time we add tokens must be at least addTokenTickInterval from now. diff --git a/src/Polly/RateLimit/RateLimitPolicy.cs b/src/Polly/RateLimit/RateLimitPolicy.cs index 34a540cf8d2..0ac3ccc29db 100644 --- a/src/Polly/RateLimit/RateLimitPolicy.cs +++ b/src/Polly/RateLimit/RateLimitPolicy.cs @@ -9,7 +9,7 @@ public class RateLimitPolicy : Policy, IRateLimitPolicy private readonly IRateLimiter _rateLimiter; internal RateLimitPolicy(IRateLimiter rateLimiter) => - _rateLimiter = rateLimiter ?? throw new ArgumentNullException(nameof(rateLimiter)); + _rateLimiter = rateLimiter; /// [DebuggerStepThrough] @@ -37,7 +37,7 @@ internal RateLimitPolicy( IRateLimiter rateLimiter, Func? retryAfterFactory) { - _rateLimiter = rateLimiter ?? throw new ArgumentNullException(nameof(rateLimiter)); + _rateLimiter = rateLimiter; _retryAfterFactory = retryAfterFactory; } diff --git a/src/Polly/Registry/PolicyRegistry.cs b/src/Polly/Registry/PolicyRegistry.cs index eb33aad8a6c..36948bf4dd0 100644 --- a/src/Polly/Registry/PolicyRegistry.cs +++ b/src/Polly/Registry/PolicyRegistry.cs @@ -25,8 +25,13 @@ public PolicyRegistry() /// This internal constructor exists solely to facilitate testing of the GetEnumerator() methods, which allow us to support collection initialisation syntax. /// /// a dictionary containing keys and policies used for testing. - internal PolicyRegistry(IDictionary registry) => - _registry = registry ?? throw new ArgumentNullException(nameof(registry)); + internal PolicyRegistry(IDictionary registry) + { +#pragma warning disable S3236 // Remove this argument from the method call; it hides the caller information. + Debug.Assert(registry != null, "This constructor is for testing only, and should not be called with a null registry."); +#pragma warning restore S3236 // Remove this argument from the method call; it hides the caller information. + _registry = registry; + } private ConcurrentDictionary ThrowIfNotConcurrentImplementation() { diff --git a/src/Polly/Retry/AsyncRetryPolicy.cs b/src/Polly/Retry/AsyncRetryPolicy.cs index 1991941df1c..75022b4921b 100644 --- a/src/Polly/Retry/AsyncRetryPolicy.cs +++ b/src/Polly/Retry/AsyncRetryPolicy.cs @@ -21,7 +21,7 @@ internal AsyncRetryPolicy( _permittedRetryCount = permittedRetryCount; _sleepDurationsEnumerable = sleepDurationsEnumerable; _sleepDurationProvider = sleepDurationProvider; - _onRetryAsync = onRetryAsync ?? throw new ArgumentNullException(nameof(onRetryAsync)); + _onRetryAsync = onRetryAsync; } /// @@ -78,7 +78,7 @@ internal AsyncRetryPolicy( _permittedRetryCount = permittedRetryCount; _sleepDurationsEnumerable = sleepDurationsEnumerable; _sleepDurationProvider = sleepDurationProvider; - _onRetryAsync = onRetryAsync ?? throw new ArgumentNullException(nameof(onRetryAsync)); + _onRetryAsync = onRetryAsync; } /// diff --git a/src/Polly/Retry/RetryPolicy.cs b/src/Polly/Retry/RetryPolicy.cs index 1313e4653fd..a3e59d321a5 100644 --- a/src/Polly/Retry/RetryPolicy.cs +++ b/src/Polly/Retry/RetryPolicy.cs @@ -22,7 +22,7 @@ internal RetryPolicy( _permittedRetryCount = permittedRetryCount; _sleepDurationsEnumerable = sleepDurationsEnumerable; _sleepDurationProvider = sleepDurationProvider; - _onRetry = onRetry ?? throw new ArgumentNullException(nameof(onRetry)); + _onRetry = onRetry; } /// @@ -71,7 +71,7 @@ internal RetryPolicy( _permittedRetryCount = permittedRetryCount; _sleepDurationsEnumerable = sleepDurationsEnumerable; _sleepDurationProvider = sleepDurationProvider; - _onRetry = onRetry ?? throw new ArgumentNullException(nameof(onRetry)); + _onRetry = onRetry; } /// diff --git a/src/Polly/Timeout/AsyncTimeoutPolicy.cs b/src/Polly/Timeout/AsyncTimeoutPolicy.cs index 03036818aa5..1617cc0fcf0 100644 --- a/src/Polly/Timeout/AsyncTimeoutPolicy.cs +++ b/src/Polly/Timeout/AsyncTimeoutPolicy.cs @@ -14,9 +14,9 @@ internal AsyncTimeoutPolicy( TimeoutStrategy timeoutStrategy, Func onTimeoutAsync) { - _timeoutProvider = timeoutProvider ?? throw new ArgumentNullException(nameof(timeoutProvider)); + _timeoutProvider = timeoutProvider; _timeoutStrategy = timeoutStrategy; - _onTimeoutAsync = onTimeoutAsync ?? throw new ArgumentNullException(nameof(onTimeoutAsync)); + _onTimeoutAsync = onTimeoutAsync; } /// @@ -58,9 +58,9 @@ internal AsyncTimeoutPolicy( TimeoutStrategy timeoutStrategy, Func onTimeoutAsync) { - _timeoutProvider = timeoutProvider ?? throw new ArgumentNullException(nameof(timeoutProvider)); + _timeoutProvider = timeoutProvider; _timeoutStrategy = timeoutStrategy; - _onTimeoutAsync = onTimeoutAsync ?? throw new ArgumentNullException(nameof(onTimeoutAsync)); + _onTimeoutAsync = onTimeoutAsync; } /// diff --git a/src/Polly/Timeout/AsyncTimeoutSyntax.cs b/src/Polly/Timeout/AsyncTimeoutSyntax.cs index e1fcac99129..96ee4d14671 100644 --- a/src/Polly/Timeout/AsyncTimeoutSyntax.cs +++ b/src/Polly/Timeout/AsyncTimeoutSyntax.cs @@ -418,8 +418,8 @@ public static AsyncTimeoutPolicy TimeoutAsync( } return new AsyncTimeoutPolicy( - timeoutProvider, - timeoutStrategy, - onTimeoutAsync); + timeoutProvider, + timeoutStrategy, + onTimeoutAsync); } } diff --git a/src/Polly/Timeout/AsyncTimeoutTResultSyntax.cs b/src/Polly/Timeout/AsyncTimeoutTResultSyntax.cs index b951a31d7c1..7e9bbc34a4b 100644 --- a/src/Polly/Timeout/AsyncTimeoutTResultSyntax.cs +++ b/src/Polly/Timeout/AsyncTimeoutTResultSyntax.cs @@ -441,8 +441,8 @@ public static AsyncTimeoutPolicy TimeoutAsync(Func( - timeoutProvider, - timeoutStrategy, - onTimeoutAsync); + timeoutProvider, + timeoutStrategy, + onTimeoutAsync); } } diff --git a/src/Polly/Timeout/TimeoutPolicy.cs b/src/Polly/Timeout/TimeoutPolicy.cs index 2e05f6a144e..107a85d0227 100644 --- a/src/Polly/Timeout/TimeoutPolicy.cs +++ b/src/Polly/Timeout/TimeoutPolicy.cs @@ -14,9 +14,9 @@ internal TimeoutPolicy( TimeoutStrategy timeoutStrategy, Action onTimeout) { - _timeoutProvider = timeoutProvider ?? throw new ArgumentNullException(nameof(timeoutProvider)); + _timeoutProvider = timeoutProvider; _timeoutStrategy = timeoutStrategy; - _onTimeout = onTimeout ?? throw new ArgumentNullException(nameof(onTimeout)); + _onTimeout = onTimeout; } /// @@ -53,9 +53,9 @@ internal TimeoutPolicy( TimeoutStrategy timeoutStrategy, Action onTimeout) { - _timeoutProvider = timeoutProvider ?? throw new ArgumentNullException(nameof(timeoutProvider)); + _timeoutProvider = timeoutProvider; _timeoutStrategy = timeoutStrategy; - _onTimeout = onTimeout ?? throw new ArgumentNullException(nameof(onTimeout)); + _onTimeout = onTimeout; } /// diff --git a/src/Polly/Timeout/TimeoutSyntax.cs b/src/Polly/Timeout/TimeoutSyntax.cs index 5c9574eae07..4bd230d23e0 100644 --- a/src/Polly/Timeout/TimeoutSyntax.cs +++ b/src/Polly/Timeout/TimeoutSyntax.cs @@ -418,8 +418,8 @@ public static TimeoutPolicy Timeout( } return new TimeoutPolicy( - timeoutProvider, - timeoutStrategy, - onTimeout); + timeoutProvider, + timeoutStrategy, + onTimeout); } } diff --git a/src/Polly/Timeout/TimeoutTResultSyntax.cs b/src/Polly/Timeout/TimeoutTResultSyntax.cs index 0bb9490ae09..878feb8f36c 100644 --- a/src/Polly/Timeout/TimeoutTResultSyntax.cs +++ b/src/Polly/Timeout/TimeoutTResultSyntax.cs @@ -437,8 +437,8 @@ public static TimeoutPolicy Timeout( } return new TimeoutPolicy( - timeoutProvider, - timeoutStrategy, - onTimeout); + timeoutProvider, + timeoutStrategy, + onTimeout); } } diff --git a/src/Polly/Utilities/SystemClock.cs b/src/Polly/Utilities/SystemClock.cs index 3cae639b5d8..8daae9011ed 100644 --- a/src/Polly/Utilities/SystemClock.cs +++ b/src/Polly/Utilities/SystemClock.cs @@ -16,6 +16,7 @@ public static class SystemClock public static Action Sleep = (timeSpan, cancellationToken) => #pragma warning restore S2223 // Non-constant static fields should not be visible { + // Stryker disable once Boolean : no means to test this if (cancellationToken.WaitHandle.WaitOne(timeSpan)) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/test/Polly.Specs/Bulkhead/BulkheadSpecs.cs b/test/Polly.Specs/Bulkhead/BulkheadSpecs.cs index 8780feba8b7..5cfb18d6762 100644 --- a/test/Polly.Specs/Bulkhead/BulkheadSpecs.cs +++ b/test/Polly.Specs/Bulkhead/BulkheadSpecs.cs @@ -94,7 +94,8 @@ public void Should_call_onBulkheadRejected_with_passed_context() // Time for the other thread to kick up and take the bulkhead. Within(CohesionTimeLimit, () => Expect(0, () => bulkhead.BulkheadAvailableCount, nameof(bulkhead.BulkheadAvailableCount))); - Should.Throw(() => bulkhead.Execute(_ => { }, contextPassedToExecute)); + Should.Throw(() => bulkhead.Execute(_ => { }, contextPassedToExecute)) + .Message.ShouldBe("The bulkhead semaphore and queue are full and execution was rejected."); #if NET tcs.SetCanceled(CancellationToken); diff --git a/test/Polly.Specs/Caching/AbsoluteTtlSpecs.cs b/test/Polly.Specs/Caching/AbsoluteTtlSpecs.cs index fc69ee26940..63c0072be70 100644 --- a/test/Polly.Specs/Caching/AbsoluteTtlSpecs.cs +++ b/test/Polly.Specs/Caching/AbsoluteTtlSpecs.cs @@ -32,7 +32,10 @@ public void Should_return_zero_ttl_if_configured_to_expire_in_past() { AbsoluteTtl ttlStrategy = new AbsoluteTtl(SystemClock.DateTimeOffsetUtcNow().Subtract(TimeSpan.FromTicks(1))); - ttlStrategy.GetTtl(new Context("someOperationKey"), null).Timespan.ShouldBe(TimeSpan.Zero); + var actual = ttlStrategy.GetTtl(new Context("someOperationKey"), null); + + actual.Timespan.ShouldBe(TimeSpan.Zero); + actual.SlidingExpiration.ShouldBeFalse(); } [Fact] @@ -44,7 +47,10 @@ public void Should_return_timespan_reflecting_time_until_expiry() AbsoluteTtl ttlStrategy = new AbsoluteTtl(tomorrow); SystemClock.DateTimeOffsetUtcNow = () => today; - ttlStrategy.GetTtl(new Context("someOperationKey"), null).Timespan.ShouldBe(TimeSpan.FromDays(1)); + var actual = ttlStrategy.GetTtl(new Context("someOperationKey"), null); + + actual.Timespan.ShouldBe(TimeSpan.FromDays(1)); + actual.SlidingExpiration.ShouldBeFalse(); } public void Dispose() => diff --git a/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs b/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs index 293bfdec1e3..61473fe8192 100644 --- a/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs +++ b/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs @@ -66,6 +66,14 @@ public void Should_throw_when_ttl_strategy_is_null() Should.Throw(action).ParamName.ShouldBe("ttlStrategy"); } + [Fact] + public void Should_throw_when_cacheProvider_is_null() + { + IAsyncCacheProvider nonGenericCacheProvider = null!; + Action action = () => nonGenericCacheProvider.AsyncFor(); + Should.Throw(action).ParamName.ShouldBe("nonGenericCacheProvider"); + } + [Fact] public void Should_throw_when_cache_key_strategy_is_null() { diff --git a/test/Polly.Specs/Caching/CacheTResultSpecs.cs b/test/Polly.Specs/Caching/CacheTResultSpecs.cs index 09bb4b1f1fc..26d2f09e3a3 100644 --- a/test/Polly.Specs/Caching/CacheTResultSpecs.cs +++ b/test/Polly.Specs/Caching/CacheTResultSpecs.cs @@ -44,6 +44,13 @@ public void Should_throw_when_action_is_null() .ParamName.ShouldBe("action"); } + [Fact] + public void For_throws_if_cache_provider_is_null() + { + ISyncCacheProvider nonGenericCacheProvider = null!; + Should.Throw(() => nonGenericCacheProvider.For()).ParamName.ShouldBe("nonGenericCacheProvider"); + } + [Fact] public void Should_throw_when_cache_provider_is_null() { diff --git a/test/Polly.Specs/Caching/ContextualTtlSpecs.cs b/test/Polly.Specs/Caching/ContextualTtlSpecs.cs index b00be44bb6a..838a8459045 100644 --- a/test/Polly.Specs/Caching/ContextualTtlSpecs.cs +++ b/test/Polly.Specs/Caching/ContextualTtlSpecs.cs @@ -115,4 +115,32 @@ public void Should_return_no_sliding_expiration_if_non_boolean_value_set_on_cont gotTtl.Timespan.ShouldBe(ttl); gotTtl.SlidingExpiration.ShouldBeFalse(); } + + [Fact] + public void TimeSpanKey_is_correct() + { + // Assert + ContextualTtl.TimeSpanKey.ShouldBe("ContextualTtlTimeSpan"); + } + + [Fact] + public void SlidingExpirationKey_is_correct() + { + // Assert + ContextualTtl.SlidingExpirationKey.ShouldBe("ContextualTtlSliding"); + } + + [Fact] + public void ContextualTtl_behaves_correctly_for_no_ttl() + { + // Arrange + var target = new ContextualTtl(); + + // Act + var actual = target.GetTtl([], null); + + // Assert + actual.Timespan.ShouldBe(TimeSpan.Zero); + actual.SlidingExpiration.ShouldBeFalse(); + } } diff --git a/test/Polly.Specs/PolicySpecs.cs b/test/Polly.Specs/PolicySpecs.cs index fb5aacb0cb3..f8c26d81719 100644 --- a/test/Polly.Specs/PolicySpecs.cs +++ b/test/Polly.Specs/PolicySpecs.cs @@ -263,5 +263,35 @@ public void Execute_and_capturing_the_policy_function_should_pass_context_to_Pol .Context.ShouldBeSameAs(executionContext); } + [Fact] + public void PolicyKey_Is_Correct() + { + // Arrange + var policy = Policy.NoOp(); + + // Act + var actual = policy.PolicyKey; + + // Assert + actual.ShouldNotBeNull(); + actual.ShouldStartWith("NoOpPolicy-"); + } + + [Fact] + public void PolicyKey_Is_Immutable() + { + // Arrange + var policy = Policy.NoOp(); + var expected = policy.PolicyKey; + + // Act + var exception = Should.Throw(() => policy.WithPolicyKey("foo")); + + // Assert + exception.ParamName.ShouldBe("policyKey"); + exception.Message.ShouldStartWith("PolicyKey cannot be changed once set; or (when using the default value after the PolicyKey property has been accessed."); + policy.PolicyKey.ShouldBe(expected); + } + #endregion } diff --git a/test/Polly.Specs/Polly.Specs.csproj b/test/Polly.Specs/Polly.Specs.csproj index 662b680b8bc..057180192ef 100644 --- a/test/Polly.Specs/Polly.Specs.csproj +++ b/test/Polly.Specs/Polly.Specs.csproj @@ -5,7 +5,7 @@ $(TargetFrameworks);net481 enable Test - 75,60,70 + 80,70,80 [Polly]* true diff --git a/test/Polly.Specs/RateLimit/AsyncRateLimitPolicySpecs.cs b/test/Polly.Specs/RateLimit/AsyncRateLimitPolicySpecs.cs index 3470ed77927..ac5bac14a6d 100644 --- a/test/Polly.Specs/RateLimit/AsyncRateLimitPolicySpecs.cs +++ b/test/Polly.Specs/RateLimit/AsyncRateLimitPolicySpecs.cs @@ -57,4 +57,26 @@ public void Should_throw_when_action_is_null() exceptionAssertions.InnerException.ShouldBeOfType() .ParamName.ShouldBe("action"); } + + [Fact] + public void Should_throw_when_pertimespan_is_negative() + { + // Act + var exception = Should.Throw(() => Policy.RateLimitAsync(1, TimeSpan.FromSeconds(-1), 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); + } + + [Fact] + public void Should_throw_when_pertimespan_is_zero() + { + // Act + var exception = Should.Throw(() => Policy.RateLimitAsync(1, TimeSpan.Zero, 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.Zero); + } } diff --git a/test/Polly.Specs/RateLimit/AsyncRateLimitPolicyTResultSpecs.cs b/test/Polly.Specs/RateLimit/AsyncRateLimitPolicyTResultSpecs.cs index de33eb804a8..3a3f34e0c97 100644 --- a/test/Polly.Specs/RateLimit/AsyncRateLimitPolicyTResultSpecs.cs +++ b/test/Polly.Specs/RateLimit/AsyncRateLimitPolicyTResultSpecs.cs @@ -73,4 +73,26 @@ public void Should_throw_when_action_is_null() exceptionAssertions.InnerException.ShouldBeOfType() .ParamName.ShouldBe("action"); } + + [Fact] + public void Should_throw_when_pertimespan_is_negative() + { + // Act + var exception = Should.Throw(() => Policy.RateLimitAsync(1, TimeSpan.FromSeconds(-1), 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); + } + + [Fact] + public void Should_throw_when_pertimespan_is_zero() + { + // Act + var exception = Should.Throw(() => Policy.RateLimitAsync(1, TimeSpan.Zero, 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.Zero); + } } diff --git a/test/Polly.Specs/RateLimit/RateLimitPolicySpecs.cs b/test/Polly.Specs/RateLimit/RateLimitPolicySpecs.cs index 0aefac0a73f..f60f8955ac3 100644 --- a/test/Polly.Specs/RateLimit/RateLimitPolicySpecs.cs +++ b/test/Polly.Specs/RateLimit/RateLimitPolicySpecs.cs @@ -57,4 +57,26 @@ public void Should_throw_when_action_is_null() exceptionAssertions.InnerException.ShouldBeOfType() .ParamName.ShouldBe("action"); } + + [Fact] + public void Should_throw_when_pertimespan_is_negative() + { + // Act + var exception = Should.Throw(() => Policy.RateLimit(1, TimeSpan.FromSeconds(-1), 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); + } + + [Fact] + public void Should_throw_when_pertimespan_is_zero() + { + // Act + var exception = Should.Throw(() => Policy.RateLimit(1, TimeSpan.Zero, 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.Zero); + } } diff --git a/test/Polly.Specs/RateLimit/RateLimitPolicyTResultSpecs.cs b/test/Polly.Specs/RateLimit/RateLimitPolicyTResultSpecs.cs index bd11d581044..c0a7997824a 100644 --- a/test/Polly.Specs/RateLimit/RateLimitPolicyTResultSpecs.cs +++ b/test/Polly.Specs/RateLimit/RateLimitPolicyTResultSpecs.cs @@ -73,4 +73,26 @@ public void Should_throw_when_action_is_null() exceptionAssertions.InnerException.ShouldBeOfType() .ParamName.ShouldBe("action"); } + + [Fact] + public void Should_throw_when_pertimespan_is_negative() + { + // Act + var exception = Should.Throw(() => Policy.RateLimit(1, TimeSpan.FromSeconds(-1), 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); + } + + [Fact] + public void Should_throw_when_pertimespan_is_zero() + { + // Act + var exception = Should.Throw(() => Policy.RateLimit(1, TimeSpan.Zero, 1)); + + // Assert + exception.ParamName.ShouldBe("perTimeSpan"); + exception.ActualValue.ShouldBe(TimeSpan.Zero); + } } diff --git a/test/Polly.Specs/RateLimit/RateLimitRejectedExceptionTests.cs b/test/Polly.Specs/RateLimit/RateLimitRejectedExceptionTests.cs index 5d327e15b63..80855cb598f 100644 --- a/test/Polly.Specs/RateLimit/RateLimitRejectedExceptionTests.cs +++ b/test/Polly.Specs/RateLimit/RateLimitRejectedExceptionTests.cs @@ -31,5 +31,17 @@ public void Ctor_Ok() rate.RetryAfter.ShouldBe(retryAfter); rate.Message.ShouldBe(Dummy); rate.InnerException.ShouldBe(exception); + + var ex = Should.Throw(() => new RateLimitRejectedException(TimeSpan.FromSeconds(-1))); + ex.ParamName.ShouldBe("retryAfter"); + ex.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); + + ex = Should.Throw(() => new RateLimitRejectedException(TimeSpan.FromSeconds(-1), rate)); + ex.ParamName.ShouldBe("retryAfter"); + ex.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); + + ex = Should.Throw(() => new RateLimitRejectedException(TimeSpan.FromSeconds(-1), "Error", rate)); + ex.ParamName.ShouldBe("retryAfter"); + ex.ActualValue.ShouldBe(TimeSpan.FromSeconds(-1)); } } diff --git a/test/Polly.Specs/Registry/ConcurrentPolicyRegistrySpecs.cs b/test/Polly.Specs/Registry/ConcurrentPolicyRegistrySpecs.cs index f54258c4e0d..d06696edaa7 100644 --- a/test/Polly.Specs/Registry/ConcurrentPolicyRegistrySpecs.cs +++ b/test/Polly.Specs/Registry/ConcurrentPolicyRegistrySpecs.cs @@ -80,6 +80,7 @@ public void Should_report_false_from_TryRemove_if_no_Policy() bool removed = _registry.TryRemove(key, out IsPolicy removedPolicy); removed.ShouldBeFalse(); + removedPolicy.ShouldBeNull(); } [Fact] diff --git a/test/Polly.Specs/Registry/PolicyRegistrySpecs.cs b/test/Polly.Specs/Registry/PolicyRegistrySpecs.cs index 8894b64563d..f662f5e3971 100644 --- a/test/Polly.Specs/Registry/PolicyRegistrySpecs.cs +++ b/test/Polly.Specs/Registry/PolicyRegistrySpecs.cs @@ -160,6 +160,26 @@ public void Should_be_able_to_retrieve_stored_Policy_using_TryGet() outPolicy.ShouldBeSameAs(policy); } + [Fact] + public void Should_return_false_if_policy_does_not_exist_TryGet() + { + var policy = Policy.NoOp(); + string key = Guid.NewGuid().ToString(); + + _registry.TryGet(key, out var outPolicy).ShouldBeFalse(); + outPolicy.ShouldBeNull(); + } + + [Fact] + public void Should_return_false_if_policy_does_not_exist_TryRemove() + { + var policy = Policy.NoOp(); + string key = Guid.NewGuid().ToString(); + + _registry.TryRemove(key, out var outPolicy).ShouldBeFalse(); + outPolicy.ShouldBeNull(); + } + [Fact] public void Should_be_able_to_retrieve_stored_PolicyTResult_using_TryGet() { diff --git a/test/Polly.Specs/ResiliencePipelineConversionExtensionsTests.cs b/test/Polly.Specs/ResiliencePipelineConversionExtensionsTests.cs index 37aebab3dc6..d703f159414 100644 --- a/test/Polly.Specs/ResiliencePipelineConversionExtensionsTests.cs +++ b/test/Polly.Specs/ResiliencePipelineConversionExtensionsTests.cs @@ -35,6 +35,30 @@ public ResiliencePipelineConversionExtensionsTests() .Build(); } + [Fact] + public void AsAsyncPolicy_Throws_If_Null() + { + // Arrange + ResiliencePipeline strategy = null!; + ResiliencePipeline strategyGeneric = null!; + + // Act and Assert + Should.Throw(strategy.AsAsyncPolicy).ParamName.ShouldBe("strategy"); + Should.Throw(strategyGeneric.AsAsyncPolicy).ParamName.ShouldBe("strategy"); + } + + [Fact] + public void AsSyncPolicy_Throws_If_Null() + { + // Arrange + ResiliencePipeline strategy = null!; + ResiliencePipeline strategyGeneric = null!; + + // Act and Assert + Should.Throw(strategy.AsSyncPolicy).ParamName.ShouldBe("strategy"); + Should.Throw(strategyGeneric.AsSyncPolicy).ParamName.ShouldBe("strategy"); + } + [Fact] public void AsSyncPolicy_Ok() { diff --git a/test/Polly.Specs/Retry/RetryForeverAsyncSpecs.cs b/test/Polly.Specs/Retry/RetryForeverAsyncSpecs.cs index 613ea5ac8a9..6a228e04cbc 100644 --- a/test/Polly.Specs/Retry/RetryForeverAsyncSpecs.cs +++ b/test/Polly.Specs/Retry/RetryForeverAsyncSpecs.cs @@ -102,6 +102,110 @@ public void Should_throw_when_onretry_exception_int_is_null() Assert.Throws("onRetry", () => policyBuilder.RetryForeverAsync(default(Action))); } + [Fact] + public void Should_throw_when_onretry_is_null() + { + Action> onRetry = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetry"); + } + + [Fact] + public void Should_throw_when_onretry_with_int_is_null() + { + Action, int> onRetry = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetry"); + } + + [Fact] + public void Should_throw_when_onretryasync_is_null() + { + Func, Task> onRetryAsync = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetryAsync); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetryAsync"); + } + + [Fact] + public void Should_throw_when_onretryasync_with_int_is_null() + { + Func, int, Task> onRetryAsync = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetryAsync); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetryAsync"); + } + + [Fact] + public void Should_throw_when_onretry_with_context_is_null() + { + Action, Context> onRetry = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetry"); + } + + [Fact] + public void Should_throw_when_onretry_with_int_and_context_is_null() + { + Action, int, Context> onRetry = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetry"); + } + + [Fact] + public void Should_throw_when_onretryasync_with_context_is_null() + { + Func, Context, Task> onRetryAsync = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetryAsync); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetryAsync"); + } + + [Fact] + public void Should_throw_when_onretryasync_with_int_and_context_is_null() + { + Func, int, Context, Task> onRetryAsync = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetryAsync); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetryAsync"); + } + [Fact] public void Should_throw_when_onretry_exception_context_is_null() { diff --git a/test/Polly.Specs/Retry/RetryTResultAsyncSpecs.cs b/test/Polly.Specs/Retry/RetryTResultAsyncSpecs.cs index f32a5c65876..27915fa8cdc 100644 --- a/test/Polly.Specs/Retry/RetryTResultAsyncSpecs.cs +++ b/test/Polly.Specs/Retry/RetryTResultAsyncSpecs.cs @@ -33,6 +33,32 @@ public void Should_throw_when_action_is_null() .ParamName.ShouldBe("action"); } + [Fact] + public void Should_throw_when_onretryasync_is_null() + { + Func, int, Task> onRetryAsync = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryAsync(1, onRetryAsync); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetryAsync"); + } + + [Fact] + public void Should_throw_when_onretry_is_null() + { + Action> onRetry = null!; + + Action policy = () => Policy + .HandleResult(ResultPrimitive.Fault) + .RetryForeverAsync(onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("onRetry"); + } + [Fact] public void Should_throw_when_retry_count_is_less_than_zero_without_context() { diff --git a/test/Polly.Specs/Retry/WaitAndRetryForeverSpecs.cs b/test/Polly.Specs/Retry/WaitAndRetryForeverSpecs.cs index f2802a1c27c..0c93f9b7697 100644 --- a/test/Polly.Specs/Retry/WaitAndRetryForeverSpecs.cs +++ b/test/Polly.Specs/Retry/WaitAndRetryForeverSpecs.cs @@ -7,6 +7,19 @@ public class WaitAndRetryForeverSpecs : IDisposable [Fact] public void Should_throw_when_sleep_duration_provider_is_null_without_context() + { + Func sleepDurationProvider = null!; + + Action policy = () => Policy + .Handle() + .WaitAndRetryForever(sleepDurationProvider); + + Should.Throw(policy) + .ParamName.ShouldBe("sleepDurationProvider"); + } + + [Fact] + public void Should_throw_when_sleep_duration_provider_is_null_without_context_with_onretry() { Action onRetry = (_, _) => { }; @@ -33,6 +46,21 @@ public void Should_throw_when_sleep_duration_provider_is_null_with_context() .ParamName.ShouldBe("sleepDurationProvider"); } + [Fact] + public void Should_throw_when_sleep_duration_provider_is_null_with_context_and_onretry_with_attempt() + { + Action onRetry = (_, _, _, _) => { }; + + Func sleepDurationProvider = null!; + + Action policy = () => Policy + .Handle() + .WaitAndRetryForever(sleepDurationProvider, onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("sleepDurationProvider"); + } + [Fact] public void Should_throw_when_onretry_action_is_null_without_context() { diff --git a/test/Polly.Specs/Retry/WaitAndRetrySpecs.cs b/test/Polly.Specs/Retry/WaitAndRetrySpecs.cs index dbe85fe1fc9..0828ef6dcf8 100644 --- a/test/Polly.Specs/Retry/WaitAndRetrySpecs.cs +++ b/test/Polly.Specs/Retry/WaitAndRetrySpecs.cs @@ -520,6 +520,20 @@ public void Should_throw_when_retry_count_is_less_than_zero_with_context() .ParamName.ShouldBe("retryCount"); } + [Fact] + public void Should_throw_when_retry_count_is_less_than_zero_with_attempts_with_context_and_attempt() + { + Func sleepDurationProvider = (_, _, _) => TimeSpan.Zero; + Action onRetry = (_, _, _, _) => { }; + + Action policy = () => + Policy.Handle() + .WaitAndRetry(-1, sleepDurationProvider, onRetry); + + Should.Throw(policy) + .ParamName.ShouldBe("retryCount"); + } + [Fact] public void Should_throw_when_retry_count_is_less_than_zero_with_attempts_with_context() { diff --git a/test/Polly.Specs/Timeout/TimeoutAsyncSpecs.cs b/test/Polly.Specs/Timeout/TimeoutAsyncSpecs.cs index 3433dcbe215..c5db1eb0278 100644 --- a/test/Polly.Specs/Timeout/TimeoutAsyncSpecs.cs +++ b/test/Polly.Specs/Timeout/TimeoutAsyncSpecs.cs @@ -1,6 +1,4 @@ -using System; - -namespace Polly.Specs.Timeout; +namespace Polly.Specs.Timeout; [Collection(Constants.SystemClockDependentTestCollection)] public class TimeoutAsyncSpecs : TimeoutSpecsBase @@ -111,6 +109,24 @@ public void Should_not_throw_when_timeout_is_infinitetimespan() Should.NotThrow(policy); } + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_timeoutstrategy() + { + Action policy = () => Policy.TimeoutAsync(0, TimeoutStrategy.Optimistic); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy() + { + Action policy = () => Policy.TimeoutAsync(-10, TimeoutStrategy.Optimistic); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + [Fact] public void Should_not_throw_when_timeout_is_infinitetimespan_with_timeoutstrategy() { @@ -128,6 +144,26 @@ public void Should_not_throw_when_timeout_is_infinitetimespan_with_ontimeout() Should.NotThrow(policy); } + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_timeoutstrategy_and_ontimeout() + { + Func onTimeout = (_, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(0, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy_and_ontimeout() + { + Func onTimeout = (_, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(-10, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + [Fact] public void Should_not_throw_when_timeout_is_infinitetimespan_with_timeoutstrategy_and_ontimeout() { @@ -177,6 +213,46 @@ public void Should_throw_when_onTimeout_is_null_with_seconds_with_full_argument_ .ParamName.ShouldBe("onTimeoutAsync"); } + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(0, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(-10, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_zero_by_timespan_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(TimeSpan.Zero, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("timeout"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_timespan_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(TimeSpan.FromSeconds(-10), TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("timeout"); + } + [Fact] public void Should_throw_when_timeoutProvider_is_null() { @@ -531,6 +607,50 @@ await Should.ThrowAsync(() => policy.ExecuteAsync(asyn contextPassedToOnTimeout!.ShouldBeSameAs(contextPassedToExecute); } + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy() + { + // Arrange + Func timeoutProvider = null!; + Action policy = () => Policy.TimeoutAsync(timeoutProvider, TimeoutStrategy.Pessimistic); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy_and_ontimeout() + { + // Arrange + Func timeoutProvider = null!; + Func onTimeoutAsync = (_, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(timeoutProvider, TimeoutStrategy.Pessimistic, onTimeoutAsync); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy_and_ontimeout_with_exception() + { + // Arrange + Func timeoutProvider = null!; + Func onTimeoutAsync = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(timeoutProvider, TimeoutStrategy.Pessimistic, onTimeoutAsync); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + [Theory] [InlineData(1)] [InlineData(2)] diff --git a/test/Polly.Specs/Timeout/TimeoutSpecs.cs b/test/Polly.Specs/Timeout/TimeoutSpecs.cs index 3ef3560d330..f26ec81aa5f 100644 --- a/test/Polly.Specs/Timeout/TimeoutSpecs.cs +++ b/test/Polly.Specs/Timeout/TimeoutSpecs.cs @@ -109,6 +109,23 @@ public void Should_not_throw_when_timeout_is_infinitetimespan() Should.NotThrow(policy); } + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy() + { + Action policy = () => Policy.Timeout(-10, TimeoutStrategy.Optimistic); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_not_throw_when_timeout_is_greater_than_zero_by_seconds_with_timeoutstrategy() + { + Action policy = () => Policy.Timeout(3, TimeoutStrategy.Optimistic); + + Should.NotThrow(policy); + } + [Fact] public void Should_not_throw_when_timeout_is_infinitetimespan_with_timeoutstrategy() { @@ -117,6 +134,25 @@ public void Should_not_throw_when_timeout_is_infinitetimespan_with_timeoutstrate Should.NotThrow(policy); } + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_ontimeout() + { + Action doNothing = (_, _, _) => { }; + Action policy = () => Policy.Timeout(-10, doNothing); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_not_throw_when_timeout_is_greater_than_zero_by_seconds_with_ontimeout() + { + Action doNothing = (_, _, _) => { }; + Action policy = () => Policy.Timeout(3, doNothing); + + Should.NotThrow(policy); + } + [Fact] public void Should_not_throw_when_timeout_is_infinitetimespan_with_ontimeout() { @@ -598,6 +634,35 @@ public void Should_call_ontimeout_with_timeout_supplied_different_for_each_execu timeoutPassedToOnTimeout.ShouldBe(timeoutFunc()); } + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy() + { + // Arrange + Func timeoutProvider = null!; + Action policy = () => Policy.Timeout(timeoutProvider, TimeoutStrategy.Pessimistic); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy_and_ontimeout() + { + // Arrange + Func timeoutProvider = null!; + Action onTimeout = (_, _, _) => { }; + Action policy = () => Policy.Timeout(timeoutProvider, TimeoutStrategy.Pessimistic, onTimeout); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + [Theory] [InlineData(1)] [InlineData(2)] @@ -779,6 +844,44 @@ public void Should_call_ontimeout_but_not_with_task_wrapping_abandoned_action__o taskPassedToOnTimeout.ShouldBeNull(); } + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_ontimeout_with_exception() + { + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(-10, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_not_throw_when_timeout_is_greater_than_zero_by_seconds_with_ontimeout_with_exception() + { + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(3, onTimeout); + + Should.NotThrow(policy); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_ontimeout_with_exception_and_strategy() + { + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(-10, TimeoutStrategy.Pessimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_not_throw_when_timeout_is_greater_than_zero_by_seconds_with_ontimeout_with_exception_and_strategy() + { + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(3, TimeoutStrategy.Pessimistic, onTimeout); + + Should.NotThrow(policy); + } + [Fact] public void Should_call_ontimeout_with_timing_out_exception__optimistic() { diff --git a/test/Polly.Specs/Timeout/TimeoutTResultAsyncSpecs.cs b/test/Polly.Specs/Timeout/TimeoutTResultAsyncSpecs.cs index 3ea53cc31c3..1243ee41361 100644 --- a/test/Polly.Specs/Timeout/TimeoutTResultAsyncSpecs.cs +++ b/test/Polly.Specs/Timeout/TimeoutTResultAsyncSpecs.cs @@ -68,6 +68,44 @@ public void Should_throw_when_timeout_is_less_than_zero_by_seconds() .ParamName.ShouldBe("seconds"); } + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_timeoutstrategy() + { + Action policy = () => Policy.TimeoutAsync(0, TimeoutStrategy.Optimistic); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy() + { + Action policy = () => Policy.TimeoutAsync(-10, TimeoutStrategy.Optimistic); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_timeoutstrategy_and_ontimeout() + { + Func onTimeout = (_, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(0, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy_and_ontimeout() + { + Func onTimeout = (_, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(-10, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + [Fact] public void Should_not_throw_when_timeout_is_greater_than_zero_by_timespan() { @@ -134,6 +172,46 @@ public void Should_not_throw_when_timeout_is_infinitetimespan_with_timeoutstrate Should.NotThrow(policy); } + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(0, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(-10, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_zero_by_timespan_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(TimeSpan.Zero, TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("timeout"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_timespan_with_timeoutstrategy_and_full_argument_list_onTimeout() + { + Func onTimeout = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(TimeSpan.FromSeconds(-10), TimeoutStrategy.Optimistic, onTimeout); + + Should.Throw(policy) + .ParamName.ShouldBe("timeout"); + } + [Fact] public void Should_throw_when_onTimeout_is_null_with_timespan() { @@ -467,6 +545,50 @@ await Should.ThrowAsync(() => policy.ExecuteAsync(asyn contextPassedToOnTimeout!.ShouldBeSameAs(contextPassedToExecute); } + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy() + { + // Arrange + Func timeoutProvider = null!; + Action policy = () => Policy.TimeoutAsync(timeoutProvider, TimeoutStrategy.Pessimistic); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy_and_ontimeout() + { + // Arrange + Func timeoutProvider = null!; + Func onTimeoutAsync = (_, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(timeoutProvider, TimeoutStrategy.Pessimistic, onTimeoutAsync); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy_and_ontimeout_with_exception() + { + // Arrange + Func timeoutProvider = null!; + Func onTimeoutAsync = (_, _, _, _) => TaskHelper.EmptyTask; + Action policy = () => Policy.TimeoutAsync(timeoutProvider, TimeoutStrategy.Pessimistic, onTimeoutAsync); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + [Theory] [InlineData(1)] [InlineData(2)] diff --git a/test/Polly.Specs/Timeout/TimeoutTResultSpecs.cs b/test/Polly.Specs/Timeout/TimeoutTResultSpecs.cs index 619c1650491..da4b7414fbb 100644 --- a/test/Polly.Specs/Timeout/TimeoutTResultSpecs.cs +++ b/test/Polly.Specs/Timeout/TimeoutTResultSpecs.cs @@ -164,6 +164,33 @@ public void Should_throw_when_onTimeout_is_null_with_seconds() .ParamName.ShouldBe("onTimeout"); } + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_and_onTimeout_is_full_argument_set() + { + // Arrange + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(-1, onTimeout); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_and_onTimeout_is_full_argument_set() + { + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(0, onTimeout); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("seconds"); + } + [Fact] public void Should_throw_when_onTimeout_is_null_with_seconds_and_onTimeout_is_full_argument_set() { @@ -215,6 +242,20 @@ public void Should_be_able_to_configure_with_timeout_func() #region Timeout operation - pessimistic + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy() + { + // Arrange + Func timeoutProvider = null!; + Action policy = () => Policy.Timeout(timeoutProvider, TimeoutStrategy.Pessimistic); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + [Fact] public void Should_throw_when_timeout_is_less_than_execution_duration__pessimistic() { @@ -517,6 +558,48 @@ public void Should_not_execute_user_delegate_if_user_cancellationToken_cancelled #region onTimeout overload - pessimistic + [Fact] + public void Should_throw_when_timeoutProvider_is_null_with_strategy_and_ontimeout() + { + // Arrange + Func timeoutProvider = null!; + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(timeoutProvider, TimeoutStrategy.Pessimistic, onTimeout); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("timeoutProvider"); + } + + [Fact] + public void Should_throw_when_timeout_is_less_than_zero_by_seconds_with_strategy_and_ontimeout() + { + // Arrange + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(-1, TimeoutStrategy.Pessimistic, onTimeout); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("seconds"); + } + + [Fact] + public void Should_throw_when_timeout_is_zero_by_seconds_with_strategy_and_ontimeout() + { + Action onTimeout = (_, _, _, _) => { }; + Action policy = () => Policy.Timeout(0, TimeoutStrategy.Pessimistic, onTimeout); + + // Act + var exception = Should.Throw(policy); + + // Assert + exception.ParamName.ShouldBe("seconds"); + } + [Fact] public void Should_call_ontimeout_with_configured_timeout__pessimistic() {