文档索引

测试与构建排障

保留文件名以兼容既有引用。本文提供排查线索,不是允许跳过失败的名单。 工作流以 ../CLAUDE.md 的「验证纪律」为准;不要为了排障先运行一遍所有检查。

怎么用这一页

  1. 保存完整日志和退出码,定位失败文件、错误文案及当时的命令。
  2. 匹配下面的症状后,核对当前配置、生成物、进程或 diff,确认根因;相同文案不保证同因。
  3. 修复已确认的问题后,重跑受影响检查。只有源码、配置或环境变化才重试原失败命令。
  4. 只有对照验证或其他直接证据才能把失败归因于既有代码、环境或他人的 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 一致 不要恢复自动重发用例

维护这一页

保留能指导下一步排查的症状、核对方法和处理方式;脚本修复后同步更新当前入口。 新增条目优先收录重复出现且有明确证据的问题,单次事故与未经验证的归因不写成永久规则。

本页内容来自仓库 docs/known-red.md·在 GitHub 上查看

测试与构建排障 · 聪明山