using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
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;
using YarpGateway.Services;
namespace YarpGateway.Tests.Integration;
///
/// 集成测试基类
/// 提供 WebApplicationFactory 和 HttpClient 的共享实例
///
public class TestFixture : IAsyncLifetime
{
public WebApplicationFactory Factory { get; private set; } = null!;
public HttpClient Client { get; private set; } = null!;
public IServiceProvider Services { get; private set; } = null!;
public async Task InitializeAsync()
{
Factory = new WebApplicationFactory()
.WithWebHostBuilder(builder =>
{
builder.UseEnvironment("Testing");
builder.ConfigureServices((context, services) =>
{
// 移除所有与 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);
}
// 使用内存数据库替换
services.AddDbContextFactory(options =>
{
options.UseInMemoryDatabase($"YarpGateway_Test_{Guid.NewGuid()}");
});
// 移除 PgSqlConfigChangeListener(因为内存数据库不支持 NOTIFY)
var listenerDescriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(Microsoft.Extensions.Hosting.IHostedService) &&
d.ImplementationType == typeof(PgSqlConfigChangeListener));
if (listenerDescriptor != null)
{
services.Remove(listenerDescriptor);
}
// 移除 Redis 相关服务(测试环境不需要)
services.RemoveAll(typeof(StackExchange.Redis.IConnectionMultiplexer));
services.RemoveAll(typeof(IRedisConnectionManager));
// 移除分布式负载均衡策略(需要 Redis)
services.RemoveAll(typeof(Yarp.ReverseProxy.LoadBalancing.ILoadBalancingPolicy));
// 添加 Mock Redis 连接
var mockRedis = new Mock();
var mockDb = new Mock();
mockDb.Setup(db => db.StringSet(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(true);
mockDb.Setup(db => db.StringGet(It.IsAny(), It.IsAny())).Returns(RedisValue.Null);
mockDb.Setup(db => db.ScriptEvaluate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(RedisResult.Create(1));
mockRedis.Setup(r => r.GetDatabase(It.IsAny(), It.IsAny