#!/usr/bin/env bash # Non-browser live-check for the host: spins up an EPHEMERAL host server (this # worktree's binary + lib + web, a temp persist dir), logs in, seeds one post, then # runs a sequence of HTTP checks printing status | content-type | body-head for each. # Catches what conformance can't — the real http-listen serving path (serving-JIT # divergence, VmSuspended renders, content-type regressions) — without a browser and # without touching live data. The non-Playwright counterpart to run-picker-check.sh. # # bash lib/host/live-check.sh # default smoke: /health /posts /feed / // # bash lib/host/live-check.sh /tags /article/ # check specific GET paths instead # # Asserts: reads are text/sx (the SX-native wire), pages are non-empty, no 5xx. # Requires the OCaml binary built (hosts/ocaml/_build/default/bin/sx_server.exe). set -uo pipefail cd "$(git rev-parse --show-toplevel)" PORT="${LIVE_PORT:-8914}" USER="admin"; PASS="live-check-pw"; SECRET="live-check-secret" PDIR=$(mktemp -d); JAR=$(mktemp); LOG=$(mktemp); HDR=$(mktemp) BASE="http://127.0.0.1:$PORT" RC=0 cleanup() { local pid pid=$(ss -lptn "sport = :$PORT" 2>/dev/null | grep -oE 'pid=[0-9]+' | head -1 | cut -d= -f2) [ -n "$pid" ] && kill "$pid" 2>/dev/null rm -f "$JAR" "$LOG" "$HDR"; rm -rf "$PDIR" } trap cleanup EXIT echo "== booting ephemeral host on :$PORT (persist=$PDIR) ==" HOST_PORT="$PORT" SX_PERSIST_DIR="$PDIR" \ SX_ADMIN_USER="$USER" SX_ADMIN_PASSWORD="$PASS" SX_SESSION_SECRET="$SECRET" \ bash lib/host/serve.sh >"$LOG" 2>&1 & for i in $(seq 1 60); do curl -sf -o /dev/null "$BASE/health" 2>/dev/null && break sleep 1; [ "$i" = "60" ] && { echo "server never came up:"; cat "$LOG"; exit 1; } done echo "== up ==" # Log in + seed one post (also exercises the form-ingest write path). curl -s -c "$JAR" -o /dev/null -X POST "$BASE/login" --data "username=$USER&password=$PASS" curl -s -b "$JAR" -o /dev/null -X POST "$BASE/new" \ --data 'title=Live Check Post&sx_content=(article (h1 "Live Check Post") (p "ok"))&status=published' # A GET check: prints " | " and flags problems. check() { local path="$1" body ct code body=$(curl -s -b "$JAR" -D "$HDR" "$BASE$path") code=$(awk 'NR==1{print $2}' "$HDR") ct=$(grep -i '^content-type:' "$HDR" | head -1 | tr -d '\r' | sed 's/content-type: *//I') printf ' %-20s %s %-26s | %s\n' "$path" "${code:-???}" "${ct:-?}" "$(printf '%s' "$body" | tr '\n' ' ' | cut -c1-70)" case "$code" in 5*) echo " !! 5xx"; RC=1 ;; esac [ -z "$body" ] && { echo " !! empty body"; RC=1; } # data endpoints must be SX, never JSON case "$path" in /posts|/feed) echo "$ct" | grep -qi 'text/sx' || { echo " !! expected text/sx, got '$ct'"; RC=1; } printf '%s' "$body" | grep -q '"ok":' && { echo " !! JSON leaked"; RC=1; } ;; esac } echo "== checks ==" if [ "$#" -gt 0 ]; then for p in "$@"; do check "$p"; done else for p in /health /posts /feed / /live-check-post/; do check "$p"; done fi echo "== done (rc $RC) ==" exit $RC