文件
hyb-oa/server/scripts/smoke-test.sh
T
wangsli f527633716 feat(server): 完成 P0+P1 后端能力(工作流、RBAC、付款归档)
P0 MVP:

- 认证:login/refresh/logout/forgot/reset + tokenVersion

- RBAC:角色到权限映射 + requirePermission

- 发票池:upload/bind/signed-url + 判重(VALID_003/VALID_005)

- 报销:create/view/submit + 单号生成

- 工作流:approve + department_manager 解析

P1 扩展:

- 动作:reject/return/withdraw(含 comment)

- transfer:任务转审 + 链路上限 3

- pay:approved -> paid + 发票归档 used_complete

- 动态审批链:approval_rules 驱动

回归脚本:

- smoke-test.sh / regression-dedup.sh / regression-workflow-actions.sh
2026-02-07 16:43:31 +08:00

209 行
8.0 KiB
Bash
可执行文件

#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:3000}"
DEVICE_FINGERPRINT="${DEVICE_FINGERPRINT:-smoke-script-device}"
EMPLOYEE_IDENTIFIER="${EMPLOYEE_IDENTIFIER:-employee@hyinda.com}"
EMPLOYEE_PASSWORD="${EMPLOYEE_PASSWORD:-EmployeePassword1234}"
MANAGER_IDENTIFIER="${MANAGER_IDENTIFIER:-manager@hyinda.com}"
MANAGER_PASSWORD="${MANAGER_PASSWORD:-ManagerPassword1234}"
FINANCE_IDENTIFIER="${FINANCE_IDENTIFIER:-finance@hyinda.com}"
FINANCE_PASSWORD="${FINANCE_PASSWORD:-FinancePassword1234}"
SAMPLE_FILE="${SAMPLE_FILE:-/tmp/hyb-oa-sample-invoice.pdf}"
if ! command -v curl >/dev/null 2>&1; then
echo "[ERROR] curl not found" >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "[ERROR] jq not found" >&2
exit 1
fi
if [[ ! -f "$SAMPLE_FILE" ]]; then
echo "[ERROR] Sample file not found: $SAMPLE_FILE" >&2
exit 1
fi
TMP_DIR="$(mktemp -d)"
cleanup() {
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
RUN_UPLOAD_FILE="$TMP_DIR/upload-$(basename "$SAMPLE_FILE")"
cp "$SAMPLE_FILE" "$RUN_UPLOAD_FILE"
printf '\n%%RUN-%s\n' "$(date +%s%N)" >> "$RUN_UPLOAD_FILE"
extract_json_field() {
local file_path="$1"
local jq_expr="$2"
jq -er "$jq_expr" "$file_path"
}
assert_status() {
local actual="$1"
local expected="$2"
local name="$3"
if [[ "$actual" != "$expected" ]]; then
echo "[ERROR] $name expected HTTP $expected but got $actual" >&2
exit 1
fi
}
assert_error_code() {
local file_path="$1"
local expected_code="$2"
local name="$3"
local actual_code
actual_code="$(jq -r '.error.code // empty' "$file_path")"
if [[ "$actual_code" != "$expected_code" ]]; then
echo "[ERROR] $name expected error code $expected_code but got $actual_code" >&2
cat "$file_path" >&2
exit 1
fi
}
echo "[1/13] Health check"
health_body="$TMP_DIR/health.json"
health_status="$(curl -sS -o "$health_body" -w "%{http_code}" "$BASE_URL/health")"
assert_status "$health_status" "200" "health"
echo "[2/13] Negative: no token should be 401"
no_token_body="$TMP_DIR/no-token.json"
no_token_status="$(curl -sS -o "$no_token_body" -w "%{http_code}" "$BASE_URL/api/reimbursements/test-id")"
assert_status "$no_token_status" "401" "no token"
echo "[3/13] Login employee"
employee_login_body="$TMP_DIR/employee-login.json"
employee_login_status="$(curl -sS -o "$employee_login_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/auth/login" \
-H "content-type: application/json" \
-H "x-device-fingerprint: $DEVICE_FINGERPRINT" \
-d "{\"identifier\":\"$EMPLOYEE_IDENTIFIER\",\"password\":\"$EMPLOYEE_PASSWORD\"}")"
assert_status "$employee_login_status" "200" "employee login"
EMPLOYEE_TOKEN="$(extract_json_field "$employee_login_body" '.data.accessToken')"
echo "[4/13] Create reimbursement draft"
create_body="$TMP_DIR/create.json"
create_status="$(curl -sS -o "$create_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/reimbursements" \
-H "authorization: Bearer $EMPLOYEE_TOKEN" \
-H "content-type: application/json" \
-d '{}')"
assert_status "$create_status" "200" "create reimbursement"
REIMBURSEMENT_ID="$(extract_json_field "$create_body" '.data.id')"
echo "[5/13] Upload invoice"
upload_body="$TMP_DIR/upload.json"
upload_status="$(curl -sS -o "$upload_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/invoice-pool/upload" \
-H "authorization: Bearer $EMPLOYEE_TOKEN" \
-F "file=@$RUN_UPLOAD_FILE")"
assert_status "$upload_status" "200" "upload invoice"
INVOICE_ID="$(extract_json_field "$upload_body" '.data.invoicePoolId')"
echo "[6/13] Bind invoice"
bind_body="$TMP_DIR/bind.json"
bind_status="$(curl -sS -o "$bind_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/invoice-pool/$INVOICE_ID/bind" \
-H "authorization: Bearer $EMPLOYEE_TOKEN" \
-H "content-type: application/json" \
-d "{\"reimbursementId\":\"$REIMBURSEMENT_ID\"}")"
assert_status "$bind_status" "200" "bind invoice"
echo "[7/13] Negative: bind same invoice again should be VALID_005"
rebind_body="$TMP_DIR/rebind.json"
rebind_status="$(curl -sS -o "$rebind_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/invoice-pool/$INVOICE_ID/bind" \
-H "authorization: Bearer $EMPLOYEE_TOKEN" \
-H "content-type: application/json" \
-d "{\"reimbursementId\":\"$REIMBURSEMENT_ID\"}")"
assert_status "$rebind_status" "409" "rebind conflict"
assert_error_code "$rebind_body" "VALID_005" "rebind conflict"
echo "[8/13] Submit reimbursement"
submit_body="$TMP_DIR/submit.json"
submit_status="$(curl -sS -o "$submit_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/reimbursements/$REIMBURSEMENT_ID/submit" \
-H "authorization: Bearer $EMPLOYEE_TOKEN" \
-H "content-type: application/json" \
-d '{}')"
assert_status "$submit_status" "200" "submit reimbursement"
echo "[9/13] Login manager"
manager_login_body="$TMP_DIR/manager-login.json"
manager_login_status="$(curl -sS -o "$manager_login_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/auth/login" \
-H "content-type: application/json" \
-H "x-device-fingerprint: ${DEVICE_FINGERPRINT}-manager" \
-d "{\"identifier\":\"$MANAGER_IDENTIFIER\",\"password\":\"$MANAGER_PASSWORD\"}")"
assert_status "$manager_login_status" "200" "manager login"
MANAGER_TOKEN="$(extract_json_field "$manager_login_body" '.data.accessToken')"
echo "[10/13] Login finance (approve permission, non-assignee)"
finance_login_body="$TMP_DIR/finance-login.json"
finance_login_status="$(curl -sS -o "$finance_login_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/auth/login" \
-H "content-type: application/json" \
-H "x-device-fingerprint: ${DEVICE_FINGERPRINT}-finance" \
-d "{\"identifier\":\"$FINANCE_IDENTIFIER\",\"password\":\"$FINANCE_PASSWORD\"}")"
assert_status "$finance_login_status" "200" "finance login"
FINANCE_TOKEN="$(extract_json_field "$finance_login_body" '.data.accessToken')"
echo "[11/13] Query pending tasks as manager"
tasks_body="$TMP_DIR/tasks.json"
tasks_status="$(curl -sS -o "$tasks_body" -w "%{http_code}" \
-X GET "$BASE_URL/api/workflow/tasks?status=pending" \
-H "authorization: Bearer $MANAGER_TOKEN")"
assert_status "$tasks_status" "200" "query workflow tasks"
TASK_ID="$(jq -er --arg rid "$REIMBURSEMENT_ID" '.data.items[] | select(.workflow_instance.entity_id == $rid) | .id' "$tasks_body" | head -n 1)"
if [[ -z "$TASK_ID" ]]; then
echo "[ERROR] No pending task found for reimbursement $REIMBURSEMENT_ID" >&2
cat "$tasks_body" >&2
exit 1
fi
echo "[12/13] Negative: finance cannot approve manager task (PERM_002)"
finance_approve_body="$TMP_DIR/finance-approve.json"
finance_approve_status="$(curl -sS -o "$finance_approve_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/workflow/tasks/$TASK_ID/approve" \
-H "authorization: Bearer $FINANCE_TOKEN" \
-H "content-type: application/json" \
-d '{}')"
assert_status "$finance_approve_status" "403" "finance approve forbidden"
assert_error_code "$finance_approve_body" "PERM_002" "finance approve forbidden"
echo "[13/13] Approve by manager and fetch signed URL"
manager_approve_body="$TMP_DIR/manager-approve.json"
manager_approve_status="$(curl -sS -o "$manager_approve_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/workflow/tasks/$TASK_ID/approve" \
-H "authorization: Bearer $MANAGER_TOKEN" \
-H "content-type: application/json" \
-d '{}')"
assert_status "$manager_approve_status" "200" "manager approve"
signed_body="$TMP_DIR/signed-url.json"
signed_status="$(curl -sS -o "$signed_body" -w "%{http_code}" \
-X POST "$BASE_URL/api/invoice-pool/$INVOICE_ID/signed-url" \
-H "authorization: Bearer $EMPLOYEE_TOKEN" \
-H "content-type: application/json" \
-d '{}')"
assert_status "$signed_status" "200" "signed url"
SIGNED_URL="$(extract_json_field "$signed_body" '.data.signedUrl')"
signed_download="$TMP_DIR/signed-download.bin"
download_status="$(curl -sS -L -o "$signed_download" -w "%{http_code}" "$SIGNED_URL")"
assert_status "$download_status" "200" "signed url download"
if [[ ! -s "$signed_download" ]]; then
echo "[ERROR] Signed URL download is empty" >&2
exit 1
fi
echo "[OK] Smoke test passed"
echo "- reimbursementId: $REIMBURSEMENT_ID"
echo "- invoiceId: $INVOICE_ID"
echo "- taskId: $TASK_ID"