# 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 = [] ========================= 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=/.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 步**已实测**。