# 🧪 YARP 网关测试覆盖计划 > 分析日期:2026-02-28 > 当前状态:**无任何测试代码** --- ## 测试项目结构 ``` tests/ └── YarpGateway.Tests/ ├── YarpGateway.Tests.csproj ├── Unit/ │ ├── Middleware/ │ │ ├── JwtTransformMiddlewareTests.cs │ │ └── TenantRoutingMiddlewareTests.cs │ ├── Services/ │ │ ├── RouteCacheTests.cs │ │ ├── RedisConnectionManagerTests.cs │ │ └── PgSqlConfigChangeListenerTests.cs │ ├── LoadBalancing/ │ │ └── DistributedWeightedRoundRobinPolicyTests.cs │ └── Config/ │ ├── DatabaseRouteConfigProviderTests.cs │ └── DatabaseClusterConfigProviderTests.cs ├── Integration/ │ ├── Controllers/ │ │ ├── GatewayConfigControllerTests.cs │ │ └── PendingServicesControllerTests.cs │ └── Middleware/ │ └── MiddlewarePipelineTests.cs └── TestHelpers/ ├── MockDbContext.cs ├── MockRedis.cs └── TestFixtures.cs ``` --- ## P0 - 必须覆盖(核心安全) ### JwtTransformMiddlewareTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldValidateJwtSignature` | 应验证 JWT 签名 | 有效签名的 JWT | 解析成功,Claims 正确 | `IOptions` | | `ShouldRejectInvalidToken` | 应拒绝无效 Token | 伪造/过期 JWT | 返回 401 或跳过处理 | - | | `ShouldExtractTenantClaim` | 应正确提取租户 ID | 含 tenant claim 的 JWT | X-Tenant-Id header 设置正确 | - | | `ShouldHandleMissingToken` | 应处理无 Token 请求 | 无 Authorization header | 继续处理(不设置 headers) | - | | `ShouldHandleMalformedToken` | 应处理格式错误 Token | 无效 JWT 格式 | 记录警告,继续处理 | - | ### TenantRoutingMiddlewareTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldValidateTenantIdAgainstJwt` | 应验证 header 与 JWT 一致 | X-Tenant-Id ≠ JWT tenant | 返回 403 Forbidden | `IRouteCache` | | `ShouldExtractServiceNameFromPath` | 应正确解析服务名 | `/api/user-service/users` | serviceName = "user-service" | - | | `ShouldFindRouteInCache` | 应从缓存找到路由 | 有效租户+服务名 | 设置正确的 clusterId | `IRouteCache` | | `ShouldHandleRouteNotFound` | 应处理路由未找到 | 不存在的服务名 | 记录警告,继续处理 | - | | `ShouldPrioritizeTenantRouteOverGlobal` | 租户路由优先于全局 | 同时存在两种路由 | 使用租户路由 | - | --- ## P1 - 重要覆盖(核心业务) ### RouteCacheTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldLoadGlobalRoutes` | 应加载全局路由 | 全局路由数据 | `_globalRoutes` 填充 | `IDbContextFactory` | | `ShouldLoadTenantRoutes` | 应加载租户路由 | 租户路由数据 | `_tenantRoutes` 填充 | - | | `ShouldReturnCorrectRoute` | 应返回正确路由 | 查询请求 | 正确的 `RouteInfo` | - | | `ShouldReturnNullForMissingRoute` | 不存在路由返回 null | 不存在的服务名 | `null` | - | | `ShouldHandleConcurrentReads` | 并发读取应安全 | 多线程读取 | 无异常,数据一致 | - | | `ShouldReloadCorrectly` | 应正确重载 | Reload 调用 | 旧数据清除,新数据加载 | - | ### RedisConnectionManagerTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldAcquireLock` | 应获取分布式锁 | 有效 key | 锁获取成功 | `IConnectionMultiplexer` | | `ShouldReleaseLockCorrectly` | 应正确释放锁 | 已获取的锁 | 锁释放成功 | - | | `ShouldNotReleaseOthersLock` | 不应释放他人锁 | 其他实例的锁 | 释放失败(安全) | - | | `ShouldHandleConnectionFailure` | 应处理连接失败 | Redis 不可用 | 记录错误,返回失败 | - | | `ShouldExecuteInLock` | 应在锁内执行操作 | 操作委托 | 操作执行,锁正确释放 | - | ### DistributedWeightedRoundRobinPolicyTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldSelectByWeight` | 应按权重选择 | 权重 [3, 1, 1] | 约 60% 选第一个 | `IConnectionMultiplexer` | | `ShouldFallbackOnLockFailure` | 锁失败应降级 | Redis 不可用 | 降级选择第一个可用 | - | | `ShouldReturnNullWhenNoDestinations` | 无目标返回 null | 空目标列表 | `null` | - | | `ShouldPersistStateToRedis` | 状态应持久化到 Redis | 多次选择 | 状态存储正确 | - | | `ShouldExpireStateAfterTTL` | 状态应在 TTL 后过期 | 1 小时后 | 状态重新初始化 | - | ### PgSqlConfigChangeListenerTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldListenForNotifications` | 应监听通知 | NOTIFY 事件 | 触发重载 | `NpgsqlConnection` | | `ShouldFallbackToPolling` | 应回退到轮询 | 通知失败 | 定时轮询检测 | - | | `ShouldReconnectOnFailure` | 失败应重连 | 连接断开 | 自动重连 | - | | `ShouldDetectVersionChange` | 应检测版本变化 | 版本号增加 | 触发重载 | - | --- ## P2 - 推荐覆盖(业务逻辑) ### GatewayConfigControllerTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldCreateTenant` | 应创建租户 | 有效 DTO | 201 Created | `IDbContextFactory` | | `ShouldRejectDuplicateTenant` | 应拒绝重复租户 | 已存在的 TenantCode | 400 BadRequest | - | | `ShouldCreateRoute` | 应创建路由 | 有效 DTO | 201 Created | - | | `ShouldDeleteTenant` | 应删除租户 | 有效 ID | 204 NoContent | - | | `ShouldReturn404ForMissingTenant` | 不存在租户返回 404 | 无效 ID | 404 NotFound | - | | `ShouldReloadConfig` | 应重载配置 | POST /config/reload | 200 OK | `IRouteCache` | ### PendingServicesControllerTests | 测试用例 | 描述 | 输入 | 预期输出 | Mock 需求 | |---------|------|------|----------|-----------| | `ShouldListPendingServices` | 应列出待处理服务 | GET 请求 | 待处理服务列表 | `IDbContextFactory` | | `ShouldAssignService` | 应分配服务 | 有效请求 | 服务实例创建 | - | | `ShouldRejectInvalidCluster` | 应拒绝无效集群 | 不存在的 ClusterId | 400 BadRequest | - | | `ShouldRejectService` | 应拒绝服务 | reject 请求 | 状态更新为 Rejected | - | --- ## 测试依赖 ```xml ``` --- ## 运行测试命令 ```bash # 运行所有测试 dotnet test # 运行特定测试类 dotnet test --filter "FullyQualifiedName~JwtTransformMiddlewareTests" # 生成覆盖率报告 dotnet test --collect:"XPlat Code Coverage" reportgenerator -reports:**/coverage.cobertura.xml -targetdir:coverage ``` --- ## 覆盖率目标 | 组件 | 目标覆盖率 | 优先级 | |------|-----------|--------| | JwtTransformMiddleware | 90% | P0 | | TenantRoutingMiddleware | 85% | P0 | | RouteCache | 80% | P1 | | DistributedWeightedRoundRobinPolicy | 80% | P1 | | Controllers | 70% | P2 | | 整体项目 | 75% | - | --- *测试计划由分析生成,建议按优先级逐步实现。*