Skip to content

Commit a7b5090

Browse files
committed
Implement BeforeInject callback
Closes Polly-Contrib#65
1 parent a18b123 commit a7b5090

14 files changed

+219
-10
lines changed

src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourAsyncWithOptionsSpecs.cs

+53
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,59 @@ public void Should_inject_behaviour_before_executing_user_delegate()
109109
injectedBehaviourExecuted.Should().BeTrue();
110110
}
111111

112+
#region BeforeInject
113+
[Fact]
114+
public async Task Should_call_before_inject_callback_before_injecting_behavior()
115+
{
116+
var beforeInjectExecuted = false;
117+
var injectedBehaviourExecuted = false;
118+
119+
var policy = MonkeyPolicy.InjectBehaviourAsync(with =>
120+
with.Behaviour(async () =>
121+
{
122+
beforeInjectExecuted.Should().BeTrue();
123+
injectedBehaviourExecuted = true;
124+
})
125+
.BeforeInject(async (context, cancellation) =>
126+
{
127+
injectedBehaviourExecuted.Should().BeFalse();
128+
beforeInjectExecuted = true;
129+
})
130+
.InjectionRate(0.6)
131+
.Enabled()
132+
);
133+
134+
await policy.ExecuteAsync(() => Task.CompletedTask);
135+
136+
beforeInjectExecuted.Should().BeTrue();
137+
injectedBehaviourExecuted.Should().BeTrue();
138+
}
139+
140+
public async Task Should_not_call_before_inject_callback_if_not_injecting()
141+
{
142+
var beforeInjectExecuted = false;
143+
var behaviorExecuted = false;
144+
145+
var policy = MonkeyPolicy.InjectBehaviourAsync(with =>
146+
with.Behaviour(async () =>
147+
{
148+
behaviorExecuted = true;
149+
})
150+
.BeforeInject(async (context, cancellation) =>
151+
{
152+
beforeInjectExecuted = true;
153+
})
154+
.InjectionRate(0.4)
155+
.Enabled()
156+
);
157+
158+
await policy.ExecuteAsync(() => Task.CompletedTask);
159+
160+
beforeInjectExecuted.Should().BeFalse();
161+
behaviorExecuted.Should().BeFalse();
162+
}
163+
#endregion
164+
112165
#region invalid threshold on configuration and execution time
113166

114167
[Fact]

src/Polly.Contrib.Simmy.Specs/Behavior/InjectBehaviourWithOptionsSpecs.cs

+53
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,59 @@ public void Should_inject_behaviour_before_executing_user_delegate()
9191
injectedBehaviourExecuted.Should().BeTrue();
9292
}
9393

94+
#region BeforeInject
95+
[Fact]
96+
public void Should_call_before_inject_callback_before_injecting_behavior()
97+
{
98+
var beforeInjectExecuted = false;
99+
var injectedBehaviourExecuted = false;
100+
101+
var policy = MonkeyPolicy.InjectBehaviour(with =>
102+
with.Behaviour(() =>
103+
{
104+
beforeInjectExecuted.Should().BeTrue();
105+
injectedBehaviourExecuted = true;
106+
})
107+
.BeforeInject((context, cancellation) =>
108+
{
109+
injectedBehaviourExecuted.Should().BeFalse();
110+
beforeInjectExecuted = true;
111+
})
112+
.InjectionRate(0.6)
113+
.Enabled()
114+
);
115+
116+
policy.Execute(() => { });
117+
118+
beforeInjectExecuted.Should().BeTrue();
119+
injectedBehaviourExecuted.Should().BeTrue();
120+
}
121+
122+
public void Should_not_call_before_inject_callback_if_not_injecting()
123+
{
124+
var beforeInjectExecuted = false;
125+
var behaviorExecuted = false;
126+
127+
var policy = MonkeyPolicy.InjectBehaviour(with =>
128+
with.Behaviour(() =>
129+
{
130+
behaviorExecuted = true;
131+
})
132+
.BeforeInject((context, cancellation) =>
133+
{
134+
beforeInjectExecuted = true;
135+
})
136+
.InjectionRate(0.4)
137+
.Enabled()
138+
);
139+
140+
policy.Execute(() => { });
141+
142+
beforeInjectExecuted.Should().BeFalse();
143+
behaviorExecuted.Should().BeFalse();
144+
}
145+
#endregion
146+
94147
#region invalid threshold on configuration and execution time
95148

96149
[Fact]

src/Polly.Contrib.Simmy/AsyncMonkeyEngine.cs

+15
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ internal static async Task<TResult> InjectBehaviourImplementationAsync<TResult>(
4141
Func<Context, CancellationToken, Task> injectedBehaviour,
4242
Func<Context, CancellationToken, Task<Double>> injectionRate,
4343
Func<Context, CancellationToken, Task<bool>> enabled,
44+
Func<Context, CancellationToken, Task> beforeInjectCallback,
4445
bool continueOnCapturedContext)
4546
{
4647
if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext))
4748
{
49+
if (beforeInjectCallback != null)
50+
{
51+
await beforeInjectCallback(context, cancellationToken);
52+
}
4853
await injectedBehaviour(context, cancellationToken).ConfigureAwait(continueOnCapturedContext);
4954
}
5055

@@ -60,6 +65,7 @@ internal static async Task<TResult> InjectExceptionImplementationAsync<TResult>(
6065
Func<Context, CancellationToken, Task<Exception>> injectedException,
6166
Func<Context, CancellationToken, Task<Double>> injectionRate,
6267
Func<Context, CancellationToken, Task<bool>> enabled,
68+
Func<Context, CancellationToken, Task> beforeInjectCallback,
6369
bool continueOnCapturedContext)
6470
{
6571
if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext))
@@ -71,6 +77,10 @@ internal static async Task<TResult> InjectExceptionImplementationAsync<TResult>(
7177

7278
if (exception != null)
7379
{
80+
if (beforeInjectCallback != null)
81+
{
82+
await beforeInjectCallback(context, cancellationToken);
83+
}
7484
throw exception;
7585
}
7686
}
@@ -85,10 +95,15 @@ internal static async Task<TResult> InjectResultImplementationAsync<TResult>(
8595
Func<Context, CancellationToken, Task<TResult>> injectedResult,
8696
Func<Context, CancellationToken, Task<Double>> injectionRate,
8797
Func<Context, CancellationToken, Task<bool>> enabled,
98+
Func<Context, CancellationToken, Task> beforeInjectCallback,
8899
bool continueOnCapturedContext)
89100
{
90101
if (await ShouldInjectAsync(context, cancellationToken, injectionRate, enabled, continueOnCapturedContext).ConfigureAwait(continueOnCapturedContext))
91102
{
103+
if (beforeInjectCallback != null)
104+
{
105+
await beforeInjectCallback(context, cancellationToken);
106+
}
92107
return await injectedResult(context, cancellationToken).ConfigureAwait(continueOnCapturedContext);
93108
}
94109

src/Polly.Contrib.Simmy/Behavior/AsyncInjectBehaviourPolicy.cs

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace Polly.Contrib.Simmy.Behavior
1010
public class AsyncInjectBehaviourPolicy : AsyncMonkeyPolicy
1111
{
1212
private readonly Func<Context, CancellationToken, Task> _behaviour;
13+
private readonly Func<Context, CancellationToken, Task> _beforeInjectCallback;
1314

1415
[Obsolete]
1516
internal AsyncInjectBehaviourPolicy(Func<Context, CancellationToken, Task> behaviour, Func<Context, CancellationToken, Task<Double>> injectionRate, Func<Context, CancellationToken, Task<bool>> enabled)
@@ -22,6 +23,7 @@ internal AsyncInjectBehaviourPolicy(InjectBehaviourAsyncOptions options)
2223
: base(options.InjectionRate, options.Enabled)
2324
{
2425
_behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal));
26+
_beforeInjectCallback = options.BeforeInjectCallback;
2527
}
2628

2729
/// <inheritdoc/>
@@ -35,6 +37,7 @@ protected override Task<TResult> ImplementationAsync<TResult>(Func<Context, Canc
3537
_behaviour,
3638
InjectionRate,
3739
Enabled,
40+
_beforeInjectCallback,
3841
continueOnCapturedContext);
3942
}
4043
}
@@ -46,6 +49,7 @@ protected override Task<TResult> ImplementationAsync<TResult>(Func<Context, Canc
4649
public class AsyncInjectBehaviourPolicy<TResult> : AsyncMonkeyPolicy<TResult>
4750
{
4851
private readonly Func<Context, CancellationToken, Task> _behaviour;
52+
private readonly Func<Context, CancellationToken, Task> _beforeInjectCallback;
4953

5054
[Obsolete]
5155
internal AsyncInjectBehaviourPolicy(Func<Context, CancellationToken, Task> behaviour, Func<Context, CancellationToken, Task<Double>> injectionRate, Func<Context, CancellationToken, Task<bool>> enabled)
@@ -58,6 +62,7 @@ internal AsyncInjectBehaviourPolicy(InjectBehaviourAsyncOptions options)
5862
: base(options.InjectionRate, options.Enabled)
5963
{
6064
_behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal));
65+
_beforeInjectCallback = options.BeforeInjectCallback;
6166
}
6267

6368
/// <inheritdoc/>
@@ -70,6 +75,7 @@ protected override Task<TResult> ImplementationAsync(Func<Context, CancellationT
7075
_behaviour,
7176
InjectionRate,
7277
Enabled,
78+
_beforeInjectCallback,
7379
continueOnCapturedContext);
7480
}
7581
}

src/Polly.Contrib.Simmy/Behavior/InjectBehaviourPolicy.cs

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ namespace Polly.Contrib.Simmy.Behavior
99
public class InjectBehaviourPolicy : Simmy.MonkeyPolicy
1010
{
1111
private readonly Action<Context, CancellationToken> _behaviour;
12+
private readonly Action<Context, CancellationToken> _beforeInjectCallback;
1213

1314
[Obsolete]
1415
internal InjectBehaviourPolicy(Action<Context, CancellationToken> behaviour, Func<Context, CancellationToken, double> injectionRate, Func<Context, CancellationToken, bool> enabled)
@@ -21,6 +22,7 @@ internal InjectBehaviourPolicy(InjectBehaviourOptions options)
2122
: base(options.InjectionRate, options.Enabled)
2223
{
2324
_behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal));
25+
_beforeInjectCallback = options.BeforeInjectCallback;
2426
}
2527

2628
/// <inheritdoc/>
@@ -32,7 +34,8 @@ protected override TResult Implementation<TResult>(Func<Context, CancellationTok
3234
cancellationToken,
3335
(ctx, ct) => _behaviour(ctx, ct),
3436
InjectionRate,
35-
Enabled);
37+
Enabled,
38+
_beforeInjectCallback);
3639
}
3740
}
3841
/// <summary>
@@ -42,6 +45,7 @@ protected override TResult Implementation<TResult>(Func<Context, CancellationTok
4245
public class InjectBehaviourPolicy<TResult> : MonkeyPolicy<TResult>
4346
{
4447
private readonly Action<Context, CancellationToken> _behaviour;
48+
private readonly Action<Context, CancellationToken> _beforeInjectCallback;
4549

4650
[Obsolete]
4751
internal InjectBehaviourPolicy(Action<Context, CancellationToken> behaviour, Func<Context, CancellationToken, double> injectionRate, Func<Context, CancellationToken, bool> enabled)
@@ -54,6 +58,7 @@ internal InjectBehaviourPolicy(InjectBehaviourOptions options)
5458
: base(options.InjectionRate, options.Enabled)
5559
{
5660
_behaviour = options.BehaviourInternal ?? throw new ArgumentNullException(nameof(options.BehaviourInternal));
61+
_beforeInjectCallback = options.BeforeInjectCallback;
5762
}
5863

5964
/// <inheritdoc/>
@@ -65,7 +70,8 @@ protected override TResult Implementation(Func<Context, CancellationToken, TResu
6570
cancellationToken,
6671
(ctx, ct) => _behaviour(ctx, ct),
6772
InjectionRate,
68-
Enabled);
73+
Enabled,
74+
_beforeInjectCallback);
6975
}
7076
}
7177
}

src/Polly.Contrib.Simmy/InjectOptionsAsyncBase.cs

+5
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,10 @@ public abstract class InjectOptionsAsyncBase
1818
/// Lambda to check if this policy is enabled in current context
1919
/// </summary>
2020
internal Func<Context, CancellationToken, Task<bool>> Enabled { get; set; }
21+
22+
/// <summary>
23+
/// Lambda to call immediately before injecting
24+
/// </summary>
25+
internal virtual Func<Context, CancellationToken, Task> BeforeInjectCallback { get; set; }
2126
}
2227
}

src/Polly.Contrib.Simmy/InjectOptionsAsyncBaseExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,17 @@ public static InjectOptionsAsyncBase InjectionRate(this InjectOptionsAsyncBase o
6060
options.InjectionRate = injectionRateProvider;
6161
return options;
6262
}
63+
64+
/// <summary>
65+
/// Configure a callback to be run immediately before injecting chaos.
66+
/// </summary>
67+
/// <param name="options">The configuration object.</param>
68+
/// <param name="beforeInjectCallback">A delegate to be run immediately before injecting chaos.</param>
69+
/// <returns></returns>
70+
public static InjectOptionsAsyncBase BeforeInject(this InjectOptionsAsyncBase options, Func<Context, CancellationToken, Task> beforeInjectCallback)
71+
{
72+
options.BeforeInjectCallback = beforeInjectCallback;
73+
return options;
74+
}
6375
}
6476
}

src/Polly.Contrib.Simmy/InjectOptionsBase.cs

+5
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,10 @@ public abstract class InjectOptionsBase
1717
/// Lambda to check if this policy is enabled in current context
1818
/// </summary>
1919
internal Func<Context, CancellationToken, bool> Enabled { get; set; }
20+
21+
/// <summary>
22+
/// Lambda to call immediately before injecting
23+
/// </summary>
24+
internal Action<Context, CancellationToken> BeforeInjectCallback { get; set; }
2025
}
2126
}

src/Polly.Contrib.Simmy/InjectOptionsBaseExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,17 @@ public static InjectOptionsBase InjectionRate(this InjectOptionsBase options, Fu
5959
options.InjectionRate = injectionRateProvider;
6060
return options;
6161
}
62+
63+
/// <summary>
64+
/// Configure a callback to be run immediately before injecting chaos.
65+
/// </summary>
66+
/// <param name="options">The configuration object.</param>
67+
/// <param name="beforeInjectCallback">A delegate to be run immediately before injecting chaos.</param>
68+
/// <returns></returns>
69+
public static InjectOptionsBase BeforeInject(this InjectOptionsBase options, Action<Context, CancellationToken> beforeInjectCallback)
70+
{
71+
options.BeforeInjectCallback = beforeInjectCallback;
72+
return options;
73+
}
6274
}
6375
}

src/Polly.Contrib.Simmy/Latency/AsyncInjectLatencyPolicy.cs

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Polly.Contrib.Simmy.Latency
1111
public class AsyncInjectLatencyPolicy : AsyncMonkeyPolicy
1212
{
1313
private readonly Func<Context, CancellationToken, Task<TimeSpan>> _latencyProvider;
14+
private readonly Func<Context, CancellationToken, Task> _beforeInjectCallback;
1415

1516
[Obsolete]
1617
internal AsyncInjectLatencyPolicy(
@@ -26,6 +27,7 @@ internal AsyncInjectLatencyPolicy(InjectLatencyAsyncOptions options)
2627
: base(options.InjectionRate, options.Enabled)
2728
{
2829
_latencyProvider = options.LatencyInternal ?? throw new ArgumentNullException(nameof(options.LatencyInternal));
30+
_beforeInjectCallback = options.BeforeInjectCallback;
2931
}
3032

3133
/// <inheritdoc/>
@@ -52,6 +54,7 @@ await SystemClock.SleepAsync(
5254
},
5355
InjectionRate,
5456
Enabled,
57+
_beforeInjectCallback,
5558
continueOnCapturedContext);
5659
}
5760
}
@@ -62,6 +65,7 @@ await SystemClock.SleepAsync(
6265
public class AsyncInjectLatencyPolicy<TResult> : AsyncMonkeyPolicy<TResult>
6366
{
6467
private readonly Func<Context, CancellationToken, Task<TimeSpan>> _latencyProvider;
68+
private readonly Func<Context, CancellationToken, Task> _beforeInjectCallback;
6569

6670
[Obsolete]
6771
internal AsyncInjectLatencyPolicy(
@@ -77,6 +81,7 @@ internal AsyncInjectLatencyPolicy(InjectLatencyAsyncOptions options)
7781
: base(options.InjectionRate, options.Enabled)
7882
{
7983
_latencyProvider = options.LatencyInternal ?? throw new ArgumentNullException(nameof(options.LatencyInternal));
84+
_beforeInjectCallback = options.BeforeInjectCallback;
8085
}
8186

8287
/// <inheritdoc/>
@@ -103,6 +108,7 @@ await SystemClock.SleepAsync(
103108
},
104109
InjectionRate,
105110
Enabled,
111+
_beforeInjectCallback,
106112
continueOnCapturedContext);
107113
}
108114
}

0 commit comments

Comments
 (0)