sam-gong-game

測試計畫

TEST_PLAN — 測試計畫

<!-- SDLC QA — Layer 4:Test Plan Document -->


Document Control

欄位內容
DOC-IDTEST-PLAN-SAM-GONG-GAME-20260422
專案名稱三公遊戲(Sam Gong 3-Card Poker)即時多人線上平台
文件版本v1.0-draft
狀態DRAFT(STEP-14 自動生成)
作者Evans Tseng(由 /devsop-autodev STEP-14 自動生成)
日期2026-04-22
來源 EDDEDD-SAM-GONG-GAME-20260422 v1.4-draft
來源 PRDPRD-SAM-GONG-GAME-20260421 v0.14-draft

Change Log

版本日期作者變更摘要
v1.0-draft2026-04-22/devsop-autodev STEP-14初稿;包含 Unit/Integration/E2E/Performance/UAT 五層完整測試計畫;依 EDD §8.4 Testing Strategy 生成

1. Overview

1.1 測試目標

本測試計畫涵蓋三公遊戲(Sam Gong 3-Card Poker)線上多人平台的全面品質驗證,確保:

  1. 遊戲邏輯正確性:HandEvaluator、SettlementEngine、BankerRotation 等核心模組結果可靠
  2. 防沉迷合規性:符合台灣法規對成人(2h 提醒)與未成年(2h 硬停)的要求
  3. 安全性:JWT 驗證、Rate Limit、SQL Injection 防護、封號即時踢出等機制有效
  4. 效能達標:500 CCU 下 P95 WS 延遲 ≤ 100ms,SLA ≥ 99.5%/月
  5. 業務驗收:所有 PRD 驗收標準(Acceptance Criteria)通過 UAT

1.2 覆蓋率要求

指標目標值CI 門控
Lines Coverage≥ 80%PR 合併阻擋
Branches Coverage≥ 80%PR 合併阻擋
Functions Coverage≥ 80%PR 合併阻擋
Statements Coverage≥ 80%PR 合併阻擋
HandEvaluator 測試向量≥ 200 個CI 驗證

CI 命令:

jest --coverage --coverageThreshold='{"global":{"lines":80,"branches":80,"functions":80,"statements":80}}'

1.3 測試環境概覽

環境用途觸發條件
local開發者本機單元測試本地 npm test
dev持續整合(Unit + Integration)push to feature/*
stagingE2E + 效能測試push to develop
prodUAT + Smoke Test(上線後)部署完成後

2. Test Strategy

2.1 測試層次架構

┌─────────────────────────────────────────────────────┐
│  UAT(User Acceptance Testing)                      │
│  業務驗收、端到端場景、真實使用者操作流程              │
├─────────────────────────────────────────────────────┤
│  Performance Tests(效能測試)                        │
│  k6 WS 負載、壓力、尖峰、耐久測試                     │
├─────────────────────────────────────────────────────┤
│  E2E Tests(端到端測試)                              │
│  多玩家真實房間、完整遊戲週期、滲透測試                │
├─────────────────────────────────────────────────────┤
│  Integration Tests(整合測試)                        │
│  @colyseus/testing Room 測試、REST API 整合、DB 測試  │
├─────────────────────────────────────────────────────┤
│  Unit Tests(單元測試)                               │
│  Jest、純函數、各模組獨立測試                          │
└─────────────────────────────────────────────────────┘

2.2 各層測試策略

層次框架執行頻率範圍隔離層級
UnitJest + ts-jest每次 commit純函數、類別方法完全 Mock 外部依賴
IntegrationJest + @colyseus/testing每次 PRRoom 生命週期、API、DB真實 Redis/DB 容器(testcontainers)
E2EJest + Playwright/Colyseus Client SDK每次 develop 分支完整遊戲流程Staging 環境
Performancek6每週 + 每次 Release500 CCU 並發Staging 環境
UAT手動 + BDDRelease 前PRD AC 驗收Staging/Beta 環境

2.3 測試資料策略


3. Unit Test Plan

工具:Jest 29.x + ts-jest + @colyseus/testing

執行命令npm run test:unit

測試目錄tests/unit/

3.1 HandEvaluator Tests

模組server/src/game/HandEvaluator.ts

描述:測試 3 張牌點數計算(mod 10)、三公判定、D8 同點比牌(花色、牌值)邏輯。

測試向量格式:TC-ID | 描述 | 輸入(3 張牌)| 預期輸出 | 優先級

3.1.1 基本點數計算(calculate)

TC-ID描述輸入預期輸出優先級
TC-HE-001三公(J+Q+K)所有人頭牌[{value:'J'},{value:'Q'},{value:'K'}]{ points:0, is_sam_gong:true, hand_type:'sam_gong' }P0
TC-HE-002三公(10+Q+K)[{value:'10'},{value:'Q'},{value:'K'}]{ points:0, is_sam_gong:true, hand_type:'sam_gong' }P0
TC-HE-003三公(J+J+8)point=0+0+8=8, NOT三公[{value:'J'},{value:'J'},{value:'8'}]{ points:8, is_sam_gong:false, hand_type:'8' }P0
TC-HE-0049點(A+8=1+8=9)[{value:'A',point:1},{value:'8',point:8},{value:'K',point:0}]{ points:9, is_sam_gong:false, hand_type:'9' }P0
TC-HE-0058點(3+5=8)[{value:'3'},{value:'5'},{value:'K'}]{ points:8, is_sam_gong:false, hand_type:'8' }P0
TC-HE-0060點(非三公,2+4+4=10 mod 10=0)[{value:'2'},{value:'4'},{value:'4'}]{ points:0, is_sam_gong:false, hand_type:'0' }P0
TC-HE-0071點(A+K+K=1+0+0=1)[{value:'A'},{value:'K'},{value:'K'}]{ points:1, is_sam_gong:false, hand_type:'1' }P1
TC-HE-0082點(A+A=1+1=2)[{value:'A'},{value:'A'},{value:'K'}]{ points:2, is_sam_gong:false, hand_type:'2' }P1
TC-HE-0095點(2+3=5)[{value:'2'},{value:'3'},{value:'Q'}]{ points:5, is_sam_gong:false, hand_type:'5' }P1
TC-HE-0106點(6+J+Q=6+0+0=6)[{value:'6'},{value:'J'},{value:'Q'}]{ points:6, is_sam_gong:false, hand_type:'6' }P1
TC-HE-0117點(7+K=7+0=7)[{value:'7'},{value:'K'},{value:'J'}]{ points:7, is_sam_gong:false, hand_type:'7' }P1
TC-HE-012整數運算邊界:不使用浮點[{value:'9'},{value:'9'},{value:'9'}]{ points:7, is_sam_gong:false, hand_type:'7' }P0
TC-HE-013A 點數為 1(非 11)[{value:'A'},{value:'9'},{value:'9'}]{ points:9, is_sam_gong:false, hand_type:'9' }P0
TC-HE-0144點(4+K+Q=4+0+0=4)[{value:'4'},{value:'K'},{value:'Q'}]{ points:4, is_sam_gong:false, hand_type:'4' }P1
TC-HE-0153點(3+J+Q=3+0+0=3)[{value:'3'},{value:'J'},{value:'Q'}]{ points:3, is_sam_gong:false, hand_type:'3' }P1

3.1.2 D8 同點比牌(tiebreak)

TC-ID描述輸入(hand1 vs hand2)預期輸出優先級
TC-HE-016花色比較:♠ 優於 ♥hand1=[{suit:'spade',value:'3'},...], hand2=[{suit:'heart',value:'3'},...]1(hand1 勝)P0
TC-HE-017花色比較:♥ 優於 ♦hand1=[{suit:'heart',value:'3'},...], hand2=[{suit:'diamond',value:'3'},...]1(hand1 勝)P0
TC-HE-018花色比較:♦ 優於 ♣hand1=[{suit:'diamond',value:'3'},...], hand2=[{suit:'club',value:'3'},...]1(hand1 勝)P0
TC-HE-019花色相同,牌值比較:K > Qhand1=[{suit:'spade',value:'K'},...], hand2=[{suit:'spade',value:'Q'},...]1(hand1 勝)P0
TC-HE-020牌值比較:A 最小hand1=[{suit:'spade',value:'2'},...], hand2=[{suit:'spade',value:'A'},...]1(hand1 勝)P0
TC-HE-021完全相同手牌(平手)hand1=hand2(相同花色/牌值組合)0(平手)P0
TC-HE-022最大花色比較(取手中最大花色張)hand1=[spade2, club3, club4], hand2=[heart2, heart3, heart4]1(hand1 勝,spade > heart)P1
TC-HE-023花色相同下最大牌值比較hand1=[spade_K, spade_2, spade_3], hand2=[spade_Q, spade_J, spade_A]1(hand1 勝,K > Q)P1

3.1.3 compare(閒家 vs 莊家)

TC-ID描述輸入預期輸出優先級
TC-HE-024閒家三公 vs 莊家 9 點player=三公, banker=9pt'win'P0
TC-HE-025莊家三公 vs 閒家 9 點player=9pt, banker=三公'lose'P0
TC-HE-026閒家 9 點 vs 莊家 8 點player=9pt, banker=8pt'win'P0
TC-HE-027閒家 5 點 vs 莊家 7 點player=5pt, banker=7pt'lose'P0
TC-HE-028同點 D8 tiebreak 閒家勝player=8pt spade, banker=8pt heart'win'P0
TC-HE-029同點 D8 tiebreak 完全平手player=8pt, banker=8pt(同花色同牌值)'tie'P0

3.2 SettlementEngine Tests

模組server/src/game/SettlementEngine.ts

描述:測試三步驟結算邏輯、Rake 計算、莊家破產先到先得、全員棄牌、籌碼守恆。

TC-ID描述輸入情境預期輸出優先級
TC-SE-001正常結算:1 贏家(N=1 倍),1 輸家player1(call,win,pt=8), player2(call,lose,pt=5), banker_bet=1000, pot=1000player1 net=+1000, player2 net=-1000, rake=50(floor(1000×0.05)), 守恆驗證通過P0
TC-SE-002三公贏家(N=3 倍)結算player1(call,三公,win), banker_bet=500, pot=500(player2 lose)player1 net=+1500, player2 net=-500, rake=25, 莊家 net=-(1500+25)+(500-25)=-1025P0
TC-SE-0039 點贏家(N=2 倍)結算player1(call,9pt,win), banker_bet=500player1 net=+1000, rake 計算正確P0
TC-SE-004全員 Fold(all_fold)情境所有閒家 foldpot=0, rake=0, banker escrow 退回, all_fold=trueP0
TC-SE-0052 人桌唯一閒家 Foldbanker+1 閒家,閒家 foldall_fold=true, pot=0, rake=0, banker_chips 退回 escrowP0
TC-SE-0066 人桌全員 Fold5 名閒家全部 foldall_fold=true, pot=0, rake=0, 莊家 escrow 退回P0
TC-SE-007莊家破產先到先得(順時針)banker_chips=800, 2 贏家各 bet=500, 順時針 seat1 先seat1 獲全額支付(N=1=500), seat2 進入 insolvent_winners, net_chips=-500P0
TC-SE-008Insolvent Winner net_chips 必須非零莊家破產,贏家未獲支付insolvent_winner.net_chips = -called_bet(NOT 0)P0
TC-SE-009Rake 計算:pot=0 時 rake=0全員 fold,pot=0rake_amount=0(不適用最少 1 規則)P0
TC-SE-010Rake 計算:pot=1 時 rake=1(min 1 規則)pot=1,floor(1×0.05)=0,but min=1rake_amount=1P0
TC-SE-011Rake 計算:pot=100,rake=5pot=100rake_amount=5(floor(100×0.05)=5)P1
TC-SE-012平手(tie)情境player 同點同 D8,result=tienet_chips=0, payout_amount=0(退注),不入底池P0
TC-SE-013籌碼守恆驗證任意合法結算sum(all net_chips) + rake_amount === 0P0
TC-SE-014多贏家情境(2 贏 1 輸)player1(win,N=1), player2(win,N=1), player3(lose)每位贏家各得 banker_bet,輸家底池扣 rake 歸莊,守恆P1
TC-SE-015莊家 net_chips 計算2 閒家:1 贏家 N=1, 1 輸家banker_net = loser_bet - rake - winner_payoutP0

3.3 BankerRotation Tests

模組server/src/game/BankerRotation.ts

描述:測試首莊決定、順時針輪莊、跳過不合格莊家、環繞邊界。

TC-ID描述輸入預期輸出優先級
TC-BR-001首莊:持最多籌碼者[p0=5000, p1=3000, p2=2000]banker=p0(seat 0)P0
TC-BR-002首莊:同籌碼按進入順序[p0=5000, p1=5000]banker=p0(先進入者)P1
TC-BR-003順時針輪莊:seat 0 → seat 1currentBanker=0, seats=[0,1,2,3]nextBanker=1P0
TC-BR-004環繞邊界:最後玩家 → 第一玩家currentBanker=3, seats=[0,1,2,3]nextBanker=0P0
TC-BR-005跳過破產莊家(chip < min_bet)seats=[0,1,2], seat1 chip=0, min_bet=100跳過 seat1,nextBanker=2P0
TC-BR-006所有候選莊家均不合格所有玩家 chip < min_bet回到 waiting 狀態(返回 -1 或特殊標記)P0
TC-BR-007中途離場玩家從輪莊序列移除seats=[0,1,2], seat1 離場後 rotatenextBanker 跳過 seat1P1
TC-BR-0082 人桌輪莊currentBanker=0, seats=[0,1]nextBanker=1P0
TC-BR-009skipInsolventBanker 環繞seats=[0,1,2], seat0 破產, currentBanker=2skipInsolventBanker → 跳過 seat0,選 seat1P1

3.4 AntiAddictionManager Tests

模組server/src/game/AntiAddictionManager.ts

描述:測試成人 2h 提醒、未成年 2h 硬停、Write-Through 機制、UTC+8 午夜重置。

TC-ID描述輸入預期輸出優先級
TC-AA-001成人連續遊玩 < 2h,無提醒trackAdultSession,sessionSeconds=3600status=normal(無警告)P0
TC-AA-002成人連續遊玩達 2h,觸發提醒sessionSeconds=7200status=warning, 送出 anti_addiction_warning { type:'adult' }P0
TC-AA-003成人 2h 提醒後重複:未確認不重置提醒後繼續計時仍維持 warning 狀態P1
TC-AA-004成人確認提醒後計時重置onAdultWarningConfirmed(playerId)sessionSeconds 重置為 0P0
TC-AA-005成人離線 > 30min 後重新連線,計時重置離線 31min 後重連sessionSeconds 重置為 0P1
TC-AA-006未成年每日 < 2h,可繼續遊玩trackUnderageDaily,dailySeconds=3600status=normalP0
TC-AA-007未成年每日達 2h,觸發硬停dailySeconds=7200status=hard_stop, 送出 anti_addiction_signal { type:'underage' }, WS Close 4003P0
TC-AA-008未成年每日上限 UTC+8 午夜重置次日 00:00 UTC+8 後dailySeconds 重置為 0,可重新連線P0
TC-AA-009getTaiwanMidnightTimestamp 計算正確任意 UTC 時間返回下一個 UTC+8 00:00 的 Unix msP0
TC-AA-010Write-Through:每局 settled 後寫入 PostgreSQLsettle 事件觸發DB users.daily_play_seconds 更新正確P0
TC-AA-011Redis failover 後從 PostgreSQL 回填Redis 重啟從 DB 回填計時資料,不丟失P0
TC-AA-012confirm_anti_addiction payload 驗證 { type:'adult' }發送 { type:'adult' }onAdultWarningConfirmed 被呼叫P0
TC-AA-013未成年牌局中達到 2h,等待本局結算後登出遊戲進行中達限scheduleUnderageLogout 設定,本局結算後觸發 WS Close 4003P0

3.5 TutorialScriptEngine Tests

模組server/src/game/TutorialScriptEngine.ts

描述:測試教學固定劇本載入、三局腳本正確性、牌張唯一性。

TC-ID描述輸入預期輸出優先級
TC-TS-001R1 劇本:莊家三公(K♠/Q♥/J♦)loadScript(1)banker_hand=['K♠','Q♥','J♦'], is_sam_gong=true, expected_outcome='banker_win'P0
TC-TS-002R1 劇本:閒家 1pt(A♠/2♣/8♦)loadScript(1)player_hand=['A♠','2♣','8♦'](1+2+8=11 mod 10=1pt)P0
TC-TS-003R2 劇本:莊家 5pt(5♠/A♥/9♦)loadScript(2)banker_hand=['5♠','A♥','9♦'](5+1+9=15 mod 10=5pt)P0
TC-TS-004R2 劇本:閒家 3pt(3♦/K♣/Q♠)loadScript(2)player_hand=['3♦','K♣','Q♠'](3+0+0=3pt)P0
TC-TS-005R3 劇本:平局(force_tie=true)loadScript(3)banker=6pt, player=6pt, force_tie=true, expected_outcome='tie'P0
TC-TS-00618 張牌全部唯一(無重複)loadScript(1/2/3) 全部18 張牌張集合無任何重複P0
TC-TS-007非法 round 數值報錯loadScript(4 as any)拋出 ErrorP1
TC-TS-008tutorial_mode=false 時不使用固定劇本正常遊戲模式DeckManager 使用 crypto.randomInt 洗牌P0

3.6 DeckManager Tests

模組server/src/game/DeckManager.ts

TC-ID描述輸入預期輸出優先級
TC-DM-001buildDeck 產生 52 張唯一牌buildDeck()deck.length=52,無重複P0
TC-DM-002shuffle 後仍為 52 張唯一牌shuffle()洗牌後 deck.length=52,無重複P0
TC-DM-003禁止 Math.random(使用 crypto.randomInt)靜態分析程式碼不含 Math.randomP0
TC-DM-004deal(3) 返回 3 張牌deal(3)cards.length=3P0
TC-DM-0056 人桌 deal 18 張,牌張唯一deal 6 次 × 318 張無重複P0

3.7 Rate Limit Tests

模組server/src/middleware/rateLimit.ts

TC-ID描述輸入預期輸出優先級
TC-RL-001認證端點 < 30 次/min 不觸發限制29 req/min/IP to /auth/*HTTP 200(正常回應)P0
TC-RL-002認證端點達 30 次/min 觸發限制30 req/min/IP to /auth/*HTTP 429 + Retry-After headerP0
TC-RL-003高敏感端點 5 次/min 限制6 req/min/user to /player/daily-chipHTTP 429P0
TC-RL-004一般端點 60 次/min 限制61 req/min/user to /player/meHTTP 429P0
TC-RL-005IP 全局 300 次/min 限制301 req/min/IPHTTP 429P0

4. Integration Test Plan

工具:Jest + @colyseus/testing + testcontainers

執行命令npm run test:integration

測試目錄tests/integration/

前提條件:Docker 運行中(PostgreSQL + Redis 容器自動啟動)

4.1 Room Lifecycle Tests

工具@colyseus/testingColyseusTestServer.create(SamGongRoom)

TC-ID描述步驟預期結果優先級
TC-RL-INT-001Room onCreate 初始化狀態正確createRoom('sam_gong', { tier:'青銅廳' })state.phase='waiting', state.tier_config.min_bet=100P0
TC-RL-INT-002玩家加入:JWT 驗證通過connectTo(room, { token: validJWT })onJoin 成功,PlayerState 初始化P0
TC-RL-INT-003玩家加入:JWT 失效拒絕connectTo(room, { token: expiredJWT })onJoin 拋出 ServerErrorP0
TC-RL-INT-004玩家加入:籌碼不足拒絕connectTo(room, chip_balance=500, tier entry_chips=1000)onJoin 拋出 ServerError(insufficient_chips)P0
TC-RL-INT-0052 玩家加入後自動進入 dealing phaseconnectTo × 2state.phase='dealing'P0
TC-RL-INT-006玩家斷線 30s 重連視窗disconnect client, reconnect within 30sPlayerState 保留,推送 myHandP0
TC-RL-INT-007玩家斷線超過 30s 自動 Folddisconnect client, wait 31splayer.is_folded=trueP0
TC-RL-INT-008onDispose 寫入 game_sessionsdestroyRoomDB 存在對應 game_sessions 記錄P0
TC-RL-INT-009房間最大容量 6 人connectTo × 7第 7 位拒絕加入(room_full)P1
TC-RL-INT-01060s 無人加入自動解散createRoom, 不加入玩家,等 60sRoom 銷毀(onDispose 呼叫)P1

4.2 Game Flow Tests

描述:完整遊戲流程從 onCreate 到 settled phase 的整合測試。

TC-ID描述步驟預期結果優先級
TC-GF-001完整遊戲一局(2 人)join×2 → dealing → banker_bet → call → showdown → settledphase 依序正確轉換,net_chips 正確P0
TC-GF-002banker_bet 計時器超時自動最低下注不發送 banker_bet,等 30sbanker_bet = min_bet,phase='player-bet'P0
TC-GF-003閒家 call 計時器超時自動 Fold不發送 call/fold,等 30splayer.is_folded=trueP0
TC-GF-004結算後 5s 自動開始下一局settled 後等 5sphase='dealing',round_number+1P0
TC-GF-005結算後玩家不足進入 waitingsettled 後玩家 < 2phase='waiting'P1
TC-GF-006多局連續輪莊正確完成 3 局banker_seat_index 每局正確輪換P0
TC-GF-007resetForNextRound 正確清空狀態第 2 局開始前bet_amount=0, is_folded=false, settlement 清空P0

4.3 WebSocket Message Tests

描述:所有 Client → Server 訊息類型的整合驗證。

TC-ID描述訊息類型輸入預期結果優先級
TC-WS-001banker_bet 合法下注banker_bet{ amount: 200 }(min=100, max=500)state.banker_bet_amount=200, phase='player-bet'P0
TC-WS-002banker_bet 下注超過上限拒絕banker_bet{ amount: 600 }(max=500)error { code:'bet_out_of_range' }P0
TC-WS-003banker_bet 在 player-bet phase 發送拒絕banker_betphase='player-bet' 時發送error { code:'invalid_phase' }P0
TC-WS-004call 合法跟注call{} 在 player-bet phase,本人輪次player.bet_amount=banker_bet_amountP0
TC-WS-005call 籌碼不足拒絕callchip_balance < banker_bet_amounterror { code:'insufficient_chips' }P0
TC-WS-006fold 合法棄牌fold{} 在 player-bet phase,本人輪次player.is_folded=true, bet_amount=0P0
TC-WS-007see_cards 莊家查看手牌see_cards{} 在 banker-bet phase,is_banker=true發送私人 myHand 訊息P1
TC-WS-008send_chat 合法訊息send_chat{ text: '你好' }(≤200字)廣播至房間P1
TC-WS-009send_chat 超長訊息拒絕send_chat{ text: 'x'.repeat(201) }errorP1
TC-WS-010confirm_anti_addictionconfirm_anti_addiction{ type:'adult' }antiAddiction.onAdultWarningConfirmed 呼叫P0
TC-WS-011非當前輪次玩家操作拒絕call非輪次玩家發送error(非本人輪次)P0
TC-WS-012封號後 WS Close 4001 即時踢出Admin ban → Redis Pub/Subclient.leave(4001)P0

4.4 REST API Integration Tests

描述:對 Express REST API 端點的整合測試(真實 DB + Redis)。

TC-ID描述端點輸入預期回應優先級
TC-API-001用戶登入成功POST /api/v1/auth/login{ credential: valid }200, { access_token, refresh_token }P0
TC-API-002用戶登入失敗(錯誤憑證)POST /api/v1/auth/login{ credential: invalid }401, { error:'account_not_found' }P0
TC-API-003Refresh Token RotationPOST /api/v1/auth/refresh{ refresh_token: valid }200, 新 access_token 與 refresh_tokenP0
TC-API-004取得玩家資料(JWT 驗證)GET /api/v1/player/meAuthorization: Bearer validJWT200, 玩家資料P0
TC-API-005取得玩家資料(無 JWT)GET /api/v1/player/me無 Authorization header401P0
TC-API-006每日籌碼領取(冪等)POST /api/v1/player/daily-chip同一天呼叫兩次第一次 200,第二次 400(已領取)P0
TC-API-007救援籌碼申請(< 500 籌碼)POST /api/v1/player/rescue-chipchip_balance=300200, new_balance=1300P0
TC-API-008救援籌碼申請(籌碼足夠拒絕)POST /api/v1/player/rescue-chipchip_balance=600403P0
TC-API-009排行榜查詢GET /api/v1/leaderboardweek=current200, 玩家清單(僅 show_in_leaderboard=true)P0
TC-API-010OTP 年齡驗證流程POST /api/v1/auth/otp/send + /verify有效 OTP200, age_verified=trueP0
TC-API-011SQL Injection 防護POST /api/v1/auth/login{ credential: "'; DROP TABLE users;--" }400 或 401,DB 無影響P0
TC-API-012封號 API(Admin)POST /api/v1/admin/player/:id/banAdmin JWT + player_id200, player is_banned=true, Redis key 設定P0
TC-API-013Rate Limit auth 端點(30/min/IP)POST /api/v1/auth/login ×31快速連續呼叫第 31 次返回 429P0
TC-API-014健康檢查端點GET /api/v1/health200 { status:'ok' }P1
TC-API-015就緒探針(DB + Redis 連線)GET /api/v1/health/readyDB/Redis 正常200P0

5. E2E Test Plan

工具:Jest + Colyseus Client SDK + 真實 Staging 環境

執行命令npm run test:e2e

測試目錄tests/e2e/

前提條件:Staging 環境部署完成,Fixtures 資料已載入

5.1 Happy Path:正常遊戲流程

TC-ID描述步驟預期結果優先級
TC-E2E-001新用戶註冊 → 年齡驗證 → 教學 → 進入青銅廳register → OTP verify → tutorial → join matchmaking成功進入青銅廳遊戲房間P0
TC-E2E-0022 人完整一局遊戲2 玩家加入 → 莊家下注 → 閒家 Call → Showdown → 結算籌碼正確增減,phase 依序正確P0
TC-E2E-0036 人滿房遊戲6 玩家加入 → 完整一局所有玩家結算正確,輪莊正確P0
TC-E2E-004私人房間建立與加入POST /rooms/private → 取得 room_code → 其他玩家用 room_code 加入成功建立私人房間,其他玩家正常加入P1
TC-E2E-005排行榜查詢正確反映遊戲結果遊戲結束後查詢排行榜本週淨籌碼正確更新(≤ 1min 延遲)P1
TC-E2E-006每日任務完成流程完成 3 局 → GET /tasks → POST /tasks/:id/complete任務標記完成,獎勵發放正確P1
TC-E2E-007救援籌碼自動觸發結算後 chip_balance < 500Server 自動發送 rescue_chips 通知 + 籌碼更新P0
TC-E2E-008斷線重連正常流程遊戲進行中斷線 → 30s 內重連恢復 PlayerState,收到 myHand 私人訊息P0

5.2 Edge Cases:邊界情境

TC-ID描述步驟預期結果優先級
TC-E2E-EC-0012 人桌唯一閒家 Fold → all_fold1 莊家 + 1 閒家,閒家 foldall_fold=true, pot=0, rake=0, 莊家 escrow 退回P0
TC-E2E-EC-0026 人桌全員 Fold5 名閒家全部 foldall_fold=true, banker escrow 退回P0
TC-E2E-EC-003莊家破產先到先得莊家籌碼不足支付所有贏家順時針順序先支付,後者進入 insolvent_winnersP0
TC-E2E-EC-004莊家籌碼 < min_bet 跳過輪莊莊家破產後輪莊跳過破產莊家,下一位符合資格者成為莊家P0
TC-E2E-EC-005所有玩家籌碼不足莊家資格全員 chip < min_betRoom 返回 waiting 狀態P1
TC-E2E-EC-006同時多位玩家救援籌碼競態2 玩家同時觸發 rescue chipDB 原子操作確保只發放一次(冪等)P0
TC-E2E-EC-007斷線超過 30s 自動 Fold 並繼續遊戲玩家斷線 31sis_folded=true,遊戲繼續進行P0
TC-E2E-EC-008未成年遊玩 2h 後本局結算登出未成年玩家連續 2h本局結算完成後 WS Close 4003P0
TC-E2E-EC-009多裝置登入踢出舊裝置(Close 4005)同帳號從第二裝置登入舊裝置收到 WS Close 4005P1
TC-E2E-EC-010pot=1 時 rake=1(最小 rake 規則)1 名輸家 bet=1rake_amount=1(floor(0.05)=0 → min=1)P0
TC-E2E-EC-011Insolvent winner net_chips 必須是 -called_bet莊家破產,贏家未獲支付net_chips = -called_bet(非 0)P0
TC-E2E-EC-012教學模式完整 3 局流程tutorial_mode=true,完成 R1/R2/R3tutorial_completed=true,導向正式遊戲P1

5.3 Security Tests:滲透測試場景

TC-ID描述測試方式預期結果優先級
TC-SEC-001JWT 失效後拒絕所有操作使用過期 Access Token 呼叫任何 API401 { error:'token_expired' }P0
TC-SEC-002封號後即時踢出 WS(Close 4001)Admin ban → 等待 Redis Pub/Sub60s 內所有 Active WS 收到 Close 4001P0
TC-SEC-003SQL Injection 防護(多端點)Payload: ' OR 1=1; --'; DROP TABLE users;--400/401,DB 無結構變更P0
TC-SEC-004Rate Limit auth 端點(30/min/IP)31 次/min 對 /auth/login第 31 次 429 + Retry-AfterP0
TC-SEC-005Rate Limit WS 訊息(10/s/連線)每秒發送 > 10 條 WS 訊息超限訊息被丟棄/返回 rate_limit 錯誤P0
TC-SEC-006手牌資訊隔離(Wireshark 測試)6 人房間,Wireshark 抓包每位玩家封包僅含自身手牌,無他人手牌P0
TC-SEC-007Client 無法呼叫 Server-only 模組嘗試 import HandEvaluator 於 Client bundleCI build 失敗(TypeScript project references 邊界)P0
TC-SEC-008Admin API 非內網拒絕從外部 IP 呼叫 /api/v1/admin/*403 或連線拒絕P0
TC-SEC-009CORS 非允許域名拒絕Origin: https://attacker.comAccess-Control-Allow-Origin 不包含攻擊者域名P0
TC-SEC-010Refresh Token 一次性使用(Rotation)使用相同 Refresh Token 兩次第二次返回 401 { error:'token_revoked' }P0
TC-SEC-011非法操作(非輪次玩家下注)非輪次閒家發送 callerror(非法操作)P0
TC-SEC-012下注金額超出餘額拒絕banker_bet > chip_balanceerror { code:'insufficient_chips' }P0

6. Performance Test Plan

工具:k6 + @colyseus/loadtest

執行命令npm run test:perf

測試目錄tests/performance/

前提條件:Staging 環境 k8s 部署(≥ 2 Colyseus Pod)

6.1 Load Test:500 CCU

目標:驗證 NFR-02(P95 WS 延遲 ≤ 100ms,500 CCU)

// tests/performance/ws-load.js
import ws from 'k6/ws';
import { check } from 'k6';

export let options = {
  vus: 500,          // 500 Virtual Users = 500 CCU
  duration: '10m',   // 持續 10 分鐘
  thresholds: {
    'ws_connecting': ['p(95)<1000'],       // WS 建立 ≤ 1s
    'ws_msgs_received': ['p(95)<100'],     // 收到訊息 P95 ≤ 100ms
    'ws_session_duration': ['p(95)<15000'], // 會話持續
  },
};
TC-ID指標目標值測試持續時間
TC-PERF-LT-001P95 WS 延遲≤ 100ms10 分鐘穩態
TC-PERF-LT-002P99 WS 延遲≤ 500ms10 分鐘穩態
TC-PERF-LT-003WebSocket 連線成功率≥ 99.5%10 分鐘
TC-PERF-LT-004API 錯誤率(5xx)≤ 0.1%10 分鐘
TC-PERF-LT-005每秒 WS 訊息吞吐量≥ 330 msg/s(估算峰值)10 分鐘
TC-PERF-LT-006PostgreSQL P95 查詢延遲≤ 50ms測試期間監控
TC-PERF-LT-007Redis P95 操作延遲≤ 5ms測試期間監控
TC-PERF-LT-008記憶體使用量(每 Pod)≤ 2Gi測試期間監控
TC-PERF-LT-009CPU 使用率(每 Pod)≤ 70%(HPA 觸發門檻)測試期間監控

6.2 Stress Test:逐步增加至失效點

目標:找出系統極限,驗證 HPA 自動擴展行為

export let options = {
  stages: [
    { duration: '5m', target: 200 },   // 爬升至 200 CCU
    { duration: '5m', target: 500 },   // 爬升至 500 CCU
    { duration: '5m', target: 1000 },  // 爬升至 1,000 CCU
    { duration: '5m', target: 2000 },  // 爬升至 2,000 CCU(目標上限)
    { duration: '5m', target: 0 },     // 冷卻
  ],
};
TC-ID場景觀察指標預期行為優先級
TC-PERF-ST-001200 CCU 穩態延遲、錯誤率P95 ≤ 100ms,無錯誤P0
TC-PERF-ST-002500 CCU 穩態延遲、錯誤率P95 ≤ 100ms,NFR-02 通過P0
TC-PERF-ST-0031,000 CCU 壓力測試HPA 觸發時機CPU > 70% 後 HPA 新增 PodP1
TC-PERF-ST-0042,000 CCU 極限測試系統降級行為優雅降級(HTTP 503 + Retry-After),不崩潰P1
TC-PERF-ST-005失效點識別錯誤率飆升時的 CCU記錄失效 CCU 值,作為容量規劃依據P1

6.3 Spike Test:瞬間湧入

目標:驗證突發流量下系統穩定性(如遊戲事件、社群擴散)

export let options = {
  stages: [
    { duration: '1m', target: 100 },   // 正常負載
    { duration: '30s', target: 800 },  // 瞬間湧入(8x)
    { duration: '3m', target: 800 },   // 維持高峰
    { duration: '1m', target: 100 },   // 回落
  ],
};
TC-ID場景觀察指標預期行為優先級
TC-PERF-SK-001瞬間湧入 800 CCU(30s 內)連線成功率、延遲連線成功率 ≥ 95%,延遲可暫時升高P1
TC-PERF-SK-002HPA 反應時間Pod 新增時間CPU 超限後 5 分鐘內新 Pod 就緒P1
TC-PERF-SK-003流量回落後延遲恢復回落後 P95 延遲恢復至 ≤ 100ms(5 分鐘內)P1

6.4 Endurance Test:長時間穩定性

目標:驗證 SLA 99.5%/月(error budget ≤ 3.65h/月),記憶體洩漏偵測

export let options = {
  vus: 300,            // 300 CCU 穩態
  duration: '4h',      // 4 小時耐久測試
};
TC-ID場景觀察指標預期行為優先級
TC-PERF-EN-0014 小時 300 CCU 穩態每小時 P95 延遲、錯誤率P95 始終 ≤ 100ms,錯誤率 ≤ 0.5%P0
TC-PERF-EN-002記憶體洩漏偵測每小時 Pod 記憶體使用量記憶體無持續增長趨勢P1
TC-PERF-EN-003DB 連線池穩定性pgBouncer 連線數保持在 max_client_conn=200 以下P0
TC-PERF-EN-004Redis 操作穩定性Redis P95 延遲始終 ≤ 5msP0
TC-PERF-EN-005Colyseus Room 洩漏偵測活躍 Room 數量無廢棄 Room 累積(onDispose 正確清理)P1

6.5 DB Failover Test

目標:驗證 NFR-18(PostgreSQL Failover ≤ 5 分鐘)

TC-ID描述步驟預期結果優先級
TC-PERF-DB-001Primary DB 強制故障強制終止 PostgreSQL Primary PodReplica 在 5min 內提升為 PrimaryP0
TC-PERF-DB-002Failover 期間 API 降級Failover 過程中持續呼叫 API短暫 503,Failover 完成後自動恢復P0
TC-PERF-DB-003結算事務原子性(Failover 期間)Failover 時正在進行結算已提交事務完整保存,未提交事務回滾P0
TC-PERF-DB-004Redis Sentinel Failover強制終止 Redis MasterSentinel ≤ 60s 完成切換P0
TC-PERF-DB-005季度 Failover 演練定期演練(每季一次)記錄 RTO,確保 ≤ 5min 目標持續達標P1

7. UAT(User Acceptance Testing)

負責人:QA Lead + Product Owner

執行環境:Staging 環境(接近生產配置)

參考文件:PRD AC(Acceptance Criteria),BRD 業務需求

7.1 遊戲核心功能驗收

UAT-ID驗收標準PRD 來源驗收方式Pass 條件
UAT-GC-001三公遊戲規則正確(三公最大,賠率 3x)REQ-003手動對局驗證三公贏家獲得 3x 賠率
UAT-GC-0029 點賠率 2x,普通點賠率 1xREQ-003手動對局驗證賠率正確
UAT-GC-003全員棄牌時莊家底注退回REQ-003手動操作 all_fold莊家籌碼恢復
UAT-GC-004莊家破產先到先得(順時針)REQ-003設計特定場景後排贏家 net_chips=-called_bet
UAT-GC-005D8 平局規則(花色 → 牌值)REQ-003設計同點場景正確判定平局或勝負
UAT-GC-006輪莊機制:順時針,跳過破產REQ-001連續 5 局驗證輪莊順序正確
UAT-GC-007廳別進場門檻驗證REQ-001籌碼不足嘗試進場拒絕進場,顯示錯誤
UAT-GC-008計時器 30s 超時自動行動REQ-001 D11/D12等待超時自動最低下注/自動 Fold

7.2 防沉迷功能驗收

UAT-ID驗收標準PRD 來源驗收方式Pass 條件
UAT-AA-001成人連續 2h 顯示提醒彈窗REQ-015 AC-1模擬 2h 計時(加速測試)彈窗出現,遊戲繼續
UAT-AA-002成人確認提醒後計時重置REQ-015 AC-1點擊確認按鈕計時重置,2h 後再次提醒
UAT-AA-003未成年每日 2h 後強制登出REQ-015 AC-3模擬 2h(加速測試)本局結算後自動登出,顯示下線提示
UAT-AA-004未成年次日 UTC+8 00:00 後可重連REQ-015 AC-3次日模擬可正常連線遊戲
UAT-AA-005Server 端計時(不信任 Client)REQ-015Client 修改時間Server 計時不受影響

7.3 合規功能驗收

UAT-ID驗收標準PRD 來源驗收方式Pass 條件
UAT-CP-001虛擬籌碼不可兌換聲明顯示在 8 個畫面REQ-013 AC-5逐一查看畫面8 個畫面均顯示,≥ 12pt 字體
UAT-CP-002新帳號 OTP 年齡驗證(出生年份 + 6 碼 OTP)REQ-014新帳號流程100% 新帳號需完成年齡驗證
UAT-CP-003OTP 3 次錯誤失效REQ-014輸入 3 次錯誤 OTP第 3 次後 OTP 失效
UAT-CP-004Cookie 同意橫幅(首次載入)REQ-016首次開啟 Web 版顯示 Cookie 同意橫幅
UAT-CP-005帳號刪除 7 工作日內完成REQ-019申請刪除帳號7 工作日內收到刪除確認
UAT-CP-006聊天訊息 30 日後自動刪除REQ-007 AC-3查詢 30 日前訊息訊息不存在

7.4 社交功能驗收

UAT-ID驗收標準PRD 來源驗收方式Pass 條件
UAT-SO-001房間內聊天(≤ 200 字)REQ-007發送聊天訊息訊息廣播至房間
UAT-SO-002舉報玩家功能REQ-007舉報他人舉報記錄建立,狀態='pending'
UAT-SO-003週排行榜顯示(≤ 1min 更新)REQ-006完成遊戲後查詢排行榜排名在 1min 內更新
UAT-SO-004隱藏排行榜功能REQ-006設定 show_in_leaderboard=false排行榜不顯示該玩家
UAT-SO-005私人房間(6 位英數 room_code)REQ-010建立私人房間取得 6 位 room_code

7.5 教學功能驗收

UAT-ID驗收標準PRD 來源驗收方式Pass 條件
UAT-TT-001教學 R1:展示三公最高牌型REQ-012 AC-5完成教學 R1正確顯示三公說明
UAT-TT-002教學 R2:展示普通比點REQ-012 AC-5完成教學 R2正確顯示 5pt > 3pt
UAT-TT-003教學 R3:展示平局概念REQ-012 AC-5完成教學 R3正確顯示平局情境
UAT-TT-004教學完成後 tutorial_completed=trueREQ-012完成 3 局教學DB 更新 tutorial_completed=true
UAT-TT-005教學模式不可進入正式廳別REQ-012tutorial_mode=true 嘗試進入青銅廳拒絕,引導完成教學

8. Test Environment

8.1 docker-compose.test.yml

# tests/docker-compose.test.yml
version: '3.9'

services:
  postgres-test:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: sam_gong_test
      POSTGRES_USER: test_user
      POSTGRES_PASSWORD: test_password
    ports:
      - "5433:5432"  # 避免與 local dev DB 衝突
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test_user -d sam_gong_test"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis-test:
    image: redis:7-alpine
    ports:
      - "6380:6379"  # 避免與 local Redis 衝突
    command: redis-server --save "" --appendonly no  # 測試環境無需持久化
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 10

  colyseus-test:
    build:
      context: ../server
      dockerfile: Dockerfile.test
    environment:
      NODE_ENV: test
      DATABASE_URL: postgresql://test_user:test_password@postgres-test:5432/sam_gong_test
      REDIS_URL: redis://redis-test:6379
      JWT_PRIVATE_KEY: ${TEST_JWT_PRIVATE_KEY}
      JWT_PUBLIC_KEY: ${TEST_JWT_PUBLIC_KEY}
      TEST_OTP_BYPASS_CODE: "123456"
    depends_on:
      postgres-test:
        condition: service_healthy
      redis-test:
        condition: service_healthy
    ports:
      - "2568:2567"  # Colyseus WS(測試端口)
      - "3002:3000"  # REST API(測試端口)

8.2 測試資料庫管理

Schema 遷移:測試前執行 npm run db:migrate:test(node-pg-migrate + 測試 DB)

資料清理:每個 describe block 的 afterEach 執行 TRUNCATE(保留 Schema,清空資料)

Fixtures 腳本

// tests/fixtures/seed.ts
export async function seedTestData(db: Pool) {
  // 基本測試帳號
  await db.query(`
    INSERT INTO users (id, display_name, chip_balance, age_verified, is_minor)
    VALUES
      ('test-player-1', '測試玩家1', 50000, true, false),
      ('test-player-2', '測試玩家2', 50000, true, false),
      ('test-minor-1', '未成年玩家', 10000, true, true),
      ('test-banker-1', '測試莊家', 100000, true, false)
    ON CONFLICT (id) DO UPDATE SET chip_balance = EXCLUDED.chip_balance;
  `);
}

export async function cleanupTestData(db: Pool) {
  await db.query(`TRUNCATE chip_transactions, game_sessions, game_rooms,
                           daily_tasks, leaderboard_weekly, player_reports,
                           chat_messages, refresh_tokens RESTART IDENTITY CASCADE`);
}

8.3 環境變數(測試環境)

# .env.test
NODE_ENV=test
DATABASE_URL=postgresql://test_user:test_password@localhost:5433/sam_gong_test
REDIS_URL=redis://localhost:6380
TEST_OTP_BYPASS_CODE=123456
JWT_PRIVATE_KEY=<test RSA private key>
JWT_PUBLIC_KEY=<test RSA public key>
SYSTEM_ACCOUNT_UUID=00000000-0000-0000-0000-000000000001
RESCUE_CHIP_THRESHOLD=500
RESCUE_CHIP_AMOUNT=1000

8.4 Mock 策略詳細配置

依賴單元測試 Mock整合測試 Mock
Redisioredis-mock npm 套件真實 Redis 容器(docker-compose.test.yml)
PostgreSQLpg-mem npm 套件(in-memory)testcontainers 真實 PostgreSQL 容器
OTP SMS環境變數 TEST_OTP_BYPASS_CODE=123456同左(固定繞過碼)
JWT 公私鑰測試專用 RSA 2048-bit Key Pair(不用於生產)同左
AWS KMSJest mock(jest.mock('aws-sdk')同左
Colyseus Client@colyseus/testing ColyseusTestServer真實 Staging Colyseus Server

9. Test Coverage Requirements

9.1 覆蓋率目標

模組目標覆蓋率測試案例數(最低)
HandEvaluator.ts≥ 90%≥ 200 個測試向量
SettlementEngine.ts≥ 90%≥ 50 個測試案例
BankerRotation.ts≥ 85%≥ 20 個測試案例
AntiAddictionManager.ts≥ 85%≥ 20 個測試案例
TutorialScriptEngine.ts≥ 80%≥ 10 個測試案例
DeckManager.ts≥ 80%≥ 10 個測試案例
REST API Endpoints≥ 80%全端點覆蓋
SamGongRoom.ts≥ 80%整合測試覆蓋
整體覆蓋率≥ 80%(lines/branches/functions)CI 門控

9.2 CI 門控設定

// jest.config.ts
{
  "coverageThreshold": {
    "global": {
      "lines": 80,
      "branches": 80,
      "functions": 80,
      "statements": 80
    },
    "./server/src/game/HandEvaluator.ts": {
      "lines": 90,
      "branches": 90,
      "functions": 90
    },
    "./server/src/game/SettlementEngine.ts": {
      "lines": 90,
      "branches": 90,
      "functions": 90
    }
  }
}

9.3 CI Pipeline 測試階段

# .github/workflows/ci.yml 測試相關步驟

jobs:
  test:
    runs-on: ubuntu-22.04
    services:
      postgres:
        image: postgres:16
        env: { POSTGRES_DB: sam_gong_test, POSTGRES_USER: test, POSTGRES_PASSWORD: test }
        options: --health-cmd pg_isready
      redis:
        image: redis:7
        options: --health-cmd "redis-cli ping"

    steps:
      - name: Lint(含 Client bundle 關鍵字掃描)
        run: npm run lint

      - name: TypeScript 型別檢查
        run: npm run type-check

      - name: Unit Tests(含覆蓋率)
        run: npm run test:unit -- --coverage

      - name: Integration Tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/sam_gong_test
          REDIS_URL: redis://localhost:6379

      - name: 覆蓋率上傳(Codecov)
        uses: codecov/codecov-action@v3

      - name: Semgrep SAST(含 SQL Injection 規則)
        run: semgrep --config rules/sql-injection.yaml --config p/security-audit

      - name: HandEvaluator 向量數驗證(≥ 200)
        run: node scripts/verify-test-vectors.js

9.4 測試覆蓋率排除項目

下列項目不計入覆蓋率要求(排除理由:純配置、型別定義、生成代碼):

// jest.config.ts coveragePathIgnorePatterns
[
  "node_modules",
  "tests/",
  "src/types/",         // TypeScript 型別定義文件
  "src/config/",        // 純配置檔案
  "src/migrations/",    // DB Migration 腳本
  "src/seeds/",         // 測試資料 Seed
]

10. Test Schedule & Milestones

10.1 測試時程

里程碑日期負責人完成條件
單元測試框架建立2026-05-01Dev TeamJest + ts-jest 設定完成,HandEvaluator ≥ 50 向量
HandEvaluator 200 向量完成2026-06-21QA + Dev≥ 200 測試向量通過,CI 驗證
Integration Test 完成2026-06-30QARoom 生命週期 + REST API 整合測試通過
E2E 測試完成(Happy Path)2026-07-15QA所有 Happy Path 場景通過
E2E 測試完成(Edge Cases)2026-07-21QA所有邊界條件場景通過
效能測試(500 CCU)通過2026-07-28QA + DevOpsP95 ≤ 100ms 驗證通過
滲透測試2026-08-01Security Team無高危漏洞,報告完成
UAT 完成2026-08-14QA + PM + PO所有 UAT 場景通過,PO 簽署
GA 上線2026-08-21All所有里程碑通過

10.2 測試里程碑 Pass 條件

階段Pass 條件
Alpha(2026-06-21)Unit Tests 全通過;覆蓋率 ≥ 80%;HandEvaluator ≥ 200 向量
Beta(2026-07-28)Integration + E2E 全通過;效能 500 CCU P95 ≤ 100ms 通過
Release Candidate(2026-08-14)滲透測試完成(無高危);UAT 全通過;SLA 指標驗證
GA(2026-08-21)所有測試通過;Production Smoke Test 通過;監控告警設定完成

10.3 測試退出條件(Exit Criteria)

以下任一情況發生,暫停測試並通知開發:

  1. P0 優先級測試案例失敗(不允許帶已知 P0 缺陷上線)
  2. 覆蓋率低於 80%(阻擋 PR 合併)
  3. 效能測試 P95 > 100ms(需效能優化後重測)
  4. 籌碼守恆驗證失敗(sum(net_chips) + rake ≠ 0)
  5. 安全測試:SQL Injection 或手牌洩漏測試失敗

Appendix A:測試工具版本清單

工具版本用途
Jest29.x單元測試 + 整合測試框架
ts-jest29.xTypeScript 支援
@colyseus/testing0.15.xColyseus Room 測試
@colyseus/loadtest0.15.xRoom 吞吐量測試
k60.50.xWebSocket/HTTP 效能測試
testcontainers3.x整合測試 DB/Redis 容器
ioredis-mock8.xRedis Mock(單元測試)
pg-mem2.xPostgreSQL in-memory(單元測試)
Semgrep1.xSAST 靜態分析(CI)
Playwright1.xE2E UI 測試(Client 端)

Appendix B:關鍵測試向量(HandEvaluator 精選)

以下為 200+ 測試向量集的代表性樣本(完整向量集位於 tests/fixtures/hand-evaluator-vectors.json):

// tests/fixtures/hand-evaluator-vectors.ts
export const HAND_EVALUATOR_VECTORS = [
  // 三公(Sam Gong)— 所有組合(J/Q/K/10 各 4 花色)
  { cards: [{suit:'spade',value:'J'},{suit:'heart',value:'Q'},{suit:'diamond',value:'K'}],
    expected: { points:0, is_sam_gong:true, hand_type:'sam_gong' }},
  { cards: [{suit:'club',value:'10'},{suit:'spade',value:'Q'},{suit:'heart',value:'K'}],
    expected: { points:0, is_sam_gong:true, hand_type:'sam_gong' }},
  // 9點
  { cards: [{suit:'spade',value:'9'},{suit:'heart',value:'K'},{suit:'diamond',value:'J'}],
    expected: { points:9, is_sam_gong:false, hand_type:'9' }},
  { cards: [{suit:'spade',value:'A'},{suit:'heart',value:'8'},{suit:'diamond',value:'K'}],
    expected: { points:9, is_sam_gong:false, hand_type:'9' }},
  // 8點
  { cards: [{suit:'spade',value:'8'},{suit:'heart',value:'K'},{suit:'diamond',value:'J'}],
    expected: { points:8, is_sam_gong:false, hand_type:'8' }},
  // ... 完整 200 個向量見 tests/fixtures/hand-evaluator-vectors.json
];

文件生成:/devsop-autodev STEP-14(2026-04-22)

下次審查:Alpha 測試完成後(2026-06-21)