template-monorepo/scripts/e2e-lib.sh

125 lines
3.9 KiB
Bash
Raw Normal View History

2026-05-21 23:52:39 +00:00
#!/usr/bin/env bash
# Shared helpers for e2e-run / e2e-up / e2e-down.
# shellcheck disable=SC2034
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
# Colors非 TTY 自動關掉CI log 才不會有 ANSI 噪音)
if [[ -t 1 ]]; then
E2E_BOLD=$'\033[1m'; E2E_DIM=$'\033[2m'; E2E_GREEN=$'\033[32m'
E2E_CYAN=$'\033[36m'; E2E_YELLOW=$'\033[33m'; E2E_RESET=$'\033[0m'
else
E2E_BOLD=""; E2E_DIM=""; E2E_GREEN=""; E2E_CYAN=""; E2E_YELLOW=""; E2E_RESET=""
fi
# e2e_step "1/6" "fresh docker compose"
e2e_step() {
local idx="$1"; shift
printf "\n${E2E_BOLD}${E2E_CYAN}== [%s] %s ==${E2E_RESET}\n" "$idx" "$*"
}
# e2e_info "啟動 mailhoghttp://localhost:8025"
e2e_info() { printf "${E2E_DIM}>> %s${E2E_RESET}\n" "$*"; }
e2e_ok() { printf "${E2E_GREEN}✔ %s${E2E_RESET}\n" "$*"; }
e2e_warn() { printf "${E2E_YELLOW}! %s${E2E_RESET}\n" "$*"; }
2026-05-21 23:52:39 +00:00
e2e_root_dir() {
cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd
}
test(e2e): 加 banner / e2e-list / k6 風格 user journey 讓「我有哪些測試、現在在測什麼」一眼看得到,並補上跨 endpoint 的狀態流測試: 每個測試開頭印中文 banner - 新增 e2eStep(t, id, method, path, desc) helper(test/e2e/setup_test.go) - 17 個 contract test 開頭加 banner,go test -v 會逐個顯示 ▶ [M-01] GET /api/v1/members/me — 讀 profile(tenant/uid/status) - 對外 ID 與 docs/e2e-testing.md 的測試覆蓋矩陣對齊 新增 make e2e-list - scripts/e2e-list.sh 掃 _test.go,分兩節印 contract tests + journeys; 每個 journey 列出所有 step ID + 描述(Step 用 ▶、SkipStep 用 ⊘) scripts 彩色 step banner + optional MailHog - scripts/e2e-lib.sh 抽共用 helpers(e2e_step/info/ok/warn、e2e_print_services) - e2e-run.sh / e2e-up.sh 改用 step banner + 服務面板(執行完印出 Mongo/Redis/ Gateway/MailHog 的 URL) - E2E_WITH_SMTP=1 會額外起 MailHog(http://localhost:8025),方便肉眼確認流程 k6 風格 user journey - 新增 test/e2e/journey.go:NewJourney + Step + SkipStep + Summary, 任一步 fail 自動 skip 後續,輸出 ▶ [J-x.y] 階層 banner - J-1 Tenant Owner 入職第一天(12 steps):/me → PATCH → email verify → phone verify → TOTP enroll/verify/replay/disable - J-2 Tenant Admin 建 qa_engineer 角色 → 指派 → 二人視角驗證 → 撤銷(8 steps) - J-3 Session 生命週期 refresh → /me → logout → 舊 token 401(4 steps,ZZZ 排最後) - J-4 完整註冊 → 登入(5 steps stub,標 SkipStep;接 ZITADEL container 後改 Step 即可) - make e2e-journey / make test-e2e-journey 拆獨立 target;e2e-run.sh 透過 E2E_MODE=journey + E2E_TEST_PATTERN_ZZZ 切換 docs/e2e-testing.md - 首節改為「我現在有哪些測試?make e2e-list」並附 banner 範例輸出 - 加 Journeys 章節:journey 列表、執行範例、失敗時的輸出、寫新 journey 範本 - 補 e2e-journey / test-e2e-journey / E2E_WITH_SMTP 環境變數 Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 09:18:36 +00:00
# 把所有 e2e services 列出來(給使用者知道環境準備好了什麼)
e2e_print_services() {
local with_smtp="${1:-}"
echo
printf "${E2E_BOLD}E2E 環境服務${E2E_RESET}\n"
printf " %-12s %-32s %s\n" "MongoDB" "127.0.0.1:27017" "${E2E_DIM}database=gateway_e2e${E2E_RESET}"
printf " %-12s %-32s %s\n" "Redis" "127.0.0.1:6379" "${E2E_DIM}OTP / Casbin policy / blacklist${E2E_RESET}"
printf " %-12s %-32s %s\n" "Gateway" "http://127.0.0.1:18888" "${E2E_DIM}health: /api/v1/health${E2E_RESET}"
if [[ "${with_smtp}" == "1" ]]; then
printf " %-12s %-32s %s\n" "MailHog" "http://127.0.0.1:8025" "${E2E_DIM}SMTP=1025E2E_WITH_SMTP=1${E2E_RESET}"
fi
echo
}
2026-05-21 23:52:39 +00:00
# Stop gateway started for E2E: pid file → port listeners → stale go run orphans.
e2e_stop_gateway() {
local port="${1:-18888}"
local pid_file="${2:-}"
if [[ -n "${pid_file}" && -f "${pid_file}" ]]; then
local pid
pid="$(cat "${pid_file}")"
if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
echo ">> stopping gateway pid=${pid}"
kill "${pid}" 2>/dev/null || true
for _ in $(seq 1 10); do
kill -0 "${pid}" 2>/dev/null || break
sleep 0.2
done
if kill -0 "${pid}" 2>/dev/null; then
kill -9 "${pid}" 2>/dev/null || true
fi
wait "${pid}" 2>/dev/null || true
fi
rm -f "${pid_file}"
fi
if command -v lsof >/dev/null 2>&1; then
local pids
pids="$(lsof -ti tcp:"${port}" 2>/dev/null | tr '\n' ' ' || true)"
if [[ -n "${pids// /}" ]]; then
echo ">> stopping listener(s) on :${port} (${pids})"
# shellcheck disable=SC2086
kill ${pids} 2>/dev/null || true
sleep 0.5
pids="$(lsof -ti tcp:"${port}" 2>/dev/null | tr '\n' ' ' || true)"
if [[ -n "${pids// /}" ]]; then
# shellcheck disable=SC2086
kill -9 ${pids} 2>/dev/null || true
fi
fi
fi
# go run leaves a compiled binary under $TMPDIR; kill by e2e config path if still up.
if command -v pgrep >/dev/null 2>&1; then
while IFS= read -r orphan; do
[[ -z "${orphan}" ]] && continue
echo ">> stopping orphan gateway pid=${orphan}"
kill "${orphan}" 2>/dev/null || true
done < <(pgrep -f "gateway(-e2e)? .*${port}|gateway.go -f .*e2e.yaml" 2>/dev/null || true)
fi
}
# Build a real binary so $! is the server PID (go run only tracks the wrapper).
e2e_start_gateway() {
local root="$1"
local config="$2"
local port="$3"
local pid_file="$4"
local bin="${root}/.cache/e2e-gateway"
mkdir -p "${root}/.cache"
e2e_stop_gateway "${port}" "${pid_file}"
echo ">> building e2e gateway binary"
(cd "${root}" && go build -o "${bin}" gateway.go)
echo ">> starting gateway on :${port}"
GATEWAY_E2E=1 "${bin}" -f "${config}" &
local pid=$!
echo "${pid}" > "${pid_file}"
echo "${pid}"
}
e2e_wait_gateway() {
local port="$1"
local url="http://127.0.0.1:${port}/api/v1/health"
for i in $(seq 1 60); do
if curl -sf "${url}" >/dev/null; then
return 0
fi
sleep 1
done
echo "timeout waiting for gateway ${url}" >&2
return 1
}