fengling-gateway/.planning/codebase/TEST_PLAN.md
movingsam 52f4b7616e docs: add security audit and test plan
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-02-28 18:38:38 +08:00

7.9 KiB
Raw Blame History

🧪 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<JwtConfig>
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<GatewayDbContext>
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 -

测试依赖

<!-- YarpGateway.Tests.csproj -->
<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
  <PackageReference Include="xunit" Version="2.7.0" />
  <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7" />
  <PackageReference Include="Moq" Version="4.20.70" />
  <PackageReference Include="FluentAssertions" Version="6.12.0" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
  <PackageReference Include="Testcontainers.Redis" Version="3.7.0" />
  <PackageReference Include="Testcontainers.PostgreSql" Version="3.7.0" />
</ItemGroup>

运行测试命令

# 运行所有测试
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% -

测试计划由分析生成,建议按优先级逐步实现。