using System.Security.Claims;
using System.Text;
using System.Text.Json;
using Fengling.Platform.Domain.AggregatesModel.GatewayAggregate;
using Fengling.Platform.Domain.AggregatesModel.TenantAggregate;
using Microsoft.IdentityModel.Tokens;
using YarpGateway.Data;
using YarpGateway.Models;
namespace YarpGateway.Tests.Integration;
///
/// 集成测试数据准备
///
public static class TestData
{
#region 租户数据
public static async Task SeedTenantsAsync(GatewayDbContext dbContext)
{
var tenants = new List
{
new()
{
TenantCode = "tenant1",
Name = "Tenant One",
Status = TenantStatus.Active,
CreatedAt = DateTime.UtcNow
},
new()
{
TenantCode = "tenant2",
Name = "Tenant Two",
Status = TenantStatus.Active,
CreatedAt = DateTime.UtcNow
},
new()
{
TenantCode = "default",
Name = "Default Tenant",
Status = TenantStatus.Active,
CreatedAt = DateTime.UtcNow
}
};
foreach (var tenant in tenants)
{
if (!dbContext.Tenants.Any(t => t.TenantCode == tenant.TenantCode))
{
dbContext.Tenants.Add(tenant);
}
}
await dbContext.SaveChangesAsync();
}
#endregion
#region 路由数据
public static async Task SeedRoutesAsync(GatewayDbContext dbContext)
{
var routes = new List
{
// 全局路由 - member 服务
new()
{
Id = Guid.CreateVersion7().ToString("N"),
TenantCode = "",
ServiceName = "member",
ClusterId = "member-cluster",
Match = new GwRouteMatch { Path = "/api/member/**" },
Priority = 1,
Status = 1,
IsGlobal = true,
IsDeleted = false,
CreatedTime = DateTime.UtcNow
},
// 全局路由 - order 服务
new()
{
Id = Guid.CreateVersion7().ToString("N"),
TenantCode = "",
ServiceName = "order",
ClusterId = "order-cluster",
Match = new GwRouteMatch { Path = "/api/order/**" },
Priority = 1,
Status = 1,
IsGlobal = true,
IsDeleted = false,
CreatedTime = DateTime.UtcNow
},
// 租户专属路由 - tenant1 的 member 服务
new()
{
Id = Guid.CreateVersion7().ToString("N"),
TenantCode = "tenant1",
ServiceName = "member",
ClusterId = "tenant1-member-cluster",
Match = new GwRouteMatch { Path = "/api/member/**" },
Priority = 0, // 更高优先级
Status = 1,
IsGlobal = false,
IsDeleted = false,
CreatedTime = DateTime.UtcNow
}
};
foreach (var route in routes)
{
if (!dbContext.GwTenantRoutes.Any(r => r.Id == route.Id))
{
dbContext.GwTenantRoutes.Add(route);
}
}
await dbContext.SaveChangesAsync();
}
#endregion
#region 集群和目标数据
public static async Task SeedClustersAsync(GatewayDbContext dbContext)
{
var clusters = new List
{
// member-cluster - 默认集群
new()
{
Id = Guid.CreateVersion7().ToString("N"),
ClusterId = "member-cluster",
Name = "Member Service Cluster",
LoadBalancingPolicy = GwLoadBalancingPolicy.RoundRobin,
Status = 1,
CreatedTime = DateTime.UtcNow,
Destinations = new List
{
new()
{
DestinationId = "default",
Address = "http://default-member:8080",
Weight = 1,
Status = 1,
TenantCode = null // 默认目标
}
}
},
// tenant1-member-cluster - tenant1 专属集群
new()
{
Id = Guid.CreateVersion7().ToString("N"),
ClusterId = "tenant1-member-cluster",
Name = "Tenant1 Member Service Cluster",
LoadBalancingPolicy = GwLoadBalancingPolicy.RoundRobin,
Status = 1,
CreatedTime = DateTime.UtcNow,
Destinations = new List
{
new()
{
DestinationId = "tenant1-dest",
Address = "http://tenant1-member:8080",
Weight = 1,
Status = 1,
TenantCode = "tenant1" // 租户专属目标
}
}
},
// order-cluster - 默认集群
new()
{
Id = Guid.CreateVersion7().ToString("N"),
ClusterId = "order-cluster",
Name = "Order Service Cluster",
LoadBalancingPolicy = GwLoadBalancingPolicy.RoundRobin,
Status = 1,
CreatedTime = DateTime.UtcNow,
Destinations = new List
{
new()
{
DestinationId = "default",
Address = "http://default-order:8080",
Weight = 1,
Status = 1,
TenantCode = null // 默认目标
}
}
}
};
foreach (var cluster in clusters)
{
if (!dbContext.GwClusters.Any(c => c.ClusterId == cluster.ClusterId))
{
dbContext.GwClusters.Add(cluster);
}
}
await dbContext.SaveChangesAsync();
}
#endregion
#region K8s Service 模拟数据
///
/// 创建模拟的 K8s Service 发现数据
///
public static GwPendingServiceDiscovery CreateK8sService(
string serviceName,
string @namespace,
Dictionary labels,
string clusterIp = "10.96.123.45",
int podCount = 1)
{
return new GwPendingServiceDiscovery
{
K8sServiceName = serviceName,
K8sNamespace = @namespace,
K8sClusterIP = clusterIp,
Labels = JsonSerializer.Serialize(labels),
DiscoveredPorts = "[8080]",
PodCount = podCount,
Status = (int)PendingConfigStatus.Pending,
DiscoveredAt = DateTime.UtcNow,
Version = 1,
IsDeleted = false
};
}
///
/// 创建带路由标签的 K8s Service
///
public static GwPendingServiceDiscovery CreateRoutedK8sService(
string serviceName,
string prefix,
string clusterName,
string destination = "default",
string @namespace = "default")
{
return CreateK8sService(
serviceName,
@namespace,
new Dictionary
{
["app-router-name"] = serviceName,
["app-router-prefix"] = prefix,
["app-cluster-name"] = clusterName,
["app-cluster-destination"] = destination
});
}
#endregion
#region JWT Token 生成
private static readonly SymmetricSecurityKey TestSigningKey = new(
Encoding.UTF8.GetBytes("test-signing-key-that-is-long-enough-for-hs256-algorithm"));
///
/// 生成测试用 JWT Token
///
public static string GenerateJwtToken(
string userId,
string tenantCode,
string[]? roles = null,
DateTime? expires = null)
{
var claims = new List
{
new(ClaimTypes.NameIdentifier, userId),
new("sub", userId),
new("tenant", tenantCode),
new("tenant_id", tenantCode),
new(ClaimTypes.Name, $"test-user-{userId}"),
new("name", $"Test User {userId}")
};
if (roles != null)
{
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
claims.Add(new Claim("role", role));
}
}
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = expires ?? DateTime.UtcNow.AddHours(1),
Issuer = "test-issuer",
Audience = "test-audience",
SigningCredentials = new SigningCredentials(TestSigningKey, SecurityAlgorithms.HmacSha256)
};
var tokenHandler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
///
/// 获取 tenant1 的测试 Token
///
public static string GetTenant1Token(string userId = "user1")
{
return GenerateJwtToken(userId, "tenant1", new[] { "user" });
}
///
/// 获取 tenant2 的测试 Token
///
public static string GetTenant2Token(string userId = "user2")
{
return GenerateJwtToken(userId, "tenant2", new[] { "user" });
}
///
/// 获取默认租户的测试 Token
///
public static string GetDefaultToken(string userId = "default-user")
{
return GenerateJwtToken(userId, "default", new[] { "user" });
}
#endregion
}
#region 支持模型
///
/// 待确认配置状态
///
public enum PendingConfigStatus
{
Pending = 0,
Confirmed = 1,
Rejected = 2
}
///
/// PendingServiceDiscovery 扩展方法
///
public static class PendingServiceDiscoveryExtensions
{
///
/// 获取解析后的标签
///
public static Dictionary GetParsedLabels(this GwPendingServiceDiscovery discovery)
{
return JsonSerializer.Deserialize>(discovery.Labels) ?? new();
}
///
/// 获取路由名称
///
public static string GetRouteName(this GwPendingServiceDiscovery discovery)
{
var labels = GetParsedLabels(discovery);
return labels.GetValueOrDefault("app-router-name", discovery.K8sServiceName);
}
///
/// 获取路由前缀
///
public static string GetRoutePrefix(this GwPendingServiceDiscovery discovery)
{
var labels = GetParsedLabels(discovery);
return labels.GetValueOrDefault("app-router-prefix", $"/api/{discovery.K8sServiceName}");
}
///
/// 获取集群名称
///
public static string GetClusterName(this GwPendingServiceDiscovery discovery)
{
var labels = GetParsedLabels(discovery);
return labels.GetValueOrDefault("app-cluster-name", discovery.K8sServiceName);
}
///
/// 获取目标 ID
///
public static string GetDestinationId(this GwPendingServiceDiscovery discovery)
{
var labels = GetParsedLabels(discovery);
return labels.GetValueOrDefault("app-cluster-destination", "default");
}
///
/// 构建服务地址
///
public static string BuildAddress(this GwPendingServiceDiscovery discovery)
{
var host = discovery.K8sClusterIP ?? $"{discovery.K8sServiceName}.{discovery.K8sNamespace}";
var ports = JsonSerializer.Deserialize(discovery.DiscoveredPorts) ?? new[] { 8080 };
var port = ports.FirstOrDefault(8080);
return $"http://{host}:{port}";
}
}
#endregion