test(parity): add api smoke assertions
This commit is contained in:
@@ -19,12 +19,14 @@ KEEP_ALIVE="true"
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: scripts/parity_frontend_smoke.sh [--check|--print|--boot-only]
|
||||
Usage: scripts/parity_frontend_smoke.sh [--check|--print|--boot-only|--api-smoke] [--no-seed]
|
||||
|
||||
Modes:
|
||||
--check Validate local prerequisites and write a readiness report.
|
||||
--print Print the exact boot commands without starting processes.
|
||||
--boot-only Start GoChat and Vite, verify /health and frontend HTTP, then exit.
|
||||
--api-smoke Run seed plus Chatwoot-frontend API assertions against a running GoChat backend.
|
||||
--no-seed Skip seed during --api-smoke and use GOCHAT_SEED_* values already present in DB.
|
||||
|
||||
Environment:
|
||||
CHATWOOT_DIR Chatwoot checkout path. Default: reference/chatwoot
|
||||
@@ -38,11 +40,15 @@ Environment:
|
||||
USAGE
|
||||
}
|
||||
|
||||
RUN_SEED="true"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--check) MODE="check" ;;
|
||||
--print) MODE="print" ;;
|
||||
--boot-only) KEEP_ALIVE="false" ;;
|
||||
--api-smoke) MODE="api-smoke" ;;
|
||||
--no-seed) RUN_SEED="false" ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "unknown argument: $arg" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
@@ -77,6 +83,12 @@ $status
|
||||
scripts/parity_frontend_smoke.sh --boot-only
|
||||
\`\`\`
|
||||
|
||||
## API Smoke Command
|
||||
|
||||
\`\`\`bash
|
||||
scripts/parity_frontend_smoke.sh --api-smoke
|
||||
\`\`\`
|
||||
|
||||
## Backend
|
||||
|
||||
- URL: http://$API_HOST:$API_PORT
|
||||
@@ -101,21 +113,21 @@ GOCHAT_SEED_INBOX_NAME="B12 Smoke Website Inbox" \\
|
||||
go run ./cmd/gochat seed
|
||||
\`\`\`
|
||||
|
||||
The seed command creates deterministic login/account/inbox/contact/conversation data plus SLA, CustomRole, AgentCapacity, and Captain fixtures for the smoke paths.
|
||||
The seed command creates deterministic login/account/inbox/contact/company/conversation data plus CSAT, SLA, CustomRole, AgentCapacity, and Captain fixtures for the smoke paths.
|
||||
|
||||
## Smoke Matrix
|
||||
|
||||
| Area | Current result | Owner if failing |
|
||||
| --- | --- | --- |
|
||||
| Boot GoChat backend | Not run in check mode | B12.1 |
|
||||
| Boot reused Chatwoot Vite frontend | Not run in check mode | B12.1 |
|
||||
| Auth/profile | Pending browser/API smoke | B2/B12.2 |
|
||||
| Inbox list/settings | Pending browser/API smoke | B5/B12.2 |
|
||||
| Conversation list/detail/message send | Pending browser/API smoke | B3/B12.2 |
|
||||
| Contact/company views | Pending browser/API smoke | B4/B12.2 |
|
||||
| Widget config/message | Pending browser/API smoke | B12.2 |
|
||||
| Public CSAT | Pending browser/API smoke | B8/B12.2 |
|
||||
| Enterprise screens | Pending browser/API smoke | B7-B11/B12.3 |
|
||||
| Boot GoChat backend | ${BOOT_BACKEND_RESULT:-Not run in check mode} | B12.1 |
|
||||
| Boot reused Chatwoot Vite frontend | ${BOOT_FRONTEND_RESULT:-Not run in check mode} | B12.1 |
|
||||
| Auth/profile | ${AUTH_PROFILE_RESULT:-Pending browser/API smoke} | B2/B12.2 |
|
||||
| Inbox list/settings | ${INBOX_RESULT:-Pending browser/API smoke} | B5/B12.2 |
|
||||
| Conversation list/detail/message send | ${CONVERSATION_RESULT:-Pending browser/API smoke} | B3/B12.2 |
|
||||
| Contact/company views | ${CRM_RESULT:-Pending browser/API smoke} | B4/B12.2 |
|
||||
| Widget config/message | ${WIDGET_RESULT:-Pending browser/API smoke} | B12.2 |
|
||||
| Public CSAT | ${CSAT_RESULT:-Pending browser/API smoke} | B8/B12.2 |
|
||||
| Enterprise screens | ${ENTERPRISE_RESULT:-Pending browser/API smoke} | B7-B11/B12.3 |
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -154,9 +166,166 @@ wait_url() {
|
||||
print_commands() {
|
||||
echo "Backend: ${backend_cmd[*]}"
|
||||
echo "Frontend: (cd $CHATWOOT_DIR && ${frontend_cmd[*]})"
|
||||
echo "API smoke: scripts/parity_frontend_smoke.sh --api-smoke"
|
||||
echo "Report: $REPORT_PATH"
|
||||
}
|
||||
|
||||
tmp_file() {
|
||||
local file
|
||||
file="$(mktemp "$LOG_DIR/smoke.XXXXXX")"
|
||||
echo "$file"
|
||||
}
|
||||
|
||||
json_value() {
|
||||
local file="$1"
|
||||
local expr="$2"
|
||||
node -e 'const fs = require("fs"); const data = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); const value = Function("data", "return " + process.argv[2])(data); if (value === undefined || value === null) process.exit(2); if (typeof value === "object") console.log(JSON.stringify(value)); else console.log(String(value));' "$file" "$expr"
|
||||
}
|
||||
|
||||
json_assert() {
|
||||
local file="$1"
|
||||
local expr="$2"
|
||||
local label="$3"
|
||||
node -e 'const fs = require("fs"); const data = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); if (!Function("data", "return " + process.argv[2])(data)) { console.error("assertion failed: " + process.argv[3]); process.exit(1); }' "$file" "$expr" "$label"
|
||||
echo "ok: $label"
|
||||
}
|
||||
|
||||
header_value() {
|
||||
local file="$1"
|
||||
local name="$2"
|
||||
awk -v key="${name,,}" 'BEGIN { FS=":" } { current=tolower($1); if (current == key) { sub(/^[[:space:]]+/, "", $2); sub(/\r$/, "", $2); print $2; exit } }' "$file"
|
||||
}
|
||||
|
||||
authed_curl() {
|
||||
curl -fsS \
|
||||
-H "access-token: $ACCESS_TOKEN" \
|
||||
-H "client: $CLIENT" \
|
||||
-H "uid: $UID_HEADER" \
|
||||
-H "token-type: $TOKEN_TYPE" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
run_api_smoke() {
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
local seed_file account_id inbox_id contact_id company_id conversation_display_id conversation_uuid
|
||||
seed_file="$(tmp_file)"
|
||||
if [[ "$RUN_SEED" == "true" ]]; then
|
||||
echo "seeding smoke data..."
|
||||
(cd "$ROOT" && env GOCACHE="${GOCACHE:-/tmp/gochat-gocache}" GOMODCACHE="${GOMODCACHE:-/tmp/gochat-gomodcache}" go run ./cmd/gochat seed) >"$seed_file"
|
||||
else
|
||||
cat >"$seed_file" <<SEED
|
||||
{"admin_email":"${GOCHAT_SEED_ADMIN_EMAIL:-admin@gochat.local}","admin_password":"${GOCHAT_SEED_ADMIN_PASSWORD:-changeme}","account_id":"${GOCHAT_SMOKE_ACCOUNT_ID:-1}","inbox_id":"${GOCHAT_SMOKE_INBOX_ID:-1}","contact_id":"${GOCHAT_SMOKE_CONTACT_ID:-1}","company_id":"${GOCHAT_SMOKE_COMPANY_ID:-1}","conversation_display_id":"${GOCHAT_SMOKE_CONVERSATION_DISPLAY_ID:-1}","conversation_uuid":"${GOCHAT_SMOKE_CONVERSATION_UUID:-}"}
|
||||
SEED
|
||||
fi
|
||||
cp "$seed_file" "$LOG_DIR/seed.json"
|
||||
|
||||
local admin_email admin_password
|
||||
admin_email="$(json_value "$seed_file" 'data.admin_email')"
|
||||
admin_password="$(json_value "$seed_file" 'data.admin_password')"
|
||||
account_id="$(json_value "$seed_file" 'data.account_id')"
|
||||
inbox_id="$(json_value "$seed_file" 'data.inbox_id')"
|
||||
contact_id="$(json_value "$seed_file" 'data.contact_id')"
|
||||
company_id="$(json_value "$seed_file" 'data.company_id')"
|
||||
conversation_display_id="$(json_value "$seed_file" 'data.conversation_display_id || 1')"
|
||||
conversation_uuid="$(json_value "$seed_file" 'data.conversation_uuid || ""')"
|
||||
|
||||
wait_url "http://$API_HOST:$API_PORT/health" "GoChat"
|
||||
|
||||
local headers body signin_body
|
||||
headers="$(tmp_file)"
|
||||
body="$(tmp_file)"
|
||||
signin_body="$(tmp_file)"
|
||||
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ email: process.argv[2], password: process.argv[3] }));' "$signin_body" "$admin_email" "$admin_password"
|
||||
curl -fsS -D "$headers" -o "$body" -H "Content-Type: application/json" -X POST --data-binary "@$signin_body" "http://$API_HOST:$API_PORT/auth/sign_in"
|
||||
cp "$headers" "$LOG_DIR/sign_in.headers"
|
||||
cp "$body" "$LOG_DIR/sign_in.json"
|
||||
json_assert "$body" 'data.data && data.data.email === "'"$admin_email"'"' "auth sign_in returns current user"
|
||||
|
||||
ACCESS_TOKEN="$(header_value "$headers" access-token)"
|
||||
CLIENT="$(header_value "$headers" client)"
|
||||
UID_HEADER="$(header_value "$headers" uid)"
|
||||
TOKEN_TYPE="$(header_value "$headers" token-type)"
|
||||
if [[ -z "$ACCESS_TOKEN" || -z "$CLIENT" || -z "$UID_HEADER" ]]; then
|
||||
echo "sign_in did not return DeviseTokenAuth headers" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/auth/validate_token"
|
||||
cp "$body" "$LOG_DIR/validate_token.json"
|
||||
json_assert "$body" 'data.payload && data.payload.success === true' "auth validate_token accepts frontend headers"
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/profile"
|
||||
cp "$body" "$LOG_DIR/profile.json"
|
||||
json_assert "$body" 'data.email === "'"$admin_email"'"' "profile returns Chatwoot user serializer"
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/inboxes"
|
||||
cp "$body" "$LOG_DIR/inboxes.json"
|
||||
json_assert "$body" 'Array.isArray(data.payload) && data.payload.some(inbox => Number(inbox.id) === Number("'"$inbox_id"'"))' "inbox list includes seeded widget inbox"
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/conversations?status=open"
|
||||
cp "$body" "$LOG_DIR/conversations.json"
|
||||
json_assert "$body" 'data.data && Array.isArray(data.data.payload) && data.data.payload.length >= 1' "conversation list returns Chatwoot data payload"
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/conversations/$conversation_display_id/messages"
|
||||
cp "$body" "$LOG_DIR/conversation_messages.json"
|
||||
json_assert "$body" 'data.payload && Array.isArray(data.payload) && data.payload.length >= 1' "conversation messages return payload"
|
||||
|
||||
local message_body
|
||||
message_body="$(tmp_file)"
|
||||
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ content: "B12 API smoke outgoing message", message_type: "outgoing", private: false }));' "$message_body"
|
||||
body="$(tmp_file)"
|
||||
authed_curl -X POST --data-binary "@$message_body" -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/conversations/$conversation_display_id/messages"
|
||||
cp "$body" "$LOG_DIR/message_create.json"
|
||||
json_assert "$body" 'data.content === "B12 API smoke outgoing message"' "message create accepts dashboard payload"
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/contacts/$contact_id"
|
||||
cp "$body" "$LOG_DIR/contact.json"
|
||||
json_assert "$body" 'data.payload && Number(data.payload.id) === Number("'"$contact_id"'")' "contact show returns payload"
|
||||
|
||||
body="$(tmp_file)"
|
||||
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/companies/$company_id"
|
||||
cp "$body" "$LOG_DIR/company.json"
|
||||
json_assert "$body" 'data.payload && Number(data.payload.id) === Number("'"$company_id"'")' "company show returns payload"
|
||||
|
||||
body="$(tmp_file)"
|
||||
curl -fsS -X POST -o "$body" "http://$API_HOST:$API_PORT/api/v1/widget/config?website_token=gochat-smoke-widget-token"
|
||||
cp "$body" "$LOG_DIR/widget_config.json"
|
||||
json_assert "$body" 'data.website_channel_config && data.website_channel_config.website_token === "gochat-smoke-widget-token" && data.contact && data.contact.pubsub_token' "widget config returns auth token"
|
||||
local widget_token
|
||||
widget_token="$(json_value "$body" 'data.contact.pubsub_token')"
|
||||
|
||||
body="$(tmp_file)"
|
||||
curl -fsS -H "X-Auth-Token: $widget_token" -H "Content-Type: application/json" -X POST --data-binary '{"message":{"content":"B12 widget smoke message"}}' -o "$body" "http://$API_HOST:$API_PORT/api/v1/widget/messages"
|
||||
cp "$body" "$LOG_DIR/widget_message.json"
|
||||
json_assert "$body" 'data.content === "B12 widget smoke message" && data.conversation_id' "widget message create works"
|
||||
|
||||
if [[ -n "$conversation_uuid" ]]; then
|
||||
body="$(tmp_file)"
|
||||
curl -fsS -o "$body" "http://$API_HOST:$API_PORT/public/api/v1/csat_survey/$conversation_uuid"
|
||||
cp "$body" "$LOG_DIR/public_csat.json"
|
||||
json_assert "$body" 'data.display_type === "emoji" && data.content' "public CSAT show returns survey payload"
|
||||
fi
|
||||
|
||||
BOOT_BACKEND_RESULT="Passed /health during API smoke" \
|
||||
AUTH_PROFILE_RESULT="Passed API smoke" \
|
||||
INBOX_RESULT="Passed API smoke" \
|
||||
CONVERSATION_RESULT="Passed API smoke" \
|
||||
CRM_RESULT="Passed contact/company API smoke" \
|
||||
WIDGET_RESULT="Passed API smoke" \
|
||||
CSAT_RESULT="Passed public show API smoke" \
|
||||
ENTERPRISE_RESULT="Pending B12.3 browser/API smoke" \
|
||||
write_report "API smoke passed for reused Chatwoot frontend core paths; live browser smoke not run in this mode." "Command run: \`scripts/parity_frontend_smoke.sh --api-smoke\`. Logs and payload captures are under \`$LOG_DIR\`. B12.2 still needs browser navigation assertions for the reused Vite app, and B12.3 must add enterprise screen assertions."
|
||||
echo "api smoke passed; report written to $REPORT_PATH"
|
||||
}
|
||||
|
||||
if [[ "$MODE" == "print" ]]; then
|
||||
print_commands
|
||||
exit 0
|
||||
@@ -171,6 +340,11 @@ if [[ "$MODE" == "check" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$MODE" == "api-smoke" ]]; then
|
||||
run_api_smoke
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
trap 'jobs -pr | xargs -r kill 2>/dev/null || true' EXIT
|
||||
|
||||
@@ -182,6 +356,8 @@ echo "starting reused Chatwoot frontend..."
|
||||
(cd "$CHATWOOT_DIR" && "${frontend_cmd[@]}") >"$LOG_DIR/chatwoot-vite.log" 2>&1 &
|
||||
wait_url "http://$FRONTEND_HOST:$FRONTEND_PORT" "Chatwoot Vite"
|
||||
|
||||
BOOT_BACKEND_RESULT="Passed boot readiness" \
|
||||
BOOT_FRONTEND_RESULT="Passed boot readiness" \
|
||||
write_report "Boot smoke passed for backend and reused Chatwoot frontend." "This run verified process boot and HTTP readiness only. B12.2 must add browser/API path assertions and update the smoke matrix."
|
||||
echo "smoke boot passed; report written to $REPORT_PATH"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user