Files
MailUI4Agents/plugins/pi-mail-bridge/test/env-preflight.mjs
JianFeeeee 0b548b8fcf test(pi-bridge): 临时目录满时不再伪装成内存缺陷 —— 前置自检 + ENOSPC 兜底
现场(2026-09-14 实测):`/tmp` 是 tmpfs,被别人占满,`statfsSync` 实读
`bavail*bsize` 只剩 **0.70 MiB**。此时 `npm test` 红一条

    not ok 323 - ★巨大的 message 行不进内存也不影响解析
      error: 'ENOSPC: no space left on device, write'

那条红的**形状指向内存**(用例名里就写着"不进内存",而它恰好是往临时目录写文件的
用例)⇒ 下一个踩到的人会去 `session-scan.mjs` 找一个**不存在**的内存缺陷。

改:
- `lib/tmp-space.mjs`:测量与判据分开,判据是纯函数 `judgeSpace`,喂字节数即可验;
  读不到可用空间(null/NaN)⇒ **不判红**(不知道 ≠ 不对,否则会造出"总在亮"的红灯)。
  **但 0 字节不是"不知道"** —— 第一版把 `<=0` 一并当"没测到",于是 `bavail` 只剩
  712 字节时前置自检放行、紧接着 17 条用例 ENOSPC 全红:前置自检装了等于没装。
  阈值 32 MiB = 实测单条用例最大写入量(`session-scan` 那条写 3×3 MiB 行 ≈ 12 MiB)
  ×2 + 8 MiB 机动,不是总容量的百分比(百分比在这套测试上没有依据)。
- `test/env-preflight.mjs`(名字不带 `.test.`,不被 glob 收进用例):
  `package.json` 的 test 改成先跑它;不足时打印实测/阈值/目录并 **exit 2**
  —— 与 `deploy/redeploy-plugin.sh` 的 `2=环境问题` 同一套约定,看到 2 才知道
  去查机器而不是查代码。文案里明写「这是环境不足,不是断言失败」。
- `session-scan.test.mjs`:兜底翻译 ENOSPC(`node --test 'test/*.test.mjs'` 会绕过
  前置脚本,这一句不管套件怎么被调起来都生效)—— 这正是治那条误导的关键。
- `test/env-guard.test.mjs`:8 条自证 —— 纯函数两头 + 边界(≥阈值算够、<阈值不够)
  + 0 字节必须红 + 读不到不判红 + 阈值有据 + 端到端 exit 2 且文案对得上。
  端到端那条**不假设本机 /tmp 仍然满**:先自己量一次,够用就跳过并说明原因,
  免得它退化成一条"总在亮"或"总在绿"的假判据。

顺带修 `deploy/check-deploy-drift.mjs` 两处同源问题:
- `selfCheck()` 要在临时目录造两棵小树,`/tmp` 满时抛 ENOSPC —— 而它是**未捕获异常**,
  堆栈指向本文件,看起来像检查器坏了。翻译成说得清的错并让 main() 报 2。
- 新增判据 ⑥「工作区干净」—— **只提示,不参与 exit code**。判据 ① 比的是
  「仓库工作区→快照」这一跳,覆盖不到「HEAD→工作区」那一跳(实证:一行未提交的
  死代码被 17:20 的快照带进生产,而 ① 报的是"逐字节一致")。做成红灯就是一条
  总在亮的判据(本文件头自己骂过的病),所以只说、不判。

验证:`npm test` 453/453(新增 8 条);`npm test` 在 /tmp 满时 exit 2 且不再跑用例;
`node deploy/check-deploy-drift.mjs --self-check` 17 条全过(含 ⑥ 的三条正反面)。
2026-09-14 19:29:44 +08:00

69 lines
2.6 KiB
JavaScript
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.

#!/usr/bin/env node
/**
* 测试前置自检:**临时目录还有空间吗**。
*
* 名字**不带 `.test.`** —— 所以它不会被 `node --test 'test/*.test.mjs'` 收进去
* 当用例跑;它由 `package.json` 的 `test` 脚本**先**跑一次。
*
* # 它在挡什么
*
* 2026-09-14 实测:`/tmp` 是 tmpfs,被别的项目/别的 agent 的东西占满,
* `bavail` 只剩 0.70 MiB。此时 `npm test` 会红一条
* `★巨大的 message 行不进内存也不影响解析`(`ENOSPC`)—— 那条红**长得像内存缺陷**,
* 而真相是环境不足。这个脚本把"环境不足"在**跑测试之前**就大声说出来。
*
* # 退出码约定(与 `deploy/redeploy-plugin.sh` 同一套)
*
* 0 = 环境够用,继续跑测试
* 2 = **环境问题**(不是断言失败)
*
* 写清 `2` 的意义很关键:CI 或人看到 `1` 会去查代码,看到 `2` 才知道去查机器。
*/
import { statfsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { judgeSpace, MIN_FREE_BYTES } from '../lib/tmp-space.mjs';
/** 测量:进程**实际能写**的字节数。读不到返回 null(交给判据放行)。 */
function measureAvailBytes(dir) {
try {
if (typeof statfsSync !== 'function') return null; // Node < 18.15
const s = statfsSync(dir);
if (!s || !s.bsize) return null;
// 用 `bavail`(非特权进程可用的块数),**不是** `bfree`(含 root 保留块):
// 这里回答的是"我写不写得进去",不是"机器上空闲多少"。
return s.bavail * s.bsize;
} catch {
return null; // 不知道 ≠ 不对
}
}
const dir = tmpdir();
const availBytes = measureAvailBytes(dir);
const verdict = judgeSpace({ availBytes });
if (verdict.ok) {
console.log(`[env-preflight] 临时目录 ${dir}:${verdict.note}`);
process.exit(0);
}
console.error(`
[env-preflight] 环境不足:临时目录写不下,**这不是断言失败**。
目录 ${dir}
可用 ${(availBytes / 1048576).toFixed(1)} MiB
需要 ${(MIN_FREE_BYTES / 1048576).toFixed(1)} MiB
判定 ${verdict.note}
测试用例会往临时目录写十几 MiB 的文件。空间不够时它报的是 ENOSPC,
而那看起来像代码缺陷 —— 曾经就是这么误导过一次。
药方(任选其一):
TMPDIR=<有空间的目录> npm test # 换临时目录,最省事
清理 ${dir} 下不属于你的东西之前先问主人 # 别误删别人的缓存
退出码 2 = 环境问题(与 deploy/redeploy-plugin.sh 的 2 同义),不是代码问题。
`);
process.exit(2);