Skip to content

plugin-audit 的 zh-CN 本地化用例在全仓并行下必然超时:冷 import('@objectstack/core') 撞 20s testTimeout #4186

Description

@os-zhuang

发现于 ADR-0116 的收尾验证(#4131 / PR #4163#4185)。与那些改动无关,是一个独立的测试基础设施问题。

现象

packages/plugins/plugin-audit/src/audit-writers.test.ts 里的

× localizes verb + object label to the workspace locale (zh-CN)   20094ms
  • 单独跑 pnpm --filter @objectstack/plugin-audit test:4 文件 / 46 用例全过。
  • 全仓 pnpm test 下必失败,我这里连续两次复现(20347ms / 20094ms),都是撞 20s 的 testTimeout,不是断言失败。

机制(已定位)

罪魁是 helper 里的动态导入

// audit-writers.test.ts:544
async function makeI18n() {
    const { createMemoryI18n } = await import('@objectstack/core');
    const { AuditTranslations } = await import('./translations/index.js');
    ...
}

关键证据是同文件内相邻用例的耗时对比

用例 耗时
localizes verb + object label …(zh-CN) ← 全文件第一个调用 makeI18n() 20094ms(超时)
localizes the generic update fallback ← 紧随其后,调用同一个 helper 104ms
falls back to the object def label, then English… 1ms

也就是说付出代价的只有第一次冷加载 @objectstack/core(一个 110KB+ 的 barrel,带大量传递依赖):模块解析 + 转换在 turbo 把十几个包的 vitest worker 同时压满时超过 20 秒;之后命中缓存就回到毫秒级。单独跑时机器不拥塞,冷加载够快,所以永远看不到。

为什么值得修而不是忍

这个用例目前把"全仓 pnpm test 是否绿"变成了一个取决于机器负载的问题——本地看到的红灯与代码质量无关,会持续消耗每个跑全量测试的人的注意力(我这次就为它多花了两轮排查)。而且它是"沉默的负载探测器":机器越忙越红,最容易在最需要可信信号的时候骗人。

可能的修法(未定,列给接手的人)

  1. 把动态导入提到模块顶层的静态 import——最直接。createMemoryI18nAuditTranslations 都没有需要延迟加载的理由(不是可选依赖、不是循环依赖规避——这点需要确认后再动)。
  2. 给这个文件或这个用例单独放宽 testTimeout——治标,且把负载敏感性留在原地。
  3. beforeAll 里预热一次导入,让冷加载成本不计入任一用例的超时预算。

倾向 1,但要先确认当初写成 await import() 是不是在规避某个循环依赖(plugin-auditcore 方向上应该不存在,但值得查一下再改)。

复现

pnpm test          # 全仓并行 → plugin-audit 必红
pnpm --filter @objectstack/plugin-audit test   # 单独 → 46/46 绿

注意:pnpm test 2>&1 | grep ... 这类写法拿到的退出码是管道末端命令的,会把这次失败显示成"成功"——排查时请直接取 pnpm test 自身的退出码(我第一轮就是这么误判为全绿的)。

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions