pbls/README.md
agent.develop 1ce57f3716 [M5a-DISC.2-A] 交付件卫生清理 + build.sh 7b 实测证据补齐
- git rm scripts/patch_build_7b.py(一次性构建期补丁脚本,移出应用仓;证据附件保留在
  projects/pbls/docs/02-develop/patches/m5a-disc2-patch_build_7b.py)
- README §期望结果:场景 1 / 场景 2 分别对应各自实测日志,不再以场景 2 日志代证场景 1,
  并如实记录场景 1 的受控截取执行方式与两处既有问题(内网源不可达、DDL 方言门禁命中注释)
2026-09-22 22:17:30 +08:00

98 lines
6.4 KiB
Markdown
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.

# pbls
PBL Agent OS 验证原型应用(唯一入口 `app/pbls.py`,端口以
`projects/pbls/env/test.json` 为唯一事实源)。业务模块见机构 `modules/pbl_*`。
## 测试依赖(DISC.2)
**结论:pytest 已显式声明,不再依赖临时注入 `PYTHONPATH`。** 宿主 `python3` 上没有 pytest
(`python3 -m pytest` → `No module named pytest`),且本机宿主 user site 是只读文件系统
(`pip install --user` 报 `[Errno 30] Read-only file system`),所以测试依赖的落点是 **venv**。
### 新增/相关文件清单
| 路径 | 层级 | 职责 |
|---|---|---|
| `apps/pbls/requirements-test.txt` | 应用级 | 测试前置依赖清单(`pytest>=7.0,<10`),由 `build.sh` **第 7b 步**装进应用 venv |
| `apps/pbls/build.sh` 第 7b 步 | 应用级 | `pip install -r requirements-test.txt` + `import pytest` 自证;`PBL_SKIP_TEST_DEPS=1` 可跳过 |
| `modules/pbl_evidence/requirements-dev.txt` | 模块级 | 同一版本区间的模块侧清单(不装本包也能跑离线单测的最短路径) |
| `modules/pbl_evidence/scripts/setup_test_env.sh` | 模块级 | 建 `.venv-test` 并安装 `requirements-dev.txt`(含仓库卫生门禁) |
| `modules/pbl_evidence/scripts/activate_test_env.sh` | 模块级 | 打印激活/直接调用测试 venv 解释器的方式 |
| `modules/pbl_evidence/scripts/run_tests.sh` | 模块级 | 一条命令跑测试:无 pytest 时自动引导,再执行 pytest |
| `modules/pbl_evidence/README.md` §Running tests | 文档 | 排障入口 + `PBL_EVIDENCE_TEST_DB` 开关用法 |
### 为什么是两份清单(应用级 vs 模块级)
- `requirements-test.txt`(应用级)服务**整应用**的测试环境:跑 `build.sh` 后 venv 内即有 pytest,
任何 `modules/pbl_*/tests/` 都能用同一个解释器执行。
- `requirements-dev.txt`(模块级)服务**只测本模块**的 CI/离线场景:不安装 pbls 应用、不装本包
(`pip install -e .`)也能一条命令装好依赖。
- 两份**版本区间同源**(`>=7.0,<10`),避免"应用装了 9.x、模块 CI 装了 7.x"导致用例语义漂移。
- 两者都刻意与运行时清单 `requirements.txt` 分离:pytest 不进生产镜像,也不进
`build.sh` 第 5 步的运行时依赖核验(该步只核验 ahserver/sqlor/apppublic/appbase/rbac/PyMySQL)。
### 推荐调用顺序
```bash
# 场景 1:完整应用环境(部署/联调机)
bash apps/pbls/build.sh # 第 7 步 venv → 7b 测试依赖 → 8 装模块
source apps/pbls/venv/bin/activate
python3 -m pytest modules/pbl_evidence/tests/test_m5a_idempotency.py -v
# 场景 2:只跑 pbl_evidence 单测(测试机/CI,最短路径)
bash modules/pbl_evidence/scripts/run_tests.sh # 无 pytest 时自动引导 .venv-test
# 场景 3:只要环境不要跑(手工用 venv 解释器)
bash modules/pbl_evidence/scripts/setup_test_env.sh
bash modules/pbl_evidence/scripts/activate_test_env.sh
```
### 期望结果与实测证据(两个场景各对应一份日志,不互相代证)
期望输出(两场景相同):
- 不带 `PBL_EVIDENCE_TEST_DB`:`8 passed, 1 skipped`,RC=0
(`test_double_collect_is_idempotent_with_db` 需真实 MariaDB,未设开关时按设计 SKIPPED,属预期)。
- 带 `PBL_EVIDENCE_TEST_DB`(用法见 `modules/pbl_evidence/README.md` §Running tests):`9 passed`。
- 禁止为凑通过修改测试断言逻辑。
| 场景 | 解释器 | 实测日志 | 状态 |
|---|---|---|---|
| **场景 1**(`build.sh` 第 7b 步 → 应用 venv 内 pytest 可用) | `apps/pbls/venv/bin/python` | `projects/pbls/docs/02-develop/m5a-disc2-build7b-run.log` | **已实测** |
| **场景 2**(模块级引导 `run_tests.sh` / `.venv-test`) | `modules/pbl_evidence/.venv-test/bin/python` | `projects/pbls/docs/02-develop/m5a-disc2-pytest-run.log` | **已实测** |
场景 1 日志中的关键真实输出行(由 shell `tee` 捕获,非手写):
```
[build.sh][...] 7b 自证通过: pytest 9.1.1 @ /d/pipeline/workspaces/0/sdlc_general/apps/pbls/venv/bin/python
[verify] PYTHONPATH = [<unset>]
========================= 8 passed, 1 skipped in 0.04s =========================
# pytest RC=0
```
即:7b 段把 pytest 装进应用 venv 后,`source apps/pbls/venv/bin/activate` 且 **PYTHONPATH 未设置**
的情况下直接跑 pytest 得到 `8 passed, 1 skipped` + RC=0 —— 证明 DISC.2 的原始痛点(靠临时注入
`PYTHONPATH=<ws>/.pylibs` 才跑得通)已被消除。
#### 场景 1 的执行方式(受控截取,未强跑全流程)与已知限制
`build.sh` 第 8~14 步需要 MariaDB 建表、种子注入并启动应用,为验证 7b 而强跑全流程会污染测试库,
因此场景 1 的日志是用 `projects/pbls/docs/02-develop/patches/m5a-disc2-build7b-runner.sh`
**按标记行从 `build.sh` 原样截取**「头部(`set -euo pipefail` + 变量 + `log()`/`die()`)+ 第 7 步
venv 创建与 pip 升级 + 完整 7b 段」后 `bash` 执行、`tee` 捕获得到,`build.sh` 本体一个字节未改
(日志末尾附该文件 md5 以供核对)。日志中同时打印了实际执行的截取脚本(`cat -n`)供逐行复核。
两处如实说明(均属本任务范围外的既有问题,已作为发现项上报 PM,未在本任务内改动):
1. 截取时**跳过**了第 7 步的 `pip install -r requirements.txt`:该步指向机构内网 PyPI 源
`http://10.20.30.41:8081`,本工作空间不可达(`curl` rc=124),会先于 7b 失败退出。
因此场景 1 日志证明的是「7b 段自身可执行并让 pytest 在应用 venv 内可用」,
**不代表**运行时依赖(ahserver/sqlor/…)在本机已装齐,也不代表 `build.sh` 全流程在本机跑通。
2. 首轮曾按「截取第 1 行起的全部前缀」执行,`build.sh` 第 4 步 DDL 方言门禁 die:
`grep -Eiq '...|REFERENCES|ENUM\(|TIMESTAMP'` 命中了 `scripts/ddl/pbls_tables.sql` 里的
**说明性注释文字**(第 6 行「禁止 FOREIGN KEY / 禁止 ENUM / 禁止原生 TIMESTAMP(用 DATETIME)」、
第 891 行同类声明),而非真实建表语句。该门禁与 DDL 注释的冲突是既有问题(与 DISC.2 无关),
修它需要改第 4 步 grep 或改 DDL 注释,超出本任务范围,故本次截取范围从第 7 步起。
结论:`build.sh` 第 0~6 步门禁本次**未实测**(静态核验 + 首轮部分实跑:第 0~3 步与 6.1/6.2
真实通过,第 4 步因上述注释冲突 die),第 7b 步**已实测**。