Files
hack-house/hh/scripts/smoke.sh
T
leetcrypt 9959e09fe2
CI / rust client (hh) (macos-latest) (push) Waiting to run
CI / rust client (hh) (ubuntu-latest) (push) Waiting to run
CI / rust coverage (push) Waiting to run
CI / python server (3.10) (push) Waiting to run
CI / python server (3.11) (push) Waiting to run
CI / python server (3.12) (push) Waiting to run
CI / headless e2e smoke (push) Waiting to run
CI / dependency audit (push) Waiting to run
CI / secret scanning (push) Waiting to run
docs(scripts): add --help to remaining scripts; document scripts + layout in README
- Add a -h/--help guard (prints the usage header) to the five scripts
  that lacked one: smoke.sh, smoke-e2e.sh, test-features.sh,
  demo-save-load.sh, sandbox-bootstrap.sh. All 15 hh/scripts/*.sh now
  respond to --help.
- README: new "Scripts" section detailing every script (setup, hosting,
  sandbox provisioning, tests) and noting each takes --help; plus an
  archive note for the film-* recording scripts.
- README: new "Window layout" section documenting live pane resizing
  (F4 fullscreen, click/F5 to select + arrows to resize) and the
  /layout save/load/list/rm/reset presets, with matching keybinding-table
  rows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-07 13:20:07 -07:00

83 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# hack-house smoke test
# Exercises the full use-case path end to end against a live server:
# rust unit tests → SRP self-test → boot server → rust client handshake +
# round-trip → cross-language (python decrypts what the rust client sent).
# Run from anywhere: hh/scripts/smoke.sh
set -uo pipefail
# -h/--help: print the usage header above and exit.
case "${1:-}" in
-h|--help|-help) sed -n '2,/^set /{/^set /d;s/^# \{0,1\}//;p}' "$0"; exit 0 ;;
esac
HERE="$(cd "$(dirname "$0")/.." && pwd)" # .../hh
ROOT="$(cd "$HERE/.." && pwd)" # repo root
PY="$ROOT/.venv/bin/python"
BIN="$HERE/target/debug/hack-house"
PORT="${PORT:-4199}"
PW="${PW:-labtest}"
fail() { echo "✖ SMOKE FAIL: $1"; exit 1; }
echo "── 1/5 rust unit tests (srp vectors + fernet interop) ──"
( cd "$HERE" && cargo test --quiet ) || fail "cargo test"
echo "── 2/5 build + SRP self-test (Rust SRP ≡ Python srp) ──"
( cd "$HERE" && cargo build --quiet ) || fail "cargo build"
"$BIN" selftest | grep -q "selftest passed" || fail "selftest"
echo "── 3/5 boot server on :$PORT ──"
"$PY" "$ROOT/cmd_chat.py" serve 127.0.0.1 "$PORT" --password "$PW" --no-tls \
>/tmp/hh-smoke-srv.log 2>&1 &
SRV=$!
trap 'kill $SRV 2>/dev/null' EXIT
for _ in $(seq 1 20); do
curl -s --max-time 2 "http://127.0.0.1:$PORT/health" 2>/dev/null | grep -q '"status":"ok"' && break
sleep 1
done
curl -s "http://127.0.0.1:$PORT/health" 2>/dev/null | grep -q '"status":"ok"' || fail "server did not come up"
echo "── 4/5 rust client: SRP auth + encrypted round-trip ──"
"$BIN" handshake 127.0.0.1 "$PORT" smoke-rust --password "$PW" --no-tls \
| tee /tmp/hh-smoke-rust.log | grep -q "round-trip ✓" || fail "rust handshake/round-trip"
echo "── 5/5 cross-language: python decrypts the rust-sent message ──"
"$PY" - "$PORT" "$PW" <<'PYEOF' || fail "python cross-language read"
import sys, asyncio, base64, json
import srp, requests, websockets
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
from cryptography.hazmat.primitives import hashes
srp.rfc5054_enable()
port, pw = sys.argv[1], sys.argv[2].encode()
base, wsb = f"http://127.0.0.1:{port}", f"ws://127.0.0.1:{port}"
async def main():
usr = srp.User(b"chat", pw, hash_alg=srp.SHA256)
_, A = usr.start_authentication()
r = requests.post(f"{base}/srp/init",
json={"username": "smoke-py", "A": base64.b64encode(A).decode()}).json()
uid = r["user_id"]
B, salt, rs = (base64.b64decode(r[k]) for k in ("B", "salt", "room_salt"))
M = usr.process_challenge(salt, B)
v = requests.post(f"{base}/srp/verify",
json={"user_id": uid, "username": "smoke-py",
"M": base64.b64encode(M).decode()}).json()
room = Fernet(base64.urlsafe_b64encode(
HKDF(algorithm=hashes.SHA256(), length=32, salt=rs,
info=b"cmd-chat-room-key").derive(pw)))
async with websockets.connect(f"{wsb}/ws/chat?user_id={uid}&ws_token={v['ws_token']}") as ws:
data = json.loads(await asyncio.wait_for(ws.recv(), 5))
msgs = [room.decrypt(m["text"].encode()).decode()
for m in data.get("messages", []) if m.get("text")]
hits = [m for m in msgs if "house is open" in m]
assert hits, f"rust-sent message not found/decryptable: {msgs!r}"
print(" ✓ python decrypted rust-sent message:", hits)
asyncio.run(main())
PYEOF
echo
echo "✓ SMOKE PASS — crypto · SRP · fernet · cross-language relay all green"