# End-to-end smoke for discourse-go: builds the CLI in the Docker # builder, boots the FAKE Discourse in a container on 127.0.0.1:8610, # drives the real binary from the host through env vars (0600 temp env # file), asserts the happy paths + failure classes + redaction, and tears # everything down. No live forum is ever contacted. Only exact container # IDs spawned here are killed. set -e cd "$(dirname "$0")/.." IMAGE="golang@sha256:e8c859f5632dcfde7b32d2012b4351728f6437930887c2f6a91ea242459e5514" PORT=8610 SMOKE_KEY="smoke-key-do-not-use" CID="" cleanup() { if [ -n "$CID" ]; then docker rm -f "$CID" >/dev/null 2>&1 || true fi rm -rf .smoke } trap cleanup EXIT INT TERM mkdir -p .smoke umask 077 echo "--- build CLI (docker builder)" docker run --rm -v "$PWD:/h" -w /h \ -u "$(id -u):$(id -g)" -e HOME=/tmp -e GOFLAGS=-buildvcs=false \ "$IMAGE" go build -o bin/discourse-go ./cmd/discourse-go echo "--- boot fake Discourse (container, port $PORT)" CID=$(docker run -d --rm \ -v "$PWD:/h" -w /h \ -u "$(id -u):$(id -g)" -e HOME=/tmp -e GOFLAGS=-buildvcs=false \ -p 127.0.0.1:$PORT:8610 \ "$IMAGE" go run ./smoke/fakediscourse -addr :8610) # wait for the fake to answer (any HTTP response proves it is up) wait_up() { python3 - "$PORT" <<'PYEOF' import sys, urllib.request, urllib.error port = sys.argv[1] req = urllib.request.Request('http://127.0.0.1:%s/session/current.json' % port, headers={'Api-Key': 'probe', 'Api-Username': 'probe'}) try: urllib.request.urlopen(req, timeout=2) except urllib.error.HTTPError: sys.exit(0) # got an HTTP answer: server is up except Exception: sys.exit(1) # not yet sys.exit(0) PYEOF } i=0 until [ -n "$CID" ] && [ "$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null)" = "true" ] && wait_up; do i=$((i+1)) if [ "$i" -ge 60 ]; then echo "smoke: fake server did not come up; logs:" >&2 docker logs "$CID" >&2 || true exit 1 fi sleep 1 done # 0600 env file = the credential path the docs prescribe ENVF=".smoke/env" printf 'DISCOURSE_URL=http://127.0.0.1:%s\nDISCOURSE_API_KEY=%s\nDISCOURSE_API_USERNAME=smoker\n' \ "$PORT" "$SMOKE_KEY" > "$ENVF" CLI() { ( set -a; . "$ENVF"; set +a; exec ./bin/discourse-go "$@" ) } capture="$PWD/.smoke/out" touch "$capture" echo "--- whoami" CLI whoami | tee -a "$capture" | grep -q '"username": "smoker"' || { echo "smoke: whoami failed" >&2; exit 1; } echo "--- categories list" CLI categories list | tee -a "$capture" | grep -q '"mopac-briefings"' || { echo "smoke: categories list failed" >&2; exit 1; } echo "--- categories create + typed 404 check" CLI categories create "Smoke Cat" -color 25AAE2 | tee -a "$capture" | grep -q '"slug": "smoke-cat"' || { echo "smoke: category create failed" >&2; exit 1; } CLI raw POST /definitely/not/there -data '{}' 2>>"$capture" >>"$capture" || true grep -q "not found" "$capture" || { echo "smoke: 404 class missing" >&2; exit 1; } echo "--- topics create / list / post reply / update" CLI topics create -title "Smoke Topic" -category 2 -raw "first body" | tee -a "$capture" | grep -q '"topic_id"' || { echo "smoke: topic create failed" >&2; exit 1; } CLI topics list -category 2 | tee -a "$capture" | grep -q '"Smoke Topic"' || { echo "smoke: topics list failed" >&2; exit 1; } CLI topics list -slug mopac-briefings | tee -a "$capture" | grep -q '"Smoke Topic"' || { echo "smoke: topics list by slug failed" >&2; exit 1; } POST_ID=$(CLI posts create -topic 10 -raw "reply body" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') CLI posts update "$POST_ID" -raw "edited body" -reason smoke | tee -a "$capture" | grep -q '"raw": "edited body"' || { echo "smoke: post update failed" >&2; exit 1; } echo "--- raw passthrough" CLI raw GET /latest.json | tee -a "$capture" | grep -q '"topic_list"' || { echo "smoke: raw GET failed" >&2; exit 1; } echo "--- search" CLI search "Smoke Topic" | tee -a "$capture" | grep -q '"Smoke Topic"' || { echo "smoke: search failed" >&2; exit 1; } echo "--- private message send + delivery record" CLI messages send -title "Smoke PM" -to ops,reachableceo -raw "pm body" | tee -a "$capture" | grep -q '"topic_slug": "smoke-pm"' || { echo "smoke: PM send failed" >&2; exit 1; } CLI raw GET /__pms.json | tee -a "$capture" | grep -q '"target_usernames": "ops,reachableceo"' || { echo "smoke: PM recipients not recorded" >&2; exit 1; } echo "--- webhook verify: good sig exits 0, bad sig exits 2" WHSEC="smoke-webhook-secret" printf '{"post":{"id":101}}' > .smoke/hook-body SIG=$(python3 -c 'import hmac,hashlib,sys; print("sha256="+hmac.new(sys.argv[1].encode(),open(sys.argv[2],"rb").read(),hashlib.sha256).hexdigest())' "$WHSEC" .smoke/hook-body) ( set -a; DISCOURSE_WEBHOOK_SECRET="$WHSEC"; set +a; exec ./bin/discourse-go webhook verify "$SIG" ) < .smoke/hook-body >>"$capture" 2>&1 \ || { echo "smoke: webhook verify (good sig) failed" >&2; exit 1; } set +e ( set -a; DISCOURSE_WEBHOOK_SECRET="$WHSEC"; set +a; exec ./bin/discourse-go webhook verify "sha256=deadbeef" ) < .smoke/hook-body >"$PWD/.smoke/badsig" 2>&1 code=$? set -e if [ "$code" -ne 2 ]; then echo "smoke: bad webhook sig exit=$code want 2" >&2; exit 1; fi grep -q "bad signature" "$PWD/.smoke/badsig" || { echo "smoke: bad-signature message missing" >&2; exit 1; } echo "--- sso verify: decoded payload + tamper rejection" SSOSEC="smoke-sso-secret" SSO=$(python3 -c 'import base64; print(base64.b64encode(b"nonce=smoke123&return_sso_url=https%3A%2F%2Fapp.test%2Fcb").decode())') SSIG=$(python3 -c 'import hmac,hashlib,sys; print(hmac.new(sys.argv[1].encode(),sys.argv[2].encode(),hashlib.sha256).hexdigest())' "$SSOSEC" "$SSO") ( set -a; DISCOURSE_SSO_SECRET="$SSOSEC"; set +a; exec ./bin/discourse-go sso verify "$SSO" "$SSIG" ) | tee -a "$capture" | grep -q '"nonce": "smoke123"' \ || { echo "smoke: sso verify failed" >&2; exit 1; } set +e ( set -a; DISCOURSE_SSO_SECRET="$SSOSEC"; set +a; exec ./bin/discourse-go sso verify "$SSO" "deadbeef" ) >/dev/null 2>&1 code=$? set -e if [ "$code" -ne 2 ]; then echo "smoke: bad sso sig exit=$code want 2" >&2; exit 1; fi echo "--- redaction: no key material in any captured output" if grep -q "$SMOKE_KEY" "$capture"; then echo "smoke: KEY LEAKED in output" >&2 exit 1 fi echo "--- bad key: exit code 2 + typed error" set +e ( set -a; . "$ENVF"; set +a; DISCOURSE_API_KEY=wrong-key exec ./bin/discourse-go whoami ) >"$PWD/.smoke/bad" 2>&1 code=$? set -e if [ "$code" -ne 2 ]; then echo "smoke: bad key exit=$code want 2" >&2; exit 1; fi grep -q "HTTP 403" "$PWD/.smoke/bad" || { echo "smoke: typed error missing" >&2; exit 1; } echo "smoke: OK"