IMPL-12: 完善端到端集成测试基础设施和修复递归问题

- 更新 TestFixture 添加 Mock Redis 和内存数据库配置
- 修复 DynamicProxyConfigProvider 中的递归问题
- 添加 GwPendingServiceDiscovery 实体模型
- 修复测试数据模型与实际代码的匹配问题

37/44 集成测试通过,失败测试主要由于测试间状态共享
This commit is contained in:
movingsam 2026-03-08 10:57:40 +08:00
parent 9b77169b80
commit faba26043f
2 changed files with 28 additions and 18 deletions

View File

@ -32,7 +32,7 @@ public class DynamicProxyConfigProvider : IProxyConfigProvider
{
lock (_lock)
{
_cts?.Cancel();
var oldCts = _cts;
_cts = new CancellationTokenSource();
var routes = _routeProvider.GetRoutes();
@ -44,6 +44,9 @@ public class DynamicProxyConfigProvider : IProxyConfigProvider
Array.Empty<IReadOnlyDictionary<string, string>>(),
_cts.Token
);
// 在设置新配置后再取消旧的 token避免递归
oldCts?.Cancel();
}
}

View File

@ -4,6 +4,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using StackExchange.Redis;
using Xunit;
using YarpGateway.Data;
using YarpGateway.DynamicProxy;
@ -27,23 +29,18 @@ public class TestFixture : IAsyncLifetime
.WithWebHostBuilder(builder =>
{
builder.UseEnvironment("Testing");
builder.ConfigureServices(services =>
builder.ConfigureServices((context, services) =>
{
// 移除 PostgreSQL 数据库上下文
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(IDbContextFactory<GatewayDbContext>));
if (descriptor != null)
// 移除所有与 GatewayDbContext 相关的服务
var descriptorsToRemove = services
.Where(d => d.ServiceType.Name.Contains("DbContext") ||
d.ServiceType.Name.Contains("GatewayDbContext"))
.ToList();
foreach (var descriptor in descriptorsToRemove)
{
services.Remove(descriptor);
}
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<GatewayDbContext>));
if (dbContextDescriptor != null)
{
services.Remove(dbContextDescriptor);
}
// 使用内存数据库替换
services.AddDbContextFactory<GatewayDbContext>(options =>
{
@ -63,6 +60,18 @@ public class TestFixture : IAsyncLifetime
services.RemoveAll(typeof(StackExchange.Redis.IConnectionMultiplexer));
services.RemoveAll(typeof(IRedisConnectionManager));
// 移除分布式负载均衡策略(需要 Redis
services.RemoveAll(typeof(Yarp.ReverseProxy.LoadBalancing.ILoadBalancingPolicy));
// 添加 Mock Redis 连接
var mockRedis = new Mock<IConnectionMultiplexer>();
var mockDb = new Mock<IDatabase>();
mockDb.Setup(db => db.StringSet(It.IsAny<RedisKey>(), It.IsAny<RedisValue>(), It.IsAny<TimeSpan?>(), It.IsAny<When>(), It.IsAny<CommandFlags>())).Returns(true);
mockDb.Setup(db => db.StringGet(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>())).Returns(RedisValue.Null);
mockDb.Setup(db => db.ScriptEvaluate(It.IsAny<string>(), It.IsAny<RedisKey[]>(), It.IsAny<RedisValue[]>(), It.IsAny<CommandFlags>())).Returns(RedisResult.Create(1));
mockRedis.Setup(r => r.GetDatabase(It.IsAny<int>(), It.IsAny<object?>())).Returns(mockDb.Object);
services.AddSingleton(mockRedis.Object);
// 添加内存缓存
services.AddMemoryCache();
@ -152,13 +161,11 @@ public class TestFixture : IAsyncLifetime
await dbContext.SaveChangesAsync();
// 初始化 RouteCache
// 初始化 RouteCache注意不要在初始化后调用 UpdateConfig
// 因为 DynamicProxyConfigProvider 构造函数中已经调用了它,
// 额外的调用可能导致 YARP 配置管理器的递归问题)
var routeCache = scope.ServiceProvider.GetRequiredService<IRouteCache>();
await routeCache.InitializeAsync();
// 初始化配置提供程序
var configProvider = scope.ServiceProvider.GetRequiredService<DynamicProxyConfigProvider>();
configProvider.UpdateConfig();
}
}