# 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 | 提交评论 `` | 渲染为纯文本,不执行 | 任意 | P0 | | XSS-004 | Canvas 标题 XSS | Canvas 标题输入 `` | 不执行 | 管理员 | P0 | | XSS-005 | Prompt 标题 XSS | Prompt 标题输入 `">` | 不执行 | 管理员 | P0 | | XSS-006 | 用户名 XSS | 注册时用户名 `` | 验证拦截或转义 | 无 | P0 | | XSS-007 | 搜索框 XSS | 搜索框 `` | 不执行,搜索结果安全显示 | 无 | P0 | | XSS-008 | Bio/个人简介 XSS | Admin 修改简介输入 XSS payload | 不执行 | 管理员 | P0 | | XSS-009 | 标签 XSS | 添加标签 `` | 转义存储,安全显示 | 管理员 | P0 | | XSS-010 | URL 参数 XSS | 访问 `?name=` | 不执行 | 无 | 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 | 上传含 `