Files
CFDivePlatform/openspec/changes/auth-tests/tasks.md
T
a620906209 6f446f601f test(auth): 新增三角色登入/註冊/登出、P2 帳號鎖定、OAuth state 驗證測試
新增 34 個 Feature 測試(AuthLoginTest 19、AuthLockoutTest 11、
AuthOAuthTest 4)涵蓋 member/provider/admin 的註冊、登入、登出與跨角色
隔離;P2 帳號鎖定的 Fixed Window 計數、companion cache key 與 email
正規化;以及 P2 OAuth state 的 CSRF 驗證與一次性消耗。

同時新增 .gitea/workflows/test.yml 在 push/PR 時於 CI 自動執行測試
(含 GD 等擴充套件安裝),並補上對應的 OpenSpec 變更文件
(proposal/design/spec/tasks,34 項驗收情境與測試 1:1 對應)。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 04:01:33 +08:00

8.2 KiB
Raw Blame History

1. AuthLoginTest — 三角色登入/註冊/登出

  • 1.1 [整合測試] 建立 tests/Feature/AuthLoginTest.php,加入 RefreshDatabase trait 與 helper methodscreateMembercreateProvidercreateAdmin
  • 1.2 [整合測試] 實作 test_member_register_successPOST /api/member/register 回傳 201 + DB 有對應 member 用戶
  • 1.3 [整合測試] 實作 test_member_register_duplicate_email_returns_422:重複 email 回傳 422
  • 1.4 [整合測試] 實作 test_member_login_success:正確帳密回傳 200 + token
  • 1.5 [整合測試] 實作 test_member_login_wrong_password_returns_401:錯誤密碼回傳 401
  • 1.6 [整合測試] 實作 test_member_login_inactive_account_returns_403is_active=false 回傳 403
  • 1.7 [整合測試] 實作 test_provider_account_cannot_use_member_loginrole=provider 帳號打 member login 回傳 401(查詢以 role 過濾,跨角色視同帳號不存在)
  • 1.8 [整合測試] 實作 test_member_logout_revokes_token:登出後 token 失效(DB 確認 token 已刪除),且以同一 token 再打 /api/member/profile 得 401(需先 Auth::forgetGuards() 清除 RequestGuard 在同一測試內快取的已解析 user,否則會讀到快取而誤判為 200)
  • 1.9 [整合測試] 實作 test_provider_logout_revokes_tokenProvider 登出後 token 失效,且以同一 token 再打 /api/provider/profile 得 401(同樣需 Auth::forgetGuards()
  • 1.10 [整合測試] 實作 test_admin_logout_revokes_tokenAdmin 登出後 token 失效,且以同一 token 再打 /api/admin/profile 得 401(同樣需 Auth::forgetGuards()
  • 1.12 [整合測試] 實作 test_provider_login_successProvider 正確帳密回傳 200 + token
  • 1.13 [整合測試] 實作 test_provider_login_inactive_account_returns_403is_active=false 回傳 403
  • 1.14 [整合測試] 實作 test_member_account_cannot_use_provider_loginrole=member 帳號打 provider login 回傳 401(查詢以 role 過濾,跨角色視同帳號不存在)
  • 1.15 [整合測試] 實作 test_admin_login_successAdmin 正確帳密回傳 200 + token
  • 1.16 [整合測試] 實作 test_member_cannot_use_admin_loginrole=member 帳號打 admin login 回傳 401(查詢以 role 過濾,跨角色視同帳號不存在)
  • 1.17 [整合測試] 實作 test_provider_cannot_use_admin_loginrole=provider 帳號打 admin login 回傳 401(查詢以 role 過濾,跨角色視同帳號不存在)
  • 1.18 [整合測試] 實作 test_provider_register_successPOST /api/provider/register 回傳 201 + DB 有對應 provider 用戶(design.md 載明三角色註冊均須覆蓋,原 1.x 僅含 member 註冊測試,此處補齊)
  • 1.19 [整合測試] 實作 test_provider_register_duplicate_email_returns_422:重複 email 回傳 422
  • 1.20 [整合測試] 實作 test_admin_register_successPOST /api/admin/register 回傳 201 + DB 有對應 admin 用戶
  • 1.21 [整合測試] 實作 test_admin_register_duplicate_email_returns_422:重複 email 回傳 422

2. AuthLockoutTest — P2 帳號鎖定

  • 2.1 [整合測試] 建立 tests/Feature/AuthLockoutTest.phpsetUp() 加入 Cache::flush() 確保測試間隔離
  • 2.2 [整合測試] 實作 test_four_failures_do_not_lock_account:同一帳號失敗 4 次均回傳 401,帳號未鎖定
  • 2.3 [整合測試] 實作 test_fifth_failure_triggers_423_with_locked_until:第 5 次失敗回傳 423,body 含 locked_until ISO 8601 欄位
  • 2.4 [整合測試] 實作 test_locked_account_rejects_correct_password:鎖定後送出正確密碼仍回傳 423
  • 2.5 [整合測試] 實作 test_nonexistent_email_does_not_increment_counter:不存在 email 失敗 10 次後 Cache 無計數 key,不觸發鎖定
  • 2.6 [整合測試] 實作 test_successful_login_clears_failure_counter:失敗 3 次後成功登入,再失敗 4 次仍回 401(計數從 0 重算)
  • 2.7 [整合測試] 實作 test_email_case_normalization_counts_same_account:大小寫變體 email 累計失敗到同一 key,第 5 次觸發鎖定
  • 2.8 [整合測試] 實作 test_email_trim_normalization_counts_same_account:前後空白 email 累計失敗到同一 key,第 5 次觸發鎖定
  • 2.9 [整合測試] 實作 test_member_login_attempts_do_not_affect_provider_lockout_for_same_email:僅存在 provider 帳號的 email,對 member login 失敗 4 次(不增加計數)+ 對 provider login 失敗 4 次(計數=4,未鎖定,仍可正常登入)。原情境因 users.email 全域 unique 無法同時擁有 member+provider 帳號,已改寫並同步更新 spec.md
  • 2.10 [整合測試] 實作 test_account_can_login_after_lockout_entry_removed:鎖定後 Cache::forget() 移除 entry,帳號恢復可登入(回傳 200)
  • 2.11 [整合測試] 實作 test_locked_until_comes_from_companion_cache_key423 response 的 locked_until 等於 login_expires_at:member:{email} cache key 的值
  • 2.12 [整合測試] 實作 test_repeated_failures_do_not_extend_lockout_window:對應 spec.md「Fixed Window — 失敗不延長 TTL」情境(spec 已定義但原 tasks 未列出,此處補上)。第 1 次失敗寫入 login_expires_at 後,第 2~4 次失敗不應更動該值(驗證 recordLoginFailure 只在計數 key 不存在時才寫入 companion key,不隨後續失敗重設視窗)

3. AuthOAuthTest — P2 OAuth State 驗證

  • 3.1 [整合測試] 建立 tests/Feature/AuthOAuthTest.php,加入 RefreshDatabase trait;引入 Laravel\Socialite\Facades\Socialite
  • 3.2 [整合測試] 實作 test_oauth_callback_without_state_redirects_to_error:不帶 state 參數、session 無 oauth_state,斷言 redirect 含 error=oauth_failed,且 Socialite user() 不被呼叫(使用 shouldNotReceive
  • 3.3 [整合測試] 實作 test_oauth_callback_with_wrong_state_redirects_to_errorsession 設 oauth_state=correct,帶 state=wrong,斷言 redirect 含 error=oauth_failed,且 Socialite user() 不被呼叫
  • 3.4 [整合測試] 實作 test_oauth_callback_with_correct_state_completes_loginsession 設 oauth_state=abc,帶 state=abcSocialite mock 回傳假 Google user,斷言 redirect URL 含 #token=
  • 3.5 [整合測試] 實作 test_oauth_state_is_consumed_after_successful_callback:正確 state 完成一次登入後,再以相同 state 呼叫 callback,斷言第二次 redirect 含 error=oauth_failedstate 一次性消耗)

4. 驗收確認

  • 4.1 [整合測試] 執行 php artisan test --filter AuthLoginTest 全數通過,無 skip19 passed / 60 assertions
  • 4.2 [整合測試] 執行 php artisan test --filter AuthLockoutTest 全數通過,無 skip11 passed / 44 assertions
  • 4.3 [整合測試] 執行 php artisan test --filter AuthOAuthTest 全數通過,無 skip4 passed / 12 assertions
  • 4.4 [整合測試] 在容器內執行 docker exec cfdive-app php artisan testhost PHP 缺 GD 擴充套件,須在容器內跑才能涵蓋 CourseImageTest),結果:98 passed / 256 assertions,全數通過,無回歸。本次新增的 Auth 測試均已通過:AuthLoginTest 19、AuthLockoutTest 11、AuthOAuthTest 4(合計 34OAuth 部分已於 4.3 單獨驗證)

過程中排除的兩類既存失敗(均已解決,非本次改動引入)

  • CourseImageTest11 個):原本在 host PHP 跑會因缺 GD 擴充套件報 LogicException: GD extension is not installed.;確認專案 Dockerfile 已內建 GD,改用 docker exec cfdive-app php artisan test 在容器內執行即全數通過 —— 純屬執行環境差異,非程式或測試邏輯問題,往後請統一在容器內跑測試
  • AuthRateLimitTest2 個):member/provider login exceeds limit returns 429 原本預期 429 實得 401。根因是過期測試——commit 2a0e9255(P2 帳號鎖定上線時)刻意將 member/provider 的 IP throttle 從 5,1 調整為 10,1(避免 IP throttle 搶在帳號鎖定前觸發),但測試的迴圈次數與註解仍停留在舊版 5,1。已同步修正測試的迴圈次數(5→10)與區塊註解,使其對齊現行 throttle:10,1 設定,修正後全數通過