716 lines
37 KiB
Bash
Executable File
716 lines
37 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Repeatable B12 smoke harness for running the reused Chatwoot frontend against GoChat.
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
CHATWOOT_DIR="${CHATWOOT_DIR:-$ROOT/reference/chatwoot}"
|
|
LOG_DIR="${GOCHAT_SMOKE_LOG_DIR:-$ROOT/.tmp/frontend-smoke}"
|
|
REPORT_PATH="${GOCHAT_SMOKE_REPORT:-$ROOT/docs/parity/frontend_smoke_report.md}"
|
|
API_HOST="${GOCHAT_SMOKE_API_HOST:-127.0.0.1}"
|
|
API_PORT="${GOCHAT_SMOKE_API_PORT:-3000}"
|
|
FRONTEND_HOST="${GOCHAT_SMOKE_FRONTEND_HOST:-localhost}"
|
|
FRONTEND_PORT="${GOCHAT_SMOKE_FRONTEND_PORT:-3036}"
|
|
SEARCH_ENGINE="${GOCHAT_SMOKE_SEARCH_ENGINE:-meilisearch}"
|
|
MEILI_HOST="${GOCHAT_SMOKE_MEILI_HOST:-http://127.0.0.1:7700}"
|
|
MEILI_API_KEY="${GOCHAT_SMOKE_MEILI_API_KEY:-gochat_dev}"
|
|
READY_TIMEOUT_SECONDS="${GOCHAT_SMOKE_READY_TIMEOUT_SECONDS:-180}"
|
|
MODE="run"
|
|
KEEP_ALIVE="true"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: scripts/parity_frontend_smoke.sh [--check|--print|--boot-only|--api-smoke|--browser-smoke|--enterprise-smoke|--enterprise-browser-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.
|
|
--browser-smoke Run API smoke, then drive the reused Chatwoot frontend in headless Chrome.
|
|
--enterprise-smoke Run API smoke plus enterprise feature API assertions against GoChat.
|
|
--enterprise-browser-smoke Run enterprise smoke, then navigate reused Chatwoot enterprise screens in headless Chrome.
|
|
--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
|
|
GOCHAT_SMOKE_API_PORT GoChat backend port. Default: 3000
|
|
GOCHAT_SMOKE_FRONTEND_HOST Vite frontend host. Default: localhost
|
|
GOCHAT_SMOKE_FRONTEND_PORT Vite frontend port. Default: 3036
|
|
GOCHAT_SMOKE_LOG_DIR Log directory. Default: .tmp/frontend-smoke
|
|
GOCHAT_SMOKE_REPORT Markdown report path. Default: docs/parity/frontend_smoke_report.md
|
|
GOCHAT_SMOKE_SEARCH_ENGINE Search engine for boot smoke. Default: meilisearch
|
|
GOCHAT_SMOKE_MEILI_HOST Meilisearch URL. Default: http://127.0.0.1:7700
|
|
GOCHAT_SMOKE_MEILI_API_KEY Meilisearch API key. Default: gochat_dev
|
|
GOCHAT_SMOKE_READY_TIMEOUT_SECONDS Readiness wait timeout. Default: 180
|
|
GOCHAT_SMOKE_CHROME Chrome binary for browser smoke. Default: /usr/bin/google-chrome
|
|
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" ;;
|
|
--browser-smoke) MODE="browser-smoke" ;;
|
|
--enterprise-smoke) MODE="enterprise-smoke" ;;
|
|
--enterprise-browser-smoke) MODE="enterprise-browser-smoke" ;;
|
|
--no-seed) RUN_SEED="false" ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) echo "unknown argument: $arg" >&2; usage; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
backend_cmd=(env GOCHAT_ENV=development GOCHAT_SERVER_HOST="$API_HOST" GOCHAT_SERVER_PORT="$API_PORT" GOCHAT_SERVER_MODE=debug GOCHAT_SEARCH_ENGINE="$SEARCH_ENGINE" GOCHAT_SEARCH_HOST="$MEILI_HOST" GOCHAT_SEARCH_API_KEY="$MEILI_API_KEY" GOCHAT_CAPTAIN_ENABLED=false go run ./cmd/gochat serve)
|
|
frontend_cmd=(env CHATWOOT_API_HOST="http://$API_HOST:$API_PORT" pnpm exec vite --host "$FRONTEND_HOST" --port "$FRONTEND_PORT")
|
|
|
|
need() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
echo "missing required command: $1" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
write_report() {
|
|
local status="$1"
|
|
local notes="$2"
|
|
mkdir -p "$(dirname "$REPORT_PATH")"
|
|
cat >"$REPORT_PATH" <<REPORT
|
|
# Frontend Smoke Report
|
|
|
|
Updated: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
|
|
## Status
|
|
|
|
$status
|
|
|
|
## Boot Command
|
|
|
|
\`\`\`bash
|
|
scripts/parity_frontend_smoke.sh --boot-only
|
|
\`\`\`
|
|
|
|
## API Smoke Command
|
|
|
|
\`\`\`bash
|
|
scripts/parity_frontend_smoke.sh --api-smoke
|
|
\`\`\`
|
|
|
|
## Browser Smoke Command
|
|
|
|
\`\`\`bash
|
|
scripts/parity_frontend_smoke.sh --browser-smoke
|
|
\`\`\`
|
|
|
|
## Enterprise Smoke Command
|
|
|
|
\`\`\`bash
|
|
scripts/parity_frontend_smoke.sh --enterprise-smoke
|
|
\`\`\`
|
|
|
|
## Enterprise Browser Smoke Command
|
|
|
|
\`\`\`bash
|
|
scripts/parity_frontend_smoke.sh --enterprise-browser-smoke
|
|
\`\`\`
|
|
|
|
## Backend
|
|
|
|
- URL: http://$API_HOST:$API_PORT
|
|
- Command: \`${backend_cmd[*]}\`
|
|
- Search: \`$SEARCH_ENGINE\` at \`$MEILI_HOST\`
|
|
- Log: \`$LOG_DIR/gochat.log\`
|
|
|
|
## Reused Chatwoot Frontend
|
|
|
|
- URL: http://$FRONTEND_HOST:$FRONTEND_PORT
|
|
- Source: \`$CHATWOOT_DIR\`
|
|
- Command: \`(cd $CHATWOOT_DIR && ${frontend_cmd[*]})\`
|
|
- Log: \`$LOG_DIR/chatwoot-vite.log\`
|
|
|
|
## Seed Path
|
|
|
|
\`\`\`bash
|
|
GOCHAT_SEED_ADMIN_EMAIL=admin@gochat.local \\
|
|
GOCHAT_SEED_ADMIN_PASSWORD=changeme \\
|
|
GOCHAT_SEED_ACCOUNT_NAME="B12 Smoke Account" \\
|
|
GOCHAT_SEED_INBOX_NAME="B12 Smoke Website Inbox" \\
|
|
go run ./cmd/gochat seed
|
|
\`\`\`
|
|
|
|
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 | ${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 |
|
|
| Search/indexing | ${SEARCH_RESULT:-Pending search API smoke} | B6/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
|
|
|
|
$notes
|
|
|
|
When using the default Meilisearch-first boot command, start Meilisearch separately, for example:
|
|
|
|
\`docker run --rm -p 7700:7700 -e MEILI_MASTER_KEY=$MEILI_API_KEY getmeili/meilisearch:latest\`
|
|
REPORT
|
|
}
|
|
|
|
check_prereqs() {
|
|
need go
|
|
need curl
|
|
need node
|
|
need pnpm
|
|
test -d "$CHATWOOT_DIR" || { echo "missing Chatwoot checkout: $CHATWOOT_DIR" >&2; return 1; }
|
|
test -f "$CHATWOOT_DIR/package.json" || { echo "missing Chatwoot package.json" >&2; return 1; }
|
|
test -f "$ROOT/cmd/gochat/main.go" || { echo "missing GoChat server entrypoint cmd/gochat/main.go" >&2; return 1; }
|
|
}
|
|
|
|
wait_url() {
|
|
local url="$1"
|
|
local label="$2"
|
|
for _ in $(seq 1 "$READY_TIMEOUT_SECONDS"); do
|
|
if curl -fsS "$url" >/dev/null 2>&1; then
|
|
echo "$label ready: $url"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "$label did not become ready: $url" >&2
|
|
return 1
|
|
}
|
|
|
|
print_commands() {
|
|
echo "Backend: ${backend_cmd[*]}"
|
|
echo "Frontend: (cd $CHATWOOT_DIR && ${frontend_cmd[*]})"
|
|
echo "API smoke: scripts/parity_frontend_smoke.sh --api-smoke"
|
|
echo "Browser smoke: scripts/parity_frontend_smoke.sh --browser-smoke"
|
|
echo "Enterprise smoke: scripts/parity_frontend_smoke.sh --enterprise-smoke"
|
|
echo "Enterprise browser smoke: scripts/parity_frontend_smoke.sh --enterprise-browser-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"
|
|
}
|
|
|
|
json_matches() {
|
|
local file="$1"
|
|
local expr="$2"
|
|
node -e 'const fs = require("fs"); const data = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); if (!Function("data", "return " + process.argv[2])(data)) process.exit(1);' "$file" "$expr"
|
|
}
|
|
|
|
authed_json_assert_retry() {
|
|
local url="$1"
|
|
local file="$2"
|
|
local expr="$3"
|
|
local label="$4"
|
|
for _ in $(seq 1 20); do
|
|
if authed_curl -o "$file" "$url" && json_matches "$file" "$expr"; then
|
|
echo "ok: $label"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
json_assert "$file" "$expr" "$label"
|
|
}
|
|
|
|
run_search_reindex() {
|
|
local account_id="$1"
|
|
if [[ "${SEARCH_ENGINE,,}" != "meilisearch" ]]; then
|
|
return 0
|
|
fi
|
|
echo "reindexing smoke search documents..."
|
|
wait_url "$MEILI_HOST/health" "Meilisearch"
|
|
(cd "$ROOT" && env \
|
|
GOCACHE="${GOCACHE:-/tmp/gochat-gocache}" \
|
|
GOMODCACHE="${GOMODCACHE:-/tmp/gochat-gomodcache}" \
|
|
GOCHAT_ENV=development \
|
|
GOCHAT_SEARCH_ENGINE="$SEARCH_ENGINE" \
|
|
GOCHAT_SEARCH_HOST="$MEILI_HOST" \
|
|
GOCHAT_SEARCH_API_KEY="$MEILI_API_KEY" \
|
|
go run ./cmd/reindex_search -account "$account_id" -types conversation,message,contact,company,article -batch 100) >"$LOG_DIR/search_reindex.log"
|
|
}
|
|
|
|
extract_json_object() {
|
|
local input_file="$1"
|
|
local output_file="$2"
|
|
node -e '
|
|
const fs = require("fs");
|
|
const text = fs.readFileSync(process.argv[1], "utf8");
|
|
for (let start = text.indexOf("{"); start !== -1; start = text.indexOf("{", start + 1)) {
|
|
for (let end = text.length; end > start; end = text.lastIndexOf("}", end - 1)) {
|
|
if (end === -1) break;
|
|
const candidate = text.slice(start, end + 1);
|
|
try {
|
|
const parsed = JSON.parse(candidate);
|
|
if (!parsed || !parsed.admin_email || !parsed.account_id) continue;
|
|
fs.writeFileSync(process.argv[2], candidate + "\n");
|
|
process.exit(0);
|
|
} catch (_) {}
|
|
}
|
|
}
|
|
console.error("could not extract JSON object from " + process.argv[1]);
|
|
process.exit(1);
|
|
' "$input_file" "$output_file"
|
|
}
|
|
|
|
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 portal_id article_id conversation_display_id conversation_uuid
|
|
seed_file="$(tmp_file)"
|
|
if [[ "$RUN_SEED" == "true" ]]; then
|
|
echo "seeding smoke data..."
|
|
seed_raw_file="$(tmp_file)"
|
|
(cd "$ROOT" && env GOCACHE="${GOCACHE:-/tmp/gochat-gocache}" GOMODCACHE="${GOMODCACHE:-/tmp/gochat-gomodcache}" go run ./cmd/gochat seed) >"$seed_raw_file"
|
|
cp "$seed_raw_file" "$LOG_DIR/seed.raw.log"
|
|
extract_json_object "$seed_raw_file" "$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}","portal_id":"${GOCHAT_SMOKE_PORTAL_ID:-1}","article_id":"${GOCHAT_SMOKE_ARTICLE_ID:-1}","conversation_id":"${GOCHAT_SMOKE_CONVERSATION_ID:-1}","conversation_display_id":"${GOCHAT_SMOKE_CONVERSATION_DISPLAY_ID:-1}","conversation_uuid":"${GOCHAT_SMOKE_CONVERSATION_UUID:-}","sla_policy_id":"${GOCHAT_SMOKE_SLA_POLICY_ID:-1}","custom_role_id":"${GOCHAT_SMOKE_CUSTOM_ROLE_ID:-1}","capacity_policy_id":"${GOCHAT_SMOKE_CAPACITY_POLICY_ID:-1}","captain_assistant_id":"${GOCHAT_SMOKE_CAPTAIN_ASSISTANT_ID:-1}"}
|
|
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')"
|
|
portal_id="$(json_value "$seed_file" 'data.portal_id || 1')"
|
|
article_id="$(json_value "$seed_file" 'data.article_id || 1')"
|
|
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"
|
|
run_search_reindex "$account_id"
|
|
|
|
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)"
|
|
authed_json_assert_retry "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/search/conversations?q=Smoke" "$body" 'data.payload && Array.isArray(data.payload.conversations) && data.payload.conversations.some(conversation => Number(conversation.id) === Number("'"$conversation_display_id"'") && Number(conversation.account_id) === Number("'"$account_id"'") && conversation.contact && conversation.inbox && conversation.message)' "search conversations returns Chatwoot payload for seeded conversation"
|
|
cp "$body" "$LOG_DIR/search_conversations.json"
|
|
|
|
body="$(tmp_file)"
|
|
authed_json_assert_retry "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/search/messages?q=order&message_type=incoming&inbox_id=$inbox_id" "$body" 'data.payload && Array.isArray(data.payload.messages) && data.payload.messages.some(message => Number(message.account_id) === Number("'"$account_id"'") && Number(message.conversation_id) > 0 && message.content === "Hello, I need help with my order." && typeof message.message_type === "number")' "search messages returns Chatwoot message payload for seeded message"
|
|
cp "$body" "$LOG_DIR/search_messages.json"
|
|
|
|
body="$(tmp_file)"
|
|
authed_json_assert_retry "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/search/contacts?q=Smoke%20Customer" "$body" 'data.payload && Array.isArray(data.payload.contacts) && data.payload.contacts.some(contact => Number(contact.id) === Number("'"$contact_id"'") && contact.email === "customer@gochat.local" && contact.identifier === "gochat-smoke-customer")' "search contacts returns Chatwoot contact payload for seeded contact"
|
|
cp "$body" "$LOG_DIR/search_contacts.json"
|
|
|
|
body="$(tmp_file)"
|
|
authed_json_assert_retry "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/companies/search?q=Smoke%20Company" "$body" 'data.payload && Array.isArray(data.payload) && data.payload.some(company => Number(company.id) === Number("'"$company_id"'") && company.name === "Smoke Company" && company.domain === "gochat.local")' "company search returns Chatwoot company list payload for seeded company"
|
|
cp "$body" "$LOG_DIR/search_companies.json"
|
|
|
|
body="$(tmp_file)"
|
|
authed_json_assert_retry "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/search/articles?q=Onboarding&portal_id=$portal_id&article_status=published" "$body" 'data.payload && Array.isArray(data.payload.articles) && data.payload.articles.some(article => Number(article.id) === Number("'"$article_id"'") && article.title === "Smoke Onboarding Guide" && article.status === "published" && article.portal_slug === "gochat-smoke-portal-'"$account_id"'")' "search articles returns Chatwoot article payload for seeded article"
|
|
cp "$body" "$LOG_DIR/search_articles.json"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v2/accounts/$account_id/live_reports/conversation_metrics"
|
|
cp "$body" "$LOG_DIR/live_report_account_conversation_metric.json"
|
|
json_assert "$body" 'typeof data.open === "number" && typeof data.unattended === "number" && typeof data.unassigned === "number" && typeof data.pending === "number" && !data.success' "live report account store refresh returns raw metric object"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v2/accounts/$account_id/live_reports/grouped_conversation_metrics?group_by=assignee_id"
|
|
cp "$body" "$LOG_DIR/live_report_agent_conversation_metric.json"
|
|
json_assert "$body" 'Array.isArray(data) && data.every(row => Object.prototype.hasOwnProperty.call(row, "assignee_id") && typeof row.open === "number" && typeof row.unattended === "number" && typeof row.unassigned === "number")' "live report agent store refresh returns grouped assignee metrics"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v2/accounts/$account_id/live_reports/grouped_conversation_metrics?group_by=team_id"
|
|
cp "$body" "$LOG_DIR/live_report_team_conversation_metric.json"
|
|
json_assert "$body" 'Array.isArray(data) && data.every(row => Object.prototype.hasOwnProperty.call(row, "team_id") && typeof row.open === "number" && typeof row.unattended === "number" && typeof row.unassigned === "number")' "live report team store refresh returns grouped team metrics"
|
|
|
|
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" \
|
|
SEARCH_RESULT="Passed search 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"
|
|
}
|
|
|
|
run_browser_smoke() {
|
|
run_api_smoke
|
|
wait_url "http://$FRONTEND_HOST:$FRONTEND_PORT/vite-dev/entrypoints/dashboard.js" "Chatwoot Vite"
|
|
GOCHAT_ROOT="$ROOT" \
|
|
CHATWOOT_DIR="$CHATWOOT_DIR" \
|
|
GOCHAT_SMOKE_LOG_DIR="$LOG_DIR" \
|
|
GOCHAT_SMOKE_API_HOST="$API_HOST" \
|
|
GOCHAT_SMOKE_API_PORT="$API_PORT" \
|
|
GOCHAT_SMOKE_FRONTEND_HOST="$FRONTEND_HOST" \
|
|
GOCHAT_SMOKE_FRONTEND_PORT="$FRONTEND_PORT" \
|
|
node "$ROOT/scripts/parity_frontend_browser_smoke.mjs"
|
|
BOOT_BACKEND_RESULT="Passed /health during browser smoke" \
|
|
BOOT_FRONTEND_RESULT="Passed Vite readiness during browser smoke" \
|
|
AUTH_PROFILE_RESULT="Passed browser login plus API smoke" \
|
|
INBOX_RESULT="Passed API smoke" \
|
|
CONVERSATION_RESULT="Passed dashboard browser request plus API smoke" \
|
|
CRM_RESULT="Passed contact/company API smoke" \
|
|
SEARCH_RESULT="Passed search API smoke" \
|
|
WIDGET_RESULT="Passed API smoke" \
|
|
CSAT_RESULT="Passed public show API smoke" \
|
|
ENTERPRISE_RESULT="Pending B12.3 browser/API smoke" \
|
|
write_report "Browser smoke passed for reused Chatwoot login, dashboard boot, and widget boot; API smoke passed for core frontend paths." "Command run: \`scripts/parity_frontend_smoke.sh --browser-smoke\`. Browser report: \`$LOG_DIR/browser-smoke-report.json\`. B12.3 must add enterprise screen assertions."
|
|
echo "browser smoke passed; report written to $REPORT_PATH"
|
|
}
|
|
|
|
run_enterprise_api_smoke() {
|
|
run_api_smoke
|
|
|
|
local seed_file account_id inbox_id conversation_id conversation_display_id conversation_uuid custom_role_id capacity_policy_id captain_assistant_id sla_policy_id
|
|
seed_file="$LOG_DIR/seed.json"
|
|
account_id="$(json_value "$seed_file" 'data.account_id')"
|
|
inbox_id="$(json_value "$seed_file" 'data.inbox_id')"
|
|
conversation_id="$(json_value "$seed_file" 'data.conversation_id')"
|
|
conversation_display_id="$(json_value "$seed_file" 'data.conversation_display_id || 1')"
|
|
conversation_uuid="$(json_value "$seed_file" 'data.conversation_uuid || ""')"
|
|
custom_role_id="$(json_value "$seed_file" 'data.custom_role_id')"
|
|
capacity_policy_id="$(json_value "$seed_file" 'data.capacity_policy_id')"
|
|
captain_assistant_id="$(json_value "$seed_file" 'data.captain_assistant_id')"
|
|
sla_policy_id="$(json_value "$seed_file" 'data.sla_policy_id')"
|
|
|
|
local body request_body created_id csv_file
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/applied_slas?page=1&sla_policy_id=$sla_policy_id"
|
|
cp "$body" "$LOG_DIR/enterprise_applied_slas.json"
|
|
json_assert "$body" 'Array.isArray(data.payload) && data.meta && Number(data.meta.current_page) === 1' "SLA report list returns Chatwoot payload/meta"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/applied_slas/metrics?sla_policy_id=$sla_policy_id"
|
|
cp "$body" "$LOG_DIR/enterprise_applied_sla_metrics.json"
|
|
json_assert "$body" 'typeof data === "object" && data !== null' "SLA report metrics returns JSON"
|
|
|
|
csv_file="$(tmp_file)"
|
|
authed_curl -o "$csv_file" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/applied_slas/download?sla_policy_id=$sla_policy_id"
|
|
cp "$csv_file" "$LOG_DIR/enterprise_applied_sla_download.csv"
|
|
if ! grep -q "Conversation ID,SLA policy breached" "$csv_file"; then
|
|
echo "assertion failed: SLA download returns Chatwoot CSV headers" >&2
|
|
return 1
|
|
fi
|
|
echo "ok: SLA download returns Chatwoot CSV headers"
|
|
|
|
if [[ -n "$conversation_uuid" ]]; then
|
|
request_body="$(tmp_file)"
|
|
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ message: { submitted_values: [{ csat_survey_response: { rating: 5, feedback_message: "B12 enterprise smoke" } }] } }));' "$request_body"
|
|
body="$(tmp_file)"
|
|
curl -fsS -H "Content-Type: application/json" -X PATCH --data-binary "@$request_body" -o "$body" "http://$API_HOST:$API_PORT/public/api/v1/csat_survey/$conversation_uuid"
|
|
cp "$body" "$LOG_DIR/enterprise_public_csat_submit.json"
|
|
json_assert "$body" 'data.rating === 5 || (data.csat_survey_response && data.csat_survey_response.rating === 5)' "public CSAT submit creates account report data"
|
|
fi
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/csat_survey_responses?rating=5"
|
|
cp "$body" "$LOG_DIR/enterprise_csat_responses.json"
|
|
json_assert "$body" 'Array.isArray(data)' "CSAT report list returns raw array"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/csat_survey_responses/metrics"
|
|
cp "$body" "$LOG_DIR/enterprise_csat_metrics.json"
|
|
json_assert "$body" 'Object.prototype.hasOwnProperty.call(data, "total_count") && Object.prototype.hasOwnProperty.call(data, "ratings_count")' "CSAT metrics returns report counters"
|
|
|
|
csv_file="$(tmp_file)"
|
|
authed_curl -o "$csv_file" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/csat_survey_responses/download"
|
|
cp "$csv_file" "$LOG_DIR/enterprise_csat_download.csv"
|
|
if ! grep -q "Agent Name,Rating,Feedback Comment" "$csv_file"; then
|
|
echo "assertion failed: CSAT download returns CSV headers" >&2
|
|
return 1
|
|
fi
|
|
echo "ok: CSAT download returns CSV headers"
|
|
|
|
request_body="$(tmp_file)"
|
|
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ name: "B12 Enterprise Smoke Automation", description: "B12.3 smoke", event_name: "conversation_created", active: true, conditions: [], actions: [] }));' "$request_body"
|
|
body="$(tmp_file)"
|
|
authed_curl -X POST --data-binary "@$request_body" -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/automation_rules"
|
|
cp "$body" "$LOG_DIR/enterprise_automation_create.json"
|
|
json_assert "$body" 'data.name === "B12 Enterprise Smoke Automation" && data.event_name === "conversation_created"' "automation rule create returns raw Chatwoot rule"
|
|
created_id="$(json_value "$body" 'data.id')"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/automation_rules"
|
|
cp "$body" "$LOG_DIR/enterprise_automation_list.json"
|
|
json_assert "$body" 'data.payload && data.payload.some(rule => Number(rule.id) === Number("'$created_id'"))' "automation rule list returns created rule in payload"
|
|
|
|
request_body="$(tmp_file)"
|
|
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ name: "B12 Enterprise Smoke Macro", visibility: "global", actions: [] }));' "$request_body"
|
|
body="$(tmp_file)"
|
|
authed_curl -X POST --data-binary "@$request_body" -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/macros"
|
|
cp "$body" "$LOG_DIR/enterprise_macro_create.json"
|
|
json_assert "$body" 'data.payload && data.payload.name === "B12 Enterprise Smoke Macro" && data.payload.visibility === "global"' "macro create returns Chatwoot payload"
|
|
created_id="$(json_value "$body" 'data.payload.id')"
|
|
|
|
request_body="$(tmp_file)"
|
|
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ conversation_ids: [Number(process.argv[2])] }));' "$request_body" "$conversation_display_id"
|
|
body="$(tmp_file)"
|
|
authed_curl -X POST --data-binary "@$request_body" -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/macros/$created_id/execute"
|
|
cp "$body" "$LOG_DIR/enterprise_macro_execute.txt"
|
|
echo "ok: macro execute returns success"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/custom_roles"
|
|
cp "$body" "$LOG_DIR/enterprise_custom_roles.json"
|
|
json_assert "$body" 'Array.isArray(data) && data.some(role => Number(role.id) === Number("'$custom_role_id'") && Array.isArray(role.permissions))' "custom role list returns seeded enterprise role"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/agent_capacity_policies"
|
|
cp "$body" "$LOG_DIR/enterprise_capacity_policies.json"
|
|
json_assert "$body" 'Array.isArray(data) && data.some(policy => Number(policy.id) === Number("'$capacity_policy_id'"))' "agent capacity list returns seeded policy"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/agent_capacity_policies/$capacity_policy_id/users"
|
|
cp "$body" "$LOG_DIR/enterprise_capacity_users.json"
|
|
json_assert "$body" 'Array.isArray(data)' "agent capacity users returns array"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/audit_logs"
|
|
cp "$body" "$LOG_DIR/enterprise_audit_logs.json"
|
|
json_assert "$body" 'data.audit_logs && Array.isArray(data.audit_logs) && data.audit_logs.length >= 1 && data.current_page === 1' "audit logs return enterprise Jbuilder list"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/captain/preferences"
|
|
cp "$body" "$LOG_DIR/enterprise_captain_preferences.json"
|
|
json_assert "$body" 'data.providers && data.models && data.features' "Captain preferences returns providers/models/features"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/captain/assistants"
|
|
cp "$body" "$LOG_DIR/enterprise_captain_assistants.json"
|
|
json_assert "$body" 'data.payload && data.payload.some(assistant => Number(assistant.id) === Number("'$captain_assistant_id'"))' "Captain assistants list returns seeded assistant"
|
|
|
|
request_body="$(tmp_file)"
|
|
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ message: "B12 enterprise copilot smoke", assistant_id: Number(process.argv[2]), conversation_id: Number(process.argv[3]) }));' "$request_body" "$captain_assistant_id" "$conversation_id"
|
|
body="$(tmp_file)"
|
|
authed_curl -X POST --data-binary "@$request_body" -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/captain/copilot_threads"
|
|
cp "$body" "$LOG_DIR/enterprise_copilot_thread_create.json"
|
|
json_assert "$body" 'data.id && data.assistant && Number(data.assistant.id) === Number("'$captain_assistant_id'")' "Copilot thread create returns Chatwoot thread payload"
|
|
created_id="$(json_value "$body" 'data.id')"
|
|
|
|
body="$(tmp_file)"
|
|
authed_curl -o "$body" "http://$API_HOST:$API_PORT/api/v1/accounts/$account_id/captain/copilot_threads/$created_id/copilot_messages"
|
|
cp "$body" "$LOG_DIR/enterprise_copilot_messages.json"
|
|
json_assert "$body" 'data.payload && data.payload.length >= 1 && data.payload[0].copilot_thread' "Copilot thread messages return nested payload"
|
|
|
|
BOOT_BACKEND_RESULT="Passed /health during enterprise smoke" \
|
|
AUTH_PROFILE_RESULT="Passed API smoke" \
|
|
INBOX_RESULT="Passed API smoke" \
|
|
CONVERSATION_RESULT="Passed API smoke" \
|
|
CRM_RESULT="Passed contact/company API smoke" \
|
|
SEARCH_RESULT="Passed search API smoke" \
|
|
WIDGET_RESULT="Passed widget browser boot plus API smoke" \
|
|
CSAT_RESULT="Passed public/account/download enterprise smoke" \
|
|
ENTERPRISE_RESULT="Passed enterprise API smoke for SLA, CSAT, automation, macros, audit, custom roles, capacity, Captain, and Copilot" \
|
|
write_report "Enterprise API smoke passed for reused Chatwoot enterprise paths; live enterprise browser navigation remains optional." "Command run: \`scripts/parity_frontend_smoke.sh --enterprise-smoke\`. Logs and payload captures are under \`$LOG_DIR\`. Next B12.3 browser work should load the enterprise screens through the reused Vite app."
|
|
echo "enterprise smoke passed; report written to $REPORT_PATH"
|
|
}
|
|
|
|
run_enterprise_browser_smoke() {
|
|
run_enterprise_api_smoke
|
|
wait_url "http://$FRONTEND_HOST:$FRONTEND_PORT/vite-dev/entrypoints/dashboard.js" "Chatwoot Vite"
|
|
GOCHAT_ROOT="$ROOT" \
|
|
CHATWOOT_DIR="$CHATWOOT_DIR" \
|
|
GOCHAT_SMOKE_LOG_DIR="$LOG_DIR" \
|
|
GOCHAT_SMOKE_API_HOST="$API_HOST" \
|
|
GOCHAT_SMOKE_API_PORT="$API_PORT" \
|
|
GOCHAT_SMOKE_FRONTEND_HOST="$FRONTEND_HOST" \
|
|
GOCHAT_SMOKE_FRONTEND_PORT="$FRONTEND_PORT" \
|
|
node "$ROOT/scripts/parity_frontend_browser_smoke.mjs" --enterprise
|
|
BOOT_BACKEND_RESULT="Passed /health during enterprise browser smoke" \
|
|
BOOT_FRONTEND_RESULT="Passed Vite readiness during enterprise browser smoke" \
|
|
AUTH_PROFILE_RESULT="Passed browser login plus API smoke" \
|
|
INBOX_RESULT="Passed API smoke" \
|
|
CONVERSATION_RESULT="Passed dashboard browser request plus API smoke" \
|
|
CRM_RESULT="Passed contact/company API smoke" \
|
|
SEARCH_RESULT="Passed search API smoke" \
|
|
WIDGET_RESULT="Passed API smoke" \
|
|
CSAT_RESULT="Passed CSAT API/download and browser route requests" \
|
|
ENTERPRISE_RESULT="Passed enterprise browser route requests for SLA, CSAT, automation, macros, audit, custom roles, capacity, Captain, and Copilot" \
|
|
write_report "Enterprise browser smoke passed for reused Chatwoot enterprise route requests and widget boot; enterprise API smoke passed." "Command run: \`scripts/parity_frontend_smoke.sh --enterprise-browser-smoke\`. Browser report: \`$LOG_DIR/browser-smoke-report.json\`."
|
|
echo "enterprise browser smoke passed; report written to $REPORT_PATH"
|
|
}
|
|
|
|
if [[ "$MODE" == "print" ]]; then
|
|
print_commands
|
|
exit 0
|
|
fi
|
|
|
|
check_prereqs
|
|
|
|
if [[ "$MODE" == "check" ]]; then
|
|
env GOCACHE="${GOCACHE:-/tmp/gochat-gocache}" GOMODCACHE="${GOMODCACHE:-/tmp/gochat-gomodcache}" go test ./cmd/gochat -count=1
|
|
write_report "Harness readiness check passed; live frontend smoke not run in this mode." "Run \`scripts/parity_frontend_smoke.sh --boot-only\` after PostgreSQL, Redis, Meilisearch, and Chatwoot frontend dependencies are available."
|
|
echo "check passed; report written to $REPORT_PATH"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$MODE" == "api-smoke" ]]; then
|
|
run_api_smoke
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$MODE" == "browser-smoke" ]]; then
|
|
run_browser_smoke
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$MODE" == "enterprise-smoke" ]]; then
|
|
run_enterprise_api_smoke
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$MODE" == "enterprise-browser-smoke" ]]; then
|
|
run_enterprise_browser_smoke
|
|
exit 0
|
|
fi
|
|
|
|
mkdir -p "$LOG_DIR"
|
|
trap 'jobs -pr | xargs -r kill 2>/dev/null || true' EXIT
|
|
|
|
echo "starting GoChat backend..."
|
|
(cd "$ROOT" && "${backend_cmd[@]}") >"$LOG_DIR/gochat.log" 2>&1 &
|
|
wait_url "http://$API_HOST:$API_PORT/health" "GoChat"
|
|
|
|
echo "starting reused Chatwoot frontend..."
|
|
(cd "$CHATWOOT_DIR" && "${frontend_cmd[@]}") >"$LOG_DIR/chatwoot-vite.log" 2>&1 &
|
|
wait_url "http://$FRONTEND_HOST:$FRONTEND_PORT/vite-dev/entrypoints/dashboard.js" "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"
|
|
|
|
if [[ "$KEEP_ALIVE" == "true" ]]; then
|
|
echo "press Ctrl-C to stop both processes"
|
|
wait
|
|
fi
|