Why: - ADR-0016 reserves Compose for a serialized smoke test of the running web process. Nothing else exercises the deployment steps, a real HTTP server, or the real logging configuration, which every in-process test no-ops. Changes: - scripts/smoke.sh brings up Compose, runs both bootstrap steps, provisions a throwaway tenant, starts uvicorn, and drives the test against it - the test skips unless SMOKE_BASE_URL is set, so `uv run pytest` never invokes Compose; it asserts the ADR-0011 JSON log sink Impact: - a pre-release gate, not a per-PR one Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
99 lines
3.3 KiB
Bash
Executable File
99 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Serialized operational smoke test of the running web process (ADR-0016, plan
|
|
# 001 Phase 6).
|
|
#
|
|
# ./scripts/smoke.sh
|
|
#
|
|
# Brings up the Compose stack, runs both deployment steps for real, provisions
|
|
# a tenant, starts uvicorn, and drives `tests/e2e/test_compose_smoke.py`
|
|
# against it over a socket. This is the only Compose-based test: every other
|
|
# test uses Testcontainers and an in-process ASGI transport, which is exactly
|
|
# what makes this one worth having -- it is the only thing that exercises the
|
|
# deployment steps, the real logging configuration, and a real HTTP server.
|
|
#
|
|
# Not part of `uv run pytest`: the smoke test skips itself unless SMOKE_BASE_URL
|
|
# is set, so this script is the only way it runs. Run it before a release.
|
|
#
|
|
# Leaves the Compose stack running (it is the local dev stack); only the uvicorn
|
|
# process and the temporary log file are cleaned up.
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
|
|
|
PORT="${SMOKE_PORT:-8021}"
|
|
SLUG="smoke-$(date +%s)"
|
|
DOMAIN="smoke"
|
|
LOG_FILE="$(mktemp -t smoke-app-log.XXXXXX.jsonl)"
|
|
CONSOLE_LOG="$(mktemp -t smoke-app-console.XXXXXX.log)"
|
|
APP_PID=""
|
|
|
|
cleanup() {
|
|
if [[ -n "${APP_PID}" ]] && kill -0 "${APP_PID}" 2>/dev/null; then
|
|
kill "${APP_PID}" 2>/dev/null || true
|
|
wait "${APP_PID}" 2>/dev/null || true
|
|
fi
|
|
rm -f "${LOG_FILE}" "${CONSOLE_LOG}"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
if [[ ! -f .env ]]; then
|
|
echo "no .env found; copy .env.example first (see docs/runbook.md)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> starting Postgres, MinIO, Qdrant"
|
|
docker compose up -d --wait
|
|
|
|
echo "==> applying deployment steps"
|
|
uv run alembic upgrade head
|
|
uv run python -m src.cli.qdrant_bootstrap
|
|
|
|
echo "==> provisioning tenant '${SLUG}'"
|
|
PROVISION_OUTPUT="$(uv run python -m src.cli.provision_tenant \
|
|
--slug "${SLUG}" --domain "${DOMAIN}" --scopes files:write 2>/dev/null)"
|
|
API_KEY="$(printf '%s\n' "${PROVISION_OUTPUT}" | sed -n 's/^api_key=//p')"
|
|
if [[ -z "${API_KEY}" ]]; then
|
|
echo "provisioning did not return an api_key" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> starting the web process on port ${PORT}"
|
|
# JSON to a file sink, because the smoke test asserts the real ADR-0011 log
|
|
# output -- the one thing no in-process test can check.
|
|
LOG_JSON_FORMAT=true LOG_FILE_PATH="${LOG_FILE}" \
|
|
uv run python -m uvicorn src.main:app --host 127.0.0.1 --port "${PORT}" \
|
|
>"${CONSOLE_LOG}" 2>&1 &
|
|
APP_PID=$!
|
|
|
|
echo "==> waiting for /readyz"
|
|
for _ in $(seq 1 60); do
|
|
if curl -fsS "http://127.0.0.1:${PORT}/readyz" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
if ! kill -0 "${APP_PID}" 2>/dev/null; then
|
|
echo "the web process exited before becoming ready:" >&2
|
|
tail -20 "${CONSOLE_LOG}" >&2
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if ! curl -fsS "http://127.0.0.1:${PORT}/readyz" >/dev/null 2>&1; then
|
|
# Most often an unbootstrapped Qdrant or an unreachable embedder host; the
|
|
# runbook's health/readiness section covers reading this.
|
|
echo "the web process never became ready:" >&2
|
|
tail -20 "${CONSOLE_LOG}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "==> running the smoke test"
|
|
SMOKE_BASE_URL="http://127.0.0.1:${PORT}" \
|
|
SMOKE_API_KEY="${API_KEY}" \
|
|
SMOKE_DOMAIN="${DOMAIN}" \
|
|
SMOKE_LOG_PATH="${LOG_FILE}" \
|
|
SMOKE_QDRANT_URL="${QDRANT_URL:-http://127.0.0.1:6343}" \
|
|
SMOKE_QDRANT_COLLECTION="${QDRANT_COLLECTION:-chunks}" \
|
|
uv run python -m pytest tests/e2e/test_compose_smoke.py -q
|
|
|
|
echo "==> smoke test passed"
|