# LINE / Telegram 驗證與授權測試地圖

> 這份文件回答兩件事：
>
> 1. **改 LINE / Telegram 驗證流程時要跑哪些測試？**
> 2. **每一支測試到底在保護什麼，不要看到檔名就頭痛。**

請搭配下列文件一起看：

- `references/line-telegram-verification-lifecycle.md`
- `references/unbind-still-conversational-diagnostics.md`
- `references/line-telegram-must-run-commands.md`

---

## 一張圖先看懂

驗證 / 授權這條線至少分成 6 層：

1. **Adapter intake**
   - LINE / Telegram 訊息剛進來時，是否有被過早擋掉
2. **Gateway verification state machine**
   - `new -> pending_verification -> verified -> unbind -> new`
3. **Gateway authz**
   - pairing / allowlist / verified binding 綜合後到底放不放行
4. **Verified email handoff guard**
   - system-initiated email handoff 是否只允許 live 綁定 email
5. **Admin bind / unbind route**
   - force-bind / unbind / audit log / pairing revoke
6. **Real-path smoke**
   - 真 event 經過 adapter -> store -> verify/unbind -> auth/handoff 的端到端煙霧測試

如果你只測其中一層，很容易得到「局部看起來正常，但整條路其實壞掉」的假象。

---

## 測試檔案地圖

### 1. `tests/gateway/test_telegram_auth_check.py`

**主要保護：Telegram intake prefilter / onboarding 存活性**

關鍵測試：
- `test_unknown_dm_with_no_allowlist_passes_to_pairing`
  - 保護：沒 allowlist 時，未知 Telegram DM 仍可進 onboarding / pairing
- `test_removed_dm_user_blocked_before_pairing_when_allowlist_exists`
  - 保護：有明確 allowlist 時，被移除的 DM 使用者會在 intake 就被擋掉
- `test_runner_auth_gets_group_user_allowlist_context`
- `test_runner_auth_gets_group_chat_allowlist_context`
  - 保護：群組 allowlist 判斷要拿到正確 group context，不可誤用 DM 形狀 source

**適合在你改這些東西時跑：**
- `plugins/platforms/telegram/adapter.py::_is_user_authorized_from_message`
- Telegram group/forum gating
- onboarding / pairing 早期攔截邏輯

---

### 2. `tests/gateway/test_line_plugin.py`

**主要保護：LINE adapter gate、quota fallback、verified-email handoff 路徑**

關鍵測試區塊：
- `TestInboundAuthorizationDeferral`
  - `test_unauthorized_user_dm_is_deferred_to_gateway_authz`
  - `test_user_dm_still_deferred_when_only_group_room_allowlists_exist`
  - `test_unauthorized_group_still_rejected_at_adapter_gate`
  - 保護：LINE DM 要能進 gateway authz / verification；group/room 仍維持 adapter gate

- `TestSendRouting`
  - quota fallback、email fallback、附件/下載連結邏輯
  - `test_send_quota_fallback_blocks_when_live_binding_differs`
  - `test_send_email_file_batch_fallback_blocks_when_live_binding_differs`
  - 保護：LINE system fallback 不可繞過 live binding email guard

- `TestQuotaFallbackPostback`
  - postback / pending notice / quota fallback 後續通知邏輯

**適合在你改這些東西時跑：**
- `plugins/platforms/line/adapter.py`
- LINE onboarding defer / reject
- LINE quota fallback
- LINE verified-email handoff

---

### 3. `tests/gateway/test_verified_email_handoff_guard_helpers.py`

**主要保護：live verified email guard helper**

關鍵測試：
- Telegram
  - `test_telegram_guard_accepts_live_bound_email`
  - `test_telegram_guard_blocks_mismatched_system_handoff`
  - `test_telegram_guard_allows_explicit_other_email`
- LINE
  - `test_line_guard_accepts_live_bound_email`
  - `test_line_guard_blocks_mismatched_system_handoff`
- API server
  - `test_api_server_guard_accepts_live_bound_email`
  - `test_api_server_guard_blocks_mismatched_system_handoff`

**保護重點：**
- system-initiated handoff 只能寄給 live 綁定 email
- 舊信箱 / 錯信箱不能因為 session 或舊上下文殘留而被使用
- 使用者明確指定其他 email 時，才允許例外

**適合在你改這些東西時跑：**
- `_verified_email_target_if_bound(...)`
- `bound_email_matches_source(...)`
- 所有 quota fallback / email handoff 路徑

---

### 4. `tests/hermes_cli/test_web_verification_admin.py`

**主要保護：admin bind / unbind、verification state machine、pairing revoke、audit log**

關鍵測試：
- `test_unbind_revokes_pairing_approval`
  - 保護：unbind 時 pairing approval 會一起撤銷
- `test_unbind_removes_runner_authorization_when_pairing_was_only_grant`
  - 保護：若授權只靠 pairing，unbind 後 runner authz 也必須失效
- `test_verification_state_machine_bind_handoff_unbind`
  - 保護：
    - onboarding -> verify -> live binding 生效
    - handoff guard 生效
    - unbind 後 verified source / handoff / authz 一起失效
    - LINE 與 Telegram 兩條路都要成立
- `test_resend_endpoint_sends_verification_email`
- `test_public_verify_endpoint_accepts_token_without_session_header`
  - 保護：admin resend / public verify route contract

**適合在你改這些東西時跑：**
- `gateway/user_verification.py`
- `hermes_cli/web_server.py`
- admin force-bind / unbind / resend / verify token
- pairing revoke contract

---

### 5. `tests/gateway/test_line_telegram_verification_smoke.py`

**主要保護：真事件流的整合煙霧測試**

關鍵測試：
- `test_line_dm_verification_smoke`
- `test_telegram_dm_verification_smoke`

**保護重點：**
- 真 LINE / Telegram 事件進 adapter
- 進 GatewayUserStore onboarding
- verify 後 live binding 生效
- LINE quota fallback 真的寄到 live 綁定 email
- Telegram pairing + runner authz 真的生效
- unbind 後 handoff / authz 真的失效
- 再次進來可重新 onboarding

**這支的價值：**
不是單層 unit test，而是檢查「這整條水管接起來有沒有漏水」。

**已知測試坑：**
- Telegram text path 會先進 batching queue，不能只呼叫 `_handle_text_message()` 就假裝已 dispatch；要等 flush 完
- LINE / Telegram 第二次重送 onboarding 若直接重打同 email，可能撞到 resend/rate-limit；若你要驗重新 onboarding，本地 smoke 可先把 `verification_requests.last_sent_at` 往前調，避免節流遮住真正狀態機行為

---

## 依需求挑測試：實用對照表

| 你改的內容 | 最少要跑 | 建議再加跑 |
|---|---|---|
| Telegram DM onboarding / prefilter | `test_telegram_auth_check.py` | `test_line_telegram_verification_smoke.py` |
| LINE DM defer / group-room gate | `test_line_plugin.py` | `test_line_telegram_verification_smoke.py` |
| verified email handoff guard | `test_verified_email_handoff_guard_helpers.py` | `test_line_plugin.py`, `test_web_verification_admin.py` |
| force-bind / unbind / resend / verify token | `test_web_verification_admin.py` | `test_line_telegram_verification_smoke.py` |
| pairing revoke / unbind 後仍能聊天 | `test_web_verification_admin.py` | `test_line_telegram_verification_smoke.py`, `test_pairing.py` |
| quota fallback / 改寄 email | `test_line_plugin.py` | `test_verified_email_handoff_guard_helpers.py`, `test_line_telegram_verification_smoke.py` |
| 大改整條驗證流程 | 上面 5 支全跑 | 再加 `test_pairing.py` 與 `test_pairing_allowlist_bypass.py` |

---

## 建議執行順序

### A. 只改單點 helper / 註解 / 小修
先跑最小集合：

```bash
pytest -q \
  tests/gateway/test_verified_email_handoff_guard_helpers.py \
  tests/hermes_cli/test_web_verification_admin.py
```

### B. 改 LINE adapter / quota fallback

```bash
pytest -q \
  tests/gateway/test_line_plugin.py \
  tests/gateway/test_verified_email_handoff_guard_helpers.py \
  tests/gateway/test_line_telegram_verification_smoke.py
```

### C. 改 Telegram intake / authz

```bash
pytest -q \
  tests/gateway/test_telegram_auth_check.py \
  tests/hermes_cli/test_web_verification_admin.py \
  tests/gateway/test_line_telegram_verification_smoke.py
```

### D. 改 bind / unbind / verification store 核心

```bash
pytest -q \
  tests/hermes_cli/test_web_verification_admin.py \
  tests/gateway/test_verified_email_handoff_guard_helpers.py \
  tests/gateway/test_line_telegram_verification_smoke.py \
  tests/gateway/test_pairing.py \
  tests/gateway/test_pairing_allowlist_bypass.py
```

### E. 大改整條 LINE / Telegram 驗證流程

```bash
pytest -q \
  tests/gateway/test_line_telegram_verification_smoke.py \
  tests/hermes_cli/test_web_verification_admin.py \
  tests/gateway/test_line_plugin.py \
  tests/gateway/test_telegram_auth_check.py \
  tests/gateway/test_verified_email_handoff_guard_helpers.py \
  tests/gateway/test_pairing.py \
  tests/gateway/test_pairing_allowlist_bypass.py
```

---

## 什麼情況代表你測得還不夠

如果你只做到下面其中一種，通常都還不夠：

1. **只看 API 200 OK**
   - 你可能沒測到 pairing 還在放行
2. **只測 DB row**
   - 你可能沒測到 adapter / runner / fallback 還在工作
3. **只測 helper**
   - 真事件流可能根本沒進到 helper
4. **只測 LINE 或只測 Telegram**
   - 兩邊共享 store / authz，但 adapter 行為不同，常會只壞一邊
5. **只測第一次 onboarding**
   - rebind / unbind / 再 onboarding 的第二輪常才是 regression 現場

---

## 給未來維護者的實用建議

- **先看 lifecycle 文件，再決定要改哪一層。**
- **先跑最接近你變更點的測試，再跑 smoke。**
- **如果改到 unbind，就一定把 pairing 與 runner authz 一起想。**
- **如果改到 email handoff，就一定把 live binding guard 一起想。**
- **如果改到 adapter intake，就一定確認 onboarding 沒被你順手封死。**

一句話總結：

> 這條線最怕的不是「完全壞掉」，而是「只有某一層看起來正常」。
>
> 所以測試地圖的目的，就是逼你同時看多層，不要再被局部成功騙了。