view mrjunejune/test/config_validation_test.sh @ 264:04fee26ecce0

add authenticated JRPG conversation platform Add reusable auth/session storage, owned conversation recovery, guest quotas, admin workflows, URL-routed conversation UI, mobile frame support, and parallel browser acceptance. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Fri, 07 Aug 2026 07:34:12 -0700
parents
children 056790c4fb0d
line wrap: on
line source

#!/usr/bin/env bash
# config_validation_test.sh — startup config validation tests.
#
# Verifies that the server binary exits 1 (fail-closed) on bad config,
# starts cleanly on env-only config (no .config file), and enforces the
# loopback requirement for AUTH_DEV_INSECURE_COOKIE.
#
# Bazel passes the server binary path as $1.
set -euo pipefail

server="$(realpath "$1")"
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

pass() { printf "  %-62s PASS\n" "$1"; }
fail() { echo "FAIL: $1" >&2; exit 1; }

# A valid 64-hex-char secret (32 random bytes) for success cases.
GOOD_SECRET="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

# Helper: run $@ as a command, expect it to exit non-zero ≤ 3 seconds.
run_expect_fail() {
  local label="$1"; shift
  local out
  set +e
  out=$(timeout 3 "$@" 2>&1)
  local code=$?
  set -e
  if [[ $code -eq 0 || $code -eq 124 ]]; then
    echo "FAIL: '$label' — expected exit 1, got code $code" >&2
    echo "$out" >&2
    exit 1
  fi
  pass "$label"
}

# Helper: run $@ as a command; expect it to emit an "Initialised" or
# listen/startup log line within 5 seconds, then kill it.
run_expect_start() {
  local label="$1"; shift
  local logfile="$tmpdir/start_${RANDOM}.log"
  set +e
  timeout 5 "$@" >"$logfile" 2>&1 &
  local pid=$!
  local started=false
  for _ in $(seq 1 50); do
    sleep 0.1
    if grep -q -E 'Initialised|Listening|WTF is going on' "$logfile" 2>/dev/null; then
      started=true
      break
    fi
    if ! kill -0 "$pid" 2>/dev/null; then
      break
    fi
  done
  kill "$pid" 2>/dev/null || true
  wait "$pid" 2>/dev/null || true
  set -e
  if [[ "$started" != "true" ]]; then
    echo "FAIL: '$label' — server did not start (log below)" >&2
    cat "$logfile" >&2
    exit 1
  fi
  pass "$label"
}

echo ""
echo "[config validation]"

# ── Fail cases ─────────────────────────────────────────────────────────────────

# Missing secret → exit 1
run_expect_fail "missing AUTH_COOKIE_SECRET → exit 1" \
  env -i "TEST_TMPDIR=$tmpdir" "$server"

# Secret too short (62 hex chars = 31 bytes)
run_expect_fail "short AUTH_COOKIE_SECRET (62 hex) → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Secret with non-hex characters
run_expect_fail "non-hex AUTH_COOKIE_SECRET → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# AUTH_DEV_INSECURE_COOKIE=true with non-loopback SERVER_HOST → exit 1
run_expect_fail "insecure cookie without loopback SERVER_HOST → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=0.0.0.0" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# AUTH_DEV_INSECURE_COOKIE=true with no SERVER_HOST → exit 1
run_expect_fail "insecure cookie without any SERVER_HOST → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Bootstrap username set without hash → exit 1
run_expect_fail "bootstrap username without hash → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_BOOTSTRAP_USERNAME=admin" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Bootstrap hash with wrong format → exit 1
run_expect_fail "bootstrap hash with wrong format → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_BOOTSTRAP_USERNAME=admin" \
    "AUTH_BOOTSTRAP_PASSWORD_HASH=not-a-real-hash" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Bootstrap hash with partial/truncated format → exit 1
run_expect_fail "bootstrap hash truncated (only prefix) → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_BOOTSTRAP_USERNAME=admin" \
    "AUTH_BOOTSTRAP_PASSWORD_HASH=zenbu-scrypt\$v=1\$N=32768\$r=8\$p=1\$" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# idle TTL > abs TTL → exit 1
run_expect_fail "idle TTL exceeds abs TTL → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_SESSION_IDLE_TTL=9999999" \
    "AUTH_SESSION_ABS_TTL=100" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Malformed AUTH_SESSION_IDLE_TTL (non-integer) → exit 1
run_expect_fail "malformed AUTH_SESSION_IDLE_TTL → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_SESSION_IDLE_TTL=3600abc" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Malformed AUTH_SESSION_ABS_TTL (non-integer) → exit 1
run_expect_fail "malformed AUTH_SESSION_ABS_TTL → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_SESSION_ABS_TTL=not_a_number" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Malformed S3_URL_EXPIRES (non-integer) → exit 1
run_expect_fail "malformed S3_URL_EXPIRES → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "S3_URL_EXPIRES=3600x" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# S3_URL_EXPIRES = 0 (not positive) → exit 1
run_expect_fail "S3_URL_EXPIRES=0 (non-positive) → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "S3_URL_EXPIRES=0" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Trusted proxy with invalid chars → exit 1
run_expect_fail "invalid trusted proxy (bad chars) → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_TRUSTED_PROXY=not an ip!" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Trusted proxy with out-of-range IPv4 octet (999.999.999.999) → exit 1
run_expect_fail "trusted proxy 999.999.999.999 → exit 1" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_TRUSTED_PROXY=999.999.999.999" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# ── Success cases ─────────────────────────────────────────────────────────────
# env-only startup (no .config file) with loopback host
run_expect_start "env-only startup (no .config) with loopback host" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "MRJUNEJUNE_PORT=16969" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Valid IPv4 trusted proxy is accepted
run_expect_start "valid IPv4 trusted proxy accepted" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_TRUSTED_PROXY=10.0.0.1" \
    "MRJUNEJUNE_PORT=16970" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

# Valid IPv6 trusted proxy (canonical ::1) is accepted
run_expect_start "valid IPv6 trusted proxy (::1) accepted" \
  env -i \
    "AUTH_COOKIE_SECRET=$GOOD_SECRET" \
    "SERVER_HOST=127.0.0.1" \
    "AUTH_DEV_INSECURE_COOKIE=true" \
    "AUTH_TRUSTED_PROXY=::1" \
    "MRJUNEJUNE_PORT=16971" \
    "TEST_TMPDIR=$tmpdir" \
    "$server"

echo ""