-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce public API for Hedging Resilience Strategy
- Loading branch information
Showing
27 changed files
with
970 additions
and
4 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
src/Polly.Core.Tests/Hedging/HandleHedgingArgumentsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using Polly.Hedging; | ||
|
||
namespace Polly.Core.Tests.Hedging; | ||
|
||
public class HandleHedgingArgumentsTests | ||
{ | ||
[Fact] | ||
public void Ctor_Ok() | ||
{ | ||
var args = new HandleHedgingArguments(ResilienceContext.Get()); | ||
|
||
args.Context.Should().NotBeNull(); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/Polly.Core.Tests/Hedging/HedgingDelayArgumentsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using Polly.Hedging; | ||
|
||
namespace Polly.Core.Tests.Hedging; | ||
|
||
public class HedgingDelayArgumentsTests | ||
{ | ||
[Fact] | ||
public void Ctor_Ok() | ||
{ | ||
var args = new HedgingDelayArguments(ResilienceContext.Get(), 5); | ||
|
||
args.Context.Should().NotBeNull(); | ||
args.Attempt.Should().Be(5); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
using System.ComponentModel.DataAnnotations; | ||
using Polly.Hedging; | ||
using Polly.Strategy; | ||
|
||
namespace Polly.Core.Tests.Hedging; | ||
|
||
public class HedgingHandlerTests | ||
{ | ||
[Fact] | ||
public void SetHedging_ConfigureAsInvalid_Throws() | ||
{ | ||
var handler = new HedgingHandler(); | ||
|
||
handler | ||
.Invoking(h => h.SetHedging<int>(handler => | ||
{ | ||
handler.HedgingActionGenerator = null!; | ||
handler.ShouldHandle = null!; | ||
})) | ||
.Should() | ||
.Throw<ValidationException>() | ||
.WithMessage(""" | ||
The hedging handler configuration is invalid. | ||
Validation Errors: | ||
The ShouldHandle field is required. | ||
The HedgingActionGenerator field is required. | ||
"""); | ||
} | ||
|
||
[Fact] | ||
public void SetVoidHedging_ConfigureAsInvalid_Throws() | ||
{ | ||
var handler = new HedgingHandler(); | ||
|
||
handler | ||
.Invoking(h => h.SetVoidHedging(handler => | ||
{ | ||
handler.HedgingActionGenerator = null!; | ||
handler.ShouldHandle = null!; | ||
})) | ||
.Should() | ||
.Throw<ValidationException>() | ||
.WithMessage(""" | ||
The hedging handler configuration is invalid. | ||
Validation Errors: | ||
The ShouldHandle field is required. | ||
The HedgingActionGenerator field is required. | ||
"""); | ||
} | ||
|
||
[Fact] | ||
public void SetHedging_Empty_Discarded() | ||
{ | ||
var handler = new HedgingHandler() | ||
.SetHedging<int>(handler => | ||
{ | ||
handler.HedgingActionGenerator = args => () => Task.FromResult(10); | ||
}) | ||
.SetVoidHedging(handler => | ||
{ | ||
handler.HedgingActionGenerator = args => () => Task.CompletedTask; | ||
}); | ||
|
||
handler.IsEmpty.Should().BeTrue(); | ||
handler.CreateHandler().Should().BeNull(); | ||
} | ||
|
||
[Fact] | ||
public async Task SetHedging_Ok() | ||
{ | ||
var handler = new HedgingHandler() | ||
.SetHedging<int>(handler => | ||
{ | ||
handler.HedgingActionGenerator = args => () => Task.FromResult(0); | ||
handler.ShouldHandle.HandleResult(-1); | ||
}) | ||
.CreateHandler(); | ||
|
||
var args = new HandleHedgingArguments(ResilienceContext.Get()); | ||
handler.Should().NotBeNull(); | ||
var result = await handler!.ShouldHandleAsync(new Outcome<int>(-1), args); | ||
result.Should().BeTrue(); | ||
|
||
handler.HandlesHedging<int>().Should().BeTrue(); | ||
|
||
var action = handler.TryCreateHedgedAction<int>(ResilienceContext.Get()); | ||
action.Should().NotBeNull(); | ||
(await (action!()!)).Should().Be(0); | ||
|
||
handler.TryCreateHedgedAction<double>(ResilienceContext.Get()).Should().BeNull(); | ||
} | ||
|
||
[InlineData(true)] | ||
[InlineData(false)] | ||
[Theory] | ||
public async Task SetVoidHedging_Ok(bool returnsNullAction) | ||
{ | ||
var handler = new HedgingHandler() | ||
.SetVoidHedging(handler => | ||
{ | ||
handler.HedgingActionGenerator = args => | ||
{ | ||
args.Context.Should().NotBeNull(); | ||
if (returnsNullAction) | ||
{ | ||
return null; | ||
} | ||
|
||
return () => Task.CompletedTask; | ||
}; | ||
handler.ShouldHandle.HandleException<InvalidOperationException>(); | ||
}) | ||
.CreateHandler(); | ||
|
||
var args = new HandleHedgingArguments(ResilienceContext.Get()); | ||
handler.Should().NotBeNull(); | ||
var result = await handler!.ShouldHandleAsync(new Outcome<VoidResult>(new InvalidOperationException()), args); | ||
result.Should().BeTrue(); | ||
|
||
handler.HandlesHedging<VoidResult>().Should().BeTrue(); | ||
|
||
var action = handler.TryCreateHedgedAction<VoidResult>(ResilienceContext.Get()); | ||
if (returnsNullAction) | ||
{ | ||
action.Should().BeNull(); | ||
} | ||
else | ||
{ | ||
action.Should().NotBeNull(); | ||
(await (action!()!)).Should().Be(VoidResult.Instance); | ||
} | ||
} | ||
|
||
[Fact] | ||
public async Task ShouldHandleAsync_UnknownResultType_Null() | ||
{ | ||
var handler = new HedgingHandler() | ||
.SetHedging<int>(handler => | ||
{ | ||
handler.HedgingActionGenerator = args => () => Task.FromResult(0); | ||
handler.ShouldHandle.HandleException<InvalidOperationException>(); | ||
}) | ||
.SetHedging<string>(handler => | ||
{ | ||
handler.HedgingActionGenerator = args => | ||
{ | ||
args.Context.Should().NotBeNull(); | ||
return () => Task.FromResult("dummy"); | ||
}; | ||
}) | ||
.CreateHandler(); | ||
|
||
var args = new HandleHedgingArguments(ResilienceContext.Get()); | ||
(await handler!.ShouldHandleAsync(new Outcome<double>(new InvalidOperationException()), args)).Should().BeFalse(); | ||
handler.HandlesHedging<double>().Should().BeFalse(); | ||
handler.TryCreateHedgedAction<double>(ResilienceContext.Get()).Should().BeNull(); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyBuilderExtensionsTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using System; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Threading.Tasks; | ||
using Polly.Hedging; | ||
|
||
namespace Polly.Core.Tests.Hedging; | ||
|
||
public class HedgingResilienceStrategyBuilderExtensionsTests | ||
{ | ||
private readonly ResilienceStrategyBuilder _builder = new(); | ||
|
||
public static readonly TheoryData<Action<ResilienceStrategyBuilder>> HedgingCases = new() | ||
{ | ||
builder => | ||
{ | ||
builder.AddHedging(new HedgingStrategyOptions()); | ||
}, | ||
builder => | ||
{ | ||
builder.AddHedging(new HedgingStrategyOptions<double> | ||
{ | ||
HedgingActionGenerator = args => () => Task.FromResult<double>(0) | ||
}); | ||
}, | ||
}; | ||
|
||
[MemberData(nameof(HedgingCases))] | ||
[Theory] | ||
public void AddHedging_Ok(Action<ResilienceStrategyBuilder> configure) | ||
{ | ||
configure(_builder); | ||
_builder.Build().Should().BeOfType<HedgingResilienceStrategy>(); | ||
} | ||
|
||
[Fact] | ||
public void AddHedging_InvalidOptions_Throws() | ||
{ | ||
_builder | ||
.Invoking(b => b.AddHedging(new HedgingStrategyOptions { Handler = null! })) | ||
.Should() | ||
.Throw<ValidationException>() | ||
.WithMessage("The hedging strategy options are invalid.*"); | ||
} | ||
|
||
[Fact] | ||
public void AddHedgingT_InvalidOptions_Throws() | ||
{ | ||
_builder | ||
.Invoking(b => b.AddHedging(new HedgingStrategyOptions<double> { ShouldHandle = null! })) | ||
.Should() | ||
.Throw<ValidationException>() | ||
.WithMessage("The hedging strategy options are invalid.*"); | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
src/Polly.Core.Tests/Hedging/HedgingResilienceStrategyTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using Polly.Hedging; | ||
|
||
namespace Polly.Core.Tests.Hedging; | ||
|
||
public class HedgingResilienceStrategyTests | ||
{ | ||
private readonly HedgingStrategyOptions _options = new(); | ||
|
||
[Fact] | ||
public void Ctor_EnsureDefaults() | ||
{ | ||
var strategy = Create(); | ||
|
||
strategy.MaxHedgedAttempts.Should().Be(_options.MaxHedgedAttempts); | ||
strategy.HedgingDelay.Should().Be(_options.HedgingDelay); | ||
strategy.HedgingDelayGenerator.Should().BeNull(); | ||
strategy.HedgingHandler.Should().BeNull(); | ||
} | ||
|
||
[Fact] | ||
public void Execute_Skipped_Ok() | ||
{ | ||
var strategy = Create(); | ||
|
||
strategy.Execute(_ => 10).Should().Be(10); | ||
} | ||
|
||
[InlineData(-1)] | ||
[InlineData(-1000)] | ||
[InlineData(0)] | ||
[InlineData(1)] | ||
[InlineData(1000)] | ||
[Theory] | ||
public async Task GetHedgingDelayAsync_GeneratorSet_EnsureCorrectGeneratedValue(int seconds) | ||
{ | ||
_options.HedgingDelayGenerator.SetGenerator(args => TimeSpan.FromSeconds(seconds)); | ||
|
||
var strategy = Create(); | ||
|
||
var result = await strategy.GetHedgingDelayAsync(ResilienceContext.Get(), 0); | ||
|
||
result.Should().Be(TimeSpan.FromSeconds(seconds)); | ||
} | ||
|
||
[Fact] | ||
public async Task GetHedgingDelayAsync_NoGeneratorSet_EnsureCorrectValue() | ||
{ | ||
_options.HedgingDelay = TimeSpan.FromMilliseconds(123); | ||
|
||
var strategy = Create(); | ||
|
||
var result = await strategy.GetHedgingDelayAsync(ResilienceContext.Get(), 0); | ||
|
||
result.Should().Be(TimeSpan.FromMilliseconds(123)); | ||
} | ||
|
||
private HedgingResilienceStrategy Create() => new(_options); | ||
} |
85 changes: 85 additions & 0 deletions
85
src/Polly.Core.Tests/Hedging/HedgingStrategyOptionsTResultTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using System.ComponentModel.DataAnnotations; | ||
using Polly.Hedging; | ||
using Polly.Strategy; | ||
using Polly.Utils; | ||
|
||
namespace Polly.Core.Tests.Hedging; | ||
|
||
public class HedgingStrategyOptionsTResultTests | ||
{ | ||
[Fact] | ||
public void Ctor_EnsureDefaults() | ||
{ | ||
var options = new HedgingStrategyOptions<int>(); | ||
|
||
options.StrategyType.Should().Be("Hedging"); | ||
options.ShouldHandle.Should().NotBeNull(); | ||
options.ShouldHandle.IsEmpty.Should().BeTrue(); | ||
options.HedgingActionGenerator.Should().BeNull(); | ||
options.HedgingDelay.Should().Be(TimeSpan.FromSeconds(2)); | ||
options.MaxHedgedAttempts.Should().Be(2); | ||
} | ||
|
||
[Fact] | ||
public void Validation() | ||
{ | ||
var options = new HedgingStrategyOptions<int> | ||
{ | ||
HedgingDelayGenerator = null!, | ||
ShouldHandle = null!, | ||
MaxHedgedAttempts = -1, | ||
}; | ||
|
||
options | ||
.Invoking(o => ValidationHelper.ValidateObject(o, "Invalid.")) | ||
.Should() | ||
.Throw<ValidationException>() | ||
.WithMessage(""" | ||
Invalid. | ||
Validation Errors: | ||
The field MaxHedgedAttempts must be between 2 and 10. | ||
The ShouldHandle field is required. | ||
The HedgingActionGenerator field is required. | ||
The HedgingDelayGenerator field is required. | ||
"""); | ||
} | ||
|
||
[Fact] | ||
public async Task AsNonGenericOptions_Ok() | ||
{ | ||
var options = new HedgingStrategyOptions<int> | ||
{ | ||
HedgingDelayGenerator = new NoOutcomeGenerator<HedgingDelayArguments, TimeSpan>().SetGenerator(args => TimeSpan.FromSeconds(123)), | ||
ShouldHandle = new OutcomePredicate<HandleHedgingArguments, int>().HandleResult(-1), | ||
StrategyName = "Dummy", | ||
StrategyType = "Dummy-Hedging", | ||
HedgingDelay = TimeSpan.FromSeconds(3), | ||
MaxHedgedAttempts = 4, | ||
HedgingActionGenerator = args => () => Task.FromResult(555) | ||
}; | ||
|
||
var nonGeneric = options.AsNonGenericOptions(); | ||
|
||
nonGeneric.Should().NotBeNull(); | ||
nonGeneric.StrategyType.Should().Be("Dummy-Hedging"); | ||
nonGeneric.StrategyName.Should().Be("Dummy"); | ||
nonGeneric.Handler.IsEmpty.Should().BeFalse(); | ||
nonGeneric.MaxHedgedAttempts.Should().Be(4); | ||
nonGeneric.HedgingDelay.Should().Be(TimeSpan.FromSeconds(3)); | ||
|
||
var handler = nonGeneric.Handler.CreateHandler(); | ||
handler.Should().NotBeNull(); | ||
|
||
(await handler!.TryCreateHedgedAction<int>(ResilienceContext.Get())!()).Should().Be(555); | ||
|
||
var result = await handler!.ShouldHandleAsync(new Outcome<int>(-1), new HandleHedgingArguments(ResilienceContext.Get())); | ||
result.Should().BeTrue(); | ||
|
||
result = await handler!.ShouldHandleAsync(new Outcome<int>(0), new HandleHedgingArguments(ResilienceContext.Get())); | ||
result.Should().BeFalse(); | ||
|
||
var delay = await nonGeneric.HedgingDelayGenerator.CreateHandler(TimeSpan.Zero, _ => true)!(new HedgingDelayArguments(ResilienceContext.Get(), 4)); | ||
delay.Should().Be(TimeSpan.FromSeconds(123)); | ||
} | ||
} |
Oops, something went wrong.