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

View File

@ -4,6 +4,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using StackExchange.Redis;
using Xunit; using Xunit;
using YarpGateway.Data; using YarpGateway.Data;
using YarpGateway.DynamicProxy; using YarpGateway.DynamicProxy;
@ -27,23 +29,18 @@ public class TestFixture : IAsyncLifetime
.WithWebHostBuilder(builder => .WithWebHostBuilder(builder =>
{ {
builder.UseEnvironment("Testing"); builder.UseEnvironment("Testing");
builder.ConfigureServices(services => builder.ConfigureServices((context, services) =>
{ {
// 移除 PostgreSQL 数据库上下文 // 移除所有与 GatewayDbContext 相关的服务
var descriptor = services.SingleOrDefault( var descriptorsToRemove = services
d => d.ServiceType == typeof(IDbContextFactory<GatewayDbContext>)); .Where(d => d.ServiceType.Name.Contains("DbContext") ||
if (descriptor != null) d.ServiceType.Name.Contains("GatewayDbContext"))
.ToList();
foreach (var descriptor in descriptorsToRemove)
{ {
services.Remove(descriptor); services.Remove(descriptor);
} }
var dbContextDescriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<GatewayDbContext>));
if (dbContextDescriptor != null)
{
services.Remove(dbContextDescriptor);
}
// 使用内存数据库替换 // 使用内存数据库替换
services.AddDbContextFactory<GatewayDbContext>(options => services.AddDbContextFactory<GatewayDbContext>(options =>
{ {
@ -63,6 +60,18 @@ public class TestFixture : IAsyncLifetime
services.RemoveAll(typeof(StackExchange.Redis.IConnectionMultiplexer)); services.RemoveAll(typeof(StackExchange.Redis.IConnectionMultiplexer));
services.RemoveAll(typeof(IRedisConnectionManager)); 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(); services.AddMemoryCache();
@ -152,13 +161,11 @@ public class TestFixture : IAsyncLifetime
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
// 初始化 RouteCache // 初始化 RouteCache注意不要在初始化后调用 UpdateConfig
// 因为 DynamicProxyConfigProvider 构造函数中已经调用了它,
// 额外的调用可能导致 YARP 配置管理器的递归问题)
var routeCache = scope.ServiceProvider.GetRequiredService<IRouteCache>(); var routeCache = scope.ServiceProvider.GetRequiredService<IRouteCache>();
await routeCache.InitializeAsync(); await routeCache.InitializeAsync();
// 初始化配置提供程序
var configProvider = scope.ServiceProvider.GetRequiredService<DynamicProxyConfigProvider>();
configProvider.UpdateConfig();
} }
} }