emma-hermes/skills/devops/gateway-identity-binding-li.../references/line-telegram-smoke-test-pa...

238 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# LINE / Telegram 驗證整合煙霧測試模式
> 這份文件給未來要補或修改 smoke / integration test 的人看。
>
> 目標不是「再寫一批單元測試」,而是驗證:**真事件路徑接起來後,整條驗證 / 授權 / handoff 水管沒有漏水。**
相關背景:
- `references/line-telegram-verification-lifecycle.md`
- `references/line-telegram-test-map.md`
- `references/line-telegram-must-run-commands.md`
---
## 什麼情況該寫 smoke test
當你改到下面任一類時,不能只靠 helper / route unit tests
- LINE / Telegram adapter intake gate
- onboarding / verification state machine
- verified email handoff guard
- quota fallback / email fallback
- admin unbind / pairing revoke
- runner authz 與 verified binding 的交界
一句話判斷:
> 如果這個改動需要同時假設「adapter 有正確 dispatch」和「store / authz 也會照預期接住」,就該補 smoke test。
---
## Smoke test 的最小骨架
一支合格的 LINE / Telegram 驗證 smoke test至少要穿過這些步驟
1. **真平台事件進 adapter**
2. **adapter 真的 dispatch 到 `handle_message(...)`**
3. **`GatewayUserStore.process_inbound_message(...)` 產生 onboarding / verification decision**
4. **`verify_token(...)` 後 verified binding 生效**
5. **live auth / handoff 路徑真的吃到 verified binding**
6. **`unbind_identity(...)` 後 verified / pairing / authz / handoff 失效**
7. **同一來源再次進來時,可重新 onboarding**
如果只測到第 3 步或第 4 步,通常還不算 smoke只算比較胖的 unit test。
---
## 目前已存在的參考實作
檔案:
- `tests/gateway/test_line_telegram_verification_smoke.py`
關鍵測試:
- `test_line_dm_verification_smoke`
- `test_telegram_dm_verification_smoke`
這兩顆目前已覆蓋:
### LINE
- DM event 經 `LineAdapter._dispatch_event(...)` 進來
-`GatewayUserStore.process_inbound_message(...)`
- `verify_token(...)``get_verified_email_for_source(...)` 生效
- `_send_email_quota_fallback(...)` 真的寄到 live 綁定 email
- `unbind_identity(...)` 後 fallback 失效
- 同來源再次進來可重新要求驗證
### Telegram
- text update 經 `TelegramAdapter._handle_text_message(...)` 進來
- text batch flush 後真的 dispatch
- `verify_token(...)` 後 verified source 生效
- `PairingStore()._approve_user(...)` 後 runner authz 生效
- `unbind_identity(...)` 後 runner authz 失效
- 同來源再次進來可重新要求驗證
---
## 兩個最容易踩的坑
### 1. Telegram text handler 不是同步直送
**症狀:**
- 你呼叫 `_handle_text_message(...)`
-`decisions` / `handle_message` 沒收到任何東西
- 看起來像 Telegram 流程壞掉
**真正原因:**
- Telegram text path 先進 `_enqueue_text_event(...)`
- 真正 dispatch 發生在 `_flush_text_batch(...)`
- 如果測試沒等 flush會誤判成流程沒進去
**建議模式:**
```python
async def _run_telegram_text(adapter, update):
await adapter._handle_text_message(update, SimpleNamespace())
pending = list(adapter._pending_text_batch_tasks.values())
if pending:
await asyncio.gather(*pending)
```
**原則:**
不要只呼叫 `_handle_text_message()` 就假設 event 已送達。
---
### 2. LINE / Telegram 第二次重送 onboarding 可能撞到 resend cooldown
**症狀:**
- 你在 unbind 後立刻重送同一個 email
- 預期拿到 `send_verification_email`
- 實際卻拿到 `handled`
**真正原因:**
- `verification_requests.last_sent_at` 還在 cooldown 視窗內
- 你撞到 resend / rate-limit 保護,不是狀態機壞掉
**建議模式:**
在本地 smoke test 裡,把 `last_sent_at` 人工往前調,讓你驗的是「重新 onboarding 能否成立」,不是節流是否生效。
```sql
UPDATE verification_requests
SET last_sent_at = datetime('now', '-11 minutes')
WHERE platform = ? AND external_user_id = ?
```
**原則:**
當測試目標是驗證狀態轉換,允許把節流窗移開;但要在註解或文件說明這是為了避開 rate-limit 噪音。
---
## 建議測試設計原則
### A. 盡量走真 adapter 入口
優先選:
- LINE`_dispatch_event(...)`
- Telegram`_handle_text_message(...)`
比起直接手刻 `MessageEvent` 然後呼叫 `handle_message(...)`,這樣更能抓到:
- source 解析錯誤
- DM / group / room 分流錯誤
- prefilter / batching / postback 之類的邊界問題
### B. mock 要薄,不要整條路都 fake 掉
可以 mock
- LINE client 的 reply / push / loading
- email send function
- bot network calls
不要 mock 到:
- `GatewayUserStore.process_inbound_message(...)`
- `verify_token(...)`
- `unbind_identity(...)`
- runner `_is_user_authorized(...)`
否則你只是把 smoke test 再拆回單元測試 cosplay。
### C. 要驗「生效」與「失效」兩面
不夠只驗:
- verify 後可用
還要驗:
- unbind 後失效
- pairing revoke 後失效
- fallback / handoff 不再寄到舊綁定 email
### D. 最後一輪最好重跑 onboarding
真正容易壞的常常不是第一次 onboarding而是
- verify 過一次
- unbind
- 再次 onboarding
所以 smoke test 最後建議再讓同一來源進來一次,確認沒有殘留狀態把它卡死。
---
## 建議命名方式
推薦:
- `test_line_dm_verification_smoke`
- `test_telegram_dm_verification_smoke`
- `test_<platform>_<path>_smoke`
不推薦:
- `test_integration_1`
- `test_full_flow`
- `test_regression`
原則是:
**檔名 / test name 一眼要看得出是哪個平台、哪條路、是不是 smoke。**
---
## 什麼時候要擴充既有 smoke而不是另開新檔
優先擴充 `tests/gateway/test_line_telegram_verification_smoke.py`,如果你的改動仍屬於:
- DM onboarding / verification
- verified binding / handoff
- unbind / pairing revoke
- authz 存活與失效
只有在測試目標已明顯換成另一條主線時,再另開檔,例如:
- forum / topic / group 專用 state machine
- webhook retry / duplicate event idempotency
- home-channel / control-channel 特化行為
---
## Smoke test 完成後至少再跑什麼
最少建議:
```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
```
如果改到 pairing / allowlist bypass再加
```bash
pytest -q \
tests/gateway/test_pairing.py \
tests/gateway/test_pairing_allowlist_bypass.py
```
---
## 一句話總結
> Smoke test 的任務不是證明某個 helper 很乖,
> 而是證明 **LINE / Telegram 真事件進來後adapter、store、authz、handoff、unbind 這幾層還能一起活著回家。**