-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
18f661d
commit d800a6c
Showing
6 changed files
with
181 additions
and
2 deletions.
There are no files selected for viewing
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
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
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 Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Moq; | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace ResilientCommand.Tests | ||
{ | ||
public class BasicCommand : ResilientCommand<int> | ||
{ | ||
public BasicCommand() : base() | ||
{ | ||
} | ||
|
||
public BasicCommand(CircuitBreaker circuitBreaker, | ||
ExecutionTimeout executionTimeout, | ||
Collapser collapser, | ||
Semaphore semaphore, | ||
ICache cache) : base(circuitBreaker: circuitBreaker, | ||
executionTimeout: executionTimeout, | ||
collapser: collapser, | ||
semaphore: semaphore, | ||
cache: cache, | ||
configuration: CommandConfiguration.CreateConfiguration(c => { c.CollapserSettings.IsEnabled = true; })) | ||
{ | ||
|
||
} | ||
|
||
protected override async Task<int> RunAsync(CancellationToken cancellationToken) | ||
{ | ||
return 2; | ||
} | ||
} | ||
|
||
[TestClass] | ||
public class ResilientCommandTests | ||
{ | ||
[TestMethod] | ||
/// The execution order should be | ||
/// Check Cache => Collapse => CircuitBreaker => BulkHead => ExecutionTimeout => Set Cache | ||
public async Task Command_WithFeaturesEnabled_ExecutesInOrder() | ||
{ | ||
var tracker = new SequenceTracker(); | ||
|
||
var commandKey = new CommandKey(Guid.NewGuid().ToString()); | ||
var mockCache = new Mock<ICache>(MockBehavior.Strict) { CallBase = true }; | ||
|
||
mockCache.Setup(cache => cache.TryGet(It.IsAny<string>(), out It.Ref<It.IsAnyType>.IsAny)) | ||
.Callback(() => tracker.Next(10)); | ||
|
||
mockCache.Setup(cache => cache.TryAdd(It.IsAny<string>(), It.IsAny<It.IsAnyType>())) | ||
.Callback(() => tracker.Next(60)); | ||
|
||
|
||
var mockCollapser = new Mock<Collapser>(commandKey, new TestNotifier(), It.IsAny<CollapserSettings>()){ CallBase = true }; | ||
mockCollapser.Setup(collapser => collapser.ExecuteAsync(It.IsAny<Func<CancellationToken, Task<It.IsAnyType>>>(), It.IsAny<CancellationToken>())) | ||
.Callback(() => tracker.Next(20)); | ||
|
||
var mockCircuitBreaker = new Mock<CircuitBreaker>(commandKey, new TestNotifier(), It.IsAny<CircuitBreakerSettings>()){ CallBase = true }; | ||
mockCircuitBreaker.Setup(circuitBreaker => circuitBreaker.ExecuteAsync(It.IsAny<Func<CancellationToken, Task<It.IsAnyType>>>(), It.IsAny<CancellationToken>())) | ||
.Callback(() => tracker.Next(30)); | ||
|
||
var mockSemaphore = new Mock<Semaphore>(commandKey, new TestNotifier(), It.IsAny<SemaphoreSettings>()){ CallBase = true }; | ||
mockSemaphore.Setup(semaphore => semaphore.ExecuteAsync(It.IsAny<Func<CancellationToken, Task<It.IsAnyType>>>(), It.IsAny<CancellationToken>())) | ||
.Callback(() => tracker.Next(40)); | ||
|
||
var mockExecutionTimeout = new Mock<ExecutionTimeout>(commandKey, new TestNotifier(), It.IsAny<ExecutionTimeoutSettings>()){ CallBase = true }; | ||
mockExecutionTimeout.Setup(timeout => timeout.ExecuteAsync(It.IsAny<Func<CancellationToken, Task<It.IsAnyType>>>(), It.IsAny<CancellationToken>())) | ||
.Callback(() => tracker.Next(50)); | ||
|
||
var command = new BasicCommand(mockCircuitBreaker.Object, mockExecutionTimeout.Object, mockCollapser.Object, mockSemaphore.Object, mockCache.Object); | ||
|
||
await command.ExecuteAsync(default); | ||
} | ||
|
||
[TestMethod] | ||
public async Task Command_WithDefaultSetup_Executes() | ||
{ | ||
var command = new BasicCommand(); | ||
var result = await command.ExecuteAsync(default); | ||
|
||
Assert.AreEqual(2, result); | ||
} | ||
} | ||
} |
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,35 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace ResilientCommand.Tests | ||
{ | ||
[TestClass] | ||
public class SemaphoreTests | ||
{ | ||
[TestMethod] | ||
[ExpectedException(typeof(SemaphoreRejectedException))] | ||
public async Task Semaphore_WithFullPool_RejectsExecution() | ||
{ | ||
var cmdKey = new CommandKey(Guid.NewGuid().ToString()); | ||
var semaphore = new Semaphore(cmdKey, new TestNotifier(), new SemaphoreSettings(maxParalellism: 1)); | ||
|
||
semaphore.ExecuteAsync(async (ct) => { await Task.Delay(1000); return 2; }, default); | ||
await semaphore.ExecuteAsync(async (ct) => 2, default); | ||
} | ||
|
||
[TestMethod] | ||
[ExpectedException(typeof(OperationCanceledException))] | ||
public async Task Semaphore_WithCancelledToken_Cancels() | ||
{ | ||
var cmdKey = new CommandKey(Guid.NewGuid().ToString()); | ||
var semaphore = new Semaphore(cmdKey, new TestNotifier(), SemaphoreSettings.DefaultSemaphoreSettings); | ||
|
||
var cts = new CancellationTokenSource(); | ||
cts.Cancel(); | ||
|
||
await semaphore.ExecuteAsync<int>(async (token) => 2, cts.Token); | ||
} | ||
} | ||
} |
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,17 @@ | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
|
||
namespace ResilientCommand.Tests | ||
{ | ||
public class SequenceTracker | ||
{ | ||
int? state; | ||
|
||
public void Next(int newState) | ||
{ | ||
if (newState <= state) | ||
Assert.Fail("Bad ordering there! States should be increasing."); | ||
|
||
state = newState; | ||
} | ||
} | ||
} |
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