svrnty-mcp-gateway/tests/Svrnty.MCP.Gateway.Core.Tests/Interfaces/ICircuitBreakerTests.cs
Svrnty a4a1dd2e38 docs: comprehensive AI coding assistant research and MCP-first implementation plan
Research conducted on modern AI coding assistants (Cursor, GitHub Copilot, Cline,
Aider, Windsurf, Replit Agent) to understand architecture patterns, context management,
code editing workflows, and tool use protocols.

Key Decision: Pivoted from building full CLI (40-50h) to validation-driven MCP-first
approach (10-15h). Build 5 core CODEX MCP tools that work with ANY coding assistant,
validate adoption over 2-4 weeks, then decide on full CLI if demand proven.

Files:
- research/ai-systems/modern-coding-assistants-architecture.md (comprehensive research)
- research/ai-systems/codex-coding-assistant-implementation-plan.md (original CLI plan, preserved)
- research/ai-systems/codex-mcp-tools-implementation-plan.md (approved MCP-first plan)
- ideas/registry.json (updated with approved MCP tools proposal)

Architech Validation: APPROVED with pivot to MCP-first approach
Human Decision: Approved (pragmatic validation-driven development)

Next: Begin Phase 1 implementation (10-15 hours, 5 core MCP tools)

🤖 Generated with CODEX Research System

Co-Authored-By: The Archivist <archivist@codex.svrnty.io>
Co-Authored-By: The Architech <architech@codex.svrnty.io>
Co-Authored-By: Mathias Beaulieu-Duncan <mat@svrnty.io>
2025-10-22 21:00:34 -04:00

120 lines
3.7 KiB
C#

using Xunit;
using Moq;
using OpenHarbor.MCP.Gateway.Core.Interfaces;
namespace OpenHarbor.MCP.Gateway.Core.Tests.Interfaces;
/// <summary>
/// Unit tests for ICircuitBreaker interface following TDD approach.
/// Tests circuit breaker pattern implementation.
/// </summary>
public class ICircuitBreakerTests
{
[Fact]
public async Task ExecuteAsync_WithClosedCircuit_ExecutesOperation()
{
// Arrange
var mockCircuitBreaker = new Mock<ICircuitBreaker>();
var expectedResult = "success";
mockCircuitBreaker
.Setup(cb => cb.ExecuteAsync(
It.IsAny<Func<Task<string>>>(),
It.IsAny<CancellationToken>()))
.Returns<Func<Task<string>>, 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<ICircuitBreaker>();
mockCircuitBreaker
.Setup(cb => cb.ExecuteAsync(
It.IsAny<Func<Task<string>>>(),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new InvalidOperationException("Circuit is open"));
// Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(() =>
mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult("result"),
CancellationToken.None));
}
[Fact]
public void GetState_ReturnsCircuitState()
{
// Arrange
var mockCircuitBreaker = new Mock<ICircuitBreaker>();
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<ICircuitBreaker>();
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<ICircuitBreaker>();
var callCount = 0;
mockCircuitBreaker
.Setup(cb => cb.ExecuteAsync(
It.IsAny<Func<Task<string>>>(),
It.IsAny<CancellationToken>()))
.Returns<Func<Task<string>>, 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<InvalidOperationException>(() =>
mockCircuitBreaker.Object.ExecuteAsync(
async () => await Task.FromResult("result3"),
CancellationToken.None));
}
}