feat(06-01): add backend/load_tests/ package skeleton with Locust stub (D-04/D-05/D-06)
- backend/load_tests/__init__.py — empty package marker; stops pytest discovery - backend/load_tests/locustfile.py — DocuVaultUser skeleton (on_start, _auth_headers, list_documents, upload_document, refresh_token methods) and check_sla SLA listener; all bodies raise NotImplementedError - No application imports; TEST_EMAIL/TEST_PASSWORD from env vars only - Full implementation in plan 06-03
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# Locust load-test package — not a pytest test target
|
||||
@@ -0,0 +1,72 @@
|
||||
"""Locust load test for DocuVault — D-04, D-05, D-06.
|
||||
|
||||
Run:
|
||||
locust --headless --users 50 --spawn-rate 10 --run-time 5m \\
|
||||
--host http://localhost:8000 \\
|
||||
--csv backend/load_tests/results \\
|
||||
-f backend/load_tests/locustfile.py
|
||||
|
||||
Prerequisites: a user with LOAD_TEST_EMAIL/LOAD_TEST_PASSWORD must exist in the
|
||||
DB, or on_start will register one automatically (idempotent — 409 is ignored).
|
||||
|
||||
Full implementation is in plan 06-03. This skeleton defines the class shape and
|
||||
SLA listener so downstream CI tooling can import the file without errors.
|
||||
"""
|
||||
import os
|
||||
|
||||
from locust import HttpUser, between, events, task
|
||||
|
||||
TEST_EMAIL = os.environ.get("LOAD_TEST_EMAIL", "loadtest@example.com")
|
||||
TEST_PASSWORD = os.environ.get("LOAD_TEST_PASSWORD", "Loadtest123!@#")
|
||||
|
||||
|
||||
class DocuVaultUser(HttpUser):
|
||||
"""Simulated DocuVault end-user.
|
||||
|
||||
Scenarios (weighted tasks):
|
||||
- list_documents (weight 5) — GET /api/documents/
|
||||
- upload_document (weight 2) — POST /api/documents/upload-url flow
|
||||
- refresh_token (weight 1) — POST /api/auth/refresh
|
||||
"""
|
||||
|
||||
wait_time = between(0.5, 2.0)
|
||||
access_token: str = ""
|
||||
|
||||
def on_start(self) -> None:
|
||||
"""Authenticate before running tasks.
|
||||
|
||||
Registers the load-test user if it does not exist (409 is silently
|
||||
ignored), then performs a login to obtain an access token.
|
||||
"""
|
||||
raise NotImplementedError("implementation in 06-03")
|
||||
|
||||
def _auth_headers(self) -> dict:
|
||||
"""Return Authorization header dict for authenticated requests."""
|
||||
raise NotImplementedError("implementation in 06-03")
|
||||
|
||||
@task(5)
|
||||
def list_documents(self) -> None:
|
||||
"""GET /api/documents/ — highest-frequency task."""
|
||||
raise NotImplementedError("implementation in 06-03")
|
||||
|
||||
@task(2)
|
||||
def upload_document(self) -> None:
|
||||
"""Two-step presigned upload: POST /upload-url → PUT MinIO → POST /{id}/confirm."""
|
||||
raise NotImplementedError("implementation in 06-03")
|
||||
|
||||
@task(1)
|
||||
def refresh_token(self) -> None:
|
||||
"""POST /api/auth/refresh — rotates the httpOnly refresh cookie."""
|
||||
raise NotImplementedError("implementation in 06-03")
|
||||
|
||||
|
||||
@events.quitting.add_listener
|
||||
def check_sla(environment, **kwargs) -> None:
|
||||
"""Fail the Locust run if SLA thresholds are breached.
|
||||
|
||||
Thresholds (from D-06):
|
||||
- p95 response time > 200 ms → exit code 1
|
||||
- p99 response time > 500 ms → exit code 1
|
||||
- error ratio > 1% → exit code 1
|
||||
"""
|
||||
raise NotImplementedError("implementation in 06-03")
|
||||
Reference in New Issue
Block a user