template-monorepo/test/k6/README.md

9.6 KiB
Raw Blame History

k6 API tests

完整的 Gateway API smoke + journey 測試套件。所有對外端點都至少在 smoke/journeys/ 裡有一發(含登入 MFA、忘記/改密碼、註冊 resume

TL;DR

make k6-up           # dockermongo + redis + mailhog + postgres + zitadel
make k6-wait         # 等 ZITADEL ready + 把 PAT 寫到 env file
make k6-gateway &    # 起 gateway背景吃 etc/gateway.k6.yaml
make k6-seed-admin   # rbac journey 才需要seed tenant_admin user
make k6-all          # 跑 smoke + journey
make k6-down         # 清掉

單跑一個檔(記得先載入環境變數):

source deploy/zitadel/machinekey/k6.env
k6 run test/k6/smoke/health.js
k6 run test/k6/journeys/email_register_full.js

環境變數

變數 預設 說明
BASE_URL http://localhost:8888 Gateway base URL
MAILHOG_URL http://localhost:8025 MailHog HTTP API撈 email OTP
REDIS_ADDR localhost:6379 Redis 位址(撈 SMS OTP
TENANT_SLUG k6-tenant register payload tenant
INVITE_CODE K6INVITE tenant 開啟 invite 時用
ADMIN_EMAIL / ADMIN_PASSWORD rbac journey seeded admin
OTP_POLL_INTERVAL_MS 300 OTP poll 頻率
OTP_POLL_TIMEOUT_MS 5000 OTP poll 超時
RESEND_COOLDOWN_SECONDS 60 gateway.k6.yaml OTP 重送冷卻一致cooldown smoke 會 sleep

目錄結構

test/k6/
├── README.md
├── lib/                          # 共用 helper
│   ├── config.js                 # 環境變數 + unique() + SUCCESS_CODE
│   ├── http.js                   # get/post/...、checkEnvelope、withBearer
│   ├── otp.js                    # fetchEmailOTP (MailHog) / fetchSMSOTP (Redis)
│   ├── totp.js                   # HMAC-SHA1 TOTPP4
│   ├── auth.js                   # register / confirm / login / MFA / password helper
│   ├── member.js                 # TOTP enroll / change password helper
│   └── seed.js                   # tenant + invite + admin role bootstrapP5
├── smoke/                        # 每個端點至少一發
│   ├── health.js                 # GET /api/v1/health
│   ├── auth_public.js            # register / login / refresh / social-start (+negative)
│   ├── auth_register_resume.js   # register/resumehappy + 404/409/429/400
│   ├── auth_password.js          # password/forgot + resethappy + 各種 negative
│   ├── auth_login_mfa.js         # login/mfaMFA 前置 + negative
│   ├── auth_bearer.js            # logout
│   ├── member.js                 # me / patch / verify / TOTP / change-password negative
│   ├── permission_read.js        # catalog / me
│   └── permission_admin.js       # roles CRUD / role-permissions / user-roles / mappings / policy reload
└── journeys/                     # 完整流程
    ├── email_register_full.js    # register → confirm OTP(MailHog) → me → patch → logout
    ├── register_resume_full.js   # register → resume → confirm → me
    ├── password_forgot_reset_full.js # forgot → reset → login新密碼
    ├── login_mfa_full.js         # enroll TOTP → login MFA → me
    ├── change_password_full.js   # change password → login新密碼
    ├── login_refresh.js          # login → refresh → me → logout
    ├── email_verify.js           # register → confirm → email verify start → confirm
    ├── phone_verify.js           # register → confirm → phone verify (Redis OTP)
    ├── totp_full.js              # enroll → confirm → verify → backup-codes → disable
    ├── rbac_admin.js             # role CRUD → assign → policy reload → me/permissions
    └── token_exchange.js         # ZITADEL id_token → CloudEP JWT

OTP 怎麼撈

  • Emailmake k6-up 含 MailHog:1025 SMTP / :8025 HTTP API。Gateway 把 OTP 寄到 MailHogk6 透過 /api/v2/search?kind=to&query=<email> 撈到信件,從 body 抓 6 位數。
  • SMS → mock provider 把 body 寫到 dev:notification:last:sms:<phone>(見 mock_sender.go WithMockRedisk6 用 k6/experimental/redis 直接讀。

兩者都有 OTP_POLL_TIMEOUT_MS(預設 5 秒)保護,超時直接 fail。

覆蓋率39 endpoints

模組 端點 覆蓋
Normal GET /api/v1/health smoke/health
Auth 公開 POST /register journeys/email_register_full + smoke/auth_public
Auth 公開 POST /register/confirm journeys/email_register_full
Auth 公開 POST /register/resend smoke/auth_public
Auth 公開 POST /register/resume smoke/auth_register_resume + journeys/register_resume_full
Auth 公開 POST /password/forgot smoke/auth_password + journeys/password_forgot_reset_full
Auth 公開 POST /password/reset smoke/auth_password + journeys/password_forgot_reset_full
Auth 公開 POST /login/mfa smoke/auth_login_mfa + journeys/login_mfa_full
Auth 公開 POST /register/social/start smoke/auth_public (happy)
Auth 公開 GET /register/social/callback smoke/auth_public (negative — TODO happy)
Auth 公開 POST /login journeys/login_refresh + smoke/auth_public (negative)
Auth 公開 POST /token/refresh journeys/login_refresh + smoke/auth_public (negative)
Auth 公開 POST /token/exchange journeys/token_exchange (negative — TODO happy)
Auth 公開 POST /login/social/start smoke/auth_public (happy)
Auth 公開 GET /login/social/callback smoke/auth_public (negative — TODO happy)
Auth Bearer POST /logout smoke/auth_bearer + journeys/email_register_full
Member GET /me smoke/member + all journeys
Member PATCH /me smoke/member + journeys/email_register_full
Member POST /me/verifications/email/start smoke/member + journeys/email_verify
Member POST /me/verifications/email/confirm smoke/member (negative) + journeys/email_verify (happy)
Member POST /me/verifications/phone/start smoke/member + journeys/phone_verify
Member POST /me/verifications/phone/confirm smoke/member (negative) + journeys/phone_verify (happy)
Member GET /me/totp smoke/member + journeys/totp_full
Member POST /me/totp/enroll-start smoke/member + journeys/totp_full
Member POST /me/totp/enroll-confirm smoke/member (negative) + journeys/totp_full (happy)
Member POST /me/totp/verify smoke/member (negative) + journeys/totp_full (happy)
Member POST /me/totp/backup-codes smoke/member (negative) + journeys/totp_full (happy)
Member DELETE /me/totp smoke/member + journeys/totp_full
Member POST /me/password smoke/member (negative) + journeys/change_password_full
Perm 讀 GET /permissions/catalog smoke/permission_read + journeys/rbac_admin
Perm 讀 GET /permissions/me smoke/permission_read + journeys/rbac_admin
Perm 管理 GET /roles smoke/permission_admin + journeys/rbac_admin
Perm 管理 POST /roles smoke/permission_admin + journeys/rbac_admin
Perm 管理 PATCH /roles/:id smoke/permission_admin + journeys/rbac_admin
Perm 管理 DELETE /roles/:id smoke/permission_admin + journeys/rbac_admin
Perm 管理 GET /roles/:id/permissions smoke/permission_admin + journeys/rbac_admin
Perm 管理 PUT /roles/:id/permissions smoke/permission_admin + journeys/rbac_admin
Perm 管理 GET /users/:uid/roles smoke/permission_admin + journeys/rbac_admin
Perm 管理 POST /users/:uid/roles smoke/permission_admin + journeys/rbac_admin
Perm 管理 DELETE /users/:uid/roles/:role_id smoke/permission_admin + journeys/rbac_admin
Perm 管理 GET /role-mappings smoke/permission_admin
Perm 管理 PUT /role-mappings smoke/permission_admin
Perm 管理 DELETE /role-mappings smoke/permission_admin
Perm 管理 POST /policy/reload smoke/permission_admin + journeys/rbac_admin

RBAC 系列(需 admin seed

journeys/rbac_admin.js 需要 ADMIN_EMAIL / ADMIN_PASSWORD 環境變數。make k6-seed-admin 會跑 cmd/k6-seed-admin

  1. 用 API 註冊一個固定的 k6-admin@k6.local
  2. 從 MailHog 撈 OTP 完成 confirm
  3. 寫入 permission catalog + 預設 system roles透過 internal/model/permission/seed
  4. 指派 tenant_admin 給該 UID
  5. ADMIN_EMAIL / ADMIN_PASSWORD / ADMIN_UID 寫到 k6.env

k6-seed-admin 是冪等的,重跑沒事。

沒跑 seed 時,rbac_admin.js 會印 skip notice 並 exit 0admin endpoint 的路由存在性 已由 smoke/permission_admin.js 涵蓋)。

已知無法純 k6 跑的

  • GET /api/v1/auth/register/social/callbackGET /api/v1/auth/login/social/callback happy path 需要 Google OAuth UI 跳轉,純 k6 無法走完。Smoke 只覆蓋 negative無效 state → 400
  • POST /api/v1/auth/token/exchange happy path 需要一個來自 ZITADEL 的有效 id_token;目前 make k6-up 的 ZITADEL bootstrap 只建 service account PAT沒有開 OIDC client password grant因此 happy 路徑 TODOsmoke 只跑 negative。要開deploy/zitadel/steps.yaml 加 Application + password grant再用 /oauth/v2/token 拿 id_token。

不要 commit 的東西

  • deploy/zitadel/machinekey/zitadel-admin-sa.token / .json 已在 .gitignore
  • etc/gateway.k6.yaml 內的 secret 都是固定 dev 值,本機 / CI 可用, 上 prod。