#!/usr/bin/env bash set -euo pipefail BASE_URL="${1:-http://localhost:1202}" TMP_DIR="$(mktemp -d)" trap 'rm -rf "${TMP_DIR}"' EXIT if ! command -v curl >/dev/null 2>&1; then echo "curl is required for demo raster workflow verification" >&2 exit 1 fi if [ -n "${PYTHON_BIN:-}" ]; then PYTHON_BIN="${PYTHON_BIN}" else PYTHON_BIN="" for candidate in python3 python.exe python; do if command -v "${candidate}" >/dev/null 2>&1 && "${candidate}" -c "import json, sys" >/dev/null 2>&1; then PYTHON_BIN="${candidate}" break fi done fi if [ -z "${PYTHON_BIN}" ]; then echo "A Python interpreter is required for JSON parsing" >&2 exit 1 fi json_field() { local file_path="$1" local expression="$2" "${PYTHON_BIN}" - "$file_path" "$expression" <<'PY' import json import sys path, expression = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: payload = json.load(handle) value = payload for part in expression.split("."): if part: value = value[part] print(value) PY } require_json_data() { local file_path="$1" "${PYTHON_BIN}" - "$file_path" <<'PY' import json import sys with open(sys.argv[1], "r", encoding="utf-8") as handle: payload = json.load(handle) if "data" not in payload: raise SystemExit("Response is not a canonical GeoIntel data envelope") PY } echo "== GeoIntel demo raster workflow verification ==" echo "Base URL: ${BASE_URL}" curl -fsS -X POST "${BASE_URL%/}/api/v1/demo/workflow" > "${TMP_DIR}/demo.json" require_json_data "${TMP_DIR}/demo.json" project_id="$(json_field "${TMP_DIR}/demo.json" "data.project_id")" raster_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.raster_dataset_id")" if [ -z "${raster_dataset_id}" ] || [ "${raster_dataset_id}" = "None" ] || [ "${raster_dataset_id}" = "null" ]; then echo "Demo workflow did not return a raster_dataset_id" >&2 exit 1 fi curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets" > "${TMP_DIR}/datasets.json" require_json_data "${TMP_DIR}/datasets.json" "${PYTHON_BIN}" - "${TMP_DIR}/datasets.json" "${raster_dataset_id}" <<'PY' import json import sys path, raster_id = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: items = json.load(handle)["data"]["items"] raster = next((item for item in items if item["id"] == raster_id), None) if not raster: raise SystemExit("Demo raster dataset is missing from dataset list") if raster.get("name") != "demo_context_raster.tif": raise SystemExit(f"Unexpected demo raster name: {raster.get('name')}") if raster.get("dataset_type") != "raster": raise SystemExit(f"Unexpected demo raster dataset_type: {raster.get('dataset_type')}") if raster.get("source_name") != "fixture": raise SystemExit(f"Unexpected demo raster source_name: {raster.get('source_name')}") if raster.get("status") != "ready": raise SystemExit(f"Unexpected demo raster status: {raster.get('status')}") if raster.get("crs") != "EPSG:4326": raise SystemExit(f"Unexpected demo raster CRS: {raster.get('crs')}") PY curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets/${raster_dataset_id}/raster/inspect" > "${TMP_DIR}/inspect.json" require_json_data "${TMP_DIR}/inspect.json" "${PYTHON_BIN}" - "${TMP_DIR}/inspect.json" "${raster_dataset_id}" <<'PY' import json import sys path, raster_id = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: data = json.load(handle)["data"] if data.get("dataset_id") != raster_id: raise SystemExit("Raster inspect returned the wrong dataset_id") if data.get("ready") is not True: raise SystemExit("Raster inspect did not report ready=true") metadata = data.get("metadata") or {} if metadata.get("crs") != "EPSG:4326": raise SystemExit(f"Raster inspect metadata CRS drifted: {metadata.get('crs')}") if int(metadata.get("width") or 0) < 1 or int(metadata.get("height") or 0) < 1: raise SystemExit("Raster inspect metadata does not expose width/height") PY curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets/${raster_dataset_id}/raster/preview" > "${TMP_DIR}/preview.json" require_json_data "${TMP_DIR}/preview.json" "${PYTHON_BIN}" - "${TMP_DIR}/preview.json" <<'PY' import json import sys with open(sys.argv[1], "r", encoding="utf-8") as handle: data = json.load(handle)["data"] if data.get("ready") is not True: raise SystemExit("Raster preview did not report ready=true") preview = data.get("preview") or {} if str(preview.get("format") or "").lower() != "png": raise SystemExit(f"Unexpected preview format: {preview.get('format')}") if int(preview.get("width") or 0) < 1 or int(preview.get("height") or 0) < 1: raise SystemExit("Raster preview did not expose image dimensions") PY curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/datasets/${raster_dataset_id}/raster/stats" > "${TMP_DIR}/stats.json" require_json_data "${TMP_DIR}/stats.json" "${PYTHON_BIN}" - "${TMP_DIR}/stats.json" "${raster_dataset_id}" <<'PY' import json import sys path, raster_id = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: data = json.load(handle)["data"] if data.get("dataset_id") != raster_id: raise SystemExit("Raster stats returned the wrong dataset_id") bands = data.get("bands") or [] if not bands: raise SystemExit("Raster stats did not report band statistics") band = bands[0] if band.get("valid_pixel_count", 0) < 1: raise SystemExit("Raster stats valid_pixel_count is empty") if band.get("min") is None or band.get("max") is None: raise SystemExit("Raster stats min/max are missing") PY curl -fsS -X POST "${BASE_URL%/}/api/v1/projects/${project_id}/datasets/${raster_dataset_id}/raster/tile" \ -H "Content-Type: application/json" \ -d '{"tile_size":64,"overlap":0,"output_name":"demo_raster_smoke_tiles"}' > "${TMP_DIR}/tile.json" require_json_data "${TMP_DIR}/tile.json" "${PYTHON_BIN}" - "${TMP_DIR}/tile.json" "${raster_dataset_id}" <<'PY' import json import sys path, raster_id = sys.argv[1], sys.argv[2] with open(path, "r", encoding="utf-8") as handle: job = json.load(handle)["data"] if job.get("job_type") != "raster.tile": raise SystemExit(f"Unexpected tile job_type: {job.get('job_type')}") if job.get("status") not in {"success", "completed"}: raise SystemExit(f"Raster tile job did not complete: {job.get('status')}") result = job.get("result_json") or {} if result.get("dataset_id") != raster_id: raise SystemExit("Raster tile result returned the wrong dataset_id") if result.get("ready") is not True: raise SystemExit("Raster tile result did not report ready=true") if result.get("operation") != "raster.tile": raise SystemExit(f"Unexpected raster tile operation: {result.get('operation')}") if not result.get("manifest_path"): raise SystemExit("Raster tile result is missing manifest_path") manifest = result.get("manifest") or {} tile_paths = manifest.get("tile_paths") or [] tiles = manifest.get("tiles") or [] if result.get("count") != len(tile_paths) or len(tile_paths) != len(tiles): raise SystemExit("Raster tile manifest count does not match tile_paths/tiles") if not tile_paths: raise SystemExit("Raster tile manifest did not include any tile paths") if manifest.get("source_dataset_id") != raster_id: raise SystemExit("Raster tile manifest source_dataset_id drifted") PY echo "Demo raster workflow verification passed" echo "Project: ${project_id}" echo "Raster dataset: ${raster_dataset_id}" echo "Operations: inspect, preview, stats and raster.tile"