Testing Against It
Table of Contents
Hosted or local
The hosted copy puts everything behind one HTTPS domain. Locally, each part has its own port.
The examples below use a BASE variable so they work against either:
BASE=https://pizzaplayground.deta.my # or http://localhost:8080
Test accounts
Seeded on first boot. Every password is Password123!.
| Account | Role | Can do |
|---|---|---|
owner@playground.local | OWNER | Everything: kanban, history, reset |
cashier@playground.local | CASHIER | Kanban only; history and reset return 403 |
customer01@playground.local to customer20@playground.local | CUSTOMER | Order and see their own orders |
The repository ships test-data/customers.csv with the twenty customer logins, ready for a JMeter CSV Data Set or a k6 SharedArray. Guest checkout also works with no login at all.
Reset between runs
Every test should start from the same state, so the API owns the reset instead of the UI.
TOKEN=$(curl -s -X POST $BASE/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"owner@playground.local","password":"Password123!"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])')
curl -s -X POST $BASE/api/v1/admin/reset -H "Authorization: Bearer $TOKEN"
That clears orders and payments and restarts the order numbers at 1001. Add ?full=true to also reseed users and the menu. It runs in one transaction and returns in well under a second. Put it in your JMeter setUp thread group or your k6 setup().
On the hosted copy, a reset clears everyone's orders, not just yours. Reset your local copy instead.
For load testers
autoPay: trueonPOST /api/v1/ordersmarks the order paid inside the same transaction and never touches the mock gateway. Use it when you want order throughput without the payment hop in the loop.X-Mock-Delay: <milliseconds>onPOST /api/v1/orders(forwarded to the gateway) or on any mock request directly makes the gateway slow. The hosted copy only exposes the mock's/paypage, so there use it onPOST /api/v1/orders. Good for showing how a slow third party backs up your thread pool.- Order numbers come from a database sequence starting at 1001, so a run of 5,000 orders after a reset ends at 6000. Handy for a sanity check in the report.
- Prices are computed on the server. The request carries ids and quantities only, so a load script cannot accidentally send a wrong total.
- Every error has the same shape:
{"error": "...", "requestId": "..."}with a 4xx or 5xx status. Assert onerror, not on prose.
For UI testers
Every control Playwright needs has a stable data-testid. Click the Customize button on a card, not the card itself.
| Page | Test ids |
|---|---|
| Menu | menu-pizza-card, customize, customizer-size-<id>, customizer-crust-<id>, customizer-topping-<id>, add-to-cart, cart-count, cart-open |
| Checkout | checkout-name, checkout-email, checkout-phone, checkout-type-PICKUP, checkout-type-DELIVERY, checkout-submit, checkout-error |
| Mock pay page | #pay-button, #fail-button (plain ids, it is not a React page) |
| Confirmation | order-number, payment-status |
| Login and register | login-form, register-form, auth-error, admin-login-error |
| Admin | kanban-column-NEW, kanban-column-PREPARING, kanban-column-READY, order-card (with data-order-number), advance-status, cancel-order, refresh-orders, history-from, history-to, history-load, history-table, history-forbidden, admin-logout |
The admin board refreshes itself every five seconds. In a test, click refresh-orders instead of waiting.
API map
Base path /api/v1. Swagger UI (hosted or http://localhost:8080/swagger-ui.html) has the full contract.
| Endpoint | Auth | Purpose |
|---|---|---|
POST /auth/register, POST /auth/login | none | Get a JWT |
GET /menu | none | Pizzas, sizes, crusts, toppings |
POST /orders | optional | Create an order, returns payUrl or, with autoPay, a paid order |
GET /orders/{id} | none | One order |
GET /orders/mine | CUSTOMER | The caller's orders |
POST /payments/callback | HMAC signature | The gateway's webhook |
GET /payments/{orderId}/status | none | What the confirmation page polls |
GET /admin/orders?status= | OWNER or CASHIER | Kanban data |
PATCH /admin/orders/{id}/status | OWNER or CASHIER | Move an order |
GET /admin/orders/history?from=&to= | OWNER | Completed and cancelled orders |
POST /admin/reset[?full=true] | OWNER | Clean slate |
Tips
- Use
127.0.0.1in scripts iflocalhostmisbehaves. Some tools resolvelocalhostto IPv6 first; the containers listen on IPv4. - CORS allows
http://localhost:3000and the hosted domain. Only matters if you point a browser at a different origin; API tools do not care. - Load-test locally, not on the hosted copy. It is one small shared server.
- The JWT lasts twelve hours. Fetch one token in a setUp group and share it, rather than logging in per virtual user.