---
date: 2026-05-11
type: bug
tags: [ci, jest, integration-test, oom, arg-parsing, deploy-uat]
---

# `--runInBand` 误传顶层 → batch by module 被绕过 → L1 OOM 复发

## 起因

PR #325 merge 到 staging 后，deploy-uat workflow 的 `post-deploy-regression` job
失败：

```
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed -
JavaScript heap out of memory
exit status 134
scripts/run-backend-integration.sh: line 49:
  node "${JEST_BIN}" --config "${JEST_CONFIG}" --forceExit --runInBand "$@"
```

OOM 长得跟 issue #260 完全一样——但当时 PR #314 已经做了 batch by module 治根，
理论上不该再炸。

## 根因

`run-backend-integration.sh` 的"是否走 batch 模式"判定：

```bash
# 任一参数包含路径 / 选项 → 单次 jest 透传，保留旧行为
if [[ $# -gt 0 ]]; then
  run_jest_once "$@"
  exit $?
fi
```

判定看 `$#`，不区分"路径"和"选项"。

`deploy-uat.yml::post-deploy-regression` 调用：

```bash
bash scripts/run-backend-integration.sh --force-reset --runInBand
```

执行过程：

1. `$1 == "--force-reset"` 被脚本顶部 `shift` 消化 → `$@` 还剩 `--runInBand`
2. `[[ $# -gt 0 ]]` 判定 true（因为 `--runInBand` 还在）
3. 走"单次 jest 透传"分支 → **绕过 batch by module**
4. 单进程 jest 跑全部 36 suite → ~325s 后 heap 撞 2GB OOM

**两个 bug 叠加**：

- 调用方多写 `--runInBand`（其实脚本内部 `run_jest_once` 本来就传，line 49 显式 `--runInBand`）
- 脚本的"参数即路径"判定不区分 `-`/非 `-` 前缀

`quality-gates.yml::backend-integration` 没踩坑因为它**只**传 `--force-reset`，
PR #314 当时改的也是这条路径——遗漏了 deploy-uat 的旁路。

## 修复

**双层防御**：

1. **deploy-uat.yml**：去掉冗余的 `--runInBand`，加注释禁止再加 jest 选项

   ```bash
   # 禁止再加 --runInBand（或任何 -- 开头 jest 选项）。
   # run-backend-integration.sh 的"无参数 = batch by module"判定看 $#，
   # 任何 jest 选项都会被误判为"用户指定路径"，绕过 batch → OOM。
   (cd testing && bash scripts/run-backend-integration.sh --force-reset)
   ```

2. **run-backend-integration.sh**：参数检测改为只看"路径"（非 `-` 开头）

   ```bash
   HAS_PATH=false
   for arg in "$@"; do
     [[ "$arg" != -* ]] && HAS_PATH=true && break
   done
   if $HAS_PATH; then
     run_jest_once "$@"
     exit $?
   fi
   # 走到这里：参数全是 jest 选项 → 仍走 batch，选项透传给每个 module
   ```

## 教训

**新增 CI 调用方时不要盲目复制旧调用方的参数。**`--runInBand` 在 PR #261 时代是
"防 jest 并发撞库"的关键参数，但 PR #314 把它下沉到 `run_jest_once()` 内部之后，
外层再传就是冗余 + 触发 arg 判定 bug。

**"参数即路径"判定是常见反模式**——任何同时接受 path 和 option 的脚本，参数检测
必须区分 `-` 前缀。

**遗漏的旁路是 CI 漂移的高频原因**。`run-backend-integration.sh` 三个调用方
（quality-gates::backend-integration, deploy-uat::post-deploy-regression, 本地手动），
PR #314 只改了第一个。下次类似改动应 `grep -r "run-backend-integration.sh"` 全仓
找出所有调用方一并同步。

## 关联

- 触发：PR #325 (release 2026-W20 develop→staging) 合并后的 deploy-uat 失败 run #2272
- 同主题 learning：[2026-05-11-jest-batch-by-module.md](2026-05-11-jest-batch-by-module.md)（batch 模式的设计说明）
- 历史：issue #260（OOM 原始报告）、PR #261（NODE_OPTIONS 止血）、PR #314（batch 治根）
- 修复 PR：（本 commit）
