#!/bin/bash # ============================================================================ # End-to-end test: the real bot against the fake Signal server. # # Boots the protocol-faithful fake, boots the unmodified bot pointed at it, # replays every scenario and exits non-zero on the first mismatch. No Signal # account, no network, no container. # ============================================================================ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR REPO_ROOT="$(dirname "$SCRIPT_DIR")" readonly REPO_ROOT readonly PYTHON="${PYTHON:-$REPO_ROOT/.venv/bin/python}" readonly FAKE_PORT="${FAKE_PORT:-8099}" readonly BOT_PORT="${BOT_PORT:-8098}" readonly FAKE_URL="http://127.0.0.1:${FAKE_PORT}" readonly WATCHED_GROUP="group.e2e.watched==" readonly REPLY_GROUP="group.e2e.reply==" readonly OTHER_GROUP="group.e2e.other==" FAKE_PID="" BOT_PID="" TEMP_DIR="" cleanup() { [[ -n "$BOT_PID" ]] && kill "$BOT_PID" 2>/dev/null || true [[ -n "$FAKE_PID" ]] && kill "$FAKE_PID" 2>/dev/null || true wait 2>/dev/null || true [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR" return 0 } trap cleanup EXIT wait_for_url() { local url="$1" name="$2" attempt=0 until curl -sf "$url" >/dev/null 2>&1; do attempt=$((attempt + 1)) if [[ $attempt -ge 100 ]]; then echo "Error: $name did not come up at $url" >&2 exit 1 fi sleep 0.1 done } wait_for_subscription() { local attempt=0 until [[ "$(curl -sf "${FAKE_URL}/_control/ready" | tr -dc '0-9')" != "0" ]]; do attempt=$((attempt + 1)) if [[ $attempt -ge 100 ]]; then echo "Error: the bot never subscribed to the receive websocket" >&2 exit 1 fi sleep 0.1 done } validate_requirements() { if [[ ! -x "$PYTHON" ]]; then echo "Error: no interpreter at $PYTHON. Run: uv venv .venv && uv pip install -e '.[dev]'" >&2 exit 1 fi command -v curl >/dev/null || { echo "Error: curl is required" >&2; exit 1; } } main() { validate_requirements TEMP_DIR="$(mktemp -d)" echo "== booting the fake Signal server on :${FAKE_PORT}" export FAKE_PORT "$PYTHON" -m uvicorn fake_signal.server:app \ --host 127.0.0.1 --port "$FAKE_PORT" --log-level warning & FAKE_PID=$! wait_for_url "${FAKE_URL}/v1/about" "fake signal server" echo "== booting the bot on :${BOT_PORT}, pointed at the fake" env -u CAT_API \ PHONE_NUMBER="+15550000000" \ GROUP_ID="$WATCHED_GROUP" \ GROUP_ID_SEND="$REPLY_GROUP" \ API_BASE_URL="$FAKE_URL" \ DATABASE_PATH="${TEMP_DIR}/e2e.sqlite3" \ CAT_API_URL="${FAKE_URL}/_fixtures/cat-api" \ DOG_API_URL="${FAKE_URL}/_fixtures/dog-api" \ BIND_PORT="$BOT_PORT" \ "$PYTHON" -m signal_bot & BOT_PID=$! wait_for_url "http://127.0.0.1:${BOT_PORT}/healthz" "bot" wait_for_subscription echo "== replaying scenarios" "$PYTHON" -m fake_signal.cli \ "$REPO_ROOT"/fake_signal/scenarios/*.yaml \ --base-url "$FAKE_URL" \ --group-id "$WATCHED_GROUP" \ --group-id-send "$REPLY_GROUP" \ --other-group "$OTHER_GROUP" } main "$@"