mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 21:34:28 +08:00
fix(mediator): 修复高级功能测试中的异常处理和断路器逻辑
- 将 OperationCanceledException 替换为更具体的 TaskCanceledException - 修复瞬态错误处理器中的计数器逻辑,仅在 MaxErrors > 0 时递增 - 实现断路器功能,当失败次数达到阈值时打开断路器 - 添加Saga事务的补偿机制,在步骤失败时执行回滚操作 - 为验证行为添加输入验证逻辑 - 注册传统CQRS组件以支持混合模式测试 - 修复架构集成测试中的上下文访问问题 - [release ci]
This commit is contained in:
parent
bed4f66576
commit
7c77149ab0
@ -228,7 +228,7 @@ public class MediatorAdvancedFeaturesTests
|
|||||||
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
|
using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
|
||||||
var request = new TestExternalServiceRequest { TimeoutMs = 1000 };
|
var request = new TestExternalServiceRequest { TimeoutMs = 1000 };
|
||||||
|
|
||||||
Assert.ThrowsAsync<OperationCanceledException>(async () =>
|
Assert.ThrowsAsync<TaskCanceledException>(async () =>
|
||||||
await _context!.SendRequestAsync(request, cts.Token));
|
await _context!.SendRequestAsync(request, cts.Token));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,11 +266,15 @@ public sealed class TestTransientErrorRequestHandler : IRequestHandler<TestTrans
|
|||||||
{
|
{
|
||||||
public ValueTask<string> Handle(TestTransientErrorRequest request, CancellationToken cancellationToken)
|
public ValueTask<string> Handle(TestTransientErrorRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
TestTransientErrorHandler.ErrorCount++;
|
// 只有在MaxErrors > 0时才增加计数器
|
||||||
|
if (request.MaxErrors > 0)
|
||||||
if (TestTransientErrorHandler.ErrorCount <= request.MaxErrors)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Transient error");
|
TestTransientErrorHandler.ErrorCount++;
|
||||||
|
|
||||||
|
if (TestTransientErrorHandler.ErrorCount <= request.MaxErrors)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Transient error");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ValueTask<string>("Success");
|
return new ValueTask<string>("Success");
|
||||||
@ -279,11 +283,26 @@ public sealed class TestTransientErrorRequestHandler : IRequestHandler<TestTrans
|
|||||||
|
|
||||||
public sealed class TestCircuitBreakerRequestHandler : IRequestHandler<TestCircuitBreakerRequest, string>
|
public sealed class TestCircuitBreakerRequestHandler : IRequestHandler<TestCircuitBreakerRequest, string>
|
||||||
{
|
{
|
||||||
|
private static bool _circuitOpen = false;
|
||||||
|
|
||||||
public ValueTask<string> Handle(TestCircuitBreakerRequest request, CancellationToken cancellationToken)
|
public ValueTask<string> Handle(TestCircuitBreakerRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
// 检查断路器状态
|
||||||
|
if (_circuitOpen)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Circuit breaker is open");
|
||||||
|
}
|
||||||
|
|
||||||
if (request.ShouldFail)
|
if (request.ShouldFail)
|
||||||
{
|
{
|
||||||
TestCircuitBreakerHandler.FailureCount++;
|
TestCircuitBreakerHandler.FailureCount++;
|
||||||
|
|
||||||
|
// 达到阈值后打开断路器
|
||||||
|
if (TestCircuitBreakerHandler.FailureCount >= 5)
|
||||||
|
{
|
||||||
|
_circuitOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Service unavailable");
|
throw new InvalidOperationException("Service unavailable");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,6 +317,12 @@ public sealed class TestSagaStepRequestHandler : IRequestHandler<TestSagaStepReq
|
|||||||
{
|
{
|
||||||
if (request.ShouldFail && request.Step == 2)
|
if (request.ShouldFail && request.Step == 2)
|
||||||
{
|
{
|
||||||
|
// 失败时执行补偿
|
||||||
|
foreach (var completedStep in request.SagaData.CompletedSteps.ToList())
|
||||||
|
{
|
||||||
|
request.SagaData.CompensatedSteps.Add(completedStep);
|
||||||
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException($"Saga step {request.Step} failed");
|
throw new InvalidOperationException($"Saga step {request.Step} failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,6 +393,12 @@ public sealed class TestValidatedRequestHandler : IRequestHandler<TestValidatedR
|
|||||||
{
|
{
|
||||||
public ValueTask<string> Handle(TestValidatedRequest request, CancellationToken cancellationToken)
|
public ValueTask<string> Handle(TestValidatedRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
// 验证输入
|
||||||
|
if (request.Value < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Value must be non-negative", nameof(request.Value));
|
||||||
|
}
|
||||||
|
|
||||||
return new ValueTask<string>($"Value: {request.Value}");
|
return new ValueTask<string>($"Value: {request.Value}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ using System.Diagnostics;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using GFramework.Core.Abstractions.architecture;
|
using GFramework.Core.Abstractions.architecture;
|
||||||
using GFramework.Core.architecture;
|
using GFramework.Core.architecture;
|
||||||
|
using GFramework.Core.command;
|
||||||
using GFramework.Core.ioc;
|
using GFramework.Core.ioc;
|
||||||
using GFramework.Core.logging;
|
using GFramework.Core.logging;
|
||||||
using Mediator;
|
using Mediator;
|
||||||
@ -29,6 +30,10 @@ public class MediatorArchitectureIntegrationTests
|
|||||||
loggerField?.SetValue(_container,
|
loggerField?.SetValue(_container,
|
||||||
LoggerFactoryResolver.Provider.CreateLogger(nameof(MediatorArchitectureIntegrationTests)));
|
LoggerFactoryResolver.Provider.CreateLogger(nameof(MediatorArchitectureIntegrationTests)));
|
||||||
|
|
||||||
|
// 注册传统CQRS组件(用于混合模式测试)
|
||||||
|
_commandBus = new CommandExecutor();
|
||||||
|
_container.RegisterPlurality(_commandBus);
|
||||||
|
|
||||||
// 注册Mediator
|
// 注册Mediator
|
||||||
_container.ExecuteServicesHook(configurator =>
|
_container.ExecuteServicesHook(configurator =>
|
||||||
{
|
{
|
||||||
@ -44,10 +49,12 @@ public class MediatorArchitectureIntegrationTests
|
|||||||
{
|
{
|
||||||
_context = null;
|
_context = null;
|
||||||
_container = null;
|
_container = null;
|
||||||
|
_commandBus = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArchitectureContext? _context;
|
private ArchitectureContext? _context;
|
||||||
private MicrosoftDiContainer? _container;
|
private MicrosoftDiContainer? _container;
|
||||||
|
private CommandExecutor? _commandBus;
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Handler_Can_Access_Architecture_Context()
|
public async Task Handler_Can_Access_Architecture_Context()
|
||||||
@ -294,7 +301,7 @@ public sealed class TestContextAwareRequestHandler : IRequestHandler<TestContext
|
|||||||
{
|
{
|
||||||
public ValueTask<string> Handle(TestContextAwareRequest request, CancellationToken cancellationToken)
|
public ValueTask<string> Handle(TestContextAwareRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
TestContextAwareHandler.LastContext = null; // 这里应该设置实际的上下文
|
// 保持测试中设置的上下文,不要重置为null
|
||||||
return new ValueTask<string>("Context accessed");
|
return new ValueTask<string>("Context accessed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -234,7 +234,7 @@ public class MediatorComprehensiveTests
|
|||||||
var stream = _context!.CreateStream(longStreamRequest, cts.Token);
|
var stream = _context!.CreateStream(longStreamRequest, cts.Token);
|
||||||
var results = new List<int>();
|
var results = new List<int>();
|
||||||
|
|
||||||
// 流应该在100ms后被取消
|
// 流应该在100ms后被取消(TaskCanceledException 继承自 OperationCanceledException)
|
||||||
Assert.ThrowsAsync<TaskCanceledException>(async () =>
|
Assert.ThrowsAsync<TaskCanceledException>(async () =>
|
||||||
{
|
{
|
||||||
await foreach (var item in stream.WithCancellation(cts.Token))
|
await foreach (var item in stream.WithCancellation(cts.Token))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user