From 6cb927fb757c839a0f522aa6e8af332b0803d680 Mon Sep 17 00:00:00 2001 From: ZLoo Date: Mon, 22 Jul 2024 23:23:41 +0300 Subject: [PATCH 1/2] Fix warning CA1062#AsyncCachePolicy --- src/Polly/Caching/AsyncCachePolicy.cs | 46 ++++++++++++---- test/Polly.Specs/Caching/CacheAsyncSpecs.cs | 53 +++++++++++++++++++ .../Caching/CacheTResultAsyncSpecs.cs | 42 +++++++++++++++ 3 files changed, 132 insertions(+), 9 deletions(-) diff --git a/src/Polly/Caching/AsyncCachePolicy.cs b/src/Polly/Caching/AsyncCachePolicy.cs index bfa7bc9335c..935c533a614 100644 --- a/src/Polly/Caching/AsyncCachePolicy.cs +++ b/src/Polly/Caching/AsyncCachePolicy.cs @@ -4,7 +4,6 @@ namespace Polly.Caching; /// /// A cache policy that can be applied to the results of delegate executions. /// -#pragma warning disable CA1062 // Validate arguments of public methods public class AsyncCachePolicy : AsyncPolicy { private readonly IAsyncCacheProvider _asyncCacheProvider; @@ -43,13 +42,31 @@ protected override Task ImplementationAsync( Func action, Context context, CancellationToken cancellationToken, - bool continueOnCapturedContext) => action(context, cancellationToken); // Pass-through/NOOP policy action, for void-returning executions through the cache policy. + bool continueOnCapturedContext) + { + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + + // Pass-through/NOOP policy action, for void-returning executions through the cache policy. + return action(context, cancellationToken); + } /// - [DebuggerStepThrough] - protected override Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken, - bool continueOnCapturedContext) => - AsyncCacheEngine.ImplementationAsync( + //[DebuggerStepThrough] + protected override Task ImplementationAsync( + Func> action, + Context context, + CancellationToken cancellationToken, + bool continueOnCapturedContext) + { + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + + return AsyncCacheEngine.ImplementationAsync( _asyncCacheProvider.AsyncFor(), _ttlStrategy.For(), _cacheKeyStrategy, @@ -62,6 +79,7 @@ protected override Task ImplementationAsync(Func @@ -104,9 +122,18 @@ internal AsyncCachePolicy( /// [DebuggerStepThrough] - protected override Task ImplementationAsync(Func> action, Context context, CancellationToken cancellationToken, - bool continueOnCapturedContext) => - AsyncCacheEngine.ImplementationAsync( + protected override Task ImplementationAsync( + Func> action, + Context context, + CancellationToken cancellationToken, + bool continueOnCapturedContext) + { + if (action is null) + { + throw new ArgumentNullException(nameof(action)); + } + + return AsyncCacheEngine.ImplementationAsync( _asyncCacheProvider, _ttlStrategy, _cacheKeyStrategy, @@ -119,5 +146,6 @@ protected override Task ImplementationAsync(Func> action = null!; + Func actionVoid = null!; + + IAsyncCacheProvider asyncCacheProvider = new StubCacheProvider(); + ITtlStrategy ttlStrategy = new ContextualTtl(); + Func cacheKeyStrategy = (_) => string.Empty; + Action onCacheGet = (_, _) => { }; + Action onCacheMiss = (_, _) => { }; + Action onCachePut = (_, _) => { }; + Action? onCacheGetError = null; + Action? onCachePutError = null; + + var instance = Activator.CreateInstance( + typeof(AsyncCachePolicy), + flags, + null, + [ + asyncCacheProvider, + ttlStrategy, + cacheKeyStrategy, + onCacheGet, + onCacheMiss, + onCachePut, + onCacheGetError, + onCachePutError, + ], + null)!; + var instanceType = instance.GetType(); + var methods = instanceType.GetMethods(flags); + var methodInfo = methods.First(method => method is { Name: "ImplementationAsync", ReturnType.Name: "Task`1" }); + var generic = methodInfo.MakeGenericMethod(typeof(EmptyStruct)); + + var func = () => generic.Invoke(instance, [action, new Context(), CancellationToken.None, false]); + + var exceptionAssertions = func.Should().Throw(); + exceptionAssertions.And.Message.Should().Be("Exception has been thrown by the target of an invocation."); + exceptionAssertions.And.InnerException.Should().BeOfType() + .Which.ParamName.Should().Be("action"); + + methodInfo = methods.First(method => method is { Name: "ImplementationAsync", ReturnType.Name: "Task" }); + + func = () => methodInfo.Invoke(instance, [actionVoid, new Context(), CancellationToken.None, false]); + + exceptionAssertions = func.Should().Throw(); + exceptionAssertions.And.Message.Should().Be("Exception has been thrown by the target of an invocation."); + exceptionAssertions.And.InnerException.Should().BeOfType() + .Which.ParamName.Should().Be("action"); + } + [Fact] public void Should_throw_when_cache_provider_is_null() { diff --git a/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs b/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs index 8b4b9874d98..8f5d58464fd 100644 --- a/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs +++ b/test/Polly.Specs/Caching/CacheTResultAsyncSpecs.cs @@ -5,6 +5,48 @@ public class CacheTResultAsyncSpecs : IDisposable { #region Configuration + [Fact] + public void Should_throw_when_action_is_null() + { + var flags = BindingFlags.NonPublic | BindingFlags.Instance; + Func> action = null!; + + IAsyncCacheProvider asyncCacheProvider = new StubCacheProvider().AsyncFor(); + ITtlStrategy ttlStrategy = new ContextualTtl().For(); + Func cacheKeyStrategy = (_) => string.Empty; + Action onCacheGet = (_, _) => { }; + Action onCacheMiss = (_, _) => { }; + Action onCachePut = (_, _) => { }; + Action? onCacheGetError = null; + Action? onCachePutError = null; + + var instance = Activator.CreateInstance( + typeof(AsyncCachePolicy), + flags, + null, + [ + asyncCacheProvider, + ttlStrategy, + cacheKeyStrategy, + onCacheGet, + onCacheMiss, + onCachePut, + onCacheGetError, + onCachePutError, + ], + null)!; + var instanceType = instance.GetType(); + var methods = instanceType.GetMethods(flags); + var methodInfo = methods.First(method => method is { Name: "ImplementationAsync", ReturnType.Name: "Task`1" }); + + var func = () => methodInfo.Invoke(instance, [action, new Context(), CancellationToken.None, false]); + + var exceptionAssertions = func.Should().Throw(); + exceptionAssertions.And.Message.Should().Be("Exception has been thrown by the target of an invocation."); + exceptionAssertions.And.InnerException.Should().BeOfType() + .Which.ParamName.Should().Be("action"); + } + [Fact] public void Should_throw_when_cache_provider_is_null() { From 0860b532e04d6fb5e23682c2323c33a04fbe447a Mon Sep 17 00:00:00 2001 From: ZLoo Date: Mon, 22 Jul 2024 23:31:30 +0300 Subject: [PATCH 2/2] Fix PR --- src/Polly/Caching/AsyncCachePolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Polly/Caching/AsyncCachePolicy.cs b/src/Polly/Caching/AsyncCachePolicy.cs index 935c533a614..89e60c6bed8 100644 --- a/src/Polly/Caching/AsyncCachePolicy.cs +++ b/src/Polly/Caching/AsyncCachePolicy.cs @@ -54,7 +54,7 @@ protected override Task ImplementationAsync( } /// - //[DebuggerStepThrough] + [DebuggerStepThrough] protected override Task ImplementationAsync( Func> action, Context context,