SMOKE_TEST.md 5.4 KB

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:

BASE=https://eks-relay.easyklima.com
SECRET=...   # RELAY_SHARED_SECRET, read from .env — do not echo it
ADMIN=...    # ADMIN_TOKEN

1. Liveness

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

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

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

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.

MSG=$RANDOM
PAYLOAD=$(cat <<JSON
{
  "event": "message_created",
  "id": $MSG,
  "message_type": "incoming",
  "content_type": "incoming_email",
  "content": "Test techniczny relaya — prosze zignorowac.",
  "conversation": {
    "id": 1311, "inbox_id": 1, "channel": "Channel::Email",
    "labels": [], "custom_attributes": {},
    "additional_attributes": {"mail_subject": "Relay smoke test"},
    "meta": {"sender": {"id": 1, "name": "Relay Test", "email": "relay-test@example.com"}}
  },
  "sender": {"id": 1, "name": "Relay Test", "email": "relay-test@example.com"}
}
JSON
)

# First delivery → 202 with a jobId
curl -s -X POST $BASE/webhooks/chatwoot -H 'Content-Type: application/json' -d "$PAYLOAD" | jq

# Same message id again → 200 with duplicate:true and NO new job
curl -s -X POST $BASE/webhooks/chatwoot -H 'Content-Type: application/json' -d "$PAYLOAD" | jq

Confirm in the DB:

curl -s "$BASE/admin/messages?conversationId=1311" -H "Authorization: Bearer $ADMIN" | jq
curl -s "$BASE/admin/events?conversationId=1311"   -H "Authorization: Bearer $ADMIN" | jq
curl -s "$BASE/admin/jobs"                          -H "Authorization: Bearer $ADMIN" | jq

Expect exactly one ProcessedMessage row for that message id and exactly one job. On the server the same data is available offline:

sudo docker compose exec relay npm run events -- --conversation 1311
sudo docker compose exec relay npm run events -- --jobs

Non-actionable events must be skipped with 200:

for t in '"outgoing"' ; do
  curl -s -X POST $BASE/webhooks/chatwoot -H 'Content-Type: application/json' \
    -d "{\"event\":\"message_created\",\"id\":$RANDOM,\"message_type\":$t,\"content\":\"x\",\"conversation\":{\"id\":1311}}" | jq -c
done
# expect {"ok":true,"skipped":true,"reason":"not_incoming:outgoing"}

6. Ticket creation (writes to Chatwoot — test conversation only)

curl -s -X POST $BASE/tools/new_ticket \
  -H 'Content-Type: application/json' -H "Authorization: Bearer $SECRET" \
  -d '{"conversationId": <TEST_CONV_ID>, "summary": "Smoke test handoff"}' | jq

Expect {"ok":true,"ticketNumber":"EKS-YYYYMMDD-<id>","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=<id>webhook_accepted, then flowise_response, then reply_sent.
  4. Confirm the reply is visible in Chatwoot and in Flowise → View Messages under session chatwoot:<id>.
  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.