mirror of
https://github.com/penpot/penpot.git
synced 2026-06-16 04:12:03 +00:00
Penpot Performance Tests
k6-based load and performance test suite for the Penpot backend. Measures HTTP RPC latency, throughput, and error rates under synthetic user load.
Prerequisites
- k6 — Install from https://k6.io/docs/get-started/installation/
- Running Penpot backend — Local devenv (
http://localhost:6060) or a remote instance
Quick Start
# Smoke test — 1 VU, 1 iteration, demo mode
./run.sh smoke
# Full lifecycle with 10 VUs, 5 iterations each
./run.sh lifecycle -v 10 -n 5
# Use registration flow instead of demo profiles
./run.sh lifecycle -m register -v 5 -n 1
# Point to a remote backend
./run.sh lifecycle -u https://penpot.example.com
# Show all options
./run.sh help
Test Scripts
scripts/lifecycle.js — Full User Lifecycle
Simulates a realistic user journey from account creation through CRUD operations:
- Register — Create a new user (demo profile or full registration)
- Login — Authenticate and obtain session cookie
- Get Profile — Fetch current user profile
- Get Teams — List user teams
- Create Project — Create a new project in the default team
- Create File — Create a new design file in the project
- Get File — Fetch the file with its data (pages, objects)
- Update File — Add a rectangle shape (tests optimistic concurrency)
- Upload Image — Upload a PNG to the file's media objects
- Delete File — Remove the file
- Delete Project — Remove the project
- Logout — End the session
Each VU performs the full flow independently, creating and cleaning up its own artifacts.
Configuration
Options for run.sh lifecycle:
| Flag | Env Variable | Default | Description |
|---|---|---|---|
-u URL |
PENPOT_BASE_URL |
http://localhost:6060 |
Penpot backend URL |
-v NUM |
— | 1 |
Number of virtual users |
-n NUM |
— | 1 |
Iterations per VU |
-m MODE |
PENPOT_REGISTER_MODE |
demo |
demo or register |
-k PATH |
K6 |
k6 |
Path to k6 binary |
Register Modes
demo(default): Uses thecreate-demo-profileRPC endpoint. Requires thedemo-usersfeature flag to be enabled on the backend. Fastest for testing.register: Uses the full two-step registration flow (prepare-register-profile+register-profile). Works without any feature flags but is slower.
Shared Client (lib/penpot-client.js)
The shared client module wraps the Penpot backend RPC API using plain JSON (not Transit). Key features:
- JSON transport: Uses
Content-Type: application/jsonfor POST bodies andAccept: application/json(or_fmt=jsonfor GET) for responses. - Cookie-based auth: k6 automatically manages session cookies per VU.
- Session headers: Generates
x-session-idandx-external-session-idUUIDs per VU. - Tagged metrics: Every request is tagged with
rpc_commandfor k6 metric slicing.
Results
Test results are written to results/<timestamp>/ as JSON. k6 also prints a summary to stdout with percentile breakdowns per RPC command.
Thresholds
The lifecycle script includes built-in thresholds that will cause k6 to exit with a non-zero code if exceeded:
http_req_duration p95 < 5000ms(global)http_req_failed < 1%(global)- Per-command thresholds for login, profile, project, file, and update operations
Adding New Flows
To add a new test flow:
- Create
scripts/<flow-name>.js - Import the shared client:
import { createClient } from "../lib/penpot-client.js"; - Implement the flow using the client methods
- Add a command in
run.sh
Architecture Notes
- The backend supports both Transit JSON and plain JSON. This test suite uses plain JSON for simplicity (no Transit encoder needed in k6).
- JSON request keys are in kebab-case (matching Clojure conventions). JSON response keys are in camelCase (backend's default JSON encoding).
update-filesends theidparameter both in the query string and in the POST body, matching the frontend's behavior.- The backend uses optimistic concurrency control (
revn) for file updates. The test retries once on conflict.