执行日期:2026-08-08 环境:本地(docker postgres/redis + mock 上游 :19090 + API :8080) 测试账号:
test-e2e@dotouch.ai/Test1234(余额 $100.00 · role=admin) 凭据文件:/tmp/dotouch-test-creds.json(含 API Key 与 JWT) 目标:完整走通「注册 → 登录 → Key → 充值 → 调用 → 计量 → 管理」全链路
| 组件 | 地址 | 状态 |
|---|---|---|
| PostgreSQL | localhost:5432 | ✅ 运行中 |
| Redis | localhost:6379 | ✅ 运行中 |
| Mock 上游(模拟百炼) | localhost:19090 | ✅ 运行中(scripts/mock-upstream.mjs) |
| API | localhost:8080(/v1) | ✅ 运行中(DASHSCOPE_BASE_URL 指向 mock) |
/tmp/dotouch-api.log 的 [EMAIL] 行读取(8 位大写字母数字)| 步骤 | 操作 | 预期 |
|---|---|---|
| 1.1 | POST /v1/auth/send-code {email: new-user@test.ai, purpose: register} | 201 |
| 1.2 | 从 API 日志读取 8 位验证码 | 格式 [A-Z0-9]{8} |
| 1.3 | POST /v1/auth/register {email, password: Test1234, code: 错误码} | 400 验证码错误 |
| 1.4 | POST /v1/auth/register {正确验证码} | 201 注册成功 |
| 1.5 | 同一邮箱重复注册 | 409 已注册 |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 2.1 | 正确密码登录 | 200 + accessToken |
| 2.2 | 错误密码 ×5 | 401 ×5 |
| 2.3 | 第 6 次错误密码 | 429 登录限流(15 分钟) |
| 2.4 | 清 Redis 后正确密码 | 200 |
| 2.5 | GET /v1/auth/me(带 JWT) | 200,含 email/role=admin |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 3.1 | POST /v1/keys {name} | 201,返回完整 dt_ Key(仅一次) |
| 3.2 | GET /v1/keys | 200,Key 脱敏(仅 prefix) |
| 3.3 | POST /v1/keys/:id/rotate | 201,返回新 Key |
| 3.4 | 旧 Key 调 POST /v1/chat/completions(带 Bearer) | 401 无效 Key(⚠️ 须用需认证的网关接口,/v1/models 为公开接口) |
| 3.5 | 新 Key 调 /v1/chat/completions | 200(证明轮换生效) |
| 3.6 | DELETE /v1/keys/:id | 撤销成功 |
| 3.7 | 已撤销 Key 调 POST /v1/chat/completions | 401(⚠️ 同上,用网关接口验证) |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 4.1 | GET /v1/wallet(JWT) | 200,balanceCents=10000,含流水 |
| 4.2 | dev-credit +$10(+1000 cents) | 余额 11000 |
| 4.3 | GET /v1/wallet | 200,余额 11000,流水 +1 条 deposit |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 5.1 | GET /v1/models(无认证) | 200,≥236 个模型 |
| 5.2 | 抽查 glm-5.2 / deepseek-v4 定价元数据 | 含 input/output 价格 |
| 5.3 | 未认证调 /v1/chat/completions | 401 invalid_api_key |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 6.1 | POST /v1/chat/completions {model: glm-5.2, messages, Bearer dt_ Key} | 200 OpenAI 格式响应 |
| 6.2 | 响应含 usage(prompt/completion tokens) | 有 usage 字段 |
| 6.3 | stream: true | SSE 流式响应(data: 块 + [DONE]) |
| 6.4 | 未知模型 {model: no-such-model} | 404 model_not_found |
| 6.5 | 无效 Key | 401 |
| 6.6 | GET /v1/wallet | 余额按 usage 扣减(>0) |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 7.1 | GET /v1/usage(JWT) | 200,含 6.1/6.3 的请求记录 |
| 7.2 | GET /v1/usage/summary | 200,按日聚合,totalTokens>0 |
| 7.3 | 记录中 costCents 与钱包扣减一致 | 一致 |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 8.1 | POST /v1/auth/forgot {email} | 200(不泄露存在性) |
| 8.2 | 日志读重置验证码 | 8 位 |
| 8.3 | POST /v1/auth/reset {email, code, newPassword: NewPass567} | 201 重置成功 |
| 8.4 | 旧密码登录 | 401 |
| 8.5 | 新密码登录 | 200 |
| 8.6 | 重置回 Test1234(复用 8.1-8.3 流程) | 201,恢复原密码 |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 9.1 | GET /v1/admin/stats(admin JWT) | 200,users/totalRevenueCents/totalTokens |
| 9.2 | GET /v1/admin/users?q=test-e2e | 200,含该用户 |
| 9.3 | PUT /v1/admin/models/glm-5.2 {inputPriceCents: 99, outputPriceCents: 309} | 200 |
| 9.4 | GET /v1/models → glm-5.2 价格更新 | 99/309 生效 |
| 9.5 | 普通用户 JWT 调 /v1/admin/stats | 403 拒绝 |
| 9.6 | POST /v1/admin/models/sync | 501 占位 |
| 步骤 | 操作 | 预期 |
|---|---|---|
| 10.1 | 连续 61 次请求(超 60 req/min) | 第 61 次 429 |
| 10.2 | 错误响应体为 OpenAI 格式 {error:{message,type,code}} | 格式正确 |