Files
gaokao-volunteer-system/locustfile.py
Hermes Agent 3f46811e49
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
release: cut v2.1
2026-06-13 14:49:58 +08:00

108 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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")