# Smoke test runbook — EKS Support Relay Run top to bottom after every deployment. Steps 1–5 are non-destructive. Steps 6–7 write to Chatwoot and must use a **test conversation**. Set up once per shell: ```bash BASE=https://eks-relay.easyklima.com SECRET=... # RELAY_SHARED_SECRET, read from .env — do not echo it ADMIN=... # ADMIN_TOKEN ``` ## 1. Liveness ```bash curl -s -o /dev/null -w '%{http_code}\n' $BASE/health # expect 200 curl -s $BASE/health | jq ``` Expect `ok: true`, a service name, and an uptime. No secrets in the body. ## 2. Readiness ```bash curl -s $BASE/ready | jq ``` Expect `ok: true` and a `dependencies` block. Each dependency reports `{ok, status, target}` where `target` is host+path only. `configured` is a set of booleans — it says *whether* a secret is set, never what it is. `database` and `chatwoot` must be `ok`. `woocommerce` / `wpStore` may report non-200 if the shop's Cloudflare challenges the call; that is a routing issue, not a relay fault. ## 3. Tools auth ```bash curl -s -o /dev/null -w '%{http_code}\n' -X POST $BASE/tools/get_payment_methods \ -H 'Content-Type: application/json' -d '{}' # expect 401 curl -s -o /dev/null -w '%{http_code}\n' -X POST $BASE/tools/get_payment_methods \ -H 'Content-Type: application/json' -H 'Authorization: Bearer wrong' -d '{}' # expect 401 ``` ## 4. Read-only tool calls ```bash curl -s -X POST $BASE/tools/get_payment_methods \ -H 'Content-Type: application/json' -H "Authorization: Bearer $SECRET" \ -d '{"language":"pl"}' | jq curl -s -X POST $BASE/tools/get_shipping_data \ -H 'Content-Type: application/json' -H "Authorization: Bearer $SECRET" \ -d '{"language":"pl"}' | jq '.data[:3]' curl -s -X POST $BASE/tools/get_shipping_data \ -H 'Content-Type: application/json' -H "Authorization: Bearer $SECRET" \ -d '{"country":"DE","language":"de"}' | jq ``` `get_payment_methods` needs admin-level Woo consumer keys; if the key is read-only it answers `200` with `ok:false, code:"NOT_IMPLEMENTED"` — that is the designed degradation, and the Flowise tool surfaces it as an explanation. ## 5. Webhook fixture and idempotency Use a conversation id that exists in Chatwoot but has no live customer. ```bash MSG=$RANDOM PAYLOAD=$(cat <, "summary": "Smoke test handoff"}' | jq ``` Expect `{"ok":true,"ticketNumber":"EKS-YYYYMMDD-","status":"created"}`. Call it a second time — expect the **same** number with `"status":"existing"`. In Chatwoot verify the conversation now has the `ticket` label and a `ticket_number` custom attribute. Afterwards, remove the label and clear the attribute to return the test conversation to its previous state. ## 7. End-to-end e-mail test 1. Send an e-mail to the test inbox `aiac-aws@easyklima.com`. 2. Wait for Chatwoot's IMAP poll and confirm a conversation appears (filter: *status = all*, inbox *Skrzynka EasyKlima support*). 3. `GET /admin/events?conversationId=` → `webhook_accepted`, then `flowise_response`, then `reply_sent`. 4. Confirm the reply is visible in Chatwoot and in Flowise → *View Messages* under session `chatwoot:`. 5. Force a handoff (ask something that requires a human). Confirm the `ticket` label, the `ticket_number`, and that further messages log `skipped_ticket_mode` instead of calling Flowise. ## 8. Rollback Point the Chatwoot webhook back at the PHP relay URL. The PHP deployment on the shop's web server is untouched and remains functional. --- ## Recorded run — 2026-08-20 (initial deployment) Commit `f36fc3d`, host `acmycar` / `18.168.156.244`, domain `https://eks-relay.easyklima.com`. | Step | Result | |---|---| | `docker compose config` | OK, one service | | `docker compose ps` | `eks-relay Up (healthy)` | | `GET /health` | `200`, `mode: prod` | | `GET /ready` | `200`, `ok: true` — database, chatwoot, flowise, woocommerce, wpStore all `ok` | | `/tools/*` without a bearer | `401 UNAUTHORIZED` on all seven endpoints | | `/tools/get_payment_methods` | `200`, gateways returned (Przelewy24, PayPal, Stripe, Bancontact, bank transfer, …) | | `/tools/get_shipping_data` (zones) | `200`, zones with ISO country codes | | `/tools/get_shipping_data` (`zoneId=22`, `language=cs`) | `200`, DHL/DHL-COD priced in **CZK** — multicurrency path confirmed | | `/tools/get_product_data` (`search=gaz`) | `200`, published products with prices | | `/tools/get_car_data` (VW, 2016) | `200`, model disambiguation list | | `/tools/get_order_data` (bogus order) | `403 UNAUTHORIZED`, no third-party data leaked | | Webhook, delivery 1 | `202` + `jobId` | | Webhook, delivery 2 (same message id) | `200 duplicate:true`, **no second job** | | Webhook, outgoing message | `200 skipped: not_incoming:outgoing` | | Worker outcome | job `done`; `ProcessedMessage` = `skipped/ticket_mode`; audit `webhook_accepted` → `skipped_ticket_mode` | | `/tools/new_ticket` (test conversation 1600) | `EKS-20260820-1600`, `status: created` | | `/tools/new_ticket` again | same number, `status: existing` | | Chatwoot after ticket | label `ticket` + `ticket_number` + `handoff` + `handoff_reason` set | | Follow-up message on that conversation | conversation fetched from Chatwoot, `skipped_ticket_mode`, Flowise **not** called | | `git pull` + `docker compose restart` | new code live in ~0.4 s, no rebuild, no dependency reinstall | | `RELAY_MODE=dev` + file touch | process reloaded in place (uptime 47 s → 12 s), no restart, no rebuild | Test conversation 1600 was restored afterwards (labels and custom attributes cleared, local `Ticket` row deleted). Mode was set back to `prod`. **Not yet run:** step 7, the end-to-end e-mail test. It needs the Chatwoot webhook repointed at this relay, which is a deliberate, separately approved change.