# 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** |
---
## 二、测试用例
---
### 模块 1:Home 主页 (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 |
---
### 模块 2:Auth 认证服务 (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 |
---
### 模块 3:Blog 博客服务 (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 |
---
### 模块 4:Canvas 画布服务 (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 |
---
### 模块 5:Prompt 提示词服务 (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 | 模板变量 | 创建模板提示词,填写 variables(CSV 格式) | 变量保存成功 | 管理员 | 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 新建文章,标题 ``,发布后访问 | 渲染为纯文本,不执行脚本 | 管理员 | P0 |
| XSS-002 | 文章内容 XSS | 内容输入 `
` | 不执行,显示 broken image | 管理员 | P0 |
| XSS-003 | 评论 XSS | 提交评论 `