跳至主要内容

US-002 角色權限控制 - 手動測試指南

測試日期: ____________ 測試人員: ____________ 測試環境: npm run dev (localhost:3000)


測試目標

  1. 驗證不同角色登入後,Application Launcher 依據角色層級正確顯示可用 Applet
  2. 驗證 Mock API 的權限檢查中間件是否正確運作
  3. 確保不同角色的用戶只能訪問其授權的 API 和頁面

測試前準備

1. 啟動開發伺服器

npm run dev

2. 開啟瀏覽器工具

  • 開發者工具(F12)→ Network 標籤(檢視 API 請求)
  • 開發者工具(F12)→ Console 標籤(查看審計日誌)

3. 測試帳號

根據 src/mocks/data/seeds.ts,可用測試帳號:

角色中文名稱Username (Email)Password權限層級
ROLE_ADMIN系統管理員admin@florist.comPassword123!5 (最高)
ROLE_OWNER店主owner@florist.comPassword123!4
ROLE_SALES店員staff@florist.comPassword123!3
ROLE_FLORIST花藝設計師designer@florist.comPassword123!2
ROLE_DELIVERY送貨員delivery@florist.comPassword123!1 (最低)

測試場景

Scenario 0: Application Launcher 依角色層級顯示 Applet ✅

目標: 驗證不同角色登入後,Application Launcher 依據角色層級正確過濾可用 Applet

操作步驟

  1. 以系統管理員登入

    • 用戶名:admin@florist.com
    • 密碼:Password123!
    • 點擊 Header 右側的九宮格圖示(Grid3x3)開啟 Application Launcher
    • ✅ 驗證: Application Launcher 顯示完整 Applet 列表:
      • 首頁、訂單管理、客戶管理、商品管理、報表分析、系統設定
  2. 登出並以店主登入

    • 用戶名:owner@florist.com
    • 點擊九宮格圖示開啟 Application Launcher
    • ✅ 驗證: Application Launcher 顯示:
      • 首頁、訂單管理、客戶管理、商品管理、報表分析
    • ✅ 驗證: 「系統設定」Applet 不可見
  3. 登出並以店員登入

    • 用戶名:staff@florist.com
    • 點擊九宮格圖示開啟 Application Launcher
    • ✅ 驗證: Application Launcher 顯示:
      • 首頁、訂單管理、客戶管理
    • ✅ 驗證: 「商品管理」、「報表分析」、「系統設定」Applet 不可見
  4. 登出並以設計師登入

    • 用戶名:designer@florist.com
    • 點擊九宮格圖示開啟 Application Launcher
    • ✅ 驗證: Application Launcher 僅顯示:首頁
  5. 登出並以送貨員登入

    • 用戶名:delivery@florist.com
    • 點擊九宮格圖示開啟 Application Launcher
    • ✅ 驗證: Application Launcher 僅顯示:首頁

預期結果

  • ✅ 角色層級越高,可見的 Applet 數量越多
  • ✅ 高權限角色可看到低權限角色的所有 Applet
  • ✅ Applet 依據 requiredRoles 正確過濾

Scenario 1: 店員 (ROLE_SALES) 可以創建訂單 ✅

預期結果: 成功 (201 Created)

操作步驟:

  1. staff@florist.com / Password123! 登入
  2. 在瀏覽器 Console 執行以下代碼獲取 Access Token:
// 獲取當前 Redux store 中的 Access Token
const token = window.__REDUX_STORE__.getState().iam.me.authorization.access_token;
console.log('Access Token:', token);
  1. 使用 Token 創建訂單:
// 創建訂單 (需要 ROLE_SALES 或更高角色)
const token = window.__REDUX_STORE__.getState().iam.me.authorization.access_token;

function getFormattedDatePlusThreeDays() {
const today = new Date();
today.setDate(today.getDate() + 3);

const year = today.getFullYear();
const month = (today.getMonth() + 1).toString().padStart(2, '0'); // Months are 0-indexed
const day = today.getDate().toString().padStart(2, '0');

return `${year}-${month}-${day}`;
}

const deliveryDate = getFormattedDatePlusThreeDays();

fetch('/api/v1/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify({
customerId: 'cust-123', // 李大華(種子數據中的測試客戶)
deliveryAddress: '台北市信義區',
deliveryPhone: '0912345678',
deliveryDate: deliveryDate,
deliveryTimeSlot: 'morning', // 配送時段:morning/afternoon/evening
items: [
{
productId: 'prod-1',
quantity: 1,
}
],
})
})
.then(r => r.json())
.then(console.log)
.catch(console.error);

驗證:

  • Response status = 201
  • Response body 包含新訂單資料(orderNumber, id, status = 'pending_confirmation')
  • Console 顯示成功訊息

Scenario 2: 花藝設計師 (ROLE_FLORIST) 嘗試創建訂單被拒絕 ❌

預期結果: 失敗 (403 Forbidden)

操作步驟:

  1. 登出當前用戶
  2. designer@florist.com / Password123! 登入
  3. 在瀏覽器 Console 執行(同 Scenario 1 步驟 3 的代碼)

驗證:

  • Response status = 403
  • Response body 包含錯誤訊息:
{
"message": "權限不足:此操作需要 ROLE_SALES 或更高角色,您的角色為 ROLE_FLORIST",
"status": 403,
"error": "PERMISSION_DENIED",
...
}
  • Browser Console 顯示審計日誌:
[MSW Audit] 403 Forbidden - User role: ROLE_FLORIST, Method: POST, Path: /api/v1/orders, Required: ROLE_SALES

Scenario 3: 店主 (ROLE_OWNER) 可以刪除訂單 ✅

預期結果: 成功 (200 OK)

操作步驟:

  1. 登出當前用戶
  2. owner@florist.com / Password123! 登入
  3. 先查看訂單列表,獲取訂單 ID:
const token = window.__REDUX_STORE__.getState().iam.me.authorization.access_token;
let orderId;
fetch('/api/v1/orders', {
headers: {
'Authorization': `Bearer ${token}`,
}
})
.then(r => r.json())
.then(orders => {
console.log('訂單列表:', orders);
if (orders.length > 0) {
console.log('第一筆訂單 ID:', orders[0].id);
orderId = orders[0].id;
}
});
  1. 刪除訂單(需要 ROLE_OWNER 或更高角色):
const token = window.__REDUX_STORE__.getState().iam.me.authorization.access_token;

fetch(`/api/v1/orders/${orderId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
}
})
.then(r => r.json())
.then(console.log)
.catch(console.error);

驗證:

  • Response status = 200
  • Response body = { "message": "訂單已刪除" }

Scenario 4: 店員 (ROLE_SALES) 嘗試刪除訂單被拒絕 ❌

預期結果: 失敗 (403 Forbidden)

操作步驟:

  1. 登出當前用戶
  2. staff@florist.com / Password123! 登入
  3. 執行同 Scenario 3 步驟 4 的刪除操作

驗證:

  • Response status = 403
  • Response body 包含權限不足錯誤訊息
  • Console 顯示審計日誌

Scenario 5: 未登入用戶訪問訂單 API 被拒絕 ❌

預期結果: 失敗 (401 Unauthorized)

操作步驟:

  1. 登出所有用戶(或使用無痕模式)
  2. 在瀏覽器 Console 執行(不帶 Authorization header):
fetch('/api/v1/orders')
.then(async r => {
console.log('Status:', r.status);
console.log('Body:', await r.json());
})
.catch(console.error);

驗證:

  • Response status = 401
  • Response body 包含 "Unauthorized" 錯誤訊息

Scenario 6: 驗證 Token 自動附加功能 ✅

預期結果: 透過應用程式 UI 發出的 API 請求會自動附加 Authorization header

操作步驟:

  1. 以任意角色登入(例如:staff@florist.com / Password123!
  2. 在應用程式中瀏覽訂單列表頁面(透過 Application Launcher 開啟「訂單管理」)
  3. 開啟瀏覽器開發者工具(F12) → Network 標籤
  4. 刷新頁面(F5)或執行任何觸發 API 請求的操作
  5. 在 Network 標籤中找到 orders 請求
  6. 點擊該請求 → Headers 標籤

驗證:

  • Request Headers 應包含:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

備註:

  • ✅ 應用程式內的 apiClient (Axios) 會自動從 Redux store 讀取 token 並附加到請求中
  • ✅ 這個機制在 src/services/apiClient.ts 的 request interceptor 實作
  • ❌ 在 Console 中手動執行的 fetch() 不會自動附加 Token(需手動設置)

替代測試方式(透過 Console 驗證 Redux 中的 Token):

// 檢查 Redux store 中是否有 token
const token = window.__REDUX_STORE__.getState().iam.me.authorization.access_token;
console.log('Token exists:', !!token);
console.log('Token preview:', token?.substring(0, 50) + '...');

權限矩陣

API 端點System AdminShop OwnerSales StaffFloristDelivery
POST /orders
GET /orders
PUT /orders/:id
DELETE /orders/:id
GET /products
POST /products
GET /settings

測試結果記錄

完成測試後,請在下方記錄結果:

✅ 通過的場景

前端權限(Application Launcher 過濾)

  • Scenario 0: Application Launcher 依角色層級顯示 Applet

API 權限(Mock API)

  • Scenario 1: 店員可以創建訂單
  • Scenario 2: 設計師無法創建訂單
  • Scenario 3: 店主可以刪除訂單
  • Scenario 4: 店員無法刪除訂單
  • Scenario 5: 未登入用戶被拒絕
  • Scenario 6: Token 自動附加

⚠️ 發現的問題

(記錄任何異常或錯誤)





測試檢查清單

Application Launcher 權限過濾

  • 系統管理員看到所有 Applet(6 項)
  • 店主看到 5 項 Applet(無系統設定)
  • 店員看到 3 項 Applet(首頁、訂單、客戶)
  • 設計師僅看到首頁
  • 送貨員僅看到首頁

API 權限矩陣

  • 高層級角色可執行低層級角色的操作
  • 未授權請求返回 403 Forbidden
  • 未登入請求返回 401 Unauthorized
  • 審計日誌正確記錄權限拒絕事件

UI/UX

  • Application Launcher 使用類似 macOS Launchpad 的視覺設計
  • Applet 圖示使用彩色圓角背景
  • 分類標籤可過濾 Applet
  • 搜尋功能可快速定位 Applet

測試結論

  • 測試通過: ☐ 是 / ☐ 否
  • 備註: ___________________________________________________________
  • 測試人員簽名: ________________ 日期: ________________

最後更新: 2025-12-10