6.7 KiB
LINE / Telegram 驗證整合煙霧測試模式
這份文件給未來要補或修改 smoke / integration test 的人看。
目標不是「再寫一批單元測試」,而是驗證:真事件路徑接起來後,整條驗證 / 授權 / handoff 水管沒有漏水。
相關背景:
references/line-telegram-verification-lifecycle.mdreferences/line-telegram-test-map.mdreferences/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,至少要穿過這些步驟:
- 真平台事件進 adapter
- adapter 真的 dispatch 到
handle_message(...) GatewayUserStore.process_inbound_message(...)產生 onboarding / verification decisionverify_token(...)後 verified binding 生效- live auth / handoff 路徑真的吃到 verified binding
unbind_identity(...)後 verified / pairing / authz / handoff 失效- 同一來源再次進來時,可重新 onboarding
如果只測到第 3 步或第 4 步,通常還不算 smoke,只算比較胖的 unit test。
目前已存在的參考實作
檔案:
tests/gateway/test_line_telegram_verification_smoke.py
關鍵測試:
test_line_dm_verification_smoketest_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 綁定 emailunbind_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,會誤判成流程沒進去
建議模式:
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 能否成立」,不是節流是否生效。
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_smoketest_telegram_dm_verification_smoketest_<platform>_<path>_smoke
不推薦:
test_integration_1test_full_flowtest_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 完成後至少再跑什麼
最少建議:
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,再加:
pytest -q \
tests/gateway/test_pairing.py \
tests/gateway/test_pairing_allowlist_bypass.py
一句話總結
Smoke test 的任務不是證明某個 helper 很乖, 而是證明 LINE / Telegram 真事件進來後,adapter、store、authz、handoff、unbind 這幾層還能一起活著回家。