Files
gaokao-volunteer-system/locustfile.py

108 lines
3.0 KiB
Python
Raw Normal View History

2026-06-13 14:49:58 +08:00
"""T11.1 Locust 性能基准脚本。
目标 T6.1 FastAPI 管理后台骨架做最小真实压测验证计划中的
"10 并发"目标并记录各端点的延迟/错误率基线
压测模型
- AdminBrowseUser模拟已登录后台管理员覆盖 /me /meta /orders /stats
- LoginBurstUser模拟登录高峰持续打 /api/auth/login
运行示例
GAOKAO_JWT_SECRET=$(python3 - <<'PY'
print('x'*64)
PY
) \
locust -f locustfile.py --host http://127.0.0.1:18080 \
--headless -u 10 -r 2 -t 1m --csv reports/perf/t11_1
"""
from __future__ import annotations
import os
from typing import Optional
from locust import HttpUser, between, task
ADMIN_USER = os.getenv("GAOKAO_ADMIN_USER", "admin")
ADMIN_PASS = os.getenv("GAOKAO_ADMIN_PASS", "admin123")
class AuthenticatedHttpUser(HttpUser):
abstract = True
wait_time = between(0.3, 1.2)
token: Optional[str] = None
def on_start(self) -> None:
self.login()
def login(self) -> None:
with self.client.post(
"/api/auth/login",
json={"username": ADMIN_USER, "password": ADMIN_PASS},
name="POST /api/auth/login",
catch_response=True,
) as resp:
if resp.status_code != 200:
resp.failure(f"login failed: {resp.status_code} {resp.text[:200]}")
self.token = None
return
data = resp.json()
token = data.get("access_token")
if not token:
resp.failure("login response missing access_token")
self.token = None
return
self.token = token
self.client.headers.update({"Authorization": f"Bearer {token}"})
resp.success()
class AdminBrowseUser(AuthenticatedHttpUser):
weight = 4
@task(5)
def me(self) -> None:
self.client.get("/api/auth/me", name="GET /api/auth/me")
@task(4)
def meta(self) -> None:
self.client.get("/api/meta", name="GET /api/meta")
@task(6)
def list_orders(self) -> None:
self.client.get("/api/orders?limit=50&offset=0", name="GET /api/orders")
@task(3)
def order_stats(self) -> None:
self.client.get("/api/stats/orders", name="GET /api/stats/orders")
@task(1)
def detail_missing(self) -> None:
with self.client.get(
"/api/orders/non-existent-order-id",
name="GET /api/orders/{id} [404]",
catch_response=True,
) as resp:
if resp.status_code == 404:
resp.success()
else:
resp.failure(f"expected 404, got {resp.status_code}")
class LoginBurstUser(HttpUser):
weight = 1
wait_time = between(1.0, 3.0)
@task(4)
def login(self) -> None:
self.client.post(
"/api/auth/login",
json={"username": ADMIN_USER, "password": ADMIN_PASS},
name="POST /api/auth/login",
)
@task(1)
def health(self) -> None:
self.client.get("/health", name="GET /health")