Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
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
|