diff --git a/src/Polly.Core/PublicAPI.Shipped.txt b/src/Polly.Core/PublicAPI.Shipped.txt index 8564f3e84a4..111cffcf0e6 100644 --- a/src/Polly.Core/PublicAPI.Shipped.txt +++ b/src/Polly.Core/PublicAPI.Shipped.txt @@ -421,7 +421,7 @@ Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions.ChaosOutcomeStrategyOptions() -> void Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions.OnOutcomeInjected.get -> System.Func, System.Threading.Tasks.ValueTask>? Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions.OnOutcomeInjected.set -> void -Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions.OutcomeGenerator.get -> System.Func?>>! +Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions.OutcomeGenerator.get -> System.Func>>? Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions.OutcomeGenerator.set -> void Polly.Simmy.Outcomes.OnOutcomeInjectedArguments Polly.Simmy.Outcomes.OnOutcomeInjectedArguments.Context.get -> Polly.ResilienceContext! @@ -542,7 +542,7 @@ static Polly.Simmy.ChaosLatencyPipelineBuilderExtensions.AddChaosLatency(this Polly.ResiliencePipelineBuilder! builder, double injectionRate, System.Func! resultGenerator) -> Polly.ResiliencePipelineBuilder! static Polly.Simmy.ChaosOutcomePipelineBuilderExtensions.AddChaosOutcome(this Polly.ResiliencePipelineBuilder! builder, Polly.Simmy.Outcomes.ChaosOutcomeStrategyOptions! options) -> Polly.ResiliencePipelineBuilder! static Polly.Simmy.Fault.FaultGenerator.implicit operator System.Func>!(Polly.Simmy.Fault.FaultGenerator! generator) -> System.Func>! -static Polly.Simmy.Outcomes.OutcomeGenerator.implicit operator System.Func?>>!(Polly.Simmy.Outcomes.OutcomeGenerator! generator) -> System.Func?>>! +static Polly.Simmy.Outcomes.OutcomeGenerator.implicit operator System.Func>>!(Polly.Simmy.Outcomes.OutcomeGenerator! generator) -> System.Func>>! static Polly.TimeoutResiliencePipelineBuilderExtensions.AddTimeout(this TBuilder! builder, Polly.Timeout.TimeoutStrategyOptions! options) -> TBuilder! static Polly.TimeoutResiliencePipelineBuilderExtensions.AddTimeout(this TBuilder! builder, System.TimeSpan timeout) -> TBuilder! static readonly Polly.ResiliencePipeline.Empty -> Polly.ResiliencePipeline! diff --git a/src/Polly.Core/Simmy/Fault/FaultGenerator.cs b/src/Polly.Core/Simmy/Fault/FaultGenerator.cs index d1dcf31049e..89495267adb 100644 --- a/src/Polly.Core/Simmy/Fault/FaultGenerator.cs +++ b/src/Polly.Core/Simmy/Fault/FaultGenerator.cs @@ -79,6 +79,6 @@ public FaultGenerator AddException(int weight = DefaultWeight) var generatorDelegate = generator._helper.CreateGenerator(); - return args => new ValueTask(generatorDelegate(args.Context)!.Value.Exception); + return args => new ValueTask(generatorDelegate(args.Context).Exception); } } diff --git a/src/Polly.Core/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensions.cs b/src/Polly.Core/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensions.cs index 8f2160c821c..f64019fe795 100644 --- a/src/Polly.Core/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensions.cs +++ b/src/Polly.Core/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensions.cs @@ -28,7 +28,7 @@ public static class ChaosOutcomePipelineBuilderExtensions InjectionRate = injectionRate, OutcomeGenerator = (_) => { - return new ValueTask?>(Outcome.FromResult(resultGenerator())); + return new ValueTask>(Outcome.FromResult(resultGenerator())); } }); return builder; diff --git a/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategy.cs b/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategy.cs index 1a6c075ff03..2f1311d3516 100644 --- a/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategy.cs +++ b/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategy.cs @@ -11,12 +11,12 @@ public ChaosOutcomeStrategy(ChaosOutcomeStrategyOptions options, ResilienceSt { _telemetry = telemetry; OnOutcomeInjected = options.OnOutcomeInjected; - OutcomeGenerator = options.OutcomeGenerator; + OutcomeGenerator = options.OutcomeGenerator!; } public Func, ValueTask>? OnOutcomeInjected { get; } - public Func?>> OutcomeGenerator { get; } + public Func>> OutcomeGenerator { get; } protected internal override async ValueTask> ExecuteCore(Func>> callback, ResilienceContext context, TState state) { @@ -25,7 +25,7 @@ protected internal override async ValueTask> ExecuteCore(Func if (await ShouldInjectAsync(context).ConfigureAwait(context.ContinueOnCapturedContext)) { var outcome = await OutcomeGenerator(new(context)).ConfigureAwait(context.ContinueOnCapturedContext); - var args = new OnOutcomeInjectedArguments(context, outcome.Value); + var args = new OnOutcomeInjectedArguments(context, outcome); _telemetry.Report(new(ResilienceEventSeverity.Information, ChaosOutcomeConstants.OnOutcomeInjectedEvent), context, args); if (OnOutcomeInjected is not null) @@ -33,7 +33,7 @@ protected internal override async ValueTask> ExecuteCore(Func await OnOutcomeInjected(args).ConfigureAwait(context.ContinueOnCapturedContext); } - return new Outcome(outcome.Value.Result); + return new Outcome(outcome.Result); } return await StrategyHelper.ExecuteCallbackSafeAsync(callback, context, state).ConfigureAwait(context.ContinueOnCapturedContext); diff --git a/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategyOptions.cs b/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategyOptions.cs index cf5f8a2085b..02024f58f9e 100644 --- a/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategyOptions.cs +++ b/src/Polly.Core/Simmy/Outcomes/ChaosOutcomeStrategyOptions.cs @@ -28,5 +28,5 @@ public class ChaosOutcomeStrategyOptions : ChaosStrategyOptions /// Defaults to . This property is required. /// [Required] - public Func?>> OutcomeGenerator { get; set; } = default!; + public Func>>? OutcomeGenerator { get; set; } } diff --git a/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs b/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs index 95d961f3d4c..bc0bae2c21c 100644 --- a/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs +++ b/src/Polly.Core/Simmy/Outcomes/OutcomeGenerator.cs @@ -108,13 +108,13 @@ public OutcomeGenerator AddResult(Func gene /// /// The generator instance. [EditorBrowsable(EditorBrowsableState.Never)] - public static implicit operator Func?>>(OutcomeGenerator generator) + public static implicit operator Func>>(OutcomeGenerator generator) { Guard.NotNull(generator); var generatorDelegate = generator._helper.CreateGenerator(); - return args => new ValueTask?>(generatorDelegate(args.Context)); + return args => new ValueTask>(generatorDelegate(args.Context)); } } diff --git a/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs b/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs index 6ec4090a0f2..4a39d7794eb 100644 --- a/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs +++ b/src/Polly.Core/Simmy/Utils/GeneratorHelper.cs @@ -19,11 +19,11 @@ public void AddOutcome(Func> generator, int _weights.Add(weight); } - internal Func?> CreateGenerator() + internal Func> CreateGenerator() { if (_factories.Count == 0) { - return _ => null; + throw new InvalidOperationException("No outcome generators have been added."); } var totalWeight = _totalWeight; @@ -45,7 +45,8 @@ public void AddOutcome(Func> generator, int } } - return null; + // Should never reach here! + return default; }; } } diff --git a/src/Snippets/Docs/Chaos.Outcome.cs b/src/Snippets/Docs/Chaos.Outcome.cs index a8c004a0a81..85a03bb05df 100644 --- a/src/Snippets/Docs/Chaos.Outcome.cs +++ b/src/Snippets/Docs/Chaos.Outcome.cs @@ -63,7 +63,7 @@ public static void OutcomeUsage() OutcomeGenerator = static args => { var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); - return new ValueTask?>(Outcome.FromResult(response)); + return Outcome.FromResultAsValueTask(response); }, InjectionRate = 0.1 }) @@ -99,13 +99,12 @@ public static void OutcomeGeneratorDelegates() // The same behavior can be achieved with delegates OutcomeGenerator = args => { - Outcome? outcome = Random.Shared.Next(350) switch + Outcome outcome = Random.Shared.Next(350) switch { < 100 => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.InternalServerError)), < 150 => Outcome.FromResult(new HttpResponseMessage(HttpStatusCode.TooManyRequests)), < 250 => Outcome.FromResult(CreateResultFromContext(args.Context)), - < 350 => Outcome.FromException(new TimeoutException()), - _ => null + _ => Outcome.FromException(new TimeoutException()) }; return ValueTask.FromResult(outcome); diff --git a/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensionsTests.cs b/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensionsTests.cs index 0bcc4f11207..8e672bf262f 100644 --- a/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensionsTests.cs +++ b/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomePipelineBuilderExtensionsTests.cs @@ -16,7 +16,7 @@ public class ChaosOutcomePipelineBuilderExtensionsTests { InjectionRate = 0.6, Randomizer = () => 0.5, - OutcomeGenerator = (_) => new ValueTask?>(Outcome.FromResult(100)) + OutcomeGenerator = (_) => Outcome.FromResultAsValueTask(100) }); AssertResultStrategy(builder, true, 0.6, new(100)); diff --git a/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomeStrategyTests.cs b/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomeStrategyTests.cs index b5359a41618..57771643047 100644 --- a/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomeStrategyTests.cs +++ b/test/Polly.Core.Tests/Simmy/Outcomes/ChaosOutcomeStrategyTests.cs @@ -61,7 +61,7 @@ public void Given_not_enabled_should_not_inject_result() InjectionRate = 0.6, Enabled = false, Randomizer = () => 0.5, - OutcomeGenerator = (_) => new ValueTask?>(Outcome.FromResult(fakeResult)) + OutcomeGenerator = (_) => Outcome.FromResultAsValueTask(fakeResult) }; var sut = CreateSut(options); @@ -81,7 +81,7 @@ public async Task Given_enabled_and_randomly_within_threshold_should_inject_resu { InjectionRate = 0.6, Randomizer = () => 0.5, - OutcomeGenerator = (_) => new ValueTask?>(Outcome.FromResult(fakeResult)), + OutcomeGenerator = (_) => Outcome.FromResultAsValueTask(fakeResult), OnOutcomeInjected = args => { args.Context.Should().NotBeNull(); @@ -117,7 +117,7 @@ public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_re InjectionRate = 0.3, Enabled = false, Randomizer = () => 0.5, - OutcomeGenerator = (_) => new ValueTask?>(Outcome.FromResult(fakeResult)) + OutcomeGenerator = (_) => Outcome.FromResultAsValueTask(fakeResult) }; var sut = CreateSut(options); @@ -132,29 +132,6 @@ public void Given_enabled_and_randomly_not_within_threshold_should_not_inject_re _onOutcomeInjectedExecuted.Should().BeFalse(); } - [Fact] - public async Task Given_enabled_and_randomly_within_threshold_should_inject_result_even_as_null() - { - Outcome? nullOutcome = Outcome.FromResult(null); - var options = new ChaosOutcomeStrategyOptions - { - InjectionRate = 0.6, - Randomizer = () => 0.5, - OutcomeGenerator = (_) => new ValueTask?>(nullOutcome) - }; - - var sut = CreateSut(options); - var response = await sut.ExecuteAsync(async _ => - { - _userDelegateExecuted = true; - return await Task.FromResult(HttpStatusCode.OK); - }); - - response.Should().Be(null); - _userDelegateExecuted.Should().BeFalse(); - _onOutcomeInjectedExecuted.Should().BeFalse(); - } - [Fact] public async Task Should_not_execute_user_delegate_when_it_was_cancelled_running_the_strategy() { @@ -168,7 +145,7 @@ public async Task Should_not_execute_user_delegate_when_it_was_cancelled_running cts.Cancel(); return new ValueTask(true); }, - OutcomeGenerator = (_) => new ValueTask?>(Outcome.FromResult(HttpStatusCode.TooManyRequests)) + OutcomeGenerator = (_) => Outcome.FromResultAsValueTask(HttpStatusCode.TooManyRequests) }; var sut = CreateSut(options); diff --git a/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs b/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs index 924de797690..a56fc30923a 100644 --- a/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs +++ b/test/Polly.Core.Tests/Simmy/Outcomes/OutcomeGeneratorTests.cs @@ -12,7 +12,7 @@ public void AddException_Generic_Ok() generator.AddException(); - Generate(generator)!.Value.Exception.Should().BeOfType(); + Generate(generator).Exception.Should().BeOfType(); } [Fact] @@ -22,7 +22,7 @@ public void AddException_Factory_Ok() generator.AddException(() => new InvalidOperationException()); - Generate(generator)!.Value.Exception.Should().BeOfType(); + Generate(generator).Exception.Should().BeOfType(); } [Fact] @@ -37,7 +37,7 @@ public void AddException_FactoryWithResilienceContext_Ok() return new InvalidOperationException(); }); - Generate(generator)!.Value.Exception.Should().BeOfType(); + Generate(generator).Exception.Should().BeOfType(); } [Fact] @@ -50,7 +50,7 @@ public void AddResult_Factory_Ok() return "dummy"; }); - Generate(generator)!.Value.Result.Should().Be("dummy"); + Generate(generator).Result.Should().Be("dummy"); } [Fact] @@ -65,12 +65,12 @@ public void AddResult_FactoryWithResilienceContext_Ok() return "dummy"; }); - Generate(generator)!.Value.Result.Should().Be("dummy"); + Generate(generator).Result.Should().Be("dummy"); } - private static Outcome? Generate(OutcomeGenerator generator) + private static Outcome Generate(OutcomeGenerator generator) { - Func?>> func = generator; + Func>> func = generator; return func(new OutcomeGeneratorArguments(ResilienceContextPool.Shared.Get())).AsTask().Result; } diff --git a/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs b/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs index 258124cec77..0622725385d 100644 --- a/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs +++ b/test/Polly.Core.Tests/Simmy/Utils/GeneratorHelperTests.cs @@ -6,11 +6,14 @@ namespace Polly.Core.Tests.Simmy.Utils; public class GeneratorHelperTests { [Fact] - public void CreateGenerator_NoGenerators_Ok() + public void CreateGenerator_NoGenerators_ThrowsInvalidOperationException() { var helper = new GeneratorHelper(_ => 10); - helper.CreateGenerator()(ResilienceContextPool.Shared.Get()).Should().BeNull(); + Action act = () => helper.CreateGenerator(); + act.Should() + .Throw() + .WithMessage("No outcome generators have been added."); } [Fact] @@ -32,18 +35,18 @@ public void AddOutcome_EnsureWeightRespected() var generator = helper.CreateGenerator(); weight = 0; - generator(context)!.Value.Result.Should().Be(1); + generator(context).Result.Should().Be(1); weight = 39; - generator(context)!.Value.Result.Should().Be(1); + generator(context).Result.Should().Be(1); weight = 40; - generator(context)!.Value.Result.Should().Be(2); + generator(context).Result.Should().Be(2); maxWeight.Should().Be(120); } [Fact] - public void Generator_OutsideRange_ReturnsNull() + public void Generator_OutsideRange_ReturnsOutcomeWithDefault() { var context = ResilienceContextPool.Shared.Get(); var helper = new GeneratorHelper(_ => 1000); @@ -51,6 +54,6 @@ public void Generator_OutsideRange_ReturnsNull() helper.AddOutcome(_ => Outcome.FromResult(1), 40); var generator = helper.CreateGenerator(); - generator(context).Should().BeNull(); + generator(context).Should().Be(Outcome.FromResult(default(int))); } }