测试与构建排障
保留文件名以兼容既有引用。本文提供排查线索,不是允许跳过失败的名单。
工作流以 ../CLAUDE.md 的「验证纪律」为准;不要为了排障先运行一遍所有检查。
怎么用这一页
- 保存完整日志和退出码,定位失败文件、错误文案及当时的命令。
- 匹配下面的症状后,核对当前配置、生成物、进程或 diff,确认根因;相同文案不保证同因。
- 修复已确认的问题后,重跑受影响检查。只有源码、配置或环境变化才重试原失败命令。
- 只有对照验证或其他直接证据才能把失败归因于既有代码、环境或他人的 WIP。 暂时无法确认时报告“原因未确认”,不要写成“已知失败”而跳过。
1. 已经删掉的命令:npm run lint
package.json 当前没有 lint 脚本,不把它作为验证门禁。需要类型检查用 npm run typecheck。
若任务是引入 lint,则单独安装依赖和配置;不要因旧指引而调用不存在的命令。
2. Prisma client 缺失或陈旧
- 症状:
@prisma/client没有导出模型、PrismaClient缺少模型,或 schema 默认值与测试写入结果不同。 - 核对:是否刚运行
npm ci或修改 schema;postinstall当前只准备静态资源,不生成 client。 - 处理:确认没有其他进程占用生成物后,执行
npx prisma generate,再重跑原失败检查。 - 生成后仍失败,继续核对 schema、导入与数据库,不把所有模型相关错误都归为生成物陈旧。
3. .next/types 陈旧导致的类型错误
- 症状:
npx tsc --noEmit报.next/types中的路由文件缺失或引用已删除页面。 - 只查源码类型用
npm run typecheck,不为此先构建。 - 需要完整构建用
npm run build;scripts/build.mjs已自动清理.next/types。 - 若脚本报告清理失败,检查具体文件与进程占用;不要直接删整个
.next,也不要循环重跑 build。
4. Prisma 生成报 EPERM ... rename
- 检查目标路径、访问权限及是否有 dev / start / generate 进程占用 Prisma client。
- 不群杀 node,不仅凭端口号就停止进程。先确认命令行、用途和归属;只停止本任务启动 或明确获授权停止的 PID。他人的服务应协调等待,或使用独立工作区。
- 解除已确认的占用后重新 generate,并重跑受影响检查。
5. webServer was not able to start(e2e)
先读 webServer 日志,再检查:
- 端口是否被占用,是否已有另一个 Playwright / 测试服务在运行。
.next和 realtime 构建产物是否存在、是否对应当前源码。npm run e2e不自动 build; 需要新构建时先npm run build,或使用npm run e2e:ci -- <spec>。- 测试库、种子、环境变量及启动命令是否正确。
只处理确认的原因;不要无条件“杀进程 + build”。页面探针只连接已有服务, 不负责启动或初始化测试环境,Playwright 运行完成也不代表 webServer 会继续保留。
6. 测试超时
vitest.config.ts 当前将 testTimeout / hookTimeout 设为 30 秒。
若报 5000ms,核对所加载的 config、用例局部 timeout 和调用参数;不能直接断言没有加载配置。
单独跑通过、全量失败可能与资源争用或共享状态有关,也可能暴露实际缺陷。
只有定位原因后才调整超时,不以加大 timeout 代替检查挂起、死锁或缺少清理。
7. The table main.users does not exist / no such table:
- 跨多个文件缺表时,检查测试库 URL、schema / 迁移、模板库和其他 vitest 进程。
tests/global-setup.ts已采用哈希复用与临时文件原子发布,旧的并发复现不能证明当前错误同因。- 同一工作区仍不并发跑多套测试,避免共享目录与资源争用;单个 Vitest 内部的文件并行由配置管理。
- 清理或重建前确认目标仅为本任务的可丢弃测试数据,不碰他人活动库或真实数据。
8. 他人未完成的 WIP
git status 中有未提交文件,只能说明工作区不干净,不能证明失败由这些文件造成。
- 先核对 diff 与失败路径;不修复、恢复、stash 或摘掉他人的改动来“试一下”。
- 需要对照时用独立 worktree:在基线与包含自己改动的版本上验证同一失败场景。 记录验证的提交、补丁和结果,避免实际只测了未包含自己代码的 HEAD。
- 明确验证出差异后再报告归因;无法完成对照时说明限制,不将猜测记成既有失败。
9. 文档引用或目录守卫失败
docs-xref:核对路径、章节标题及引用格式;来源可能是本次修改,也可能是其他工作区变化。docs-catalog:新增 / 改名 / 删除docs/下文档时,同步src/lib/docs-catalog.ts, 条目 title 与文档 H1 一致;只改正文不必增加条目。- 用当前 diff 确认归属后修复,再运行相关守卫,不凭错误名称直接认定是本次引入。
10. 既有失败的判定
历史上某次全绿或失败都不代表当前状态。没有仅凭文件名就可跳过的失败名单。 报告检查命令、退出码、已确认原因和未覆盖范围;旧日志引用须注明对应提交与环境。 不为获取“当前全量耗时”而额外跑全量,数字如需保留应放在带日期的测量记录中。
11. 全量 e2e 的已知非产品失败(2026-11-09 实测)
2026-11-09 同一构建连续三轮全量:672 passed / 17 failed → 686 / 3 → 689 / 0 (desktop 全量 + mobile 响应式子集,16–18 分钟)。0 failed 是当前基线,但单库长跑仍可能 冒出单条顺序/水合 flake;先按下面方向核对。这不是跳过名单,判定方法一律是「把失败 spec 单独跑一遍」。
npx playwright test tests/e2e/<失败spec> --project=desktop # 或 --project=mobile
| 症状 | 已确认原因 | 处置 |
|---|---|---|
多条 发文失败:{"code":429,..."今日发布数量已达上限(20篇)"} |
已修:全量共用一个种子号,累计超过 BLOG_DAILY_LIMIT = 20。测试服务在 playwright.config.ts 同时给 E2E_TEST=1 与 BLOG_DAILY_LIMIT=500,且只在 DATABASE_URL 指向 tests/.tmp/ 时生效(resolveBlogDailyLimit);生产任何 env 组合都固定 20 |
再遇 429 先确认测试服务 env 生效;不要改生产默认值 |
ios-app 的「返回上页」在 /blog 找不到预期卡片 |
已修:用例此前依赖种子文章在 /blog 第一页;全量发文量大了会把它挤到下一页。现在发一篇自己的文章、用 ?scope=mine 锚定 |
新用例别依赖「种子数据一定在列表首页」 |
注册页 / mention-picker 等「单跑绿、全量红」 |
已修:goto 后立刻 fill 受控输入框,React 水合前的值会被首次渲染重置回空(注册页提交空邀请码、@ 提示不出现)。注册用 waitForHydrated(page, selector),评论/讨论用 waitCommentSectionReady |
goto 后立刻输入前先等水合;不要用 waitForTimeout 猜 |
其它分散的 expect(...).toBeVisible() / 超时 |
单库串行长跑的顺序/状态依赖;reactive-ssr-cold 在全量时进程已预热(文件头写明只验「关 JS 仍可读」,真冷启动要单跑该文件) |
单跑复核;确认顺序依赖后按 docs/testing.md §4 收紧用例,而不是加 retries |
chat-image.spec.ts 的「网络失败自动重试」 |
已在 ee8efe1d 修复(对齐 R3-373「失败不自动重发」);若再出现先确认构建与 HEAD 一致 |
不要恢复自动重发用例 |
维护这一页
保留能指导下一步排查的症状、核对方法和处理方式;脚本修复后同步更新当前入口。 新增条目优先收录重复出现且有明确证据的问题,单次事故与未经验证的归因不写成永久规则。