# ERR-20260520-003: openid-client v6 纯 ESM 让所有 integration test 炸（全 15 模块 fail），不是只有 SSO test

## 症状

PR #475 quality-gates / backend-integration check 跑 13m35s 后 fail（commit 6941dffb）。
**全 15 个 backend integration 模块（adp-sync / agent / ai-usage / audit / dingtalk /
form-management / iam-admin / iam-governance / internal-app-platform / meeting-attendance /
organization / platform-master / robot-manager / site-attendance / system）全部 fail**。

最初一个模块（system）log 显示：

```
SyntaxError: Cannot use import statement outside a module
  at backend/node_modules/openid-client/build/index.js:1
    import * as oauth from 'oauth4webapi';
    ^^^^^^
  at backend/src/modules/organization/auth/sso/sso-oidc-client.service.ts:2
    import * as client from 'openid-client';
```

## 根因

openid-client v6 是**纯 ESM**（`"type": "module"` + `import * as oauth from 'oauth4webapi'`），
ts-jest 默认 transformIgnorePatterns 排除 `node_modules`，结果 CJS 加载真模块炸。

**陷阱**：本地 `testing/backend/integration/organization/sso.api.test.ts` 顶部加了
`jest.mock('openid-client', () => ({ ... }))` 桩，所以**只跑 SSO test 本地通过**。
agent 实施时只验证了 SSO test 自己，**没意识到其它 14 个 integration test 都会间接
import `app.module` → `auth.module` → `auth.controller` → `sso-oidc-client.service` →
触发 openid-client 真加载** → 全炸。

## 修法（已采用 B：jest config moduleNameMapper）

### A. （否决）每个 integration test file 顶部加 `jest.mock('openid-client', ...)`

15 个测试模块各加一遍 boilerplate，丑且易遗漏。

### B. （采用）jest config moduleNameMapper 全局替换

新建 `testing/backend/helpers/openid-client-stub.ts`（仿现有 `otplib-stub.ts` 模式），
导出 noop 函数 + 空 Configuration class。

`testing/config/jest-backend-integration.json` 加一行：

```json
"moduleNameMapper": {
  ...
  "^openid-client$": "<rootDir>/backend/helpers/openid-client-stub.ts",
  ...
}
```

效果：任何 integration test 加载 openid-client → 拿到 stub → require 通过；
SsoOidcClientService 在 SSO test 里被 `overrideProvider` 替换，stub 内容不被实调；
production / dev 启动仍走真模块。

### C. （备选）transformIgnorePatterns 加 openid-client + oauth4webapi + jose

让 ts-jest 也转这几个包。开销大、性能差、可能 transform 失败（ESM-only 包没 CJS exports
fallback）。不如 stub 干净。

## 防御

新引入**纯 ESM 包**（package.json `"type": "module"`）时：

1. 检查依赖：`npm view <pkg> dependencies` 看是否递归 ESM-only（如 openid-client 拉
   oauth4webapi / jose）
2. **不仅跑 import 此包的 test 本身**，要跑**任何会 import `app.module` 的 integration
   test**——因为 NestJS 全应用图加载会触发 ESM 包真 require
3. 修法默认走 jest stub（B）—— 测试 boilerplate 最小、production 不受影响

## 关联

- 工单：PR #475 / issue #334 v2.4 SSO 上线
- 类似坑：`.learnings/2026-04-13-ts-node-transpile-only-deprecation.md`（ts-node ESM 兼容）
- 修复 commit：跟本 learning 同 commit
