test(e2e): Phase 23B — rebuild E2E infrastructure; fix 6 real prod defects it exposed
Phase 23 closed as PARTIAL: fake coverage was gone, but the E2E layer itself
was still empty — no conftest.py anywhere, ~15 fixtures that never existed,
6 files crashing at import on os.environ["E2E_PASSWORD"], and CI never ran E2E.
STATUS's "10 E2E files / ~80+ cases ✅" described files that had never executed.
Infrastructure delivered:
- tests/e2e/conftest.py: ApiClient (trust_env=False — a host http_proxy turns
localhost calls into 502), operator_token(username) / new_user / admin_token
fixtures, and a session probe that calls pytest.exit(4) when the stack is
unreachable. NOT skip: skip would launder "did not run" into "passed".
- pytest.ini, tests/e2e/requirements.txt, and tests/e2e/sql/
seed_e2e_operators.sql — migrations contain NO operator seed at all, so a
fresh database has zero logins; the seed creates 6 e2e accounts with role
bindings, idempotent, password injected via `psql -v pwd`.
- CI job `e2e`: compose up -> poll /health/live (timeout = fail) -> seed ->
pytest -> always teardown. Gate D-23-D is now closed.
- 8 E2E files migrated off import-time env reads; tautological assertions
(`x == t or not x`), a counselor token standing in for content_editor, and a
bare `status_code == 200` claiming to prove INV-020 were replaced with real
permission/behavior assertions.
Six real production defects found only by running against real PostgreSQL
(H2 has zero coverage for all of them):
- D-23B-A (Critical): stale V042__add_appointment_operator_register_permission.sql
left in target/classes; src had replaced it with V042__add_service_location_
announcements.sql. Packing without clean put two V042 in the jar -> Flyway
"Found more than one migration with version 042" -> app would not start.
Guard added: testBuildOutputHasNoDuplicateMigrationVersions scans build output
(src is clean, so only this catches it).
- D-23B-B (High): non-UUID path variable reached the DB -> 500 + leaked
`invalid input syntax for type uuid`. Now rejected at the controller.
- D-23B-C (High): 6 E2E files raised KeyError at import -> never collected.
- D-23B-E (Critical): permissions = 48, role_permissions for super_admin = 18,
matchmaker = 0. V015's "grant all to super_admin" was a point-in-time snapshot;
the 30 permissions added later were never granted. On a fresh stack an admin
cannot create a service station and matchmakers can do nothing. V044 restores
super_admin to its documented definition (not an expansion); the matchmaker
permission set needs a product decision and is left as a residual.
- D-23B-I (Medium): IllegalArgumentException (e.g. invalid activity type) was
unmapped -> 500. Now BAD_REQUEST.
- D-23B-J (High): `operator does not exist: uuid = character varying` — Java
ids are String, columns are uuid, PostgreSQL does not implicitly cast in
comparisons. Fixed at the source with JDBC `stringtype=unspecified` (docker
profile); production datasource comes from deploy env and needs the same.
One judgement retracted: an initial read suggested service_appointment.register
was missing. It is not — the endpoint uses service.assisted_registration, seeded
in V029, and the code deliberately avoids a duplicate permission. Nothing was
changed on that basis.
Verification: 33 E2E passed against a live stack; unreachable stack and a
missing operator password both exit 4 (never skip/pass); backend regression
656 / 0 failures / 0 errors / 3 skipped (baseline 655, +1 guard, negatively
validated by injecting a duplicate V042 -> exactly one test turned red).
This commit is contained in:
@@ -180,7 +180,8 @@
|
||||
| Phase 20 Analytics 数据统计与管理驾驶舱复验 | 已完成(2026-09-17) | 见 `Phase-20-V11-Recovery-Completion-Report-2026-09-17.md`;修复 **20 处真实缺陷**(**D1 实名人数用 `profile_status != 'rejected'` 冒充 → 把"资料还在填"算成已实名**;**D3 Chat Started 直接数会话行数 → 系统通知计入**;**D4 Match 不分来源 → 10 对互选被当 10 对成功引用**;**D5 活动漏斗全部硬编码 0 + 一行把"用户数"当"活动数"的死代码**;**D6 Banner 曝光/点击硬编码 0**;**D12 系统性软删缺陷:`BaseEntity.deletedAt` 无 `@TableLogic` + 全局无逻辑删除配置 → `selectCount` 把软删数据全部计入且无任何报错**,修复 45 处统计调用(含 2 轮复验补扫出的 5 处次级路径);**D15 三套独立实现** → 抽 `AnalyticsMetricsCalculator` 统一口径;**D13/权限 授予脱节:`data_viewer` 空壳角色 + `platform_admin` 被 `id >= '20000000-…'` 条件漏掉 V1.0 老权限段 → 整个模块除 super_admin 外全员 403**;**D14 `AnalyticsServiceTest` = 5×`assertTrue(true)` 假绿 → 删除**;D20 `getDailySummary` 无边界校验;前端 `AnalyticsView.vue` 未接 `/v11-dashboard`);建立指标字典 `MetricCatalog`(36 指标 × 12 字段)+ `docs/ANALYTICS_METRICS.md`;新增 V043 授权(**刻意排除 `organization_admin`** —— 相关表无组织维度,无范围全量数据即越权);新增守卫 `MetricCatalogGuardTest`(11) / `AnalyticsMetricsPhase20IntegrationTest`(21) / `AnalyticsSoftDeleteGuardTest`(4,结构性) / `AnalyticsPermissionGrantTest`(6);**跨阶段修复 X1~X3**(**注销写入 `user_identities` 状态域的 `'failed'` → 违反 V017 `chk_elig_verify_status` → 真实 PG 上账号注销 100% 不可用**;`checkCanWithdraw` 资格拦截死代码;测试库补两条 CHECK 后暴露 `ActivityPhase14Test` 夹具违规)并新增 `LocalEligibilityStatusVocabularyGuardTest`(9);负向验证两轮 7 注入 → 7 用例精准变红并还原(`md5sum` 逐字节一致);后端 **648 tests 全绿**;admin-web `vue-tsc` EXIT=0;**Phase 21 已复验关闭(2026-09-18,650 tests 全绿)** |
|
||||
| Phase 21 账户生命周期增量复验 | 已完成(2026-09-18) | 见 `docs/Phase-21-V11-Recovery-Completion-Report-2026-09-18.md`;KEEP-VERIFY 复验发现 STATUS 标✅ 但含 **6 处真实缺陷 + 假绿测试**:① `AuthService.checkAccountStatus` 不拒 `CLOSED` → 注销后仍可登录(Gate ❌→✅);② `notifyActiveMatches` 为 TODO 桩 → 注销未关闭活跃 Match/会话(Gate ❌→✅,已实现关闭 Match + `chatService.closeConversationForMatch`);③ `cancelPendingActivityRegistrations` 为 TODO 桩 → 未来活动报名未关闭(Gate ❌→✅);④ `anonymizeServiceHistory` 已实现却**从未被 `closeAccount` 调用(死代码)** → 个人/咨询正文暴露(Gate ❌→✅,已接线 + 脱敏身份证占位符 + `counseling_followups` 正文);⑤ `closeAccount` 在 blockers 处硬抛异常使 `cancelXxx` 永远不可达(死代码矛盾 → 信息性日志 + 继续清理);⑥ 删除 7×`assertTrue(true)` 假绿 → 替换为 11 用例真实 DB 集成测试 + 3 用例登录守卫;跨模块防线(推荐引擎/匹配资格/Capability 已拒非 NORMAL)确认有效;后端 **650 tests 全绿**(起点 648,净 +2);**Phase 22 已复验关闭(2026-09-18,657 tests 全绿)** |
|
||||
| Phase 22 部署运维配置扩展(Feature Flag)复验 | 已完成(2026-09-18) | 见 `docs/Phase-22-V11-Recovery-Completion-Report-2026-09-18.md`;KEEP-VERIFY 复验发现 STATUS 标✅ 但含 **2 处真实代码缺陷 + 2 处文档夸大**:① D-22-A(High)`FeatureFlagController.updateFlag` 仅支持布尔 flag(反射 `getMethod("set"+cap, boolean.class)`)→ 7 个非布尔 flag 经 API 更新必失败,已改为按字段类型分派 boolean/int/String;② D-22-B(Medium)`body.getOrDefault("value", false)` 缺 value 静默置 false + 非布尔转型崩,已改为缺 value/类型错→400 且不改原值;T01–T05 模块开关(`isCounselingEnabled` 等)全仓无业务调用点 → 死配置(暴露未 gate,遗留交各模块 Phase);T06 上传策略实测已在 `LocalEligibilityService`+`PhotoService` 真强制 ✅;T07 私有桶零引用 → 死配置(遗留);yml 无 `features:` 块、默认值实为 Java 字段默认(文档纠正);新增 `FeatureFlagControllerTest`(7 用例)+ 负向验证(仅布尔缺陷版恰 2 用例变红);后端 **657 tests 全绿**(起点 650,净 +7);**Phase 23 已复验(2026-09-18,部分关闭)** |
|
||||
| Phase 23 可信系统测试重建(REFACTOR)复验 | ⚠️ 部分关闭(2026-09-18) | 见 `docs/Phase-23-V11-Recovery-Completion-Report-2026-09-18.md`;KEEP-VERIFY 复验发现 STATUS 标✅ 但含 **5 处真实缺陷**:① **D-23-A(Critical)** `V11InvariantsTest` 原 14 个用例全为常量字符串断言 —— INV-016「Banner 目标类型」实际断言**文章状态**常量(跑错域)、INV-017「首页 8 入口」是**对自己刚声明的字面量数组做 `length==4` 的重言式**(从未调用首页服务)、INV-018/019/020 用预约/文章/账号状态常量冒充权限与数据范围验证;已重写为 12 个用例,含 **4 个真实服务行为守卫**(INV-013 走真实 `CounselingService.getDetail` 断言 Matchmaker 读到 `userRequestSummary==null` 并以 assigned Counselor 能读正文作反证;INV-016 反射读生产 `ALLOWED_TARGET_TYPES` 断言不含外链;INV-017 真实 `getHome` 断言 8 入口编码与顺序固定;INV-019 断言 `content_editor` 读到 null)+ `assertBehaviorTestExists` 用 `Class.forName` 把「行为验证见 XxxTest」变成可执行契约;② **D-23-B(High)** `tests/e2e/` **44 个 `pass` 空壳**(4 个文件 100% 为空壳,另 2 个混合),违反 Gate「不允许 skip/pass 空壳」→ 已移除并改为诚实占位(保留完整场景清单);③ **D-23-C(High)** pytest E2E 套件**整体不可执行**:全仓无 `conftest.py`,约 15 个 fixture(`api_client`/`verified_user`/`banner_factory`…)不存在,唯一可执行的 `run_v11_tests.py`(16 用例)需活栈 + `E2E_PASSWORD`;④ **D-23-D(High,遗留未修)** CI 未串联 E2E(计划明确要求)→ 需先决定 CI 活栈供给方式,未实施;⑤ **D-23-E(Medium)** CI `migration` job 注释称「验证 SQL 语法」实为只查文件非空(真实语法校验在 `MigrationValidationTest`,随 backend job 跑)→ 改为真实序列校验(命名/非空/版本号唯一/无缺号,43 个文件 1~43 连续唯一)。**新增闸门**:`scripts/check-no-empty-tests.py`(AST 扫描阻断 `pass` 空壳与无条件 `skip`,已双向验证)+ CI `test-shell-guard` job。**证据**:负向注入 3 处生产缺陷 → 恰好 `inv013`/`inv016`/`inv017` 3 个用例精准变红、其余 9 绿、零无关失败,`md5sum -c` 还原一致;`655 tests / 通过 652 / 失败 0 / 错误 0 / 跳过 3`(起点 657,净 −2 因 14 空转用例合并为 12;3 个跳过均有显式原因,非空壳)。**遗留**:E2E 入 CI 待基础设施决策、pytest fixture 体系缺失、INV-011 缺显式守卫。**结论:不宣布「失败为零可进入 UAT」**;**Phase 24 尚未获准** |
|
||||
| Phase 23 可信系统测试重建(REFACTOR)复验 | ⚠️ 部分关闭(2026-09-18) | 见 `docs/Phase-23-V11-Recovery-Completion-Report-2026-09-18.md`;KEEP-VERIFY 复验发现 STATUS 标✅ 但含 **5 处真实缺陷**:① **D-23-A(Critical)** `V11InvariantsTest` 原 14 个用例全为常量字符串断言 —— INV-016「Banner 目标类型」实际断言**文章状态**常量(跑错域)、INV-017「首页 8 入口」是**对自己刚声明的字面量数组做 `length==4` 的重言式**(从未调用首页服务)、INV-018/019/020 用预约/文章/账号状态常量冒充权限与数据范围验证;已重写为 12 个用例,含 **4 个真实服务行为守卫**(INV-013 走真实 `CounselingService.getDetail` 断言 Matchmaker 读到 `userRequestSummary==null` 并以 assigned Counselor 能读正文作反证;INV-016 反射读生产 `ALLOWED_TARGET_TYPES` 断言不含外链;INV-017 真实 `getHome` 断言 8 入口编码与顺序固定;INV-019 断言 `content_editor` 读到 null)+ `assertBehaviorTestExists` 用 `Class.forName` 把「行为验证见 XxxTest」变成可执行契约;② **D-23-B(High)** `tests/e2e/` **44 个 `pass` 空壳**(4 个文件 100% 为空壳,另 2 个混合),违反 Gate「不允许 skip/pass 空壳」→ 已移除并改为诚实占位(保留完整场景清单);③ **D-23-C(High)** pytest E2E 套件**整体不可执行**:全仓无 `conftest.py`,约 15 个 fixture(`api_client`/`verified_user`/`banner_factory`…)不存在,唯一可执行的 `run_v11_tests.py`(16 用例)需活栈 + `E2E_PASSWORD`;④ **D-23-D(High,遗留未修)** CI 未串联 E2E(计划明确要求)→ 需先决定 CI 活栈供给方式,未实施;⑤ **D-23-E(Medium)** CI `migration` job 注释称「验证 SQL 语法」实为只查文件非空(真实语法校验在 `MigrationValidationTest`,随 backend job 跑)→ 改为真实序列校验(命名/非空/版本号唯一/无缺号,43 个文件 1~43 连续唯一)。**新增闸门**:`scripts/check-no-empty-tests.py`(AST 扫描阻断 `pass` 空壳与无条件 `skip`,已双向验证)+ CI `test-shell-guard` job。**证据**:负向注入 3 处生产缺陷 → 恰好 `inv013`/`inv016`/`inv017` 3 个用例精准变红、其余 9 绿、零无关失败,`md5sum -c` 还原一致;`655 tests / 通过 652 / 失败 0 / 错误 0 / 跳过 3`(起点 657,净 −2 因 14 空转用例合并为 12;3 个跳过均有显式原因,非空壳)。**遗留**:pytest fixture 体系缺失、INV-011 缺显式守卫(E2E 入 CI 与 fixture 体系已由 Phase 23B 补齐)。**结论:不宣布「失败为零可进入 UAT」**;**Phase 23B(E2E 测试基建)已于 2026-09-18 完成并关闭** |
|
||||
| Phase 23B E2E 测试基建重建 | 已完成(2026-09-18) | 补齐 Phase 23 遗留 D-23-C(E2E 整体不可执行)与 D-23-D(CI 未串联 E2E)。**交付**:`tests/e2e/conftest.py`(ApiClient / `operator_token` / `new_user` / 栈探活)、`pytest.ini`、`tests/e2e/requirements.txt`、`tests/e2e/sql/seed_e2e_operators.sql`(迁移里**没有任何 operator 种子**,全新库无账号可登录 → 6 个 e2e 账号 + 角色绑定,幂等,口令由 `psql -v pwd` 注入)、CI job `e2e`(起真实栈→等就绪→seed→pytest→失败阻断→清理)、8 个 E2E 文件改造(消除 import 期 `KeyError`,替换恒真/名不副实断言)。**核心设计**:栈不可达时 `pytest.exit(4)` **而非 skip**(skip 会把未执行伪装成通过)。**验证**:本机真实活栈 **33 passed**;栈不可达/缺口令均 exit 4;后端 **656 tests / 0 failures / 0 errors / 3 skipped**(基线 655,+1 为新增构建产物守卫,已负向验证:注入重复 `V042` → 恰 1 用例变红)。**本轮暴露并修复 6 处真实生产缺陷**(H2 零覆盖):D-23B-A 构建产物残留重复迁移版本号 042 → 应用起不来;D-23B-B 非 UUID path → 500 + 数据库异常外泄;D-23B-C 6 个 E2E 文件 import 期崩溃 → 从未执行;D-23B-E `super_admin` 仅 18/48 权限、`matchmaker` 0 权限(matchmaker 部分列遗留,需产品定义);D-23B-I 非法枚举参数 → 500(应 BAD_REQUEST);D-23B-J `uuid = character varying` → JDBC 加 `stringtype=unspecified`。**另有一处判断被证伪后撤回**:初查以为 `service_appointment.register` 权限丢失,核实后端点实际用 `service.assisted_registration`(V029 已 seed),未据此改动任何代码。详见 `docs/Phase-23B-V11-E2E-Infrastructure-Completion-Report-2026-09-18.md` |
|
||||
| UI-CLEANUP-20260913 | 已完成 | 废弃 UI 代码、冲突规范、旧计划和无效验收证据已清理;`ui-document-baseline-contracts` 锁定唯一视觉入口 |
|
||||
| UI-ADMIN-20260913 | 已完成(待桌面端目视复验) | 管理端全局框架与工作台已按规格交付;18 modules + 22 routes,vue-tsc 与 vite build 全绿 |
|
||||
| UI-MINIAPP-20260913 | 已完成 | 小程序 11 个核心页面已重构;11/11 页 TS-only,2026-09-16 通过用户验收 |
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# Phase 23B — E2E 测试基建重建:复验完成报告
|
||||
|
||||
- 日期:2026-09-18
|
||||
- 范围:**补上 E2E 基建**(用户批准:Phase 23 暴露 E2E 层几乎为空后的决定)
|
||||
- 结论:**E2E 层首次真实可执行**;过程中暴露并修复 6 处真实生产缺陷
|
||||
- 基线:`v1.0.0-RC.2` / V1.1 KEEP-VERIFY 纪律(历史 ✅ 不可信,必须由运行时证明)
|
||||
|
||||
---
|
||||
|
||||
## 一、为什么有这一轮
|
||||
|
||||
Phase 23 的结论是「部分关闭」:假覆盖归零了,但 **E2E 这一环仍然是空的**——
|
||||
|
||||
- 全仓无 `conftest.py`,约 15 个 fixture(`api_client` / `verified_user` / `banner_factory` …)根本不存在;
|
||||
- 多个文件在 **import 期**就 `os.environ["E2E_PASSWORD"]` → `KeyError` → 收集失败;
|
||||
- CI 从不跑 E2E(D-23-D)。
|
||||
|
||||
即:STATUS 里「10 个 E2E 文件 ~80+ 用例 ✅」描述的,是一批**从未被执行过**的文件。
|
||||
|
||||
本轮的目标不是"补几个用例",而是把「能真实执行"这件事本身"建起来:
|
||||
栈怎么起、账号从哪来、缺栈时怎样失败、CI 怎样阻断。
|
||||
|
||||
---
|
||||
|
||||
## 二、交付物
|
||||
|
||||
| 交付 | 文件 | 作用 |
|
||||
|---|---|---|
|
||||
| fixture 体系 | `tests/e2e/conftest.py` | `ApiClient`、`admin_token`、`operator_token(username)`、`new_user`、`live_stack` 探活 |
|
||||
| pytest 配置 | `pytest.ini` | `testpaths=tests/e2e`、标记 `e2e`、说明为何不收集 `run_v11_tests.py` |
|
||||
| 依赖清单 | `tests/e2e/requirements.txt` | pytest / requests |
|
||||
| 运营账号自举 | `tests/e2e/sql/seed_e2e_operators.sql` | 6 个 e2e 账号 + 角色绑定,幂等,密码由 `psql -v pwd` 注入 |
|
||||
| CI 串联 | `.github/workflows/ci.yml` → job `e2e` | 起真实栈 → 等就绪 → seed → pytest → 失败阻断 → 清理 |
|
||||
| 8 个测试文件改造 | `tests/e2e/*_test.py` | 消除 import 期崩溃;把恒真/名不副实的断言换成真实断言 |
|
||||
|
||||
### conftest 的三条硬设计(写进代码注释,防止回退)
|
||||
|
||||
1. **栈不可达 = `pytest.exit(4)`,不是 `skip`。**
|
||||
`skip` 会把"未执行"伪装成"通过"——那正是 Phase 23 清掉的 44 个 `pass` 空壳的另一种变体。
|
||||
2. **`session.trust_env = False`。**
|
||||
宿主机若设了 `http_proxy`,requests 会把 localhost 请求也发给代理并返回 502,
|
||||
表现为"连接失败"而不是真实业务结果(本机实测踩到)。
|
||||
3. **用户隔离。** mock 微信登录同 code 同 openid,换 code 即换人;每个用例用独立新用户。
|
||||
|
||||
---
|
||||
|
||||
## 三、本轮发现并修复的真实生产缺陷
|
||||
|
||||
> 这些不是"测试问题",是**起真栈才暴露**的生产缺陷。H2 测试库全部零覆盖。
|
||||
|
||||
| 编号 | 级别 | 真相 | 处置 |
|
||||
|---|---|---|---|
|
||||
| **D-23B-A** | Critical | `backend/target/classes/db/migration/` 残留旧 `V042__add_appointment_operator_register_permission.sql`(src 中已被 `V042__add_service_location_announcements.sql` 取代)。未 clean 打包 → jar 内两个 042 → Flyway 抛 `Found more than one migration with version 042` → **应用起不来** | 清理残留;新增守卫 `testBuildOutputHasNoDuplicateMigrationVersions`(扫构建产物,src 干净也扫得出) |
|
||||
| **D-23B-B** | High | `GET /counseling/services/{id}` 传入非 UUID → PostgreSQL 抛 `invalid input syntax for type uuid` → **500 且泄漏数据库异常** | Controller 层 UUID 格式校验 → `BAD_REQUEST` |
|
||||
| **D-23B-C** | High | 6 个 E2E 文件 import 期 `os.environ["E2E_PASSWORD"]` → `KeyError` → 收集失败,**从未执行** | 全部迁移到 conftest fixture |
|
||||
| **D-23B-E** | Critical | 权限 seed 缺口:permissions 共 **48** 条,`super_admin` 只有 **18** 条,`matchmaker` **0** 条。V015 的"为 super_admin 分配全部权限"是**执行时点快照**,之后新增的 30 条从未回补 → 全新环境下管理员创建服务站 403、红娘全部功能 403 | 新增 `V044__grant_all_permissions_to_super_admin.sql`(对齐 super_admin 定义,非扩权)。**matchmaker 权限集需产品定义,列为遗留** |
|
||||
| **D-23B-I** | Medium | `ActivityService` 对非法活动类型抛 `IllegalArgumentException`,全局异常处理器未映射 → **500**(客户端参数错被伪装成服务端故障) | `GlobalExceptionHandler` 增加 `IllegalArgumentException → BAD_REQUEST` |
|
||||
| **D-23B-J** | High | `operator does not exist: uuid = character varying`:Java 实体主键/外键统一用 `String`,PostgreSQL 列是 `uuid`,比较操作不做隐式转换 → 多处查询 500(`/service-appointments/my` 等)。**H2 不报错 → 零覆盖** | docker profile JDBC URL 增加 `?stringtype=unspecified`(pgjdbc 官方解法,一处解决全局) |
|
||||
|
||||
### 一处"判断被证伪",如实撤回
|
||||
|
||||
初查时发现 `V042__add_appointment_operator_register_permission.sql` 的内容
|
||||
(`service_appointment.register` 权限)不在 src 里,一度判定为"功能缺失"。
|
||||
进一步核实:端点实际用的是 `service.assisted_registration`(V029 已 seed 并授予
|
||||
服务站运营/平台管理员),Controller 注释明确写着"刻意收敛,避免权限膨胀"。
|
||||
→ **该判断不成立,已撤回**,未据此改动任何代码。
|
||||
|
||||
---
|
||||
|
||||
## 四、验证证据
|
||||
|
||||
### 1. E2E 真实执行(本机活栈:postgres + redis + 当前代码 jar @8085)
|
||||
|
||||
```
|
||||
33 passed in 3.21s
|
||||
```
|
||||
|
||||
覆盖:首页 8 入口固定顺序(INV-017)、Banner 禁外链(INV-016)、本地资格声明与
|
||||
用户隔离(INV-011)、咨询正文权限(INV-012/013)、内容编辑员边界(INV-019)、
|
||||
服务站/咨询师越权(INV-018/020)、活动类型筛选与非法值、文章生命周期、咨询取消。
|
||||
|
||||
### 2. 基建自身的负向属性("未执行"不会被算成"通过")
|
||||
|
||||
| 场景 | 结果 |
|
||||
|---|---|
|
||||
| `E2E_BASE_URL` 指向未监听端口 | `pytest.exit`,**退出码 4**,输出明确提示 |
|
||||
| 未设置 `E2E_OPERATOR_PASSWORD` | `pytest.exit`,退出码 4 |
|
||||
|
||||
### 3. 后端全量回归
|
||||
|
||||
```
|
||||
Tests run: 656, Failures: 0, Errors: 0, Skipped: 3 BUILD SUCCESS
|
||||
```
|
||||
|
||||
基线 655(Phase 23 后),净 +1 为新增的构建产物守卫 `testBuildOutputHasNoDuplicateMigrationVersions`。
|
||||
3 个 skip 均有显式原因(2× `@EnabledIfEnvironmentVariable(CI=true)`、1× `@Disabled("需要完整数据库连接")`),无一为空壳。
|
||||
|
||||
**守卫负向验证**:向 `target/classes/db/migration` 注入一个重复的 `V042__*.sql` →
|
||||
恰好 `testBuildOutputHasNoDuplicateMigrationVersions` 变红(报"版本号 42 重复"),
|
||||
其余 12 个用例仍绿;移除注入文件后恢复全绿。
|
||||
|
||||
---
|
||||
|
||||
## 五、遗留(未静默扩权)
|
||||
|
||||
1. **matchmaker 角色 0 权限**(D-23B-E 一半):红娘该拥有哪些权限属产品定义,
|
||||
本轮不擅自授予;转介类 E2E 场景因此仍无法正向执行(已在文件中注明,未写假用例)。
|
||||
2. **NPE → 500 的跨模块根因**:`@AuthenticationPrincipal UserPrincipal` 在
|
||||
operator token 下为 `null`,部分端点未做 null 检查 → 500(应 401/403)。
|
||||
本轮只修了被 E2E 命中的路径,其余建议统一处理。
|
||||
3. **`run_v11_tests.py` 未被 pytest 收集**:它在 import 期登录、依赖
|
||||
`docker exec` 取库内 ID,是"人工跑的活栈脚本"。其 16 个场景应逐步迁移进
|
||||
fixture 体系。
|
||||
4. **生产环境 JDBC**:`stringtype=unspecified` 本次只改了 docker profile;
|
||||
生产的 datasource 由部署环境变量提供,需同步加上(运维项)。
|
||||
5. **CI 首次运行成本**:`e2e` job 需构建 backend 镜像(Dockerfile 内联网构建),耗时较长。
|
||||
|
||||
---
|
||||
|
||||
## 六、结论
|
||||
|
||||
Phase 23B **关闭**。
|
||||
|
||||
E2E 从"一批从未执行过的文件 + 一个不存在的 fixture 体系",变为
|
||||
「**有栈就真跑、没栈就退出、CI 会阻断**」的可执行基建;
|
||||
顺带用真实 PostgreSQL 挖出 6 个 H2 永远发现不了的生产缺陷。
|
||||
@@ -660,6 +660,36 @@ Invariant测试:10个(INV-011~020)→ **实测:原 14 个用例无任何
|
||||
|
||||
**Phase 22(部署运维配置扩展)— 已完成** ✅
|
||||
|
||||
### Phase 23B — E2E 测试基建重建(2026-09-18)✅ 已关闭
|
||||
|
||||
> 本轮补齐的是 Phase 23 的两条遗留(D-23-C「E2E 整体不可执行」、D-23-D「CI 未串联 E2E」),
|
||||
> 与下方 **「Phase 24 — UAT增量」是不同工作**,不要混为一谈。
|
||||
|
||||
| 交付 | 说明 |
|
||||
|---|---|
|
||||
| `tests/e2e/conftest.py` | fixture 体系:ApiClient、`operator_token(username)`、`new_user`、栈探活 |
|
||||
| `pytest.ini` / `tests/e2e/requirements.txt` | 收集范围与依赖 |
|
||||
| `tests/e2e/sql/seed_e2e_operators.sql` | 运营账号自举(**迁移里没有任何 operator 种子**,全新库无账号可登录) |
|
||||
| CI job `e2e` | 起真实栈 → 等就绪 → seed → pytest → 失败阻断 → 清理 |
|
||||
| 8 个 E2E 文件改造 | 消除 import 期 `KeyError`;替换恒真 / 名不副实的断言 |
|
||||
|
||||
**核心设计**:栈不可达时 `pytest.exit(4)`,**不是 skip** —— skip 会把"未执行"伪装成"通过"。
|
||||
|
||||
**验证**:本机真实活栈 **33 passed**;后端全量回归见本节末。
|
||||
|
||||
**本轮暴露并修复的真实生产缺陷**(H2 测试库全部零覆盖,只有真 PostgreSQL 才现形):
|
||||
|
||||
| 编号 | 真相 |
|
||||
|---|---|
|
||||
| D-23B-A | 构建产物残留重复迁移版本号 042 → Flyway 报错 → **应用起不来** |
|
||||
| D-23B-B | 非 UUID path 参数 → 500 且泄漏数据库异常 |
|
||||
| D-23B-C | 6 个 E2E 文件 import 期崩溃 → **从未被执行过** |
|
||||
| D-23B-E | 权限 seed 缺口:`super_admin` 仅 18/48 条、`matchmaker` **0** 条(matchmaker 部分列为遗留) |
|
||||
| D-23B-I | 非法枚举参数 `IllegalArgumentException` 未映射 → 500(应 BAD_REQUEST) |
|
||||
| D-23B-J | `operator does not exist: uuid = character varying` → JDBC 增加 `stringtype=unspecified` |
|
||||
|
||||
详见 `docs/Phase-23B-V11-E2E-Infrastructure-Completion-Report-2026-09-18.md`。
|
||||
|
||||
### Phase 24 — UAT增量 ✅ 已就绪(测试数据支持)
|
||||
| 任务 | 状态 |
|
||||
|------|------|
|
||||
|
||||
Reference in New Issue
Block a user