# Load Test — ForumZ

Tests the JWT-protected `GET /api/users/me` endpoint under sustained load using [k6](https://k6.io).

## How to run

```bash
# Bring up the full stack first
docker compose up --build -d

# Run the load test (from repo root)
k6 run load-test/k6-load-test.js

# Export raw metrics
k6 run --out json=load-test/results.json load-test/k6-load-test.js
```

## Test profile

| Phase     | Duration | Virtual Users |
|-----------|----------|---------------|
| Ramp-up   | 30 s     | 0 → 50        |
| Sustained | 60 s     | 50            |
| Ramp-down | 30 s     | 50 → 0        |

## Thresholds

| Metric                | Target   |
|-----------------------|----------|
| Error rate            | < 1 %    |
| P95 response time     | < 500 ms |

## Auth flow under test

Each iteration sends a `GET /api/users/me` with **both** an `Authorization: Bearer <JWT>` header **and** a `Cookie: __Secure-Fgp=<fingerprint>` value. The backend's `RequireAuth` middleware verifies the JWT signature, `iss`, `exp`, `nbf`, and then constant-time-compares SHA-256(cookie) against the `userFingerprint` claim — every protected request exercises the full OWASP token-sidejacking defense.

## Results (2026-05-17, MacBook, 3 Go replicas behind Nginx)

| Metric                 | Value         |
|------------------------|---------------|
| Total requests         | 42 208        |
| Throughput             | **352 req/s** |
| Error rate             | **0.00 %** ✓  |
| Avg latency            | 6.3 ms        |
| Median latency         | 5.5 ms        |
| P90 latency            | 11.2 ms       |
| P95 latency            | **13.3 ms** ✓ |
| P99 latency            | 23.1 ms       |
| Max latency            | 62.5 ms       |
| All checks passed      | 126 624 / 126 624 (100 %) ✓ |

All k6 thresholds **PASSED** (`p95<500ms`, `error_rate<1%`).

## Analysis

**Bottleneck**: The Nginx load balancer and Go backends handle JWT verification + fingerprint check + DB query comfortably at 352 req/s. The 100 ms think time per VU (≈9 req/s theoretical ceiling per VU × 50 VUs = 450 req/s) means the stack is not yet saturated — actual throughput is limited by the think time, not the server. Removing the sleep would push the system harder; preliminary testing shows the PostgreSQL connection pool (10 conns × 3 replicas = 30 total) would become the bottleneck under heavier concurrency.

**Cost of the OWASP hardening**: Adding the fingerprint cookie verification (one SHA-256 + one constant-time compare per request, ~microseconds) is well within measurement noise. P95 latency at 13.3 ms is actually lower than the pre-hardening run (21.9 ms), driven by an idle machine state rather than the auth code itself.

**Scaling path**: Increasing `MaxConns` in `db.go` or adding more backend replicas in `docker-compose.yml` would raise throughput further before the DB itself becomes the limit.
