Files
CFDivePlatform/openspec/specs/feature-test-coverage/spec.md
T
a620906209 49a4b89655
Run Tests / test (pull_request) Successful in 1m51s
test(feature): 補齊五個核心業務流程 Feature test——Offer/Schedule CRUD、Admin 管理、通知觸發、預約列表
ProviderOfferCrudTest(7 案例):store/update/destroy 正常流程 + 所有權邊界(403)+ 未認證(401)
ProviderScheduleCrudTest(7 案例):store/update/destroy + 容量不變式(422)+ destroy 同步取消 active bookings
AdminUserManagementTest(5 案例):列表角色過濾 + toggle-active/toggle-verified DB 副作用 + 非 admin 403
NotificationTriggerTest(5 案例):5 條直接觸發路徑,收件者各不同(BookingCreated/CancelledByMember → provider;其餘 → member)
BookingListTest(4 案例):三角色資料隔離邊界 + 未認證 401

全套件 215 passed / 529 assertions,零 regression。
同步新增 openspec/changes/feature-test-coverage/ 與 openspec/specs/feature-test-coverage/spec.md。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 18:23:11 +08:00

7.8 KiB
Raw Blame History

feature-test-coverage Specification

Purpose

定義核心業務流程端點的測試覆蓋契約:Provider 課程 CRUD、時段管理、Admin 使用者操作、通知直接觸發路徑、預約列表端點。補齊 2026-06-11 稽核報告識別的 Feature test 缺口,確保這些端點的回歸能被測試套件即時攔截。


Requirement: Provider 課程 CRUD 測試覆蓋

測試套件 SHALL 驗證 POST /api/provider/offersPUT /api/provider/offers/{id}DELETE /api/provider/offers/{id} 的正常流程與所有權邊界。

Scenario: Provider 建立課程成功

  • WHEN 已認證的 Provider 送出有效的 title / location / price / region 至 POST /api/provider/offers
  • THEN 回傳 HTTP 201body 包含 { status: true, data: { id, title, provider_id } }DB 存在對應記錄且 provider_id 為當前 Provider

Scenario: 建立課程缺少必填欄位回傳 422

  • WHEN Provider 送出缺少 title 的請求至 POST /api/provider/offers
  • THEN 回傳 HTTP 422,body 包含對應欄位的驗證錯誤

Scenario: Provider 更新自己的課程

  • WHEN 已認證的 Provider 送出有效更新欄位至 PUT /api/provider/offers/{id}(該課程屬於此 Provider
  • THEN 回傳 HTTP 200,body 包含更新後的課程資料,DB 記錄已變更

Scenario: Provider 不可更新他人課程

  • WHEN Provider A 嘗試 PUT /api/provider/offers/{id},但該課程屬於 Provider B
  • THEN 回傳 HTTP 403body 包含 { status: false }

Scenario: Provider 刪除自己的課程

  • WHEN 已認證的 Provider 送出 DELETE /api/provider/offers/{id}(該課程屬於此 Provider
  • THEN 回傳 HTTP 200body 包含 { status: true }DB 記錄已不存在

Scenario: Provider 不可刪除他人課程

  • WHEN Provider A 嘗試 DELETE /api/provider/offers/{id},但該課程屬於 Provider B
  • THEN 回傳 HTTP 403

Scenario: 未認證請求被拒絕

  • WHEN 未帶任何 token 送出 POST /api/provider/offers
  • THEN 回傳 HTTP 401

Requirement: Provider 時段管理測試覆蓋

測試套件 SHALL 驗證 POST /api/provider/schedulesPUT /api/provider/schedules/{id}DELETE /api/provider/schedules/{id} 的正常流程、所有權邊界與容量驗證。

Scenario: Provider 建立時段成功

  • WHEN Provider 送出有效的 diving_offer_id / scheduled_date(未來日期)/ start_time / max_participants 至 POST /api/provider/schedules
  • THEN 回傳 HTTP 201body 包含 { status: true, data: { id, scheduled_date, status: "open" } }

Scenario: Provider 不可為他人課程建立時段

  • WHEN Provider A 送出 Provider B 課程的 diving_offer_id 至 POST /api/provider/schedules
  • THEN 回傳 HTTP 403

Scenario: 時段日期不可為過去

  • WHEN Provider 送出 scheduled_date 為過去日期
  • THEN 回傳 HTTP 422

Scenario: Provider 更新時段人數上限

  • WHEN Provider 送出新的 max_participants 至 PUT /api/provider/schedules/{id}(新值 ≥ 目前 current_participants
  • THEN 回傳 HTTP 200DB 記錄已更新

Scenario: 人數上限不可低於已確認人數

  • WHEN Provider 嘗試將 max_participants 設為低於時段 current_participants 的值
  • THEN 回傳 HTTP 422,message 說明不可低於已確認人數

Scenario: Provider 不可更新他人時段

  • WHEN Provider A 嘗試 PUT /api/provider/schedules/{id},但該時段屬於 Provider B
  • THEN 回傳 HTTP 403

Scenario: 刪除時段同時將進行中預約標記為 provider_cancelled

  • WHEN Provider 對有 pending/confirmed 預約的時段送出 DELETE /api/provider/schedules/{id}
  • THEN 回傳 HTTP 200;時段 status 改為 cancelled(記錄保留,不刪除);該時段的 pending/confirmed booking status 改為 provider_cancelled(記錄保留,不刪除);已是終態的 bookingcompleted/expired/已取消)不受影響

Requirement: Admin 使用者管理測試覆蓋

測試套件 SHALL 驗證 Admin 查詢會員/教練列表、切換帳號啟用狀態、切換教練驗證狀態的正確行為。

Scenario: Admin 取得會員列表

  • WHEN Admin 送出 GET /api/admin/members
  • THEN 回傳 HTTP 200data 陣列只包含 role=member 的使用者

Scenario: Admin 取得教練列表

  • WHEN Admin 送出 GET /api/admin/providers
  • THEN 回傳 HTTP 200data 陣列只包含 role=provider 的使用者

Scenario: Admin 切換會員帳號啟用狀態

  • WHEN Admin 送出 PUT /api/admin/members/{id}/toggle-active(目標會員 is_active=true
  • THEN 回傳 HTTP 200DB 記錄 is_active 由 true 變為 false

Scenario: Admin 切換教練驗證狀態

  • WHEN Admin 送出 PUT /api/admin/providers/{id}/toggle-verified(目標教練 is_verified=false
  • THEN 回傳 HTTP 200provider_profiles.is_verified 由 false 變為 true

Scenario: 非 Admin 角色被拒絕

  • WHEN role=member 的使用者送出 GET /api/admin/members
  • THEN 回傳 HTTP 403

Requirement: 通知直接觸發路徑測試覆蓋

測試套件 SHALL 驗證預約 controller 操作直接觸發的通知事件,補足 Scheduler 路徑以外的覆蓋缺口。收件者依實際 controller 實作:新預約與 member 取消通知教練,其餘通知學員。

Scenario: 建立預約觸發 BookingCreatedNotification 至教練

  • WHEN Member 成功建立預約(POST /api/member/bookings
  • THEN BookingCreatedNotification 被派發至該預約所屬的 Provider

Scenario: Provider 確認預約觸發 BookingConfirmedNotification 至學員

  • WHEN Provider 對 pending 預約呼叫 PUT /api/provider/bookings/{id}/confirm
  • THEN BookingConfirmedNotification 被派發至該預約的 Member

Scenario: Provider 拒絕預約觸發 BookingRejectedNotification 至學員

  • WHEN Provider 對 pending 預約呼叫 PUT /api/provider/bookings/{id}/reject
  • THEN BookingRejectedNotification 被派發至該預約的 Member

Scenario: Member 取消預約觸發 BookingCancelledNotification 至教練

  • WHEN Member 對 pending 預約呼叫 DELETE /api/member/bookings/{id}
  • THEN BookingCancelledNotification 被派發至該預約所屬的 Provider

Scenario: Provider 取消預約觸發 BookingCancelledNotification 至學員

  • WHEN Provider 對 confirmed 預約呼叫 PUT /api/provider/bookings/{id}/cancel
  • THEN BookingCancelledNotification 被派發至該預約的 Member

Requirement: 預約列表端點測試覆蓋

測試套件 SHALL 驗證下列三個路由的資料隔離行為:

  • GET /api/member/bookingsmiddleware: auth:sanctum,隔離靠 where member_id = auth()->id()
  • GET /api/provider/bookingsmiddleware: auth:sanctum,隔離靠 whereHas schedule.provider_id
  • GET /api/admin/bookingsmiddleware: auth:sanctum + adminEnsureAdmin 於 middleware 層強制 403

Scenario: Member 只能看到自己的預約

  • WHEN Member A 呼叫 GET /api/member/bookings,系統中同時存在 Member A 與 Member B 各一筆預約
  • THEN 回傳 data 只包含 Member A 的 booking idMember B 的 booking id 不在 data 中

Scenario: Provider 只能看到屬於自己課程的預約

  • WHEN Provider A 呼叫 GET /api/provider/bookings,系統中有屬於 Provider A 與 Provider B 課程各一筆預約
  • THEN 回傳 data 只包含 Provider A 課程的 booking idProvider B 課程的 booking id 不在 data 中

Scenario: Admin 可查看所有預約

  • WHEN Admin 呼叫 GET /api/admin/bookings,系統中有 2 筆不同 Member 的預約
  • THEN 回傳 data 包含兩筆 booking id

Scenario: 未認證請求被拒絕

  • WHEN 未帶 token 呼叫 GET /api/member/bookings
  • THEN 回傳 HTTP 401