Mercurial
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 263:ee04e4e69fed | 264:04fee26ecce0 |
|---|---|
| 1 #!/usr/bin/env bash | |
| 2 # config_validation_test.sh — startup config validation tests. | |
| 3 # | |
| 4 # Verifies that the server binary exits 1 (fail-closed) on bad config, | |
| 5 # starts cleanly on env-only config (no .config file), and enforces the | |
| 6 # loopback requirement for AUTH_DEV_INSECURE_COOKIE. | |
| 7 # | |
| 8 # Bazel passes the server binary path as $1. | |
| 9 set -euo pipefail | |
| 10 | |
| 11 server="$(realpath "$1")" | |
| 12 tmpdir="$(mktemp -d)" | |
| 13 trap 'rm -rf "$tmpdir"' EXIT | |
| 14 | |
| 15 pass() { printf " %-62s PASS\n" "$1"; } | |
| 16 fail() { echo "FAIL: $1" >&2; exit 1; } | |
| 17 | |
| 18 # A valid 64-hex-char secret (32 random bytes) for success cases. | |
| 19 GOOD_SECRET="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" | |
| 20 | |
| 21 # Helper: run $@ as a command, expect it to exit non-zero ≤ 3 seconds. | |
| 22 run_expect_fail() { | |
| 23 local label="$1"; shift | |
| 24 local out | |
| 25 set +e | |
| 26 out=$(timeout 3 "$@" 2>&1) | |
| 27 local code=$? | |
| 28 set -e | |
| 29 if [[ $code -eq 0 || $code -eq 124 ]]; then | |
| 30 echo "FAIL: '$label' — expected exit 1, got code $code" >&2 | |
| 31 echo "$out" >&2 | |
| 32 exit 1 | |
| 33 fi | |
| 34 pass "$label" | |
| 35 } | |
| 36 | |
| 37 # Helper: run $@ as a command; expect it to emit an "Initialised" or | |
| 38 # listen/startup log line within 5 seconds, then kill it. | |
| 39 run_expect_start() { | |
| 40 local label="$1"; shift | |
| 41 local logfile="$tmpdir/start_${RANDOM}.log" | |
| 42 set +e | |
| 43 timeout 5 "$@" >"$logfile" 2>&1 & | |
| 44 local pid=$! | |
| 45 local started=false | |
| 46 for _ in $(seq 1 50); do | |
| 47 sleep 0.1 | |
| 48 if grep -q -E 'Initialised|Listening|WTF is going on' "$logfile" 2>/dev/null; then | |
| 49 started=true | |
| 50 break | |
| 51 fi | |
| 52 if ! kill -0 "$pid" 2>/dev/null; then | |
| 53 break | |
| 54 fi | |
| 55 done | |
| 56 kill "$pid" 2>/dev/null || true | |
| 57 wait "$pid" 2>/dev/null || true | |
| 58 set -e | |
| 59 if [[ "$started" != "true" ]]; then | |
| 60 echo "FAIL: '$label' — server did not start (log below)" >&2 | |
| 61 cat "$logfile" >&2 | |
| 62 exit 1 | |
| 63 fi | |
| 64 pass "$label" | |
| 65 } | |
| 66 | |
| 67 echo "" | |
| 68 echo "[config validation]" | |
| 69 | |
| 70 # ── Fail cases ───────────────────────────────────────────────────────────────── | |
| 71 | |
| 72 # Missing secret → exit 1 | |
| 73 run_expect_fail "missing AUTH_COOKIE_SECRET → exit 1" \ | |
| 74 env -i "TEST_TMPDIR=$tmpdir" "$server" | |
| 75 | |
| 76 # Secret too short (62 hex chars = 31 bytes) | |
| 77 run_expect_fail "short AUTH_COOKIE_SECRET (62 hex) → exit 1" \ | |
| 78 env -i \ | |
| 79 "AUTH_COOKIE_SECRET=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd" \ | |
| 80 "SERVER_HOST=127.0.0.1" \ | |
| 81 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 82 "TEST_TMPDIR=$tmpdir" \ | |
| 83 "$server" | |
| 84 | |
| 85 # Secret with non-hex characters | |
| 86 run_expect_fail "non-hex AUTH_COOKIE_SECRET → exit 1" \ | |
| 87 env -i \ | |
| 88 "AUTH_COOKIE_SECRET=ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" \ | |
| 89 "SERVER_HOST=127.0.0.1" \ | |
| 90 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 91 "TEST_TMPDIR=$tmpdir" \ | |
| 92 "$server" | |
| 93 | |
| 94 # AUTH_DEV_INSECURE_COOKIE=true with non-loopback SERVER_HOST → exit 1 | |
| 95 run_expect_fail "insecure cookie without loopback SERVER_HOST → exit 1" \ | |
| 96 env -i \ | |
| 97 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 98 "SERVER_HOST=0.0.0.0" \ | |
| 99 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 100 "TEST_TMPDIR=$tmpdir" \ | |
| 101 "$server" | |
| 102 | |
| 103 # AUTH_DEV_INSECURE_COOKIE=true with no SERVER_HOST → exit 1 | |
| 104 run_expect_fail "insecure cookie without any SERVER_HOST → exit 1" \ | |
| 105 env -i \ | |
| 106 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 107 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 108 "TEST_TMPDIR=$tmpdir" \ | |
| 109 "$server" | |
| 110 | |
| 111 # Bootstrap username set without hash → exit 1 | |
| 112 run_expect_fail "bootstrap username without hash → exit 1" \ | |
| 113 env -i \ | |
| 114 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 115 "SERVER_HOST=127.0.0.1" \ | |
| 116 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 117 "AUTH_BOOTSTRAP_USERNAME=admin" \ | |
| 118 "TEST_TMPDIR=$tmpdir" \ | |
| 119 "$server" | |
| 120 | |
| 121 # Bootstrap hash with wrong format → exit 1 | |
| 122 run_expect_fail "bootstrap hash with wrong format → exit 1" \ | |
| 123 env -i \ | |
| 124 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 125 "SERVER_HOST=127.0.0.1" \ | |
| 126 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 127 "AUTH_BOOTSTRAP_USERNAME=admin" \ | |
| 128 "AUTH_BOOTSTRAP_PASSWORD_HASH=not-a-real-hash" \ | |
| 129 "TEST_TMPDIR=$tmpdir" \ | |
| 130 "$server" | |
| 131 | |
| 132 # Bootstrap hash with partial/truncated format → exit 1 | |
| 133 run_expect_fail "bootstrap hash truncated (only prefix) → exit 1" \ | |
| 134 env -i \ | |
| 135 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 136 "SERVER_HOST=127.0.0.1" \ | |
| 137 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 138 "AUTH_BOOTSTRAP_USERNAME=admin" \ | |
| 139 "AUTH_BOOTSTRAP_PASSWORD_HASH=zenbu-scrypt\$v=1\$N=32768\$r=8\$p=1\$" \ | |
| 140 "TEST_TMPDIR=$tmpdir" \ | |
| 141 "$server" | |
| 142 | |
| 143 # idle TTL > abs TTL → exit 1 | |
| 144 run_expect_fail "idle TTL exceeds abs TTL → exit 1" \ | |
| 145 env -i \ | |
| 146 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 147 "SERVER_HOST=127.0.0.1" \ | |
| 148 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 149 "AUTH_SESSION_IDLE_TTL=9999999" \ | |
| 150 "AUTH_SESSION_ABS_TTL=100" \ | |
| 151 "TEST_TMPDIR=$tmpdir" \ | |
| 152 "$server" | |
| 153 | |
| 154 # Malformed AUTH_SESSION_IDLE_TTL (non-integer) → exit 1 | |
| 155 run_expect_fail "malformed AUTH_SESSION_IDLE_TTL → exit 1" \ | |
| 156 env -i \ | |
| 157 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 158 "SERVER_HOST=127.0.0.1" \ | |
| 159 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 160 "AUTH_SESSION_IDLE_TTL=3600abc" \ | |
| 161 "TEST_TMPDIR=$tmpdir" \ | |
| 162 "$server" | |
| 163 | |
| 164 # Malformed AUTH_SESSION_ABS_TTL (non-integer) → exit 1 | |
| 165 run_expect_fail "malformed AUTH_SESSION_ABS_TTL → exit 1" \ | |
| 166 env -i \ | |
| 167 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 168 "SERVER_HOST=127.0.0.1" \ | |
| 169 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 170 "AUTH_SESSION_ABS_TTL=not_a_number" \ | |
| 171 "TEST_TMPDIR=$tmpdir" \ | |
| 172 "$server" | |
| 173 | |
| 174 # Malformed S3_URL_EXPIRES (non-integer) → exit 1 | |
| 175 run_expect_fail "malformed S3_URL_EXPIRES → exit 1" \ | |
| 176 env -i \ | |
| 177 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 178 "SERVER_HOST=127.0.0.1" \ | |
| 179 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 180 "S3_URL_EXPIRES=3600x" \ | |
| 181 "TEST_TMPDIR=$tmpdir" \ | |
| 182 "$server" | |
| 183 | |
| 184 # S3_URL_EXPIRES = 0 (not positive) → exit 1 | |
| 185 run_expect_fail "S3_URL_EXPIRES=0 (non-positive) → exit 1" \ | |
| 186 env -i \ | |
| 187 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 188 "SERVER_HOST=127.0.0.1" \ | |
| 189 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 190 "S3_URL_EXPIRES=0" \ | |
| 191 "TEST_TMPDIR=$tmpdir" \ | |
| 192 "$server" | |
| 193 | |
| 194 # Trusted proxy with invalid chars → exit 1 | |
| 195 run_expect_fail "invalid trusted proxy (bad chars) → exit 1" \ | |
| 196 env -i \ | |
| 197 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 198 "SERVER_HOST=127.0.0.1" \ | |
| 199 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 200 "AUTH_TRUSTED_PROXY=not an ip!" \ | |
| 201 "TEST_TMPDIR=$tmpdir" \ | |
| 202 "$server" | |
| 203 | |
| 204 # Trusted proxy with out-of-range IPv4 octet (999.999.999.999) → exit 1 | |
| 205 run_expect_fail "trusted proxy 999.999.999.999 → exit 1" \ | |
| 206 env -i \ | |
| 207 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 208 "SERVER_HOST=127.0.0.1" \ | |
| 209 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 210 "AUTH_TRUSTED_PROXY=999.999.999.999" \ | |
| 211 "TEST_TMPDIR=$tmpdir" \ | |
| 212 "$server" | |
| 213 | |
| 214 # ── Success cases ───────────────────────────────────────────────────────────── | |
| 215 # env-only startup (no .config file) with loopback host | |
| 216 run_expect_start "env-only startup (no .config) with loopback host" \ | |
| 217 env -i \ | |
| 218 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 219 "SERVER_HOST=127.0.0.1" \ | |
| 220 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 221 "MRJUNEJUNE_PORT=16969" \ | |
| 222 "TEST_TMPDIR=$tmpdir" \ | |
| 223 "$server" | |
| 224 | |
| 225 # Valid IPv4 trusted proxy is accepted | |
| 226 run_expect_start "valid IPv4 trusted proxy accepted" \ | |
| 227 env -i \ | |
| 228 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 229 "SERVER_HOST=127.0.0.1" \ | |
| 230 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 231 "AUTH_TRUSTED_PROXY=10.0.0.1" \ | |
| 232 "MRJUNEJUNE_PORT=16970" \ | |
| 233 "TEST_TMPDIR=$tmpdir" \ | |
| 234 "$server" | |
| 235 | |
| 236 # Valid IPv6 trusted proxy (canonical ::1) is accepted | |
| 237 run_expect_start "valid IPv6 trusted proxy (::1) accepted" \ | |
| 238 env -i \ | |
| 239 "AUTH_COOKIE_SECRET=$GOOD_SECRET" \ | |
| 240 "SERVER_HOST=127.0.0.1" \ | |
| 241 "AUTH_DEV_INSECURE_COOKIE=true" \ | |
| 242 "AUTH_TRUSTED_PROXY=::1" \ | |
| 243 "MRJUNEJUNE_PORT=16971" \ | |
| 244 "TEST_TMPDIR=$tmpdir" \ | |
| 245 "$server" | |
| 246 | |
| 247 echo "" |