#!/usr/bin/env bash # Live M7 smoke test. Run from repo root: # bash scripts/m7_smoke.sh # # Walks through the 4 scenarios in M7_VERIFICATION.md: # # Step 2 — hypertable: query timescaledb_information.hypertables, # expect `deliveries` listed. # Step 3 — retention policy: query # timescaledb_information.jobs, expect a 7-day # retention policy on `deliveries`. # Step 4 — archiver dry-run: insert a synthetic 8d-old # row, run archiverd once, expect the row to # move from Postgres to ClickHouse and the # Postgres count to drop by 1. # Step 5 — ClickHouse archive: SELECT count(*), min, max # from ba_archive.deliveries_archive; expect # ≥1 row with created_at in the 7-30d range. # # Exit code is the number of failed checks. set -e cd "$(dirname "$0")/.." PGCMD="docker exec -i broad-announce-postgres-1 psql -U ba -d ba -A -t" CHURL=http://localhost:8123 CHCMD="curl -sS -X POST $CHURL/" fails=0 pass() { echo " ✅ $*"; } fail() { echo " ❌ $*"; fails=$((fails+1)); } ch_query() { $CHCMD --data-binary "$1" 2>/dev/null } # ── Setup: build archiverd fresh (must be v4 or later) ── ARCHIVERD_BIN=/tmp/archiverd-m7 if [[ ! -x $ARCHIVERD_BIN ]]; then echo "▸ Building $ARCHIVERD_BIN" CGO_ENABLED=0 go build -o $ARCHIVERD_BIN ./cmd/archiverd fi # Always copy the fresh build into the container. Earlier # dev runs may have left a stale binary at /tmp/archiverd. docker exec broad-announce-ingestd-1 rm -f /tmp/archiverd 2>/dev/null || true # ───────────────────────────────────────────────────────────────── echo "── M7 smoke — Timescale 7d hot + ClickHouse archive ──" echo "" # ── Step 2: hypertable exists ────────────────────────────── echo "── Step 2: deliveries is a Timescale hypertable ──" hypertables=$($PGCMD -c "SELECT hypertable_name FROM timescaledb_information.hypertables;" 2>/dev/null | head -5) if echo "$hypertables" | grep -q "deliveries"; then pass "deliveries is a hypertable" else fail "deliveries NOT in hypertables list: $hypertables" fi # ── Step 3: retention policy is 7d ───────────────────────── echo "" echo "── Step 3: 7-day retention policy is active ──" jobs=$($PGCMD -c "SELECT application_name, config FROM timescaledb_information.jobs WHERE config::text LIKE '%7 days%';" 2>/dev/null | head -5) if echo "$jobs" | grep -q "7 days"; then pass "7-day retention policy present ($jobs)" else fail "7-day retention policy not found in jobs: $jobs" fi # ── Step 4: archiver moves a synthetic 8d-old row ───────── echo "" echo "── Step 4: 8d-old synthetic row moves to ClickHouse ──" # Insert a unique row, run archiverd, verify it moved. TAG="m7-step4-$(date +%s%N)" $PGCMD -c "INSERT INTO deliveries (alert_id, company_id, individual_id, channel, target, status, attempts, last_error, payload, created_at, sent_at) VALUES ('$TAG', 'acme-001', 'ind-acme-001', 'fcm', 'fake-token', 'sent', 0, '', '{\"smoke_tag\":\"$TAG\"}'::jsonb, now() - INTERVAL '8 days', now() - INTERVAL '8 days') ON CONFLICT DO NOTHING;" >/dev/null 2>&1 pg_before=$($PGCMD -c "SELECT count(*) FROM deliveries WHERE alert_id = '$TAG';" 2>/dev/null | head -1) ch_before=$(ch_query "SELECT count(*) FROM ba_archive.deliveries_archive WHERE alert_id = '$TAG' FORMAT TabSeparated" 2>/dev/null | head -1) if [[ "$pg_before" == "1" ]]; then pass "synthetic row in postgres (count=1)" else fail "synthetic row not in postgres: count=$pg_before" fi # Run archiverd once. We use --port 8807 to avoid clashing # with anything left running in dev. docker exec broad-announce-ingestd-1 pkill -9 -f archiverd 2>/dev/null || true sleep 1 docker cp $ARCHIVERD_BIN broad-announce-ingestd-1:/tmp/archiverd 2>/dev/null timeout 10 docker exec \ -e BA_ENV=dev -e BA_HTTP_ADDR=:8807 \ -e BA_POSTGRES_DSN="postgres://ba:ba@postgres:5432/ba?sslmode=disable" \ -e BA_ARCHIVERD_CLICKHOUSE_URL="http://clickhouse:8123" \ -e BA_ARCHIVERD_RUN_EVERY_SECONDS=999999 \ -e BA_ARCHIVERD_OLDER_THAN_HOURS=168 \ -e BA_ARCHIVERD_BATCH_SIZE=10000 \ -e BA_LOG_LEVEL=info \ broad-announce-ingestd-1 /tmp/archiverd > /tmp/m7-archiverd.log 2>&1 || true if grep -q "archiver run ok" /tmp/m7-archiverd.log; then pass "archiverd run completed ok" else fail "archiverd did not complete; log tail:" tail -10 /tmp/m7-archiverd.log | sed 's/^/ /' fi pg_after=$($PGCMD -c "SELECT count(*) FROM deliveries WHERE alert_id = '$TAG';" 2>/dev/null | head -1) ch_after=$(ch_query "SELECT count(*) FROM ba_archive.deliveries_archive WHERE alert_id = '$TAG' FORMAT TabSeparated" 2>/dev/null | head -1) if [[ "$pg_after" == "0" ]]; then pass "row moved out of postgres (count=0 after)" else fail "row still in postgres: count=$pg_after" fi if [[ "$ch_after" == "1" ]]; then pass "row landed in ClickHouse (count=1)" else fail "row missing from ClickHouse: count=$ch_after (was $ch_before)" fi # ── Step 5: ClickHouse aggregate query ───────────────────── echo "" echo "── Step 5: ClickHouse archive queryable ──" archive_count=$(ch_query "SELECT count(*) FROM ba_archive.deliveries_archive FORMAT TabSeparated" 2>/dev/null | head -1) if [[ ${archive_count:-0} -ge 1 ]]; then pass "ba_archive.deliveries_archive count = $archive_count (≥1 row archived)" else fail "ClickHouse archive empty (count=$archive_count)" fi min_age=$(ch_query "SELECT dateDiff('day', min(created_at), now()) FROM ba_archive.deliveries_archive FORMAT TabSeparated" 2>/dev/null | head -1) if [[ ${min_age:-999} -ge 7 ]]; then pass "oldest archived row is $min_age days old (≥7)" else fail "oldest archived row is only $min_age days old (expected ≥7)" fi # ── Summary ───────────────────────────────────────────────── echo "" if [[ $fails -eq 0 ]]; then echo "🟢 M7 smoke PASS — all checks green" exit 0 else echo "🔴 M7 smoke FAIL — $fails check(s) failed" exit $fails fi