Files
Jog/doc/jog-system-automated-test-plan.md
T
2026-05-18 21:54:13 +08:00

349 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# Jog System 自动化测试规范(AI 测试用例唯一输入)
> **更新**:需求级**全量展开**且文件名含 Cursor 的单一输入见 [`jog-system-cursor-automated-test-spec.md`](jog-system-cursor-automated-test-spec.md)。本文档保留为精简版路由与执行现状摘要。
| 属性 | 内容 |
| :--- | :--- |
| 文档版本 | V2.0 |
| 生成日期 | 2026-05-18 |
| 用途 | **仅依赖本文档**即可推导可执行的自动化测试用例(单元 / 集成 / 契约 / E2E / 安全 / 性能冒烟);业务验收口径同时映射需求文档中的 AC 编号 |
| 仓库实际模块名 | `jog-common``jog-framework``jog-module-system``jog-module-blog``jog-module-comment``jog-admin``jog-frontend` |
| 技术栈摘要 | 后端 Spring Boot 3.4.x、Java 21、Spring Security + JWT、MyBatis-Plus、MySQL、Redis、Flyway、SpringDoc;前端 Vue 3、Vite 6、Element Plus、Pinia、Axios |
> **说明**:历史文档 `jog-system-technical-design.md` 中的路径示例(如 `/api/app/blog/articles`)与当前代码不一致时,**以本文档「附录 A」为准**(来自当前 Controller 扫描)。需求规格中以 `AC-*` 编号的验收场景,在「第 7 节」逐条映射到可自动化用例骨架。
---
## 1. 当前已执行的测试与验证(事实记录)
以下内容来自对仓库的一次质量核验(详见 `doc/jog-system-bug-list.md`),代表**截至文档日期的真实执行状況**,而非目标态。
### 1.1 已执行项
| 序号 | 类别 | 命令或方式 | 结果摘要 |
| :---: | :--- | :--- | :--- |
| T-01 | 后端编译链路 | `mvn -B test` | Maven 生命周期通过;**各模块均无自有 `src/test`Surefire 报告为 `No tests to run`**(无 JUnit 级自动化回归) |
| T-02 | 前端生产构建 | `jog-frontend``npm run build` | **Node v16.20.2**Vite 报错 `crypto.getRandomValues is not a function`,构建失败。**Node v24.15.0**:构建通过,产物按项目配置输出到 `jog-admin/src/main/resources/static` |
| T-03 | 前端依赖与脚本 | `package.json` | 仅有 `dev` / `build` / `preview`**无** `test:unit``test:e2e`、lint 脚本 |
| T-04 | API 契约(静态) | 人工对比 `jog-frontend/src/api/*.js` 与后端 Controller | 曾发现路径/方法不一致;经修复后应以附录 A 与 OpenAPI 重新做**双向校验**(当前未纳入 CI 自动化) |
| T-05 | 数据库映射(静态) | 对比实体 `@TableName` / `@TableField` 与 Flyway `V1__init_schema.sql` | 曾发现表名、字段不一致;修复记录见 BUG-003/BUG-004**仍需带库集成测试**验证) |
| T-06 | 安全配置(静态) | 对比 `JwtTokenProvider`、JWT 过滤器、`SecurityConfig` 与 Controller 前缀 | 曾发现角色 Claims、未保护路径等问题;部分已修复(见 BUG-005/006/007),**需自动化权限矩阵回归** |
### 1.2 明确未执行 / 缺失项
| 序号 | 说明 |
| :--- | :--- |
| N-01 | **无任何** `src/test/java` 下的单元测试或集成测试(仓库扫描为 0 个测试类) |
| N-02 | **无** Vitest / Playwright / Cypress 等前端或 E2E 套件 |
| N-03 | **无** CI 中可重复的契约 diffOpenAPI vs `src/api`)流水线步骤 |
---
## 2. 测试目标与质量门禁(目标态)
### 2.1 目标
- 核心链路:认证、注册、Token 刷新、文章 CRUD 与列表、评论、分类/标签、后台用户管理与审核。
- 横切能力:JWT 鉴权、角色(`ROLE_ADMIN` / `ROLE_USER`)、统一响应 `ApiResult`、分页 `PageResult`、限流、`XssFilter`、Flyway 迁移可重复执行。
- **前后端契约**:HTTP 方法、路径、请求/响应 JSON 字段与前端封装一致。
- **需求对齐**:凡本文档标记为 `(需求)` 的用例,应以 `doc/jog-system-requirements.md` 中同名 AC 为判定依据;标记为 `(实现差异)` 的,以当前代码为准并建议产品确认。
### 2.2 建议门禁(落地后)
| 阶段 | 必跑项 |
| :--- | :--- |
| 本地/PR | `mvn -B verify`(含单测+集成)、前端 `npm run build``npm run test:unit`、OpenAPI↔前端 API 契约检查 |
| 发布前 | Testcontainers 集成测试全绿、核心 Playwright 冒烟、Flyway 干净库迁移 |
| 上线后 | 部署清单 `doc/jog-system-deployment-checklist.md` 第 11 节选段自动化或半自动巡检 |
### 2.3 覆盖率期望(指导 AI 分优先级)
- 后端:Service 核心分支优先;Controller/MockMvc 覆盖所有附录 A 中带「写操作」或「鉴权敏感」的接口。
- 前端:`utils/request.js`Token、401/403/429)、`router``requiresAuth` / `requiresAdmin`)、各业务 `src/api/*.js`
- E2E:访客可读、用户发文与评论、管理员审核与删文的最短路径。
---
## 3. 测试环境与数据约定
### 3.1 运行时版本
| 组件 | 要求 |
| :--- | :--- |
| JDK | 21 |
| Node.js | **≥ 20**Vite 6 需要现代 Web Crypto;以构建成功为准,建议在 `package.json` 增加 `engines` 并在 CI 锁定) |
| MySQL / Redis | 集成测试推荐使用 Testcontainers;**禁止**让自动化测试默认连接开发机 `application.yml` 中的真实公网地址 |
### 3.2 测试 Profile
- 使用 `application-test.yml`:数据源与 Redis 来自容器或临时实例;Flyway 开启;JWT 密钥固定测试值。
- 种子用户(示例):
- 管理员:`admin@example.com` 或 Flyway 初始管理员(以 `V2__init_data.sql` 为准);
- 普通用户 A/B:用于交叉评论、点赞;
- 锁定用户:用于登录拒绝场景。
### 3.3 数据命名
- 自动化创建的数据使用前缀 `auto_test_`,用例结束清理或事务回滚。
---
## 4. 角色与鉴权预期
| 角色 | JWT RoleSpring `hasRole` | 典型路径 |
| :--- | :--- | :--- |
| 匿名 | 无 | `/api/public/**`、静态资源、`/login``/register` |
| 注册用户 | `ROLE_USER` | `/api/app/**` |
| 管理员 | `ROLE_ADMIN` | `/api/admin/**` |
**实现提示(来自代码静态阅读)**
- `SecurityConfig` 同时包含 `.requestMatchers("/api/article/**", "/api/comment/**", "/api/user/**")`——若路由已统一至 `/api/app/**`,上述行可能为历史遗留;**自动化用例应验证「仅 /api/app/** 与 /api/admin/**」即可覆盖全部业务接口**,并检测是否仍存在未保护「遗留前缀」。
- Token 内角色应与 `ROLE_ADMIN` / `ROLE_USER` 匹配(历史缺陷 BUG-005)。
---
## 5. 已知缺陷与约束(编写用例时必须考虑)
| ID | 描述 | 对测试的影响 |
| :--- | :--- | :--- |
| BUG-010(部分) | `/api/public/captcha` 等为**占位实现**,不做真实图形校验 | 用例应断言「接口存在且返回约定结构」,**不应**按真实验证码安全强度验收 |
| BUG-014 | 限流 Key 维度不足,可能全站共享桶 | 限流用例需记录为**弱保证**;修复后增加 IP/用户维度断言 |
| BUG-015 | 无单测/集成测 | 所有用例当前为**待实现**;本文件提供用例 ID 与步骤模板 |
| 实现差异 | 需求规定访客可查看评论;当前评论列表在 `CommentController` 上位于 `/api/app/comment/**`,通常需要登录 | E2E 若模拟访客读评论:以实际 HTTP 状态为准;建议单独增加 `(实现差异)` 用例记录与需求偏差 |
---
## 6. 测试分层与工具选型(供生成代码时使用)
| 分层 | 工具 | 放置目录 |
| :--- | :--- | :--- |
| 后端单元 | JUnit 5、Mockito、AssertJ | 各模块 `src/test/java` |
| 后端集成 | Spring Boot Test、MockMvc、`@SpringBootTest``@DynamicPropertySource` + TestcontainersMySQL、Redis | 建议集中在 `jog-admin/src/test/java` 或各模块 |
| 契约 | SpringDoc 生成 OpenAPI YAML + 自定义脚本对比 `jog-frontend/src/api` | `scripts/` 或 CI |
| 前端单元/组件 | Vitest、Vue Test Utils、MSW | `jog-frontend` |
| E2E | Playwright | `jog-frontend/e2e/` 或仓库根 `e2e/` |
| 性能冒烟 | k6 或 JMeter | `perf/`(可选) |
---
## 7. 需求验收矩阵 → 自动化用例映射
下列条目**直接服务** `doc/jog-system-requirements.md` 中的 AC。AI 生成用例时:保留 `需求追溯` 列;若实现不满足需求,将 `预期` 改为「记录实际行为 + xfail」。
| 用例 ID | 需求追溯 | 建议分层 | 角色 | 摘要 |
| :--- | :--- | :--- | :--- | :--- |
| R-ADM-LOGIN-01 | AC-ADM-LOGIN-01 | E2E / API | Admin | 正确凭据登录后台,返回 Token |
| R-ADM-LOGIN-02 | AC-ADM-LOGIN-02 | API | Admin | 连续错误触发验证码流程(若已实现) |
| R-ADM-LOGIN-03 | AC-ADM-LOGIN-03 | API | Admin | 连续错误锁定账号 |
| R-USER-REG-01 | AC-USER-REG-01 | API | Anonymous | 合法邮箱注册 |
| R-USER-REG-02 | AC-USER-REG-02 | API | Anonymous | 重复邮箱拒绝 |
| R-USER-REG-03 | AC-USER-REG-03 | FE 单元 / E2E | Anonymous | 弱密码前端拦截 |
| R-USER-REG-04 | AC-USER-REG-04 | API | Anonymous | 同 IP 注册限流 |
| R-USER-LOGIN-01 | AC-USER-LOGIN-01 | E2E / API | User | 用户登录成功 |
| R-USER-LOGIN-02 | AC-USER-LOGIN-02 | API / 集成 | User | 多会话驱逐最早 |
| R-USER-LOGIN-03 | AC-USER-LOGIN-03 | API | User | 错误次数锁定 |
| R-BLOG-PUB-01 | AC-BLOG-PUB-01 | E2E | Anonymous→User | 未登录跳转登录后可回跳编辑器 |
| R-BLOG-PUB-04 | AC-BLOG-PUB-04 | API | User | 私密文章对非作者不可见 |
| R-BLOG-LIST-01 | AC-BLOG-LIST-01 | API | All | 列表排序参数生效 |
| R-BLOG-LIST-05 | AC-BLOG-LIST-05 | API | User / Author | 作者见私密、他人不见 |
| R-BLOG-LIST-06 | AC-BLOG-LIST-06 | API | Author | 作者看自己文章详情浏览量不增 |
| R-COMMENT-01 | AC-COMMENT-01 | E2E | Anonymous | 未登录评论入口拒绝 |
| R-COMMENT-02 | AC-COMMENT-02 | API | User | 四级时折叠为三级平级 |
| R-COMMENT-03 | AC-COMMENT-03 | API | Admin/User | 审核开关与评论状态联动 |
| R-COMMENT-07 | AC-COMMENT-07 | API | User | XSS payload 不执行 |
(其余 AC 按同一模式扩展;性能与安全全局项见第 10、11 节。)
---
## 8. 接口级用例清单(核心回归)
**书写约定**:每条可展开为 Gherkin 或 JUnit `@DisplayName``code` 指业务码,默认成功 `200`(见项目统一约定)。
### 8.1 认证 `/api/public`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-AUTH-001 | POST | `/api/public/user/login` | Anonymous | 成功:`data``accessToken``refreshToken` 或项目实际字段名 |
| API-AUTH-002 | POST | `/api/public/admin/login` | Anonymous | 管理员成功;非管理员拒绝 |
| API-AUTH-003 | POST | `/api/public/user/register` | Anonymous | 成功创建;重复邮箱失败 |
| API-AUTH-004 | POST | `/api/public/refresh-token` | Anonymous | refresh 有效时换新 Token;无效时 401 |
| API-AUTH-005 | GET | `/api/public/captcha` | Anonymous | 200 + 占位数据结构(BUG-010 |
### 8.2 用户 `/api/app/user`、`/api/admin/user`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-USER-001 | GET | `/api/app/user/me` | User | 当前用户资料 |
| API-USER-002 | POST | `/api/app/user/{id}/follow` | User | 关注成功 |
| API-USER-003 | DELETE | `/api/app/user/{id}/follow` | User | 取关成功 |
| API-USER-004 | POST | `/api/app/user/logout` | User | 会话或黑名单生效(按实现) |
| API-USER-010 | GET | `/api/admin/user/list` | Admin | 分页 `PageResult` |
| API-USER-011 | PUT | `/api/admin/user/{id}/reset-password` | Admin | 重置后可登录 |
| API-USER-012 | PUT | `/api/admin/user/{id}/status` | Admin | 锁定后登录失败 |
### 8.3 文章(公开)`/api/public/article`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-PUB-ART-001 | GET | `/api/public/article/list` | Anonymous | 仅公开已发布;分页字段齐全 |
| API-PUB-ART-002 | GET | `/api/public/article/{id}` | Anonymous | 详情;标签为结构化 `Tag`BUG-013 修复预期) |
### 8.4 文章(用户)`/api/app/article`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-APP-ART-001 | POST | `/api/app/article` | User | 创建成功返回 id |
| API-APP-ART-002 | PUT | `/api/app/article/{id}` | User | 作者可改;非作者 403 |
| API-APP-ART-003 | GET | `/api/app/article/mine` | User | 仅本人文章 |
| API-APP-ART-004 | PUT | `/api/app/article/{id}/recycle` | User | 回收站状态 |
| API-APP-ART-005 | PUT | `/api/app/article/{id}/restore` | User | 恢复 |
| API-APP-ART-006 | DELETE | `/api/app/article/{id}` | User | 删除 |
| API-APP-ART-007 | POST | `/api/app/article/{id}/like` | User | 点赞切换 |
### 8.5 文章(管理)`/api/admin/article`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-ADM-ART-001 | GET | `/api/admin/article/list` | Admin | 后台列表 |
| API-ADM-ART-002 | DELETE | `/api/admin/article/{id}` | Admin | 管理员删除 |
### 8.6 分类 `/api/public/category`、`/api/admin/category`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-CAT-001 | GET | `/api/public/category/list` | Anonymous | 列表 |
| API-CAT-010 | POST | `/api/admin/category` | Admin | 创建 |
| API-CAT-011 | PUT | `/api/admin/category/{id}` | Admin | 更新 |
| API-CAT-012 | DELETE | `/api/admin/category/{id}` | Admin | 删除 |
### 8.7 标签 `/api/public/tag`、`/api/admin/tag`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-TAG-001 | GET | `/api/public/tag/list` | Anonymous | 列表 |
| API-TAG-010 | POST | `/api/admin/tag` | Admin | `name` 参数创建 |
### 8.8 评论 `/api/app/comment`、`/api/admin/comment`
| ID | 方法 | 路径 | 角色 | 预期要点 |
| :--- | :--- | :--- | :--- | :--- |
| API-CMT-001 | POST | `/api/app/comment` | User | 创建评论 |
| API-CMT-002 | DELETE | `/api/app/comment/{id}` | User | 删除本人评论 |
| API-CMT-003 | GET | `/api/app/comment/article/{articleId}` | User | 树形列表(**若需访客可读应另有公开接口—当前可能需登录**) |
| API-CMT-004 | POST | `/api/app/comment/{id}/like` | User | 点赞切换 |
| API-CMT-010 | GET | `/api/admin/comment/list` | Admin | 分页列表 |
| API-CMT-011 | PUT | `/api/admin/comment/{id}/audit` | Admin | 审核 |
| API-CMT-012 | DELETE | `/api/admin/comment/{id}` | Admin | 删除 |
---
## 9. 安全与负向用例(必测)
| ID | 场景 | 预期 |
| :--- | :--- | :--- |
| SEC-001 | 无 Token 调 `/api/app/article` POST | 401 |
| SEC-002 | User Token 调 `/api/admin/user/list` | 403 |
| SEC-003 | Admin Token 调 `/api/admin/**` | 200(合法操作下) |
| SEC-004 | 篡改签名的 JWT | 401 |
| SEC-005 | 已登出/黑名单 JWT | 401(若实现黑名单) |
| SEC-006 | 登录接口暴力请求 | 429 或业务限流码 |
| SEC-007 | 文章/评论 body 携带 `<script>` | 存储或展示侧无害化 |
---
## 10. 非功能与性能(源自于需求文档)
| ID | 类型 | 条件 | 工具建议 |
| :--- | :--- | :--- | :--- |
| NF-PERF-01 | 接口延迟 | 列表/详情 P95 ≤ 需求文档值 | k6 |
| NF-PERF-02 | 前端首屏 | Lighthouse 4G ≤ 2s | Lighthouse CI |
| NF-SEC-01 | 密码存储 | DB 中为 BCrypt | JDBC 断言 |
| NF-AVAIL-01 | 并发 | 500 并发错误率 < 1% | k6 |
---
## 11. 前端专项(Vitest / Playwright
| ID | 范围 | 场景 |
| :--- | :--- | :--- |
| FE-001 | `utils/request` | 请求携带 `Authorization` |
| FE-002 | `utils/request` | `code !== 200` 时 reject |
| FE-003 | `utils/request` | 401 清 token 并跳转登录 |
| FE-004 | `router` | `requiresAuth` 拦截 |
| FE-005 | `router` | `requiresAdmin` 非管理员重定向(BUG-011 相关) |
| FE-E2E-01 | Playwright | 访客首页→详情 |
| FE-E2E-02 | Playwright | 用户登录→发文→可见 |
| FE-E2E-03 | Playwright | 管理员审核评论 |
---
## 12. Flyway 与持久化
| ID | 场景 | 预期 |
| :--- | :--- | :--- |
| DB-001 | 空库启动 | `V1`/`V2` 应用成功,`flyway_schema_history` 成功 |
| DB-002 | 重复启动 | 迁移不失败 |
| DB-003 | 实体 CRUD | 插入后按 `sys_*` / `blog_*` 表可读 |
---
## 13. AI 生成用例时的输出模板(强制)
每条用例应包含以下字段(JSON 或表格均可):
```text
id: <本节已定义 ID>
title: <短标题>
layer: <unit|integration|contract|e2e>
role: <anonymous|user|admin>
preconditions: <数据与配置>
steps:
1. ...
2. ...
expected:
http: <状态码>
body: <关键 JSON 路径与值>
side_effects: <数据库/Redis 可选>
trace: <R-* / API-* / SEC-* / BUG-*>
flaky_notes: <可选>
```
---
## 附录 A:当前后端路由一览(扫描自代码,2026-05-18)
| Controller 类 | 前缀 | 方法摘要 |
| :--- | :--- | :--- |
| `AuthController` | `/api/public` | `POST /user/login``POST /admin/login``POST /user/register``GET /captcha``POST /refresh-token` |
| `UserController`(用户) | `/api/app/user` | `GET /me``GET /{id}``POST/DELETE /{id}/follow``POST /logout` |
| `UserController`(管理) | `/api/admin/user` | `GET /list``PUT /{id}/reset-password``PUT /{id}/status` |
| `PublicArticleController` | `/api/public/article` | `GET /list``GET /{id}` |
| `ArticleController` | `/api/app/article` | `POST /``PUT /{id}``GET /mine``PUT /{id}/recycle``PUT /{id}/restore``DELETE /{id}``POST /{id}/like` |
| `AdminArticleController` | `/api/admin/article` | `GET /list``DELETE /{id}` |
| `PublicCategoryController` | `/api/public/category` | `GET /list` |
| `AdminCategoryController` | `/api/admin/category` | `POST /``PUT /{id}``DELETE /{id}` |
| `PublicTagController` | `/api/public/tag` | `GET /list` |
| `AdminTagController` | `/api/admin/tag` | `POST /``name` 请求参数) |
| `CommentController` | `/api/app/comment` | `POST /``DELETE /{id}``GET /article/{articleId}``POST /{id}/like` |
| `AdminCommentController` | `/api/admin/comment` | `GET /list``PUT /{id}/audit``DELETE /{id}` |
**前端契约检查**:须将上述列表与 `jog-frontend/src/api/**/*.js` 中的 `url``method` 逐一比对。
---
## 附录 B:与历史文档的关系
| 文档 | 角色 |
| :--- | :--- |
| `jog-system-requirements.md` | 业务验收与 AC 编号来源 |
| `jog-system-technical-design.md` | 架构与表结构参考;**路径以附录 A 为准** |
| `jog-system-deployment-checklist.md` | 上线人工/半自动巡检清单,可择优改为 E2E |
| `jog-system-bug-list.md` | 历史测试执行与缺陷台账;编写回归时注意已关闭项 |
---
> **文档结束**
> 本文档 V2.0 合并了 `doc` 目录全部文件中与测试相关的信息,并校准当前仓库实现;作为 AI 编写测试用例时的**唯一输入**时,请优先遵循第 13 节用例模板及附录 A 路径,缺失的自动化实现以第 1.2 节「未执行项」为 backlog。