// k6 smoke/load test — Bookra public booking surface. // // Local smoke (script validation, not perf evidence — loopback latency is ~0ms): // k6 run tests/load/bookings.js // Load against a deployed target (run from the Proxmox host, not your box): // BASE_URL=https://api.bookra.eu VUS=50 DURATION=3m k6 run tests/load/bookings.js // Include a booking-creation pass (writes a real row — point at a test tenant): // WRITE=1 TENANT_SLUG=test-tenant BASE_URL=... k6 run tests/load/bookings.js // // Endpoints exercised: /healthz, /v1/meta/config, public availability + catalog, // and (WRITE=1) POST /v1/public/bookings — the money path, rate-limited. import http from "k6/http"; import { check, sleep, group } from "k6"; const BASE_URL = (__ENV.BASE_URL || "http://localhost:8080").replace(/\/$/, ""); const TENANT = __ENV.TENANT_SLUG || "demo"; const WRITE = __ENV.WRITE === "1"; export const options = { scenarios: { booking_surface: { executor: "constant-vus", vus: Number(__ENV.VUS || 2), duration: __ENV.DURATION || "30s", }, }, thresholds: { // 800ms covers a cold local dev server; tighten to 400 once aimed at prod. http_req_duration: ["p(95)<800"], http_req_failed: ["rate<0.01"], "checks{check:public reads ok}": ["rate>0.99"], }, }; export default function () { group("public reads", () => { // 404 on an unknown tenant slug is a correct answer, not a failure — // mark it expected so http_req_failed stays meaningful. const tenantOk = { responseCallback: http.expectedStatuses(200, 404) }; const hz = http.get(`${BASE_URL}/healthz`); const cfg = http.get(`${BASE_URL}/v1/meta/config`); const cat = http.get(`${BASE_URL}/v1/public/tenants/${TENANT}/catalog`, tenantOk); const av = http.get(`${BASE_URL}/v1/public/tenants/${TENANT}/availability`, tenantOk); check(null, { "public reads ok": hz.status === 200 && cfg.status === 200 && [200, 404].includes(cat.status) && [200, 404].includes(av.status), }); }); if (WRITE) { group("booking create", () => { const res = http.post( `${BASE_URL}/v1/public/bookings`, JSON.stringify({ tenantSlug: TENANT, serviceId: __ENV.SERVICE_ID || "svc_loadtest", startsAt: new Date(Date.now() + 864e5).toISOString(), customer: { name: "k6 loadtest", email: "k6@example.invalid" }, }), { headers: { "Content-Type": "application/json" } }, ); // 201/200 created, 400/404 validation — all prove the handler runs; // 5xx and 429-at-low-VUS are the failures that matter. check(res, { "booking handler healthy": (r) => r.status < 500 }); }); } sleep(0.5); }