Comprehensive load and stress testing suite for the Bonfire federated social networking API, built with k6. It exercises 53 Mastodon-compatible API endpoints across 10 functional categories and produces a rich HTML dashboard report alongside a machine-readable JSON summary.
- 53 endpoints covering Instance, Accounts, Timelines, Statuses, Social, Lists, Notifications, Follow Requests, Media, and Reports
- 6 built-in scenarios — from quick baselines to multi-hour soak tests
- Per-endpoint metrics — response times (min/avg/p95/p99/max), success rates, call counts
- Category-level aggregation — quickly spot which API area is slowest
- HTTP method breakdown — GET / POST / PUT / DELETE tracked independently
- Status-code distribution — 2xx / 3xx / 4xx / 5xx counters
- Health scoring — automatic 0–100 health score with pass/fail thresholds
- HTML dashboard — self-contained, dark-themed report (
bonfire_load_test_report.html) - JSON results — structured output for CI pipelines (
bonfire_load_test_results.json)
| Tool | Version | Install |
|---|---|---|
| k6 | ≥ 0.45 | brew install k6 (macOS) or see k6 installation docs |
An OAuth Bearer token with read/write access to the target Bonfire instance is required.
k6 run -e TOKEN="<your_oauth_token>" k6_bonfire_with_html_dashboard.jsThis runs the default realistic_user scenario against https://indieweb.studio with 10 virtual users for 60 seconds.
| Variable | Required | Default | Description |
|---|---|---|---|
TOKEN |
Yes | — | OAuth Bearer token for authenticated endpoints |
HOST |
No | indieweb.studio |
Target hostname (without protocol) |
HTTPS |
No | 1 |
Use HTTPS. Set to 0 for plain HTTP |
VUS |
No | 10 |
Peak number of virtual users |
DURATION |
No | 60s |
Main stage duration (e.g. 30s, 2m, 1h) |
SCENARIO |
No | realistic_user |
Test scenario to run (see below) |
WRITE_RATIO |
No | 0.8 |
Probability (0.0 – 1.0) of executing write operations |
Randomized run through all endpoint categories with write operations enabled. Good for getting a first look at overall API health.
k6 run -e TOKEN="..." -e SCENARIO=baseline k6_bonfire_with_html_dashboard.jsStages: ramp up → steady at VUS → ramp down (30 s each + DURATION steady).
Simulates real user behaviour with weighted probabilities — timelines and notifications are hit most often, lists and reports rarely. Best for understanding production-like load patterns.
k6 run -e TOKEN="..." k6_bonfire_with_html_dashboard.jsStages: 1 m ramp → DURATION at peak → 1 m taper → 2 m cool-down.
Hits every single endpoint on every VU iteration — no randomness gates. Best for per-endpoint stress testing and catching regressions across the full API surface.
k6 run -e TOKEN="..." -e SCENARIO=exhaustive -e VUS=10 -e DURATION=2m k6_bonfire_with_html_dashboard.jsStages: ramp up → steady at VUS → ramp down (30 s each + DURATION steady).
Focuses on status creation/deletion and list management with occasional timeline reads. Useful for testing write-path performance and database load.
k6 run -e TOKEN="..." -e SCENARIO=write_heavy -e WRITE_RATIO=0.8 k6_bonfire_with_html_dashboard.jsStages: ramp to 30 % VUs → steady at 50 % VUs → ramp down.
Sudden traffic spike to 2× peak VUs, holds for 1 minute, then drops back. Tests auto-scaling and recovery behaviour.
k6 run -e TOKEN="..." -e SCENARIO=spike_test -e VUS=20 k6_bonfire_with_html_dashboard.jsStages: 1 m warm-up at 5 VUs → 10 s spike to VUS×2 → 1 m hold → 10 s drop → 1 m cool-down.
Long-running constant load at 50 % of peak VUs for 2 hours. Detects memory leaks, connection exhaustion, and gradual degradation.
k6 run -e TOKEN="..." -e SCENARIO=soak_test -e VUS=20 k6_bonfire_with_html_dashboard.jsDuration: 2 hours at VUS/2 constant virtual users.
# Quick smoke test — 5 VUs, 30 seconds, default scenario
k6 run -e TOKEN="..." -e VUS=5 -e DURATION=30s k6_bonfire_with_html_dashboard.js
# Exhaustive endpoint coverage with 10 VUs for 2 minutes
k6 run -e TOKEN="..." -e VUS=10 -e DURATION=2m -e SCENARIO=exhaustive k6_bonfire_with_html_dashboard.js
# Write-heavy against a local dev instance over HTTP
k6 run -e TOKEN="..." -e HOST=localhost:4000 -e HTTPS=0 -e SCENARIO=write_heavy k6_bonfire_with_html_dashboard.js
# Spike test with 50 peak VUs
k6 run -e TOKEN="..." -e VUS=50 -e SCENARIO=spike_test k6_bonfire_with_html_dashboard.js
# 2-hour soak test
k6 run -e TOKEN="..." -e VUS=30 -e SCENARIO=soak_test k6_bonfire_with_html_dashboard.jsAfter each run, three outputs are produced:
| Output | Location | Description |
|---|---|---|
| Console summary | stdout |
Coloured text summary with all k6 metrics |
| HTML dashboard | ./bonfire_load_test_report.html |
Self-contained dark-themed report — open in any browser |
| JSON results | ./bonfire_load_test_results.json |
Machine-readable results for CI/CD integration |
- Health Score — 0–100 overall score (Healthy / Degraded / Critical)
- Summary Cards — total requests, success rate, avg/p95/max response times, threshold status
- Slowest Endpoints — top 5 endpoints by p95 latency
- Category Performance — aggregated metrics per API area
- Per-Endpoint Breakdown — every endpoint with min/avg/p95/max, pass/fail counts
- Pass/Fail Summary — visual bars showing pass rates per endpoint
- HTTP Methods — GET/POST/PUT/DELETE performance comparison
- Status Code Distribution — 2xx/3xx/4xx/5xx breakdown with bar chart
- Threshold Results — pass/fail status for each configured threshold
The test defines the following built-in thresholds:
| Threshold | Condition |
|---|---|
http_req_duration |
p95 < 2 000 ms, p99 < 5 000 ms |
http_req_failed |
rate < 5 % |
method_get_success |
rate > 99 % |
method_post_success |
rate > 95 % |
cat_timelines_duration |
p95 < 1 000 ms |
cat_statuses_duration |
p95 < 1 500 ms |
k6 exits with a non-zero code when any threshold fails, making it suitable for CI gating.
Click to expand full endpoint list
| Method | Path |
|---|---|
| GET | /.well-known/host-meta |
| GET | /.well-known/nodeinfo |
| GET | /api/v1/instance |
| GET | /api/v2/instance |
| GET | /api/v1/custom_emojis |
| Method | Path |
|---|---|
| GET | /api/v1/accounts/verify_credentials |
| GET | /api/v1/preferences |
| GET | /api/v1/accounts/:id |
| GET | /api/v1/accounts/:id/statuses |
| GET | /api/v1/accounts/:id/followers |
| GET | /api/v1/accounts/:id/following |
| GET | /api/v1/accounts/relationships |
| POST | /api/v1/accounts/:id/mute |
| POST | /api/v1/accounts/:id/unfollow |
| Method | Path |
|---|---|
| GET | /api/v1/timelines/home |
| GET | /api/v1/timelines/public |
| GET | /api/v1/timelines/tag/:tag |
| GET | /api/v1/timelines/list/:id |
| Method | Path |
|---|---|
| GET | /api/v1/statuses/:id |
| GET | /api/v1/statuses/:id/context |
| GET | /api/v1/statuses/:id/favourited_by |
| GET | /api/v1/statuses/:id/reblogged_by |
| POST | /api/v1/statuses |
| POST | /api/v1/statuses/:id/favourite |
| POST | /api/v1/statuses/:id/unfavourite |
| POST | /api/v1/statuses/:id/reblog |
| POST | /api/v1/statuses/:id/unreblog |
| POST | /api/v1/statuses/:id/unbookmark |
| DELETE | /api/v1/statuses/:id |
| Method | Path |
|---|---|
| GET | /api/v1/bookmarks |
| GET | /api/v1/favourites |
| GET | /api/v1/conversations |
| GET | /api/v1/mutes |
| GET | /api/v1/blocks |
| Method | Path |
|---|---|
| GET | /api/v1/notifications |
| POST | /api/v1/notifications/clear |
| Method | Path |
|---|---|
| GET | /api/v1/lists |
| GET | /api/v1/lists/:id |
| POST | /api/v1/lists |
| POST | /api/v1/lists/:id/accounts |
| PUT | /api/v1/lists/:id |
| DELETE | /api/v1/lists/:id |
| Method | Path |
|---|---|
| GET | /api/v1/follow_requests |
| GET | /api/v1/follow_requests/outgoing |
| POST | /api/v1/follow_requests/:id/authorize |
| POST | /api/v1/follow_requests/:id/reject |
| Method | Path |
|---|---|
| GET | /api/v1/media/:id |
| POST | /api/v1/media/:id |
| Method | Path |
|---|---|
| GET | /api/v1/reports |
| GET | /api/v1/reports/:id |
The test automatically cleans up resources it creates (statuses, lists) during the teardown phase. Statuses are created with unlisted visibility and tagged #k6test for easy identification.