using Xunit;
using Moq;
using OpenHarbor.MCP.Gateway.Core.Interfaces;
namespace OpenHarbor.MCP.Gateway.Core.Tests.Interfaces;
///
/// Unit tests for ICircuitBreaker interface following TDD approach.
/// Tests circuit breaker pattern implementation.
///
public class ICircuitBreakerTests
{
[Fact]
public async Task ExecuteAsync_WithClosedCircuit_ExecutesOperation()
{
// Arrange
var mockCircuitBreaker = new Mock();
var expectedResult = "success";
mockCircuitBreaker
.Setup(cb => cb.ExecuteAsync(
It.IsAny>>(),
It.IsAny()))
.Returns>, CancellationToken>((operation, ct) => operation());
// Act
var result = await mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult(expectedResult),
CancellationToken.None);
// Assert
Assert.Equal(expectedResult, result);
}
[Fact]
public async Task ExecuteAsync_WithOpenCircuit_ThrowsException()
{
// Arrange
var mockCircuitBreaker = new Mock();
mockCircuitBreaker
.Setup(cb => cb.ExecuteAsync(
It.IsAny>>(),
It.IsAny()))
.ThrowsAsync(new InvalidOperationException("Circuit is open"));
// Act & Assert
await Assert.ThrowsAsync(() =>
mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult("result"),
CancellationToken.None));
}
[Fact]
public void GetState_ReturnsCircuitState()
{
// Arrange
var mockCircuitBreaker = new Mock();
mockCircuitBreaker.Setup(cb => cb.GetState()).Returns("Closed");
// Act
var state = mockCircuitBreaker.Object.GetState();
// Assert
Assert.Equal("Closed", state);
mockCircuitBreaker.Verify(cb => cb.GetState(), Times.Once);
}
[Fact]
public void Reset_ResetsCircuitBreaker()
{
// Arrange
var mockCircuitBreaker = new Mock();
mockCircuitBreaker.Setup(cb => cb.Reset()).Verifiable();
// Act
mockCircuitBreaker.Object.Reset();
// Assert
mockCircuitBreaker.Verify(cb => cb.Reset(), Times.Once);
}
[Fact]
public async Task ExecuteAsync_WithMultipleFailures_OpensCircuit()
{
// Arrange
var mockCircuitBreaker = new Mock();
var callCount = 0;
mockCircuitBreaker
.Setup(cb => cb.ExecuteAsync(
It.IsAny>>(),
It.IsAny()))
.Returns>, CancellationToken>((operation, ct) =>
{
callCount++;
if (callCount >= 3)
{
throw new InvalidOperationException("Circuit is open");
}
return operation();
});
// Act & Assert
// First two calls should succeed
await mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult("result1"),
CancellationToken.None);
await mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult("result2"),
CancellationToken.None);
// Third call should fail with open circuit
await Assert.ThrowsAsync(() =>
mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult("result3"),
CancellationToken.None));
}
}