#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml" BACKEND_URL="${BACKEND_URL:-http://localhost:8000}" WAIT_SECONDS="${WAIT_SECONDS:-60}" PYTHON_BIN="${PYTHON_BIN:-python3}" EDITOR_EMAIL="${EDITOR_EMAIL:-editor@example.com}" cleanup() { docker compose -f "${COMPOSE_FILE}" down --remove-orphans >/dev/null 2>&1 || true } request() { local method="$1" local path="$2" local body="${3:-}" if [[ -n "${body}" ]]; then curl -fsS -X "${method}" -H "Content-Type: application/json" \ -H "X-Demo-User-Email: ${EDITOR_EMAIL}" --data "${body}" "${BACKEND_URL}${path}" return fi curl -fsS -X "${method}" -H "X-Demo-User-Email: ${EDITOR_EMAIL}" "${BACKEND_URL}${path}" } wait_backend() { local deadline=$((SECONDS + WAIT_SECONDS)) while (( SECONDS < deadline )); do if curl -fsS "${BACKEND_URL}/health" >/dev/null 2>&1; then return 0 fi sleep 1 done echo "Timed out waiting for backend at ${BACKEND_URL}" >&2 return 1 } json_eval() { local expression="$1" "${PYTHON_BIN}" -c "import json,sys; print(${expression})" } cd "${ROOT_DIR}" trap cleanup EXIT docker compose -f "${COMPOSE_FILE}" up --build -d wait_backend sites_json="$(request GET /api/sites)" site_id="$(printf '%s' "${sites_json}" | json_eval "json.load(sys.stdin)[0]['site']['id']")" article_body="$( SITE_ID="${site_id}" "${PYTHON_BIN}" - <<'PY' import json import os import uuid print(json.dumps({ "target_site_id": os.environ["SITE_ID"], "brief_description": f"Plan smoke flow {uuid.uuid4()}", "working_title": "Plan Smoke Flow", "content_type": "longform_guide", "primary_keyword": "plan smoke flow", })) PY )" article_json="$(request POST /api/articles "${article_body}")" article_id="$(printf '%s' "${article_json}" | json_eval "json.load(sys.stdin)['article']['id']")" questions_json="$(request POST "/api/articles/${article_id}/boundary-questions/generate")" question_patch_commands="$( QUESTIONS_JSON="${questions_json}" "${PYTHON_BIN}" - <<'PY' import json import os payload = json.loads(os.environ["QUESTIONS_JSON"]) for question in payload["questions"]: if question["is_required"]: print(f"{question['id']}\tAccepted answer for {question['category']}.") PY )" while IFS=$'\t' read -r question_id answer; do [[ -z "${question_id}" ]] && continue patch_body="$(ANSWER="${answer}" "${PYTHON_BIN}" - <<'PY' import json import os print(json.dumps({"answer": os.environ["ANSWER"]})) PY )" request PATCH "/api/articles/${article_id}/boundary-questions/${question_id}" "${patch_body}" >/dev/null done <<< "${question_patch_commands}" request POST "/api/articles/${article_id}/boundary-questions/submit" >/dev/null plan_json="$(request POST "/api/articles/${article_id}/plan/generate")" plan_id="$(printf '%s' "${plan_json}" | json_eval "json.load(sys.stdin)['plan']['id']")" edit_body="$("${PYTHON_BIN}" - <<'PY' import json print(json.dumps({ "recommended_title": "Edited smoke plan", "tone": "Direct", "audience": "Editors", "seo_notes": ["Smoke keyword"], })) PY )" edited_json="$(request PATCH "/api/articles/${article_id}/plans/${plan_id}" "${edit_body}")" edited_plan_id="$(printf '%s' "${edited_json}" | json_eval "json.load(sys.stdin)['plan']['id']")" request POST "/api/articles/${article_id}/plans/${edited_plan_id}/request-revision" '{"notes":"Smoke revision"}' >/dev/null request POST "/api/articles/${article_id}/plans/${edited_plan_id}/approve" >/dev/null research_json="$(request POST "/api/articles/${article_id}/research/start")" job_type="$(printf '%s' "${research_json}" | json_eval "json.load(sys.stdin)['job']['job_type']")" if [[ "${job_type}" != "RESEARCH" ]]; then echo "Expected RESEARCH job, got ${job_type}" >&2 exit 1 fi echo "plan flow smoke ok"