Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
VisualBean committed Jun 21, 2021
1 parent 18f661d commit d800a6c
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 2 deletions.
17 changes: 15 additions & 2 deletions ResilientCommand.Tests/CircuitBreakerTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading;
Expand Down Expand Up @@ -138,7 +138,6 @@ public async Task CircuitBreakerCommand_InDifferentGroupWithFailures_DoesNotThro
}

[TestMethod]

public async Task CircuitBreakerCommand_InSameGroupWithFailures_ThrowsBrokenCircuit()
{
var fallbackValue = "fallback";
Expand All @@ -155,6 +154,20 @@ public async Task CircuitBreakerCommand_InSameGroupWithFailures_ThrowsBrokenCirc
var response = await command2.ExecuteAsync(default); // Should go directly to fallback.

Assert.AreEqual(fallbackValue, response);
}

[TestMethod]
[ExpectedException(typeof(OperationCanceledException))]
public async Task CircuitBreaker_WithCancelledToken_Cancels()
{
var cmdKey = new CommandKey(Guid.NewGuid().ToString());
var settings = new CircuitBreakerSettings(isEnabled: false, failureThreshhold: 0.1, samplingDurationMilliseconds: int.MaxValue, minimumThroughput: 2);
var circuit = new CircuitBreaker(cmdKey, new TestNotifier(), settings);

var cts = new CancellationTokenSource();
cts.Cancel();

await circuit.ExecuteAsync<int>(async (token) => 2, cts.Token);
}
}
}
15 changes: 15 additions & 0 deletions ResilientCommand.Tests/CollapserTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ResilientCommand.Tests
Expand Down Expand Up @@ -55,5 +56,19 @@ public async Task Collapser_WithRunTimeDisable_DoesNotCollapseRequest()
result = await collapser.ExecuteAsync((ct) => { i++; return Task.FromResult(i); }, default);
result.Should().Be(2);
}

[TestMethod]
[ExpectedException(typeof(OperationCanceledException))]
public async Task Collapser_WithCancelledToken_Cancels()
{
var cmdKey = new CommandKey(Guid.NewGuid().ToString());
var settings = new CollapserSettings(isEnabled: true, window: TimeSpan.FromSeconds(1));
var collapser = new Collapser(cmdKey, new TestNotifier(), settings);

var cts = new CancellationTokenSource();
cts.Cancel();

await collapser.ExecuteAsync<int>(async (token) => 2, cts.Token);
}
}
}
85 changes: 85 additions & 0 deletions ResilientCommand.Tests/ResilientCommandTests.cs
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);
}
}
}
35 changes: 35 additions & 0 deletions ResilientCommand.Tests/SemaphoreTests.cs
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);
}
}
}
17 changes: 17 additions & 0 deletions ResilientCommand.Tests/SequenceTracker.cs
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;
}
}
}
14 changes: 14 additions & 0 deletions ResilientCommand.Tests/TimeoutTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,19 @@ public async Task Timeout_WithTimeoutDisabled_NoTimeoutIsExecuted()
var timeout = new ExecutionTimeout(cmdKey, new TestNotifier(), new ExecutionTimeoutSettings(isEnabled: false, executionTimeoutInMilliseconds: 1));
await timeout.ExecuteAsync(async (ct) => { await Task.Delay(10); return 1;}, default);
}

[TestMethod]
[ExpectedException(typeof(OperationCanceledException))]
public async Task Timeout_WithCancelledToken_Cancels()
{
var cmdKey = new CommandKey(Guid.NewGuid().ToString());
var timeout = new ExecutionTimeout(cmdKey, new TestNotifier(), new ExecutionTimeoutSettings(executionTimeoutInMilliseconds: 1));


var cts = new CancellationTokenSource();
cts.Cancel();

await timeout.ExecuteAsync(async (ct) => { await Task.Delay(10); return 1; }, cts.Token);
}
}
}

0 comments on commit d800a6c

Please sign in to comment.