← back to Stayclaim
scripts/smoke.sh
195 lines
#!/usr/bin/env bash
# smoke.sh — post-deploy regression check for stayclaim / pastdoor
# Runs locally; hits production domains. No browser required.
# Usage: ./scripts/smoke.sh [BASE_OVERRIDE]
# BASE_OVERRIDE lets you hit a staging host instead, e.g. http://localhost:3001
# Exit 0 = all pass. Exit 1 = one or more failures.
set -euo pipefail
PASS=0
FAIL=0
START=$(date +%s)
# ── config ──────────────────────────────────────────────────────────────────
WLT="${1:-https://wholivedthere.com}"
CMA="${1:-https://claimmyaddress.com}"
BBL="${1:-https://bubbesblock.com}"
# When a BASE_OVERRIDE is given all 3 surfaces collapse to the same host.
# The host-specific brand checks still work because they probe the HTML body.
CURL="curl -sS --max-time 15 --compressed"
KAMATERA_SSH="root@45.61.58.125"
DB_NAME="pastdoor"
# ── helpers ─────────────────────────────────────────────────────────────────
ok() { echo " [PASS] $*"; PASS=$((PASS+1)); }
fail() { echo " [FAIL] $*"; FAIL=$((FAIL+1)); }
check_status() {
local label="$1" url="$2" expected="${3:-200}"
local code
code=$($CURL -o /dev/null -w "%{http_code}" "$url" 2>/dev/null) || code=000
if [[ "$code" == "$expected" ]]; then
ok "$label HTTP $code"
else
fail "$label expected $expected got $code ($url)"
fi
}
# Returns body via stdout; also asserts HTTP 200.
fetch_body() {
local label="$1" url="$2"
local tmp; tmp=$(mktemp)
local code
code=$($CURL -o "$tmp" -w "%{http_code}" "$url" 2>/dev/null) || code=000
if [[ "$code" != "200" ]]; then
fail "$label HTTP $code ($url)"
rm -f "$tmp"
echo ""
return
fi
cat "$tmp"
rm -f "$tmp"
}
assert_contains() {
local label="$1" body="$2" needle="$3"
if grep -qiF "$needle" <<<"$body"; then
ok "$label contains '$needle'"
else
fail "$label missing '$needle'"
fi
}
assert_nonempty() {
local label="$1" body="$2"
if [[ -n "$body" ]]; then
ok "$label body non-empty"
else
fail "$label empty body"
fi
}
assert_size_range() {
local label="$1" body="$2" min="$3" max="$4"
local sz=${#body}
if (( sz >= min && sz <= max )); then
ok "$label size ${sz}B in [${min},${max}]"
else
fail "$label size ${sz}B outside [${min},${max}]"
fi
}
assert_json_array() {
local label="$1" body="$2" key="${3:-}"
local target="$body"
[[ -n "$key" ]] && target=$(echo "$body" | jq -r ".$key" 2>/dev/null)
if echo "$target" | jq -e 'if type=="array" then true else false end' &>/dev/null; then
ok "$label valid JSON array"
else
fail "$label not a JSON array (body: ${body:0:120})"
fi
}
assert_valid_json() {
local label="$1" body="$2"
if echo "$body" | jq . &>/dev/null; then
ok "$label valid JSON"
else
fail "$label invalid JSON (body: ${body:0:120})"
fi
}
# ── 1. Root URLs — brand strings ─────────────────────────────────────────────
echo ""
echo "=== 1. Root URLs ==="
for pair in "$WLT|wholivedthere" "$CMA|claimmyaddress" "$BBL|bubbe"; do
host="${pair%%|*}"
brand="${pair##*|}"
body=$(fetch_body "GET $host/" "$host/")
assert_nonempty "root/$brand" "$body"
assert_contains "root/$brand brand" "$body" "$brand"
done
# ── 2. Sitemap per domain ────────────────────────────────────────────────────
echo ""
echo "=== 2. Sitemaps ==="
for host in "$WLT" "$CMA" "$BBL"; do
label="sitemap $host"
body=$(fetch_body "$label" "$host/sitemap.xml")
assert_contains "$label xml-decl-or-urlset" "$body" '<url'
assert_size_range "$label size" "$body" 1024 10485760 # 1KB – 10MB
done
# ── 3. Timeline RSS ──────────────────────────────────────────────────────────
echo ""
echo "=== 3. Timeline RSS ==="
body=$(fetch_body "GET /timeline/rss.xml" "$WLT/timeline/rss.xml")
assert_contains "rss opening tag" "$body" '<rss'
# ── 4. /api/events?kind=permit (regression: was rejected with 400) ──────────
echo ""
echo "=== 4. /api/events?kind=permit ==="
body=$(fetch_body "GET /api/events?kind=permit" "$WLT/api/events?kind=permit")
assert_valid_json "events/permit response" "$body"
assert_json_array "events/permit .events" "$body" "events"
# Confirm kind filter echoed back (proves VALID_KINDS includes 'permit')
if echo "$body" | jq -e '.filters.kind == "permit"' &>/dev/null; then
ok "events/permit filters.kind=permit echoed"
else
fail "events/permit filters.kind not echoed as 'permit'"
fi
# ── 5. /api/search?q=test ───────────────────────────────────────────────────
echo ""
echo "=== 5. /api/search ==="
body=$(fetch_body "GET /api/search?q=test" "$WLT/api/search?q=test")
assert_valid_json "search response" "$body"
# ── 6. Known-good slug from DB ───────────────────────────────────────────────
echo ""
echo "=== 6. Address slug page ==="
SLUG=$(ssh -o BatchMode=yes -o ConnectTimeout=10 "$KAMATERA_SSH" \
"sudo -u postgres psql -Atq -d $DB_NAME -c \"SELECT slug FROM listing WHERE is_public=true LIMIT 1\"" 2>/dev/null \
|| echo "")
if [[ -z "$SLUG" ]]; then
fail "slug lookup could not retrieve slug from DB (ssh or psql failed)"
else
ok "slug lookup got '$SLUG'"
body=$(fetch_body "GET /address/$SLUG" "$WLT/address/$SLUG")
assert_nonempty "address/$SLUG body" "$body"
assert_contains "address/$SLUG is HTML" "$body" '<html'
fi
# ── 7. robots.txt ────────────────────────────────────────────────────────────
echo ""
echo "=== 7. robots.txt ==="
body=$(fetch_body "GET /robots.txt" "$WLT/robots.txt")
assert_contains "robots sitemap directive" "$body" "Sitemap:"
# ── Summary ──────────────────────────────────────────────────────────────────
END=$(date +%s)
ELAPSED=$(( END - START ))
TOTAL=$(( PASS + FAIL ))
echo ""
echo "============================================"
echo " Smoke result: ${PASS}/${TOTAL} passed (${ELAPSED}s)"
echo "============================================"
if (( FAIL > 0 )); then
echo " FAILED — ${FAIL} check(s) did not pass"
exit 1
fi
echo " ALL CHECKS PASSED"
exit 0