Files
group_xinghuo_jinrong/docs/memory/tests/2026-09-13-advisor-e2e/TEST-LOG-2026-09-13-AD-001.md
T

141 lines
9.4 KiB
Markdown
Raw Normal View History

# 测试日志:理财师线(顾问 Agent)端到端缺口补测
## 1. 文档元数据
| 项 | 值 |
| --- | --- |
| 测试记录编号 | `TEST-LOG-2026-09-13-AD-001` |
| 缺陷/变更标题 | 理财师线端到端覆盖缺口补测(AD7 KYC 闭环 · AD8 接缝取证 · AD9 无消费方端点 · AD10 代客下单)+ Part B/C |
| 文档版本 | v1.0 |
| 创建日期 | 2026-09-13 |
| 关联分支 | `merger` |
| 关联拍板/TODO | 本轮七项口径:产出边界「盘点 + 直接开跑补齐」· 缺陷「只报告不修」· 数据「快照+还原」· 覆盖「三条线全补」· 报告包「一线一包」 |
| 风险等级 | **LOW**(本线无 P0/P1;唯一实质缺陷在孤儿代码内) |
| 缺陷类型 | 接缝绑定错误(潜在)· 可观测性/契约观察 |
| 发现阶段 | 端到端覆盖缺口补测轮 · Part A/B/C |
## 2. 组织与责任
| 项 | 值 |
| --- | --- |
| 所属模块 | 理财师线(顾问 Agent):`app/api/advisor_agent.py` · `app/service/agent_graph.py` · `app/service/agent_tools.py` |
| 模块负责人 | — |
| 发现人 | 本轮补测 |
| 修改人 | **无(本轮只报告不修)** |
| 测试执行人 | 本轮补测 |
| 评审人 | — |
## 3. 缺陷描述
### ADV 缺陷-3 —— `build_advisor_tools` 三处工具绑定到不存在的 service 方法
| 项 | 值 |
| --- | --- |
| 缺陷编号 | `ADV-3` |
| 严重等级 | **P3**(潜在;整支为孤儿代码,无生产调用方) |
| 复现步骤 | `python scripts/dev/advisor_gap_e2e_smoke.py --only AD8` |
| 期望 | 工具应调用真实存在的 service 方法与签名 |
| 实际 | 4 条用例稳定 FAIL:`AttributeError: no attribute 'chat'` / `TypeError: missing keyword-only 'auth'` / `AttributeError: no attribute 'search'` / `AttributeError: no attribute 'check'` |
| 根因 | 见下「根因表」——工具内写的方法名与真实 service 不符(含签名形态差异) |
| 修复方案 | **本轮不修。** 修复轮方向:改工具内调用为真实名 + **同步改 `tests/test_step11_agent_graph.py` 的替身**(改真实 service 或 `spec=` 严格 Mock),否则修完仍被 `Mock()` 遮蔽 |
**根因表(逐条定位到行号):**
| 用例 | 工具 | 工具内写法 | 真实 API | 异常 |
| --- | --- | --- | --- | --- |
| AD8-03 | `manage_kyc_session(action="chat")` | `kyc_service.chat(...)` | `app/service/kyc_session_service.py:140` **`chat_session`** | `AttributeError` |
| AD8-04 | `manage_kyc_session(action="get")` | `kyc_service.get_session(session_id=)` | `app/service/kyc_session_service.py:133` `get_session(session_id, *, auth)` | `TypeError` |
| AD8-05 | `search_templates` | `template_service.search(query=, limit=)` | `app/service/template_service.py` 仅 `reload`/`try_render`/`list_published_prompts`;真搜索在 `script_template_service.py` | `AttributeError` |
| AD8-06 | `compliance_check` | `compliance_service.check(text=, scene=)` | `app/service/compliance_check_service.py:38` **`check_text(payload, *, trace_id, advisor_id)`** | `AttributeError` |
**排除"探针写错"的证据:** 同一探针路径、同一 `invoke` 方法、同样注入真实 service,**对照组 AD8-01(持仓)/ AD8-02(净值)稳定 PASS**。只有接错的三个失败 ⇒ 失败来自接缝本身,不是探针。
**遮蔽成因(AD8-07 实测):** `tests/test_step11_agent_graph.py:113` 是全仓唯一调用方,其 `:114-117` 为四个 service **全注入 `Mock()`**。`Mock()` 对任意属性访问返回可调用对象,故三者"全部通过"。实测同一调用在 Mock 下返回 `ok → <Mock name='mock.chat()' id=...>`。
### ADV 缺陷-4 —— 两条端点可用但全仓无消费方
| 项 | 值 |
| --- | --- |
| 缺陷编号 | `ADV-4` |
| 严重等级 | **P3**(观察项,非故障) |
| 复现步骤 | `python scripts/dev/advisor_gap_e2e_smoke.py --only AD9` |
| 期望 | —(记录事实) |
| 实际 | `GET /api/advisor-agent/dashboard/ping` 与 `GET /api/advisor-agent/allocation/ping` 实测 HTTP 200 且回显正确,但**全仓无任何前端/脚本引用**;`guard/check` 与 `copy/track` **有**调用方 |
| 根因 | 未被接入(设计遗留) |
| 修复方案 | 记录以备清理或接入。**本轮不修** |
## 4. 变更清单
| 类型 | 路径 | 说明 |
| --- | --- | --- |
| 新建 | `scripts/dev/advisor_gap_e2e_smoke.py` | AD7–AD10 缺口补测脚本(真实 HTTP) |
| 收复 | `scripts/e2e/09-advisor-pages.mjs` | 自仓库外 `C:/Users/Windows/e2e-jinrong/` 收回,改指向仓库内 `scripts/e2e/harness.mjs`(F-05) |
| 未改 | `scripts/dev/advisor_e2e_smoke.py` | **回归基线,一个字未动**(拍了口径:只新增分组会污染退出码对照,故另起脚本) |
| 未改 | `app/**` · `web/src/**` | 本轮边界:**不改业务代码** |
## 5. 测试执行记录
| 序号 | 类型 | 用例/命令 | 执行人 | 结果 | 证据 |
| --- | --- | --- | --- | --- | --- |
| 1 | Part A | `python scripts/dev/advisor_gap_e2e_smoke.py --report …/ad-gap.md` | 本轮 | **31 PASS / 4 FAIL / 0 SKIP / 10 INFO**(退出码 1,FAIL 全为 AD8 取证) | `_raw/ad-gap.md` |
| 2 | Part B | `node 09-advisor-pages.mjs` | 本轮 | **17 PASS / 0 FAIL / 0 SKIP**(退出码 0) | `_raw/results-09-advisor.json` · `_raw/09-advisor-pages.stdout.txt` · `_raw/shots/` |
| 3 | Part C | `python scripts/dev/advisor_e2e_smoke.py --report …/_partc-advisor.md` | 本轮 | **63 PASS / 0 FAIL / 0 SKIP**(退出码 0,**基线未退步**) | `_raw/_partc-advisor.md` · `_raw/_partc-advisor.stdout.txt` |
**分组明细(Part A):**
| 组 | 覆盖对象 | PASS | FAIL | SKIP | INFO |
| --- | --- | ---: | ---: | ---: | ---: |
| AD7 | KYC 全链路复现(建会话→读视图→chat→complete→状态机负例→越权/404) | 13 | 0 | 0 | 2 |
| AD8 | **接缝取证**(真实 service + 2 对照组 + Mock 遮蔽面) | 2 | **4** | 0 | 2 |
| AD9 | 无前端调用方的端点(含注入拦截负例) | 9 | 0 | 0 | 5 |
| AD10 | 代客下单链路(归属·越权·冒用通道·缺头) | 7 | 0 | 0 | 1 |
| | **合计** | **31** | **4** | **0** | **10** |
> **分母纪律:** AD8 的 4 条 FAIL 按 R3 **单列,不计入通过率分母**。有效通过率视为 **31 PASS / 0 FAIL(口径外)**。
**关键正面证据(逐条):**
| 用例 | 断言 | 实测 |
| --- | --- | --- |
| AD7-02 | `GET /sessions/{id}`(v1.0 时 500)→ 200 | HTTP 200 |
| AD7-04 | `POST /sessions/{id}/chat`(v1.0 时 500)→ 200 | HTTP 200 |
| AD7-04a | 返回 `parsed_fields`/`collected_fields`/`missing_fields` 三件套 | `parsed=dict collected=dict missing=list` |
| AD7-05 | `agent_message.created_at` 无 NULL(v1.0 500 根因面) | 2 行,NULL 的 `seq_no=[]` |
| AD7-06 | 非归属顾问读同一会话 → 拒 | HTTP 403 / `40302` |
| AD7-07 | 不存在的 session → 404 | HTTP 404 / `40401` |
| AD7-09 / 7-10 | completed 后再 chat / 再 complete → 409 | HTTP 409 / `40901` ×2 |
| AD9-03a | 安全文本 action 枚举 | `action='passed'`(拦截值为 `'blocked'`) |
| AD9-03b | 放行时未命中任何 GUARD_* 规则 | `guard_type=None` |
| AD9-04 | 提示词注入被拦 | HTTP 400 / `40002` |
| AD10-02a | 名下客户回显一致 | `customer_id='CUST-1001'` |
| AD10-03 | 非名下客户越权 → 拒 | HTTP 403 / `AUTH_403_NOT_ASSIGNED` |
| AD10-04 | 缺 `X-Agent-Type` → 拒 | HTTP 401 / `AUTH_401_MISSING_AGENT_TYPE` |
| AD10-05 | 不存在的客户 → 拒(非静默放行) | HTTP 403 / `AUTH_403_NOT_ASSIGNED` |
| AD10-06 | **customer 型 token 冒用 advisor 通道** → 拒 | HTTP 403 / `AUTH_403_AGENT_MISMATCH` |
**本轮我方订正(1 处,非产品缺陷):** `AD9-03a` 首跑期望写 `action='pass'` → FAIL。查源码确认真实枚举为 **`'passed'`**(`app/service/input_guard_service.py:22`),拦截为 **`'blocked'`**(`app/service/input_guard.py:111`)。**是我的期望错,不是产品错**,已订正并补 `9-03b` 把「放行」与「未命中规则」拆成两条。
## 6. 数据准备
| 数据集 | 是否重灌 | 操作 |
| --- | --- | --- |
| `jinrong_core` 交易表 | **否** | 本线 Part A **不写 Core 交易表**(AD10 只到受理与归属,未提交代客交易) |
| `jinrong_agent` KYC 会话 | 否 | AD7 新建 1 个 KYC 会话(`kyc_sess_c2f2b6f33129487d9c7187bd056590f3`),**追加型,按语义不回滚** |
| `jinrong_agent` copy/track | 否 | AD9-05 留痕 1 条(`track_dc908deaf5904aab96a7a64efd200ed1`),追加型留痕 |
| `jinrong_core` 5 写表 | — | Part C 的 `advisor_e2e_smoke.py` 会写入;**已在 Phase 6 依 `snapshot.sql` 还原并逐表 CHECKSUM 校验通过**(见横切包 `XC-001` §6) |
## 7. 结论
| 项 | 结论 |
| --- | --- |
| 需求覆盖 | 理财师线 **AD7 KYC 全链路 · AD8 接缝 · AD9 无消费方端点 · AD10 代客下单** 四组补齐;`/api/advisor-agent/*` 写操作由既有基线 63 条覆盖 |
| 回归 | ✅ **未退步**:Part C 基线 `advisor_e2e_smoke.py` **63 PASS / 0 FAIL**,退出码 0;Part B **17 PASS / 0 FAIL** |
| 剩余风险 | ① AD8 修法未实施验证(须同步改测试替身);② 代客下单未走写入侧;③ 两条 ping 端点无消费方未决 |
| 建议人工再验 | ① 打开 `/app/advisor/kyc` 与 `/app/advisor/customers` 目视确认;② 若决定启用 `agent_graph.py`,**必须先在真实 service 下跑通 AD8 组**再接入 |
## 8. 签核 / 修订历史
| 版本 | 日期 | 修订人 | 说明 |
| --- | --- | --- | --- |
| v1.0 | 2026-09-13 | 本轮补测 | 首次建立:AD7–AD10 + Part B/C 证据归档 |