#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TMP_DIR="$(mktemp -d)" MOCK_PORT="${SELFTEST_MOCK_PORT:-18080}" APP_PORT="${SELFTEST_APP_PORT:-18081}" MOCK_PID="" APP_PID="" cleanup() { if [[ -n "${APP_PID}" ]] && kill -0 "${APP_PID}" >/dev/null 2>&1; then kill "${APP_PID}" >/dev/null 2>&1 || true wait "${APP_PID}" >/dev/null 2>&1 || true fi if [[ -n "${MOCK_PID}" ]] && kill -0 "${MOCK_PID}" >/dev/null 2>&1; then kill "${MOCK_PID}" >/dev/null 2>&1 || true wait "${MOCK_PID}" >/dev/null 2>&1 || true fi rm -rf "${TMP_DIR}" } trap cleanup EXIT cd "${ROOT_DIR}" echo "[selftest] gofmt" gofmt -w backend/main.go backend/handlers/api.go backend/models/db.go backend/services/cse.go backend/services/cse_test.go backend/services/search_collectors.go backend/services/ranker.go backend/services/gemini.go backend/services/gemini_test.go echo "[selftest] python syntax" python3 -m py_compile worker/downloader.py scripts/mock_searxng.py echo "[selftest] go test" go test ./... echo "[selftest] go build" go build -o "${TMP_DIR}/ai-media-hub" ./backend echo "[selftest] start mock searxng" python3 scripts/mock_searxng.py --port "${MOCK_PORT}" >"${TMP_DIR}/mock-searxng.log" 2>&1 & MOCK_PID=$! echo "[selftest] start app" mkdir -p "${TMP_DIR}/downloads" APP_ROOT="${ROOT_DIR}" \ APP_ADDR="127.0.0.1:${APP_PORT}" \ SQLITE_PATH="${TMP_DIR}/media.db" \ DOWNLOADS_DIR="${TMP_DIR}/downloads" \ FRONTEND_DIR="${ROOT_DIR}/frontend" \ WORKER_SCRIPT="${ROOT_DIR}/worker/downloader.py" \ SEARXNG_BASE_URL="http://127.0.0.1:${MOCK_PORT}" \ "${TMP_DIR}/ai-media-hub" >"${TMP_DIR}/app.log" 2>&1 & APP_PID=$! for _ in $(seq 1 30); do if curl -fsS "http://127.0.0.1:${APP_PORT}/healthz" >/dev/null; then break fi sleep 1 done echo "[selftest] verify healthz" curl -fsS "http://127.0.0.1:${APP_PORT}/healthz" >"${TMP_DIR}/healthz.json" python3 - "${TMP_DIR}/healthz.json" <<'PY' import json import sys with open(sys.argv[1], "r", encoding="utf-8") as handle: payload = json.load(handle) if payload.get("status") != "ok": raise SystemExit(f"unexpected healthz payload: {payload}") PY echo "[selftest] verify search" for _ in $(seq 1 5); do if curl -fsS \ -H "Content-Type: application/json" \ -d '{"query":"city rain","platforms":["envato","artgrid","google video"]}' \ "http://127.0.0.1:${APP_PORT}/api/search" >"${TMP_DIR}/search.json"; then break fi sleep 1 done python3 - "${TMP_DIR}/search.json" <<'PY' import json import sys with open(sys.argv[1], "r", encoding="utf-8") as handle: payload = json.load(handle) results = payload.get("results") or [] if len(results) < 2: raise SystemExit(f"expected >= 2 search results, got {len(results)}: {payload}") if not all(item.get("link") for item in results): raise SystemExit(f"search results must include links: {payload}") PY echo "[selftest] verify upload" printf 'selftest upload\n' > "${TMP_DIR}/sample.txt" curl -fsS -F "file=@${TMP_DIR}/sample.txt" "http://127.0.0.1:${APP_PORT}/api/upload" >"${TMP_DIR}/upload.json" python3 - "${TMP_DIR}/upload.json" "${TMP_DIR}/downloads" <<'PY' import json import os import sys with open(sys.argv[1], "r", encoding="utf-8") as handle: payload = json.load(handle) filename = payload.get("filename") if not filename: raise SystemExit(f"missing filename in upload payload: {payload}") target = os.path.join(sys.argv[2], filename) if not os.path.exists(target): raise SystemExit(f"expected uploaded file at {target}") PY echo "[selftest] ok"