#!/usr/bin/env bash # End-to-end test of the /ws/edge/v1 contract against a real database and real # printers: seed a farm, claim its printers, mint an edge token, run the actual # continuum-proxy daemon, and assert that live telemetry reached Postgres and # that a command sent over HTTP reached a printer. # # This is the test that the unit tests cannot be: tests/edge_protocol.test.ts # only proves the two ends agree about the wire format. Nothing but this proves # the printer-id translation, persistTelemetry and sendPrinterCommand actually # work against a database. # # SAFETY: the only command it sends is a fan speed, and only to the printer # named by PRINTER_ID (default p1p), which it refuses to send unless that # printer reports IDLE. It never starts, pauses or cancels a print. Point it at # a printer you can see. # # Usage: # scripts/dev-env.sh up && scripts/dev-env.sh schema # scripts/e2e-edge.sh # # PRINTER_ID=p1p which printer to send the fan command to # PROXY_DIR=../continuum-proxy # KEEP_DATA=1 leave the seeded farm behind for inspection set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" PRINTER_ID="${PRINTER_ID:-p1p}" PROXY_DIR="${PROXY_DIR:-$REPO_ROOT/../continuum-proxy}" PORT="${PORT:-3010}" BASE="http://localhost:$PORT" DATABASE_URL="${DATABASE_URL:-postgres://continuum:continuum@localhost:5432/continuum}" VALKEY_URL="${VALKEY_URL:-redis://localhost:6379}" EDGE_JWT_SECRET="${EDGE_JWT_SECRET:-e2e-edge-secret}" export DATABASE_URL VALKEY_URL EDGE_JWT_SECRET PORT WORK="$(mktemp -d)" BACKEND_PID="" PROXY_PID="" psql_q() { PGPASSWORD=continuum psql -h localhost -U continuum -d continuum -tAq -c "$1"; } cleanup() { local rc=$? for pid in "$PROXY_PID" "$BACKEND_PID"; do [ -z "$pid" ] && continue kill "$pid" 2>/dev/null || true done # A gateway that outlives this script keeps reconnecting with a token for a # farm that is about to be deleted, and pollutes the next run's log. for _ in $(seq 1 10); do kill -0 "${PROXY_PID:-0}" 2>/dev/null || break sleep 0.3 done kill -9 "${PROXY_PID:-0}" 2>/dev/null || true kill -9 "${BACKEND_PID:-0}" 2>/dev/null || true if [ -z "${KEEP_DATA:-}" ] && [ -n "${FARM_ID:-}" ]; then # Printers cascade from farms. psql_q "DELETE FROM farms WHERE id = '$FARM_ID'" >/dev/null 2>&1 || true fi if [ $rc -ne 0 ]; then echo echo "--- backend log (tail) ---"; tail -30 "$WORK/backend.log" 2>/dev/null || true echo "--- proxy log (tail) ---"; tail -30 "$WORK/proxy.log" 2>/dev/null || true fi rm -rf "$WORK" exit $rc } trap cleanup EXIT fail() { echo "FAIL: $*" >&2; exit 1; } ok() { echo " ok: $*"; } # --- 0. preconditions ------------------------------------------------------ echo "==> checking the database is up and migrated" psql_q "SELECT 1" >/dev/null 2>&1 || fail "no database at $DATABASE_URL - run scripts/dev-env.sh up" psql_q "SELECT gateway_printer_id FROM printers LIMIT 0" >/dev/null 2>&1 \ || fail "printers.gateway_printer_id is missing - run scripts/dev-env.sh schema" ok "schema has the edge columns" [ -f "$PROXY_DIR/printers.toml" ] || fail "no $PROXY_DIR/printers.toml - the proxy needs real printers" # --- 1. seed --------------------------------------------------------------- echo "==> seeding a farm and claiming its printers" EDGE_PROXY_TOKEN="e2e-$(date +%s)" FARM_ID="$(psql_q "INSERT INTO farms (name, owner_id, edge_proxy_token) VALUES ('e2e farm', gen_random_uuid(), '$EDGE_PROXY_TOKEN') RETURNING id")" [ -n "$FARM_ID" ] || fail "could not create a farm" ok "farm $FARM_ID" # gateway_printer_id is the whole point: it maps the gateway's own ids from # printers.toml onto these rows. Telemetry for an id nothing claims is skipped. while read -r pid pname; do [ -z "$pid" ] && continue psql_q "INSERT INTO printers (farm_id, name, vendor, gateway_printer_id) VALUES ('$FARM_ID', '$pname', 'bambu', '$pid')" >/dev/null ok "claimed '$pid' as '$pname'" done < <(awk -F'"' '/^id *=/ {id=$2} /^name *=/ {print id, $2}' "$PROXY_DIR/printers.toml") # --- 2. backend ------------------------------------------------------------ echo "==> starting continuum-backend on :$PORT" bun run src/index.ts > "$WORK/backend.log" 2>&1 & BACKEND_PID=$! for _ in $(seq 1 60); do curl -sf "$BASE/health" >/dev/null 2>&1 && break sleep 0.5 done HEALTH="$(curl -sf "$BASE/health")" || fail "backend did not become healthy" echo "$HEALTH" | grep -q '"db":true' || fail "backend reports the database is down: $HEALTH" echo "$HEALTH" | grep -q '"valkey":true' || fail "backend reports valkey is down: $HEALTH" ok "health: $HEALTH" # --- 3. edge token --------------------------------------------------------- echo "==> minting an edge token" TOKEN="$(curl -sf -X POST "$BASE/auth/edge-token" \ -H 'content-type: application/json' \ -d "{\"farmId\":\"$FARM_ID\",\"edgeProxyToken\":\"$EDGE_PROXY_TOKEN\"}" \ | python3 -c 'import json,sys; print(json.load(sys.stdin).get("token",""))')" [ -n "$TOKEN" ] || fail "no token returned from POST /auth/edge-token" ok "token minted (${#TOKEN} chars)" # --- 4. the real gateway --------------------------------------------------- echo "==> starting the real continuum-proxy against it" ( cd "$PROXY_DIR" && cargo build --quiet 2>&1 | tail -3 ) || fail "proxy did not build" # env -C rather than a `( cd ... ) &` subshell: $! has to be the daemon itself, # or cleanup kills only the wrapper and leaves an orphan reconnecting with a # stale token. An earlier version did exactly that, and the orphan then # reported a deleted farm's printers into the next run. env -C "$PROXY_DIR" \ CONTINUUM_GATEWAY_ID=e2e-gw \ CONTINUUM_FARM_ID="$FARM_ID" \ CONTINUUM_GATEWAY_TOKEN="$TOKEN" \ CONTINUUM_UPLINK_URL="ws://localhost:$PORT/ws/edge/v1" \ CONTINUUM_GO2RTC_BIN=/nonexistent-go2rtc \ CONTINUUM_GO2RTC_CONFIG=./go2rtc.yaml \ CONTINUUM_PRINTERS_PATH=printers.toml \ RUST_LOG=info \ "$PROXY_DIR/target/debug/continuum-proxy" > "$WORK/proxy.log" 2>&1 & PROXY_PID=$! # Wait for *every* claimed printer, not just the first. Telemetry is on a 5s # timer and a Bambu on protocol v1 reports one tick behind the rest (it only # sends a full status when asked, so each tick reads the previous tick's # answer) - stopping at the first row that appears is how an earlier version of # this script silently tested only the v2 printer. CLAIMED="$(psql_q "SELECT count(*) FROM printers WHERE farm_id='$FARM_ID' AND gateway_printer_id IS NOT NULL")" echo "==> waiting for live telemetry from all $CLAIMED claimed printer(s)" for _ in $(seq 1 45); do COUNT="$(psql_q "SELECT count(*) FROM printers WHERE farm_id='$FARM_ID' AND gateway_printer_id IS NOT NULL AND last_seen_at IS NOT NULL AND vendor_state IS NOT NULL")" [ "${COUNT:-0}" -ge "$CLAIMED" ] && break sleep 1 done [ "${COUNT:-0}" -ge 1 ] || fail "no telemetry was persisted at all (see the proxy log below)" if [ "${COUNT:-0}" -lt "$CLAIMED" ]; then # Not fatal: a printer that is genuinely switched off cannot report, and that # should not fail a run against the ones that can. Name them so the # difference between "off" and "broken" is visible. echo " WARNING: only $COUNT of $CLAIMED printers reported. Silent:" psql_q "SELECT ' - ' || gateway_printer_id FROM printers WHERE farm_id='$FARM_ID' AND gateway_printer_id IS NOT NULL AND (last_seen_at IS NULL OR vendor_state IS NULL)" else ok "all $COUNT claimed printer(s) reported live telemetry" fi if grep "reported unknown printer" "$WORK/backend.log" | grep -q "$FARM_ID"; then fail "the backend could not map a gateway printer id for this farm - check gateway_printer_id" fi ok "every printer id this gateway reported mapped to a row" echo "==> what landed in the database:" PGPASSWORD=continuum psql -h localhost -U continuum -d continuum -c \ "SELECT gateway_printer_id AS gw, status, vendor_state, progress_percent AS pct, current_layer || '/' || total_layers AS layers, remaining_minutes AS mins, nozzle_temp_c AS nozzle, bed_temp_c AS bed, active_alerts AS alerts, coalesce(current_job_name, '-') AS job FROM printers WHERE farm_id = '$FARM_ID' ORDER BY gateway_printer_id" # Proves the telemetry is real rather than a row touched with nulls. psql_q "SELECT 1 FROM printers WHERE farm_id='$FARM_ID' AND vendor_state IS NOT NULL AND nozzle_temp_c IS NOT NULL LIMIT 1" | grep -q 1 \ || fail "rows were touched but carry no actual telemetry" ok "rows carry real vendor state and temperatures" # --- 5. a command, the whole way down -------------------------------------- TARGET_UUID="$(psql_q "SELECT id FROM printers WHERE farm_id='$FARM_ID' AND gateway_printer_id='$PRINTER_ID'")" [ -n "$TARGET_UUID" ] || fail "no printer claimed as '$PRINTER_ID'" GW="$(curl -sf "$BASE/printers/$TARGET_UUID/gateway")" echo "$GW" | grep -q '"gatewayOnline":true' || fail "the gateway is not registered as online: $GW" ok "gateway reports online for $PRINTER_ID" COMMAND_TESTED=no STATE="$(psql_q "SELECT vendor_state FROM printers WHERE id='$TARGET_UUID'")" if [ "$STATE" != "IDLE" ] && [ "$STATE" != "FINISH" ]; then echo " SKIPPED the command test: $PRINTER_ID reports '${STATE:-no telemetry yet}', not IDLE/FINISH." echo " Refusing to touch a printer that may be mid-print." else echo "==> sending set_fan_speed to $PRINTER_ID ($STATE) over HTTP" RESP="$(curl -s -o "$WORK/cmd.json" -w '%{http_code}' -X POST \ "$BASE/printers/$TARGET_UUID/commands" -H 'content-type: application/json' \ -d '{"action":"set_fan_speed","percent":40}')" [ "$RESP" = "200" ] || fail "command returned HTTP $RESP: $(cat "$WORK/cmd.json")" grep -q '"ok":true' "$WORK/cmd.json" || fail "command was not acknowledged: $(cat "$WORK/cmd.json")" ok "command acknowledged by the printer: $(cat "$WORK/cmd.json")" # Put it back, so this leaves the printer as it found it. curl -s -X POST "$BASE/printers/$TARGET_UUID/commands" \ -H 'content-type: application/json' \ -d '{"action":"set_fan_speed","percent":0}' > /dev/null ok "fan returned to 0" COMMAND_TESTED=yes fi # --- 6. the negative case -------------------------------------------------- echo "==> an unclaimed printer must be refused, not guessed at" ORPHAN="$(psql_q "INSERT INTO printers (farm_id, name, vendor) VALUES ('$FARM_ID', 'unclaimed', 'bambu') RETURNING id")" CODE="$(curl -s -o "$WORK/orphan.json" -w '%{http_code}' -X POST \ "$BASE/printers/$ORPHAN/commands" -H 'content-type: application/json' \ -d '{"action":"set_fan_speed","percent":0}')" [ "$CODE" = "409" ] || fail "expected HTTP 409 for an unclaimed printer, got $CODE" grep -q 'PRINTER_UNREACHABLE' "$WORK/orphan.json" || fail "wrong error: $(cat "$WORK/orphan.json")" ok "unclaimed printer refused with 409 PRINTER_UNREACHABLE" echo # Say what was actually exercised. Claiming commands work when the command test # was skipped is worse than not running it. if [ "$COMMAND_TESTED" = yes ]; then echo "PASS - telemetry and commands both verified against a real database and real printers." else echo "PARTIAL PASS - telemetry verified against a real database and real printers." echo " The command path was NOT exercised: no printer was idle enough to send to." echo " Rerun with PRINTER_ID= once one is free." fi