Files
ephron-ren-prd/qa/test-plan.md

950 lines
57 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ephron.ren 功能测试计划
**版本**: v4.0
**日期**: 2026-05-03
**站点**: https://www.ephron.ren/
**仓库**: https://gitea.ephron.ren/ephron_ren/ephron.ren
---
## 一、测试概览
### 1.1 服务架构
| 服务 | 地址 | 本地端口 | 说明 |
|------|------|----------|------|
| Home | https://www.ephron.ren | 8000 | 个人主页 + 内容管理后台 |
| Auth | https://auth.ephron.ren | 8001 | 登录注册 + 用户管理 + RBAC + 审计 |
| Blog | https://blog.ephron.ren | 8002 | 博客文章 + 评论 + 点赞 + 搜索 |
| Canvas | https://canvas.ephron.ren | 8003 | AI 生成页面作品管理 |
| Prompt | https://prompt.ephron.ren | 8004 | 提示词 CRUD + 版本管理 |
### 1.2 测试账号
| 角色 | 用户名 | 密码 | 用途 |
|------|--------|------|------|
| 管理员 | Elaina_admin | Elaina2026! | 测试全部管理后台功能 |
| 普通用户 | Elaina_user | Elaina2026! | 测试前台功能 + 权限拦截 |
| 邀请码 | 7CQ0-GE9S-L6QS | - | 注册流程测试 |
### 1.3 认证机制
- 跨服务 Cookie `ephron_auth`,域 `.ephron.ren`
- RBAC 权限模型:`user`(10) < `admin`(20) < `owner`(30)
- 细粒度权限键:如 `blog.post.create_draft``auth.user.view_active`
### 1.4 优先级定义
| 级别 | 含义 | 说明 |
|------|------|------|
| P0 | 核心功能 | 阻塞用户使用,必须通过 |
| P1 | 重要功能 | 影响体验,应尽快修复 |
| P2 | 次要功能 | 可延后处理 |
### 1.5 测试统计
| 版本 | 测试用例数 |
|------|:----------:|
| v3.0 | 366 |
| v4.0 全量 | **453** |
---
## 二、测试用例
---
### 模块 1Home 主页 (www.ephron.ren)
#### 1.1 公开页面
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| H-001 | 首页加载 | 访问 / | HTTP 200正常渲染 | 无 | P0 |
| H-002 | CSS/JS/图片 | 检查所有静态资源 | 全部 200 | 无 | P0 |
| H-003 | 响应式布局 | 调整窗口宽度至 375px/768px/1440px | 布局自适应,无溢出 | 无 | P1 |
| H-004 | 导航→博客 | 点击「博客」 | 跳转 blog.ephron.ren | 无 | P0 |
| H-005 | 导航→画布 | 点击「画布」 | 跳转 canvas.ephron.ren | 无 | P0 |
| H-006 | 导航→提示词 | 点击「提示词」 | 跳转 prompt.ephron.ren | 无 | P0 |
| H-007 | 登录链接 | 未登录时点击「未登录」 | 跳转 auth.ephron.ren/login | 无 | P0 |
| H-008 | 登出链接 | 已登录时点击用户名 | 显示登出选项 | 任意 | P1 |
| H-009 | 个人信息 | 检查 hero 区域 | 显示姓名、技能标签 | 无 | P1 |
| H-010 | 联系按钮 | 点击「联系我」 | 弹出邮箱/复制功能 | 无 | P2 |
| H-011 | 备案链接 | 点击 ICP/公安备案 | 跳转官方网站 | 无 | P2 |
| H-012 | 健康检查 | 访问 /health | 返回 `{"status":"ok"}` | 无 | P2 |
#### 1.2 管理后台 (/admin)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| H-013 | Admin 首页 | 以 Elaina_admin 访问 /admin | 显示内容编辑器,含草稿/发布/丢弃功能 | 管理员 | P0 |
| H-014 | Admin 权限拦截 | 以 Elaina_user 访问 /admin | 重定向到登录页或提示权限不足 | 普通 | P0 |
| H-015 | Admin 未登录 | 未登录访问 /admin | 重定向到 auth.ephron.ren/login?redirect=... | 无 | P0 |
| H-016 | 保存草稿 | 编辑内容后 POST /admin/save | 保存成功,不影响已发布版本 | 管理员 | P0 |
| H-016a | 结构化 JSON 内容 | 编辑 experience/projects/skills 各 section | 各 section 独立保存,含 is_draft 标记 | 管理员 | P1 |
| H-016b | Service Token 拒绝 | 带 Authorization: Bearer 访问 /admin | 返回 403审计日志记录 | 服务 | P0 |
| H-017 | 发布内容 | POST /admin/publish | 发布成功,草稿被清除 | 管理员 | P0 |
| H-018 | 丢弃草稿 | POST /admin/discard | 草稿被丢弃,已发布版本不变 | 管理员 | P1 |
| H-019 | CSRF 保护 | 不带 csrf_token 提交表单 | 返回「CSRF token 验证失败」 | 管理员 | P0 |
| H-020 | 速率限制 | 1 分钟内保存 21+ 次 | 第 21 次返回 429 | 管理员 | P1 |
| H-021 | Service Token 拦截 | 带 Authorization: Bearer *** 访问 /admin | 返回 403 | 服务 | P1 |
| H-022 | 登出 | POST /admin/logout | Cookie 清除,跳转首页 | 管理员 | P0 |
---
### 模块 2Auth 认证服务 (auth.ephron.ren)
#### 2.1 登录页面
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-001 | 登录页加载 | 访问 /login | 200显示用户名/密码表单 | 无 | P0 |
| A-002 | 空表单提交 | 不填写直接登录 | 浏览器原生验证拦截 | 无 | P0 |
| A-003 | 错误凭证 | 输入 wrong/wrong | 显示错误消息,不跳转 | 无 | P0 |
| A-004 | 管理员登录 | Elaina_admin / Elaina2026! | 登录成功,设置 Cookie | 管理员 | P0 |
| A-005 | 普通用户登录 | Elaina_user / Elaina2026! | 登录成功,设置 Cookie | 普通 | P0 |
| A-006 | 登录后跳转 | 带 redirect 参数登录 | 跳转到指定 URL | 任意 | P0 |
| A-007 | 登录成功页 | 不带 redirect 登录 | 跳转到 /login-success | 任意 | P1 |
| A-008 | 登录限流 | 1 分钟内 6 次错误 | 第 6 次 429 | 无 | P1 |
| A-009 | 注册链接 | 点击「立即注册」 | 跳转 /register | 无 | P0 |
| A-010 | 提示消息 | 带 message 参数访问 | 显示提示文字 | 无 | P1 |
#### 2.2 注册页面
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-011 | 注册页加载 | 访问 /register | 200显示注册表单 | 无 | P0 |
| A-012 | 表单字段 | 检查所有字段 | 用户名/密码/确认密码/邀请码/邮箱(可选) | 无 | P0 |
| A-013 | 空表单提交 | 不填写直接注册 | 验证拦截 | 无 | P0 |
| A-014 | 密码不一致 | 输入不同密码 | 显示「两次输入的密码不一致」 | 无 | P0 |
| A-015 | 弱密码 | 输入 12345678 | 显示密码强度不足 | 无 | P0 |
| A-015a | 弱密码黑名单 | 输入常见弱密码 (password/abc123/qwerty 等) | 拒绝,提示密码过于常见 | 无 | P0 |
| A-015b | 密码复杂度 | 输入仅数字 `12345678` / 仅小写 `abcdefgh` | 拒绝,需满足 3/4 类别(大小写+数字+特殊字符) | 无 | P0 |
| A-015c | 密码长度下限 | 输入 7 位强混合密码 | 拒绝,最少 8 字符 | 无 | P0 |
| A-015d | 密码长度上限 | 输入 200 位密码 | 拒绝,最多 128 字符 | 无 | P1 |
| A-016 | 无效邀请码 | 输入错误邀请码 | 显示「邀请码无效」 | 无 | P0 |
| A-017 | 正常注册 | 使用 7CQ0-GE9S-L6QS | 注册成功,自动登录 | 无 | P0 |
| A-017a | 注册自动登录 | 注册成功后检查 Cookie | 自动设置 ephron_auth Cookie无需再次登录 | 无 | P0 |
| A-017b | 邀请码过期 | 使用已过期的邀请码注册 | 显示「邀请码已过期」 | 无 | P0 |
| A-017c | 邀请码用尽 | 使用已达最大使用次数的邀请码 | 显示「邀请码已失效」 | 无 | P0 |
| A-018 | 用户名重复 | 使用已存在的用户名 | 显示「用户名已被使用」 | 无 | P0 |
| A-018a | 用户名黑名单 | 注册保留名 (admin/root/system/ephron 等) | 拒绝,提示用户名不可用 | 无 | P0 |
| A-018b | 用户名格式 | 输入 `1abc`(数字开头)/ `ab`(过短)/ 含特殊字符 | 验证拦截3-20 字符,字母开头,仅字母数字下划线 | 无 | P0 |
| A-018c | 邮箱格式验证 | 输入 `invalid` / `a@` / `@b.c` | 邮箱格式错误提示 | 无 | P1 |
| A-018d | 邮箱唯一性 | 使用已注册的邮箱 | 显示「邮箱已被使用」 | 无 | P1 |
| A-019 | 用户名可用性检查 | GET /api/check-username?username=xxx | 返回 `{available: true/false}` | 无 | P1 |
| A-019a | 用户名检查限流 | 1 分钟内 21+ 次请求 | 第 21 次 429 | 无 | P1 |
| A-020 | 邀请码验证 | POST /api/verify-invite | 返回 `{valid: true/false}` | 无 | P1 |
| A-020a | 邀请码验证限流 | 1 分钟内 11+ 次请求 | 第 11 次 429 | 无 | P1 |
| A-020b | 注册限流 | 1 小时内 6 次注册 | 第 6 次 429 | 无 | P1 |
#### 2.3 登出与跨服务认证
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-021 | Auth 登出 | POST /api/logout | Cookie 清除,跳转 /login | 任意 | P0 |
| A-022 | Blog 登出 | 访问 blog.ephron.ren/logout | Cookie 清除 | 任意 | P0 |
| A-023 | Canvas 登出 | 访问 canvas.ephron.ren/logout | Cookie 清除 | 任意 | P0 |
| A-024 | Prompt 登出 | 访问 prompt.ephron.ren/logout | Cookie 清除 | 任意 | P0 |
| A-025 | Home 登出 | 访问 www.ephron.ren/logout | Cookie 清除 | 任意 | P0 |
| A-026 | 跨服务 Cookie | Auth 登录后访问 Blog | Blog 显示已登录态 | 任意 | P0 |
| A-027 | 跨服务 Cookie | Auth 登录后访问 Canvas | Canvas 显示已登录态 | 任意 | P0 |
| A-028 | 跨服务 Cookie | Auth 登录后访问 Prompt | Prompt 显示已登录态 | 任意 | P0 |
| A-029 | API 验证(已登录) | GET /api/auth/verify | 200 `{authenticated: true}` | 任意 | P0 |
| A-029a | API 验证 min_role | GET /api/auth/verify?min_role=admin | 普通用户返回 403管理员返回 200 | 任意 | P0 |
| A-029b | 服务管理员认证 | GET /api/authz/service-admin | 管理员返回 200普通用户返回 403 | 任意 | P1 |
| A-030 | API 验证(未登录) | GET /api/auth/verify | 401 | 无 | P0 |
#### 2.4 管理后台 — 总览 (/admin)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-031 | Admin 首页 | 以管理员访问 /admin | 显示统计面板(活跃用户数、邀请码数、近 7 天注册等) | 管理员 | P0 |
| A-031a | 统计数据准确性 | 对比 Admin 面板数据与数据库 | 活跃用户数/邀请码数/近 7 天注册数准确 | 管理员 | P1 |
| A-032 | Admin 权限拦截 | 以普通用户访问 /admin | 重定向或权限不足提示 | 普通 | P0 |
| A-033 | Admin 未登录 | 未登录访问 /admin | 重定向到 /login?redirect=/admin | 无 | P0 |
#### 2.5 管理后台 — 邀请码管理 (/admin/invites)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-034 | 邀请码列表 | 访问 /admin/invites | 显示所有邀请码,含状态、使用次数、过期时间 | 管理员 | P0 |
| A-035 | 生成邀请码 | 填写最大使用次数/过期天数/备注,提交 | 生成新邀请码,显示在列表 | 管理员 | P0 |
| A-036 | 禁用邀请码 | 点击禁用按钮 | 状态变为禁用 | 管理员 | P0 |
| A-037 | 启用邀请码 | 点击启用按钮 | 状态恢复启用 | 管理员 | P0 |
| A-038 | 删除邀请码 | 点击删除按钮 | 邀请码及使用记录被删除 | 管理员 | P1 |
| A-038a | 删除邀请码级联 | 删除有使用记录的邀请码 | 使用记录同步删除,无孤立数据 | 管理员 | P1 |
| A-039 | CSRF 保护 | 不带 csrf_token 提交 | 返回验证失败 | 管理员 | P0 |
| A-040 | 限流 | 1 分钟内生成 11+ 个 | 第 11 个 429 | 管理员 | P1 |
#### 2.6 管理后台 — 用户管理 (/admin/users)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-041 | 活跃用户列表 | 访问 /admin/users | 显示所有活跃用户 | 管理员 | P0 |
| A-042 | 已禁用用户列表 | 访问 /admin/users/disabled | 显示已禁用用户 | 管理员 | P0 |
| A-043 | 禁用用户 | 选择用户,点击禁用 | 用户状态变为禁用 | 管理员 | P0 |
| A-044 | 禁用自己 | 尝试禁用自己的账号 | 返回「不能禁用自己」 | 管理员 | P0 |
| A-045 | 启用用户 | 选择已禁用用户,点击启用 | 用户恢复活跃 | 管理员 | P0 |
| A-046 | 删除用户 | 选择已禁用用户,点击删除 | 用户被永久删除 | 管理员 | P1 |
| A-046a | 删除用户级联 | 删除有角色/登录日志/邀请码记录的用户 | 角色关联、登录日志、邀请码使用记录同步删除 | 管理员 | P1 |
| A-046b | 删除活跃用户 | 尝试删除未禁用的用户 | 拒绝,需先禁用再删除 | 管理员 | P1 |
| A-047 | 用户详情 | 点击用户名 /admin/users/{username} | 显示用户详情和角色编辑 | 管理员 | P0 |
| A-047a | 用户详情角色编辑 | 在详情页修改用户角色 | 角色更新成功,页面刷新后反映新角色 | 管理员 | P0 |
#### 2.7 管理后台 — 角色权限管理 (/admin/roles)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-048 | 角色列表 | 访问 /admin/roles | 显示所有角色及权限 | 管理员 | P0 |
| A-049 | 创建角色 | 填写 key/name/description/权限,提交 | 创建成功 | 管理员 | P0 |
| A-050 | 删除角色 | 删除自定义角色 | 删除成功(内置角色不可删) | 管理员 | P1 |
| A-051 | 分配角色 | POST /admin/users/roles/assign | 角色绑定成功 | 管理员 | P0 |
| A-052 | 移除角色 | POST /admin/users/roles/remove | 角色移除成功 | 管理员 | P0 |
| A-053 | 批量更新角色 | POST /admin/users/roles/update | 用户角色更新 | 管理员 | P1 |
| A-054 | 角色权限不足 | 普通用户访问 /admin/roles | 重定向或提示权限不足 | 普通 | P0 |
#### 2.8 管理后台 — 审计日志 (/admin/audit)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-055 | 审计日志页 | 访问 /admin/audit | 显示操作日志列表 | 管理员 | P0 |
| A-056 | 筛选功能 | 按 actor_id/service/action/time 筛选 | 返回过滤结果 | 管理员 | P1 |
| A-057 | 时间范围 | 设置 start_time/end_time | 返回该时段日志 | 管理员 | P1 |
| A-058 | 权限控制 | 普通用户访问 | 重定向或提示权限不足 | 普通 | P0 |
#### 2.9 管理后台 — 服务账号 (/admin/service-accounts)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| A-059 | 服务账号列表 | 访问 /admin/service-accounts | 显示所有服务账号 | 管理员 | P0 |
| A-060 | 创建服务账号 | 填写名称/描述,提交 | 创建成功,自动生成 key | 管理员 | P0 |
| A-061 | 生成令牌 | POST /admin/service-accounts/tokens/generate | 生成 token显示一次 | 管理员 | P0 |
| A-062 | 吊销令牌 | 吊销已有 token | Token 失效 | 管理员 | P1 |
| A-063 | 停用服务账号 | 停用服务账号 | 账号不可用 | 管理员 | P1 |
| A-064 | 权限控制 | 普通用户访问 | 重定向或提示权限不足 | 普通 | P0 |
---
### 模块 3Blog 博客服务 (blog.ephron.ren)
#### 3.1 公开页面
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-001 | 博客首页 | 访问 / | 200显示最新文章 + 统计 | 无 | P0 |
| B-002 | 文章列表 | 访问 /posts | 显示所有已发布文章 | 无 | P0 |
| B-003 | 文章详情 | 点击文章 /posts/{slug} | Markdown 正确渲染,显示阅读时间、浏览量 | 无 | P0 |
| B-003a | 阅读时间估算 | 查看中文/英文文章 | 中文 300 字/分钟,英文 200 词/分钟计算 | 无 | P2 |
| B-003b | 浏览量计数 | 刷新文章详情页 | 浏览量递增(数据库记录) | 无 | P2 |
| B-003c | CRLF→LF 规范化 | 提交含 CRLF 的文章内容 | 保存后内容为 LF 换行 | 管理员 | P2 |
| B-004 | 代码高亮 | 查看含代码块的文章 | 语法高亮正常 | 无 | P1 |
| B-005 | 数学公式 | 查看含 LaTeX 的文章 | MathJax 渲染正常 | 无 | P1 |
| B-006 | 归档页 | 访问 /archive | 按年月分组显示 | 无 | P1 |
| B-007 | 标签列表 | 访问 /tags | 显示标签及数量 | 无 | P1 |
| B-008 | 按标签筛选 | 点击标签 /tags/{tag} | 显示该标签文章 | 无 | P1 |
| B-009 | RSS Feed | 访问 /feed | 有效 RSS XML | 无 | P1 |
| B-010 | Sitemap | 访问 /sitemap.xml | 有效 Sitemap XML | 无 | P2 |
| B-011 | 草稿不可见 | 未登录访问草稿 URL | 404 | 无 | P0 |
| B-011a | 草稿预览(已登录) | 管理员访问草稿 URL | 正常显示,含草稿标记 | 管理员 | P0 |
| B-012 | 草稿可见 | 管理员访问草稿 URL | 正常显示 | 管理员 | P0 |
| B-013 | 404 页面 | 访问 /posts/not-exist | 404 | 无 | P1 |
#### 3.2 搜索
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-014 | 简单搜索 | /posts?q=关键词 | 返回匹配文章 | 无 | P0 |
| B-015 | 全文搜索 | /posts?q=关键词&mode=fulltext | 返回匹配文章 + 高亮 | 无 | P1 |
| B-015a | 全文搜索中文分词 | /posts?q=关键词&mode=fulltext中文关键词 | jieba 分词正确,返回相关结果 | 无 | P1 |
| B-015b | 搜索结果高亮 | 查看 fulltext 搜索结果 | 匹配关键词高亮显示HTML 标签已转义 | 无 | P1 |
| B-015c | 搜索模式对比 | 同一关键词分别用 simple 和 fulltext | simple 为字符串匹配fulltext 为 BM25 排序 | 无 | P2 |
| B-016 | 空搜索 | /posts?q= | 显示所有文章 | 无 | P1 |
| B-017 | 无结果 | 搜索不存在的词 | 空结果或提示 | 无 | P1 |
#### 3.3 评论系统
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-018 | 评论显示 | 查看文章底部 | 显示已审核评论 | 无 | P0 |
| B-019 | 提交评论(已登录) | POST /api/comments/ | 成功,提示等待审核 | 任意 | P0 |
| B-020 | 提交评论(未登录) | POST /api/comments/ | 401 | 无 | P0 |
| B-021 | 回复评论 | 带 parent_id 提交 | 创建嵌套回复 | 任意 | P2 |
| B-022 | 评论限流 | 1 分钟内 6+ 条 | 第 6 条 429 | 任意 | P1 |
#### 3.4 点赞系统
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-023 | 点赞状态 | GET /api/likes/posts/{slug} | 返回 liked + like_count | 无 | P0 |
| B-024 | 点赞 | POST /api/likes/toggle | 点赞数 +1 | 无 | P0 |
| B-025 | 取消点赞 | 再次 toggle | 点赞数 -1 | 无 | P0 |
| B-025a | 点赞统计 API | GET /api/likes/stats | 返回所有文章点赞统计 | 无 | P2 |
| B-026 | 点赞限流 | 1 分钟内 11+ 次 | 第 11 次 429 | 无 | P2 |
#### 3.5 管理后台 (/admin)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-027 | Admin 首页 | 以管理员访问 /admin | 文章列表(含草稿)+ 统计 | 管理员 | P0 |
| B-028 | 搜索文章 | 在 Admin 搜索框输入关键词 | 显示匹配结果 | 管理员 | P1 |
| B-029 | 新建文章 | /admin/new填标题/内容/标签,提交 | 创建成功 | 管理员 | P0 |
| B-030 | 编辑文章 | /admin/edit/{slug},修改后保存 | 保存成功 | 管理员 | P0 |
| B-031 | 删除文章 | 点击删除 | 文章删除 | 管理员 | P0 |
| B-032 | 切换草稿 | 点击草稿/发布切换 | 状态切换 | 管理员 | P0 |
| B-033 | 切换置顶 | 点击置顶切换 | 置顶状态切换 | 管理员 | P1 |
| B-033a | 置顶文章排序 | 置顶文章在列表中 | 置顶文章排在非置顶之前 | 管理员 | P1 |
| B-034 | 图片上传 | 上传 jpg/png/gif/webp | 成功,返回 URL + Markdown 片段 | 管理员 | P1 |
| B-034a | 图片自动转 WebP | 上传 jpg/png 图片 | 自动转换为 WebP 格式,最大 1920x1080 | 管理员 | P1 |
| B-034b | 图片上传返回格式 | 检查上传响应 | 返回 URL、Markdown 片段、文件名、大小、尺寸 | 管理员 | P2 |
| B-035 | 图片大小限制 | 上传 >5MB 图片 | 返回错误 | 管理员 | P1 |
| B-036 | 内容大小限制 | 提交 >1MB 内容 | 返回错误 | 管理员 | P1 |
| B-037 | CSRF 保护 | 不带 csrf_token 提交 | 验证失败 | 管理员 | P0 |
| B-038 | 创建限流 | 1 分钟内创建 11+ 篇 | 第 11 篇 429 | 管理员 | P1 |
| B-039 | 保存限流 | 1 分钟内保存 21+ 次 | 第 21 次 429 | 管理员 | P1 |
| B-040 | 删除限流 | 1 分钟内删除 6+ 篇 | 第 6 篇 429 | 管理员 | P1 |
| B-041 | Admin 权限拦截 | 普通用户访问 /admin | 重定向或权限不足 | 普通 | P0 |
| B-042 | Admin 登出 | POST /admin/logout | Cookie 清除 | 管理员 | P0 |
#### 3.6 评论管理后台
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-043a | 评论管理页面 | 访问 /admin/comments | 显示评论管理 UI审核/删除操作) | 管理员 | P0 |
| B-043b | 评论分页 | 评论超过单页时 | 分页控件正常 | 管理员 | P1 |
#### 3.6.1 评论管理 API
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
| B-043 | 全部评论 | GET /api/comments/admin/all | 返回评论列表(含未审核) | 管理员 | P0 |
| B-044 | 待审核评论 | GET /api/comments/admin/pending | 返回待审核列表 | 管理员 | P0 |
| B-045 | 审核通过 | POST /api/comments/admin/{id}/approve | 评论变为已审核 | 管理员 | P0 |
| B-046 | 删除评论 | DELETE /api/comments/admin/{id} | 评论被删除 | 管理员 | P0 |
| B-047 | 评论详情 | GET /api/comments/admin/{id} | 返回含 IP 等敏感信息 | 管理员 | P1 |
| B-048 | 权限拦截 | 普通用户访问 admin API | 403 | 普通 | P0 |
#### 3.7 Service API
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| B-049 | 列表草稿 | GET /api/service/posts | 可管理的草稿列表 | 服务 | P1 |
| B-049a | 所有权隔离 | 服务 A 创建的草稿,服务 B 尝试访问 | 403 或 404 | 服务 | P0 |
| B-049b | 人工接管 | 服务创建的草稿,管理员编辑 | 编辑成功ownership_type 保持 service | 管理员 | P1 |
| B-050 | 创建草稿 | POST /api/service/posts | 成功,返回 slug | 服务 | P1 |
| B-051 | 更新草稿 | PATCH /api/service/posts/{slug} | 成功 | 服务 | P1 |
| B-052 | 删除草稿 | DELETE /api/service/posts/{slug} | 成功 | 服务 | P1 |
| B-053 | 无 token | 不带 Authorization | 401 | 无 | P1 |
| B-054 | 非拥有者 | 访问他人草稿 | 403 | 服务 | P1 |
---
### 模块 4Canvas 画布服务 (canvas.ephron.ren)
#### 4.1 公开页面
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| C-001 | 首页加载 | 访问 / | 200显示 Canvas 列表 | 无 | P0 |
| C-002 | 分类筛选 | 点击分类标签 | 按分类显示 | 无 | P1 |
| C-003 | 搜索 | 输入关键词 | 返回匹配结果 | 无 | P1 |
| C-004 | 预览页 | 访问 /view/{slug} | iframe 加载 Canvas 内容 | 无 | P0 |
| C-005 | 原始 HTML | 访问 /raw/{slug} | 返回原始 HTML | 无 | P0 |
| C-005a | 原始 HTML 安全 | /raw/{slug} 响应头检查 | Content-Type 为 text/html有 CSP 限制 | 无 | P0 |
| C-006 | 访问计数 | POST /api/view/{slug} | 计数递增AJAX 请求) | 无 | P1 |
| C-007 | 草稿不可见 | 未登录访问草稿 | 404 | 无 | P0 |
| C-008 | 空状态 | 无 Canvas 时 | 显示「还没有工具」 | 无 | P1 |
#### 4.2 管理后台 (/admin)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| C-009 | Admin 首页 | 以管理员访问 /admin | 显示所有 Canvas含草稿+ 统计 | 管理员 | P0 |
| C-010 | 搜索 | 在 Admin 搜索 | 显示匹配结果 | 管理员 | P1 |
| C-011 | 新建 Canvas | /admin/new填标题/slug/内容/分类,提交 | 创建成功 | 管理员 | P0 |
| C-011a | 用户指定 slug | 创建时自定义 slug | slug 保存成功,可通过自定义 slug 访问 | 管理员 | P1 |
| C-011b | slug 格式验证 | 输入大写/特殊字符/中文 slug | 拒绝,仅允许小写字母数字下划线连字符 | 管理员 | P0 |
| C-011c | 分类/来源字段 | 选择预定义分类和来源 | 正确保存并显示 | 管理员 | P1 |
| C-012 | 编辑 Canvas | /admin/edit/{slug},修改后保存 | 保存成功 | 管理员 | P0 |
| C-013 | 删除 Canvas | POST /admin/delete | 删除成功 | 管理员 | P0 |
| C-014 | 切换草稿 | POST /admin/toggle-draft | 状态切换 | 管理员 | P0 |
| C-015 | CSRF 保护 | 不带 csrf_token 提交 | 验证失败 | 管理员 | P0 |
| C-016 | 创建限流 | 1 分钟内 11+ 个 | 第 11 个 429 | 管理员 | P1 |
| C-017 | 保存限流 | 1 分钟内 21+ 次 | 第 21 次 429 | 管理员 | P1 |
| C-018 | 权限拦截 | 普通用户访问 /admin | 重定向或权限不足 | 普通 | P0 |
| C-019 | 登出 | POST /admin/logout | Cookie 清除 | 管理员 | P0 |
#### 4.3 Service API
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| C-020 | 列表草稿 | GET /api/service/canvas | 可管理的草稿列表 | 服务 | P1 |
| C-020a | 所有权隔离 | 服务 A 创建的草稿,服务 B 尝试访问 | 403 或 404 | 服务 | P0 |
| C-021 | 创建草稿 | POST /api/service/canvas | 成功,返回 slug | 服务 | P1 |
| C-022 | 更新草稿 | PATCH /api/service/canvas/{slug} | 成功 | 服务 | P1 |
| C-023 | 删除草稿 | DELETE /api/service/canvas/{slug} | 成功 | 服务 | P1 |
| C-024 | 无 token | 不带 Authorization | 401 | 无 | P1 |
---
### 模块 5Prompt 提示词服务 (prompt.ephron.ren)
#### 5.1 公开页面
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| P-001 | 首页加载 | 访问 / | 200显示提示词列表 | 无 | P0 |
| P-002 | 搜索 | /?q=关键词 | 返回匹配结果 | 无 | P1 |
| P-003 | 分类筛选 | /?category=写作 | 按分类显示 | 无 | P1 |
| P-004 | 标签筛选 | /?tag=xxx | 按标签显示 | 无 | P1 |
| P-005 | 统计信息 | 检查页面 | 显示总数/模板数/分类数 | 无 | P2 |
| P-006 | 详情页 | 访问 /prompts/{key} | 显示标题/内容/描述/标签 | 无 | P0 |
| P-006a | Public JSON API | GET /api/prompts/{key} | 返回 JSON 格式提示词(无需登录) | 无 | P0 |
| P-006b | Public API 列表 | GET /api/prompts?limit=10&offset=0 | 返回分页提示词列表 | 无 | P1 |
| P-006c | Public API 版本 | GET /api/prompts/{key}?version=2 | 返回指定版本内容 | 无 | P1 |
| P-006d | API 草稿拦截 | GET /api/prompts/{key}(该 key 为草稿) | 返回 400 或 404 | 无 | P0 |
| P-007 | 草稿不可见 | 未登录访问草稿 | 404 | 无 | P0 |
| P-008 | 404 页面 | 访问 /prompts/not-exist | 404 | 无 | P1 |
#### 5.2 管理后台 (/admin)
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| P-009 | Admin 首页 | 以管理员访问 /admin | 显示所有提示词(含草稿) | 管理员 | P0 |
| P-010 | 搜索/筛选 | 在 Admin 搜索/按分类/按标签 | 返回匹配结果 | 管理员 | P1 |
| P-011 | 新建提示词 | /admin/new填标题/内容/分类/标签/描述,提交 | 创建成功 | 管理员 | P0 |
| P-012 | 模板标记 | 创建时勾选「是模板」 | is_template 正确保存 | 管理员 | P1 |
| P-012a | 模板变量 | 创建模板提示词,填写 variablesCSV 格式) | 变量保存成功 | 管理员 | P1 |
| P-012b | 示例输入/输出 | 填写 example_input 和 example_output | 保存成功,详情页显示 | 管理员 | P2 |
| P-012c | 推荐模型 | 填写 recommended_model 字段 | 保存成功,详情页显示 | 管理员 | P2 |
| P-013 | 编辑提示词 | /admin/edit/{key},修改后保存 | 保存成功 | 管理员 | P0 |
| P-014 | 删除提示词 | POST /admin/delete/{key} | 删除成功 | 管理员 | P0 |
| P-015 | 切换草稿 | POST /admin/toggle/{key} | 状态切换 | 管理员 | P0 |
| P-016 | 版本管理 | 访问 /admin/versions/{key} | 显示版本历史 | 管理员 | P1 |
| P-017 | 切换版本 | POST /admin/set-version/{key} | 切换到指定版本 | 管理员 | P1 |
| P-017a | 版本切换后 API | 切换版本后 GET /api/prompts/{key} | 返回新切换的版本内容 | 管理员 | P1 |
| P-018 | CSRF 保护 | 不带 csrf_token 提交 | 验证失败 | 管理员 | P0 |
| P-019 | 创建限流 | 1 分钟内 11+ 个 | 第 11 个 429 | 管理员 | P1 |
| P-020 | 保存限流 | 1 分钟内 21+ 次 | 第 21 次 429 | 管理员 | P1 |
| P-021 | 权限拦截 | 普通用户访问 /admin | 重定向或权限不足 | 普通 | P0 |
| P-022 | 登出 | GET /logout | Cookie 清除 | 任意 | P0 |
#### 5.3 Service API
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| P-023 | 列表草稿 | GET /api/service/prompts | 可管理的草稿列表 | 服务 | P1 |
| P-023a | 所有权隔离 | 服务 A 创建的草稿,服务 B 尝试访问 | 403 或 404 | 服务 | P0 |
| P-024 | 创建草稿 | POST /api/service/prompts | 成功 | 服务 | P1 |
| P-025 | 更新草稿 | PATCH /api/service/prompts/{key} | 成功 | 服务 | P1 |
| P-026 | 删除草稿 | DELETE /api/service/prompts/{key} | 成功 | 服务 | P1 |
| P-027 | 无 token | 不带 Authorization | 401 | 无 | P1 |
---
### 模块 6安全与一致性
#### 6.1 安全头
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| S-001 | X-Content-Type-Options | 检查响应头 | `nosniff` | P1 |
| S-002 | X-Frame-Options | 检查响应头 | `DENY` | P1 |
| S-003 | Referrer-Policy | 检查响应头 | `strict-origin-when-cross-origin` | P1 |
| S-004 | CSP | 检查 Content-Security-Policy | 存在且合理 | P1 |
| S-004a | CSP script-src | 检查 script-src 策略 | 仅 `self` + `unsafe-inline`,无 `unsafe-eval` | P1 |
| S-004b | CSP form-action | 检查 form-action | 仅 `self` | P1 |
| S-004c | CSP frame-ancestors | 检查 frame-ancestors | `none`(防 clickjacking | P1 |
#### 6.2 一致性检查
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| S-005 | mobile.css 引入 | 检查各页面 HTML | 所有页面一致引入或不引入 | P1 |
| S-006 | loader.js 引入 | 检查各页面 HTML | 所有页面一致引入 | P1 |
| S-007 | DOCTYPE 前无多余内容 | 检查各页面源码第一字节 | 无 BOM/换行 | P1 |
| S-008 | Auth 页导航 | 访问 Auth 登录/注册页 | 有返回主页的链接 | P1 |
| S-009 | user-scalable | 检查 viewport meta | 无 `user-scalable=no` | P1 |
| S-009a | AJAX CSRF | Blog/Canvas/Prompt 图片上传和评论审核 | X-CSRF-Token header 验证通过 | P0 |
| S-009b | Cache-Control | 敏感页面auth verify/service accounts | `no-store``no-cache` | P1 |
#### 6.3 控制台检查
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| S-010 | Home 无 JS 错误 | 访问首页,检查控制台 | 无 error | P1 |
| S-011 | Auth 无 JS 错误 | 访问登录/注册页 | 无 error | P1 |
| S-012 | Blog 无 JS 错误 | 访问首页/文章详情/Admin | 无 error | P1 |
| S-013 | Canvas 无 JS 错误 | 访问首页/预览页/Admin | 无 error | P1 |
| S-014 | Prompt 无 JS 错误 | 访问首页/详情页/Admin | 无 error | P1 |
---
### 模块 7边界与异常输入
#### 7.1 XSS 注入测试
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| XSS-001 | 文章标题 XSS | Blog Admin 新建文章,标题 `<script>alert('XSS')</script>`,发布后访问 | 渲染为纯文本,不执行脚本 | 管理员 | P0 |
| XSS-002 | 文章内容 XSS | 内容输入 `<img src=x onerror=alert(1)>` | 不执行,显示 broken image | 管理员 | P0 |
| XSS-003 | 评论 XSS | 提交评论 `<svg onload=alert(1)>` | 渲染为纯文本,不执行 | 任意 | P0 |
| XSS-004 | Canvas 标题 XSS | Canvas 标题输入 `<script>alert(1)</script>` | 不执行 | 管理员 | P0 |
| XSS-005 | Prompt 标题 XSS | Prompt 标题输入 `"><script>alert(1)</script>` | 不执行 | 管理员 | P0 |
| XSS-006 | 用户名 XSS | 注册时用户名 `<script>alert(1)</script>` | 验证拦截或转义 | 无 | P0 |
| XSS-007 | 搜索框 XSS | 搜索框 `<script>alert(1)</script>` | 不执行,搜索结果安全显示 | 无 | P0 |
| XSS-008 | Bio/个人简介 XSS | Admin 修改简介输入 XSS payload | 不执行 | 管理员 | P0 |
| XSS-009 | 标签 XSS | 添加标签 `<script>alert(1)</script>` | 转义存储,安全显示 | 管理员 | P0 |
| XSS-010 | URL 参数 XSS | 访问 `?name=<script>alert(1)</script>` | 不执行 | 无 | P1 |
#### 7.2 超长字符串输入
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| XSS-011 | 超长文章标题 | 标题输入 10000 字符 | 验证最大长度限制,或截断保存 | 管理员 | P1 |
| XSS-012 | 超长文章内容 | 内容输入 10MB 文本 | 返回错误或限制1MB | 管理员 | P0 |
| XSS-013 | 超长用户名 | 注册时用户名输入 500 字符 | 验证最大长度限制 | 无 | P1 |
| XSS-014 | 超长评论 | 评论输入 50000 字符 | 验证长度限制 | 任意 | P1 |
| XSS-015 | 超长搜索查询 | 搜索框输入 5000 字符 | 验证长度限制或正常处理 | 无 | P1 |
| XSS-016 | 超长 Canvas 内容 | Canvas 内容输入 50MB | 验证大小限制 | 管理员 | P0 |
| XSS-017 | 超长 Prompt 内容 | Prompt 内容输入 100000 字符 | 保存成功(无硬性限制则可) | 管理员 | P1 |
| XSS-018 | 超长邮箱 | 邮箱字段输入 1000 字符 | 验证格式拦截 | 无 | P1 |
#### 7.3 特殊字符输入
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| XSS-019 | 特殊字符用户名 | 注册 `user|name` `user&name` `user'name` `user"name` | 验证字符白名单拦截 | 无 | P1 |
| XSS-020 | 特殊字符标题 | 文章标题含 `/\:*?"<>|` | 验证安全处理 | 管理员 | P1 |
| XSS-021 | Emoji 标题 | 标题输入 `🎉🔥💻🚀` 等多 emoji | 正常保存和显示 | 管理员 | P1 |
| XSS-022 | CJK 字符 | 标题/内容输入中日韩字符 `日本語한국어中文` | 正常保存和显示 | 管理员 | P1 |
| XSS-023 | Unicode 特殊符 | 标题含零宽字符 `` 或双向字符 | 安全处理,不破坏布局 | 管理员 | P1 |
| XSS-024 | 特殊字符搜索 | 搜索 `AND 1=1--` `OR 1=1` | 安全处理,不报错 | 无 | P1 |
| XSS-025 | SQL 注入形搜索 | 搜索 `' OR '1'='1` `'; DROP TABLE--` | 安全处理,无 SQL 错误 | 无 | P1 |
| XSS-026 | 换行符注入 | 标题含 `\n\r` 换行 | 安全保存,显示正常 | 管理员 | P1 |
#### 7.4 空内容与边界提交
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| XSS-027 | 空标题文章 | 新建文章,标题留空 | 验证拦截「标题不能为空」 | 管理员 | P0 |
| XSS-028 | 空内容文章 | 内容留空,填写标题 | 保存为草稿或提示内容不能为空 | 管理员 | P0 |
| XSS-029 | 空评论提交 | 评论框留空提交 | 验证拦截 | 任意 | P0 |
| XSS-030 | 仅空白字符 | 标题/内容仅输入空格/制表符 | 视为空内容,拦截 | 管理员 | P1 |
| XSS-031 | 全角空格 | 标题输入全角空格 ` ` | 视为有效内容或提示输入无效 | 管理员 | P1 |
| XSS-032 | 仅数字标题 | 标题输入 `123456` | 正常保存 | 管理员 | P1 |
| XSS-033 | 重复标题 | 创建两篇相同标题文章 | 允许或提示 slug 重复处理 | 管理员 | P1 |
---
### 模块 8安全性深度测试
#### 8.1 Cookie 属性验证
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEC-001 | auth cookie HttpOnly | curl 检查 `Set-Cookie: ephron_auth` | `HttpOnly` 标记存在 | P0 |
| SEC-002 | auth cookie Secure | curl (HTTP) 检查 | 生产环境应返回 Secure需 HTTPS | P0 |
| SEC-003 | auth cookie SameSite | 检查 SameSite 属性 | `Lax``Strict`,不为空 | P0 |
| SEC-004 | auth cookie Max-Age | 检查 Max-Age 或 Expires | 有合理过期时间≤30天 | P1 |
| SEC-005 | auth cookie Domain | 检查 Domain 域 | `.ephron.ren` 以便跨子域共享 | P0 |
| SEC-006 | CSRF cookie HttpOnly | 检查 ephron_csrf cookie | `HttpOnly=false`JS 需读取,双提交) | P1 |
| SEC-007 | Cookie 刷新 | 登录后等 5 分钟再次访问 | Session 保持cookie 未过期 | 任意 | P1 |
| SEC-008 | 跨域 Cookie 写入 | 从 blog.ephron.ren 访问 auth.ephron.ren | auth cookie 不被 JS 读取 | P1 |
#### 8.2 Open Redirect 深度测试
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEC-009 | redirect 到外域 | `auth.ephron.ren/login?redirect=https://evil.com` | 拒绝或重定向到安全页 | P0 |
| SEC-010 | redirect 协议相对 | `auth.ephron.ren/login?redirect=//evil.com` | 拒绝,视为相对路径处理或拒绝 | P0 |
| SEC-011 | redirect 协议注入 | `auth.ephron.ren/login?redirect=javascript:alert(1)` | 拒绝 | P0 |
| SEC-012 | redirect 路径遍历 | `auth.ephron.ren/login?redirect=/..//evil.com` | 拒绝或规范化处理 | P1 |
| SEC-013 | redirect 到子域 | `auth.ephron.ren/login?redirect=https://blog.ephron.ren` | 允许ephron.ren 子域) | P0 |
| SEC-014 | redirect 空值 | `auth.ephron.ren/login?redirect=` | 使用默认页 | P1 |
| SEC-015 | redirect URL 编码 | `auth.ephron.ren/login?redirect=%2F%2Fevil.com` | 拒绝或解码后拒绝 | P1 |
| SEC-016 | 登出后 redirect | 登出时带 redirect 参数 | 拒绝到外部 URL | P1 |
#### 8.3 CSRF Token 深度测试
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEC-017 | Token 重放 | 获取 token 后在同一 session 重复使用 | 允许(在有效期内) | P1 |
| SEC-018 | Token 过期 | 获取 token等 61 分钟后再使用 | 拒绝,返回 CSRF 验证失败 | P0 |
| SEC-019 | Token 伪造 | 表单中填入假 token `{timestamp}:fakehex` | 拒绝HMAC 验证不通过 | P0 |
| SEC-020 | Token 格式错误 | 表单 token 与 cookie 不一致 | 拒绝 | P0 |
| SEC-021 | Token 仅 cookie 无表单 | 不在表单中传 token仅 cookie | 拒绝(双提交需两者匹配) | P0 |
| SEC-022 | Token 仅表单无 cookie | 传表单 token 但不带 CSRF cookie | 拒绝 | P0 |
| SEC-023 | Token 不同服务 | 用 blog 的 token 提交到 auth 的表单 | 拒绝(各服务 token 独立) | P1 |
| SEC-024 | 修改 token 时钟偏倚 | 手动调整请求时间戳到未来/过去 | 过期 token 拒绝 | P1 |
#### 8.5 Service Account Token 认证
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEC-033 | Bearer Token 认证 | 用有效 service token 请求 /api/service/* | 200返回数据 | P0 |
| SEC-034 | Token SHA256 查找 | 数据库中 token 为 SHA256 哈希 | 明文 token 不存储 | P0 |
| SEC-035 | Token 使用追踪 | 使用 token 后检查 last_used_at/ip/ua | 记录更新 | P1 |
| SEC-036 | Token 过期 | 使用带 expires_at 的过期 token | 401 | P0 |
| SEC-037 | 吊销 Token | 吊销后使用旧 token | 401 | P0 |
| SEC-038 | Token 跨服务权限 | Blog service token 访问 Canvas API | 403各服务独立 | P0 |
#### 8.4 路径遍历与文件安全
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEC-025 | 文章 slug 路径遍历 | 访问 `/posts/../../../etc/passwd` | 404 或安全拒绝 | P0 |
| SEC-026 | Canvas slug 路径遍历 | 访问 `/view/../../secret.txt` | 404 | P0 |
| SEC-027 | Prompt key 路径遍历 | 访问 `/prompts/../../config.py` | 404 | P0 |
| SEC-028 | 管理接口越权 | 普通用户直接 POST 到 admin 接口 | 403 | 普通 | P0 |
| SEC-029 | 直接访问管理 API | curl 绕过 UI 访问 `/admin/api/...` | RBAC 拦截 | 普通 | P0 |
| SEC-030 | 文件上传绕过扩展名 | 上传 `shell.php.jpg` | 应拒绝(只允许图片类型) | 管理员 | P0 |
| SEC-031 | 文件上传 MIME 类型绕过 | POST 修改 Content-Type 但内容是 HTML | 验证文件内容而非仅 MIME | 管理员 | P1 |
| SEC-032 | 文件名 XSS | 上传含 `<script>` 文件名的图片 | 存储时清理文件名 | 管理员 | P1 |
---
### 模块 9会话管理
#### 9.1 Token 生命周期
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SES-001 | Token 过期跳转 | 等待 token 过期后访问受保护页 | 重定向到 login 页 | 任意 | P0 |
| SES-002 | Token 过期 API | 用过期 token 请求 API | 401 `{"error": "token expired"}` | 任意 | P0 |
| SES-003 | 角色降级权限刷新 | 管理员登录后 DB 改为 user刷新页面 | 页面应反映新权限(读取 DB 角色) | 管理员 | P0 |
| SES-004 | 角色升级权限刷新 | user 登录后 DB 改为 admin刷新 | 需重新登录才生效session 不自动刷新) | 普通 | P1 |
| SES-005 | 用户禁用后会话 | 用户登录后 DB 禁用账号,已存 cookie 访问 | 拒绝访问或立即登出 | 任意 | P0 |
| SES-006 | 多设备登录 | 同时两台设备用同一账号 | 两 session 均有效,或强制下线 | 任意 | P1 |
| SES-007 | 登录成功 token 刷新 | 登录后检查是否有新 session 产生 | 每次登录产生新 token | 任意 | P1 |
#### 9.2 并发会话安全
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SES-008 | 并发编辑草稿 | 两浏览器同时编辑同一文章并保存 | 后保存覆盖先前,或加锁提示冲突 | 管理员 | P1 |
| SES-009 | 并发评论 | 多用户同时对同一文章提交评论 | 均成功存储,时间戳不同 | 任意 | P1 |
| SES-010 | 并发点赞 | 多用户同时对同一文章 toggle 点赞 | 计数正确,无 race condition | 任意 | P1 |
| SES-011 | Session 固定攻击 | 登录前 cookie 值 A登录后 cookie 变为 B | 登录后 session ID 必须更换 | 任意 | P0 |
---
### 模块 10文件上传安全
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| UPL-001 | 图片上传正常 | 上传 1MB jpg 文件 | 成功,返回 URL | 管理员 | P0 |
| UPL-002 | PNG 上传 | 上传 2MB png | 成功 | 管理员 | P0 |
| UPL-003 | WebP 上传 | 上传 webp 格式 | 成功 | 管理员 | P0 |
| UPL-004 | GIF 上传 | 上传 500KB gif | 成功 | 管理员 | P0 |
| UPL-005 | 大小限制 | 上传 10MB 文件 | 返回错误「文件超过 5MB」 | 管理员 | P0 |
| UPL-006 | 双扩展名 | 上传 `shell.php.jpg` | 拒绝(后端验证真实类型) | 管理员 | P0 |
| UPL-007 | 可执行文件 | 上传 `shell.exe` | 拒绝,仅允许图片类型 | 管理员 | P0 |
| UPL-008 | 恶意 SVG | 上传含 `<script>alert(1)</script>` 的 SVG | 拒绝或净化处理 | 管理员 | P0 |
| UPL-009 | polyglot 文件 | 上传既是图片又是 HTML 的文件 | 拒绝,内容检查 | 管理员 | P0 |
| UPL-010 | 0 字节文件 | 上传 0 字节文件 | 拒绝或提示无效文件 | 管理员 | P1 |
| UPL-011 | 超大文件名 | 上传文件名 500 字符 | 截断或正常处理 | 管理员 | P1 |
| UPL-012 | 路径穿越文件名 | 上传 `../../../etc/passwd.jpg` | 规范化文件名,安全存储 | 管理员 | P0 |
| UPL-013 | 上传覆盖 | 上传与已有文件同名的图片 | UUID 化命名,不覆盖 | 管理员 | P1 |
| UPL-014 | 未登录上传 | 未登录尝试上传 | 401 重定向到登录 | 无 | P0 |
| UPL-015 | 普通用户上传 | 普通用户尝试上传 | 403 权限不足 | 普通 | P0 |
---
### 模块 11搜索边界测试
| 编号 | 测试内容 | 步骤 | 预期 | 账号 | 优先级 |
|------|----------|------|------|------|--------|
| SCH-001 | 空搜索 | `/?q=``/?q=%20%20` | 显示所有内容 | 无 | P1 |
| SCH-002 | 单字符搜索 | `/?q=a` | 有结果或空结果提示 | 无 | P1 |
| SCH-003 | 超长搜索 | `/?q=` + 10000 字符 | 验证长度限制 | 无 | P1 |
| SCH-004 | SQL 注入搜索 | `/?q=' OR 1=1--` | 安全处理,显示空结果或报错 | 无 | P0 |
| SCH-005 | 正则注入 | `/?q=(?<=.*)` 特殊正则元字符 | 安全处理 | 无 | P1 |
| SCH-006 | 搜索结果 XSS | 搜索含 `<script>` 内容后查看结果高亮 | 转义高亮显示 | 无 | P0 |
| SCH-007 | 搜索跨服务 | auth 服务搜索 `/?q=blogpost` | 正常处理auth 无搜索则跳过) | 无 | P1 |
| SCH-008 | 搜索结果分页 | 搜索结果超过单页 | 分页控件正常 | 无 | P1 |
| SCH-009 | 搜索历史 | 输入搜索词后刷新页面 | 搜索框保留上次内容 | 无 | P2 |
---
### 模块 12SEO 元数据测试
#### 12.1 Home 服务
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEO-001 | 首页 title | 访问 www.ephron.ren查看 `<title>` | 有描述性 title非默认 | P1 |
| SEO-002 | 首页 meta description | 检查 `<meta name="description">` | 有且长度合理150字符左右 | P1 |
| SEO-003 | 首页 OG 标签 | 检查 `og:title`, `og:description`, `og:image`, `og:url` | 全部存在 | P1 |
| SEO-004 | 首页 canonical | 检查 `<link rel="canonical">` | 存在,指向自身 | P2 |
| SEO-005 | 首页 robots | 检查 robots meta 或 headers | `index, follow` | P2 |
| SEO-006 | 响应式 viewport | 检查 viewport meta | 含 `width=device-width, initial-scale=1` | P1 |
| SEO-007 | 首页 icon | 检查 favicon.ico / apple-touch-icon | 存在 | P2 |
#### 12.2 Blog 服务
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEO-008 | 文章 title | 访问文章详情页 `<title>` | 含文章标题 + 站点名 | P1 |
| SEO-009 | 文章 meta description | 检查 description 内容 | 文章摘要或前 160 字符 | P1 |
| SEO-010 | 文章 OG 标签 | 检查 og:title, og:description, og:image | 全部存在 | P1 |
| SEO-011 | 文章 canonical | 检查 canonical URL | 存在且正确 | P1 |
| SEO-012 | RSS feed | 访问 /feed | 有效 XML有 items | P1 |
| SEO-013 | sitemap.xml | 访问 /sitemap.xml | 有效 XML包含所有已发布文章 | P1 |
| SEO-014 | 文章结构化数据 | 检查 JSON-LD | 有 Article 或 BlogPosting schema | P2 |
| SEO-015 | 归档页 title | 访问 /archive | 有描述性 title | P2 |
| SEO-016 | 标签页 title | 访问 /tags/{tag} | 含标签名 + 站点名 | P2 |
#### 12.3 Canvas 服务
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEO-017 | Canvas 预览页 title | 访问 /view/{slug} | 含标题 + 站点名 | P2 |
| SEO-018 | Canvas OG image | 检查 og:image | 存在(如有缩略图) | P2 |
#### 12.4 Prompt 服务
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEO-019 | Prompt 详情页 title | 访问 /prompts/{key} | 含标题 + 站点名 | P2 |
| SEO-020 | Prompt 详情页 description | 检查 meta description | 存在 | P2 |
#### 12.5 robots.txt
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| SEO-021 | robots.txt 存在 | 各子服务访问 /robots.txt | 存在内容 | P1 |
| SEO-022 | robots.txt 有效性 | 检查 sitemap 指令 | 指向正确的 sitemap URL | P1 |
---
### 模块 13无障碍Accessibility深度测试
#### 13.1 键盘导航
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| A11Y-001 | 焦点可见性 | Tab 键遍历首页 | 每个焦点元素有可见轮廓 | P1 |
| A11Y-002 | 表单键盘操作 | 在登录表单中 Tab 切换字段Enter 提交 | 顺序正确Enter 提交 | 无 | P1 |
| A11Y-003 | 模态框键盘 | 打开联系弹窗后 Tab | 焦点锁定在弹窗内 | 无 | P1 |
| A11Y-004 | 下拉菜单键盘 | 使用键盘操作下拉菜单 | 上下箭头选择Enter 确认 | 无 | P1 |
| A11Y-005 | 跳过导航 | 按 Tab 后首个链接 | 应为「跳过到主要内容」链接 | P1 |
| A11Y-006 | 编辑器键盘操作 | Blog Admin 编辑器,键盘操作 | 可用键盘完成基本编辑 | 管理员 | P1 |
#### 13.2 ARIA 与语义化
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| A11Y-007 | 图片 alt | 所有 `<img>` 有 alt 属性 | 装饰性图片有 `alt=""``aria-hidden` | P1 |
| A11Y-008 | 按钮语义 | 使用 `<button>` 而非 `<div onclick>` | 正确语义标签 | P1 |
| A11Y-009 | 表单 label | 登录表单每个 input 有 `<label>` | label 与 input 关联 | P1 |
| A11Y-010 | ARIA label | 图标按钮有 `aria-label` | 含义明确 | P1 |
| A11Y-011 | 导航 landmark | `<nav>` 标签存在 | 导航区有正确语义 | P2 |
| A11Y-012 | 标题层级 | H1/H2/H3 层级不跳过 | 逻辑清晰 | P1 |
#### 13.3 颜色对比度
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| A11Y-013 | 正文对比度 | 白色背景上正文文字 | 至少 4.5:1AA 标准) | P1 |
| A11Y-014 | 大文字对比度 | 标题文字与背景 | 至少 3:1 | P1 |
| A11Y-015 | 链接颜色 | 正文链接颜色与正文 | 4.5:1 或有下划线 | P1 |
| A11Y-016 | 错误提示颜色 | 红色错误文字 | 不仅依赖颜色传达信息 | P1 |
| A11Y-017 | 按钮 hover | 按钮 hover 状态有视觉变化 | 非仅颜色变化(加下划线/边框) | P1 |
---
### 模块 14性能测试
#### 14.1 页面加载
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| PERF-001 | 首页加载时间 | 访问首页,测量 TTFB | < 1s | P1 |
| PERF-002 | 首页完全加载 | 测量 onload 时间 | < 3s | P1 |
| PERF-003 | 文章详情加载 | 访问含代码块/LaTeX 的文章 | < 3s | P1 |
| PERF-004 | 静态资源 200 | 所有 CSS/JS/图片 | 全部 HTTP 200无 404 | P0 |
| PERF-005 | 外部资源 | 检查 Google Fonts / CDN 资源 | 可访问(中国大陆考虑 jsDelivr 备选) | P1 |
| PERF-006 | 资源重复请求 | 检查同一资源是否多次请求 | 无重复请求相同资源 | P2 |
| PERF-007 | 图片懒加载 | 博客列表页图片 | 进入视口前不加载 | P2 |
#### 14.2 Core Web Vitals简化测试
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| PERF-008 | LCP 预估 | 首页加载后最大图片/文字渲染 | < 2.5s | P2 |
| PERF-009 | CLS 检测 | 页面加载后无布局偏移 | 无意外布局移动 | P2 |
| PERF-010 | 无长任务 | 浏览器 Performance 标签 | 无 > 50ms 的主线程阻塞 | P2 |
#### 14.3 首屏渲染
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| PERF-011 | 首屏无白屏 | 访问首页 | 1s 内显示内容 | P1 |
| PERF-012 | 首屏骨架屏 | 慢网速模拟 | 有 loading 状态 | P2 |
| PERF-013 | 内联 CSS | 检查 `<head>` 内联 CSS | 首屏内容立即可用 | P2 |
---
### 模块 15移动端交互测试
#### 15.1 响应式布局
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| MOBILE-001 | 375px 布局 | Chrome DevTools 设为 iPhone 12 宽度 | 无水平滚动,布局正常 | P1 |
| MOBILE-002 | 768px 布局 | 设为 iPad 宽度 | 布局自适应 | P1 |
| MOBILE-003 | 导航响应 | 375px 下导航栏 | 汉堡菜单或适配移动端 | P1 |
| MOBILE-004 | 点击区域大小 | 按钮/链接触摸目标 | ≥ 44x44px | P1 |
| MOBILE-005 | 内容溢出 | 长标题/长单词在移动端 | 自动换行,不溢出 | P1 |
#### 15.2 触摸交互
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| MOBILE-006 | 触摸滚动 | 触摸博客列表页 | 平滑滚动 | 无 | P1 |
| MOBILE-007 | 点击反馈 | 触摸按钮 | 有视觉反馈ripple/高亮) | P1 |
| MOBILE-008 | 双击缩放 | 触摸文字 | 无意外缩放(已设 viewport | P1 |
| MOBILE-009 | 虚拟键盘 | 移动端点击评论输入框 | 键盘弹出,页面适当上推 | 任意 | P1 |
| MOBILE-010 | 横竖屏切换 | 旋转设备方向 | 布局正常重排 | P2 |
---
### 模块 16跨浏览器兼容性简化测试
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| COMP-001 | Flexbox 兼容性 | 检查 CSS 是否使用有前缀的 Flexbox | 无仅 Webkit 前缀的旧语法 | P1 |
| COMP-002 | CSS Grid 兼容性 | 检查是否用了不支持的 Grid 语法 | 提供回退方案 | P2 |
| COMP-003 | HTTP/2 支持 | 检查服务器 HTTP/2 | 服务器支持 HTTP/2 | P2 |
| COMP-004 | CORS headers | 静态资源跨域请求 | 正确 CORS 头 | P2 |
| COMP-005 | ES6+ 语法 | 检查 JS 是否用了现代语法 | 无仅旧浏览器支持的语法 | P1 |
---
### 模块 17运维与部署相关
#### 17.1 健康检查
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| OPS-001 | Home 健康检查 | `curl https://www.ephron.ren/health` | `{"status":"ok"}` | P1 |
| OPS-002 | Auth 健康检查 | `curl https://auth.ephron.ren/health` | `{"status":"ok"}` | P1 |
| OPS-003 | Blog 健康检查 | `curl https://blog.ephron.ren/health` | `{"status":"ok"}` | P1 |
| OPS-004 | Canvas 健康检查 | `curl https://canvas.ephron.ren/health` | `{"status":"ok"}` | P1 |
| OPS-005 | Prompt 健康检查 | `curl https://prompt.ephron.ren/health` | `{"status":"ok"}` | P1 |
| OPS-006 | 健康检查无认证 | 无 cookie 访问健康检查 | 正常返回(不应需要认证) | P1 |
#### 17.2 错误处理
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| OPS-007 | 自定义 404 | 访问 `/not-exist-page-xxx` | 显示自定义 404 页面,不泄露信息 | P1 |
| OPS-008 | 自定义 500 | 触发服务器错误(如有 debug 接口) | 不暴露 stack trace | P1 |
| OPS-009 | 错误页面一致性 | 各子服务 404 页面 | 风格统一 | P2 |
| OPS-010 | API 404 | GET 不存在的 API 路由 | 返回 JSON `{"detail": "Not Found"}` | P1 |
| OPS-011 | 方法不允许 | POST 到只允许 GET 的页面 | 405 Method Not Allowed | P1 |
#### 17.3 日志与审计
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| OPS-012 | 登录日志 | 用错误密码登录 3 次,成功 1 次 | 查看审计日志有记录 | 管理员 | P1 |
| OPS-013 | 管理操作日志 | Admin 执行禁用用户操作 | 审计日志有记录,含 actor/time/action | 管理员 | P1 |
| OPS-014 | 日志不含敏感 | 检查日志中是否有明文密码/token | 不应明文记录敏感信息 | P0 |
| OPS-015 | 登出日志 | 登出后检查审计日志 | 有登出记录 | 任意 | P2 |
#### 17.4 数据库迁移
| 编号 | 测试内容 | 步骤 | 预期 | 优先级 |
|------|----------|------|------|--------|
| OPS-016 | 迁移脚本存在 | 检查 `alembic/``migrations/` | 有版本化迁移脚本 | P1 |
| OPS-017 | 迁移幂等性 | 运行同一迁移两次 | 第二次应是无操作(幂等) | P1 |
| OPS-018 | 回滚脚本存在 | 检查是否有 downgrade 迁移 | 可回滚到上一版本 | P2 |
---
## 三、测试执行流程
```
Step 1 → Auth 登录(获取 Cookie
Step 2 → Home公开页面 + /admin 全流程(结构化 JSON + Service Token 拒绝)
Step 3 → Auth注册全流程密码复杂度/用户名黑名单/邮箱唯一性/邀请码过期/限流)
Step 4 → Auth/admin 全部子页面(用户/邀请码/角色/审计/服务账号)
Step 5 → Blog公开页面 + 搜索simple + fulltext + 中文分词)+ 评论 + 点赞
Step 6 → Blog/admin + 评论管理 + 图片上传WebP 转换)+ 置顶排序
Step 7 → BlogService API 所有权隔离测试
Step 8 → Canvas公开页面 + /admin + slug 验证 + Service API
Step 9 → Prompt公开页面 + Public JSON API + /admin + 版本管理 + Service API
Step 10 → 权限测试:普通用户访问所有 /admin + API min_role 验证
Step 11 → 安全测试CSRF/AJAX CSRF/限流/安全头/CSP 细节/缓存头
Step 12 → 控制台检查:每个页面的 JS 错误
Step 13 → 边界输入XSS/超长/特殊字符/空提交
Step 14 → 安全深度Cookie 属性/Open Redirect/CSRF 重放/Service Token 认证
Step 15 → 会话管理Token 过期/角色变更/并发
Step 16 → 文件上传安全:恶意文件/双扩展/SVG
Step 17 → 搜索边界:注入/超长/特殊字符
Step 18 → SEOOG 标签/canonical/RSS/sitemap
Step 19 → 无障碍:键盘导航/ARIA/颜色对比度
Step 20 → 性能:加载时间/Core Web Vitals/首屏
Step 21 → 移动端:响应式/触摸/虚拟键盘
Step 22 → 运维:健康检查/错误处理/日志/迁移
Step 23 → 源码分析:定位问题根因
Step 24 → 生成 Excel 报告
```
---
## 四、执行说明
### 执行策略
| 阶段 | 覆盖模块 | 优先级 | 预计时间 |
|------|----------|--------|----------|
| P0 优先 | 模块 1-6 全部 P0 + 模块 7-17 P0 用例 | ~120 用例 | 2-3 小时 |
| P1 全面 | 全部 P1 用例 | ~150 用例 | 2-3 小时 |
| P2 完善 | 全部 P2 用例 | ~96 用例 | 1-2 小时 |
### 环境要求
- 测试环境生产环境https://www.ephron.ren/
- 浏览器ChromeDevTools for mobile/network/console
- 网络:能够访问所有 5 个子服务
- 测试账号:见 1.2 节
### 工具建议
- 浏览器 DevToolsConsole/Network/Performance
- curlHTTP 测试、安全头检查)
- Chrome LighthouseSEO + Performance
- axe-core 或 Accessibility Insights无障碍
- Burp Suite 或 Postman安全测试
---
## 五、统计总表
| 模块 | 测试用例数 |
|------|:----------:|
| 模块 1Home | 24 |
| 模块 2Auth | 85 |
| 模块 3Blog | 69 |
| 模块 4Canvas | 29 |
| 模块 5Prompt | 36 |
| 模块 6安全与一致性 | 19 |
| 模块 7边界与异常输入 | 33 |
| 模块 8安全性深度测试 | 38 |
| 模块 9会话管理 | 11 |
| 模块 10文件上传安全 | 15 |
| 模块 11搜索边界测试 | 9 |
| 模块 12SEO 元数据测试 | 22 |
| 模块 13无障碍深度测试 | 17 |
| 模块 14性能测试 | 13 |
| 模块 15移动端交互测试 | 10 |
| 模块 16跨浏览器兼容性 | 5 |
| 模块 17运维与部署相关 | 18 |
| **全量合计** | **453** |
---
*文档版本: v4.0 | 最后更新: 2026-05-03*