Add workbench default state smoke
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="${1:-${GE_INTEL_BASE_URL:-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 workbench default-state 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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
echo "== GeoIntel workbench default-state verification =="
|
||||
echo "Base URL: ${BASE_URL}"
|
||||
|
||||
frontend_status="$(curl -fsS -o "${TMP_DIR}/index.html" -w "%{http_code}" "${BASE_URL%/}" 2>/dev/null || true)"
|
||||
if [ "${frontend_status}" != "200" ]; then
|
||||
echo "Frontend returned HTTP ${frontend_status:-none}, expected 200" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q '<div id="root"' "${TMP_DIR}/index.html"; then
|
||||
echo "Frontend shell does not expose the React root node" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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")"
|
||||
area_id="$(json_field "${TMP_DIR}/demo.json" "data.area_id")"
|
||||
candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")"
|
||||
reference_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.reference_dataset_id")"
|
||||
quality_check_id="$(json_field "${TMP_DIR}/demo.json" "data.quality_check_id")"
|
||||
|
||||
curl -fsS "${BASE_URL%/}/api/v1/projects" > "${TMP_DIR}/projects.json"
|
||||
require_json_data "${TMP_DIR}/projects.json"
|
||||
# The workbench lists below must all use canonical data.items envelopes.
|
||||
"${PYTHON_BIN}" - "${TMP_DIR}/projects.json" "${project_id}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path, project_id = sys.argv[1], sys.argv[2]
|
||||
with open(path, "r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
items = payload["data"]["items"]
|
||||
matches = [item for item in items if item["id"] == project_id]
|
||||
if not matches:
|
||||
raise SystemExit("Seeded demo project is missing from project list")
|
||||
project = matches[0]
|
||||
if project.get("name") != "GeoIntel Demo - Building QA":
|
||||
raise SystemExit(f"Unexpected default demo project name: {project.get('name')}")
|
||||
PY
|
||||
|
||||
curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/areas" > "${TMP_DIR}/areas.json"
|
||||
require_json_data "${TMP_DIR}/areas.json"
|
||||
"${PYTHON_BIN}" - "${TMP_DIR}/areas.json" "${area_id}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path, area_id = sys.argv[1], sys.argv[2]
|
||||
with open(path, "r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
items = payload["data"]["items"]
|
||||
matches = [item for item in items if item["id"] == area_id]
|
||||
if not matches:
|
||||
raise SystemExit("Seeded demo AOI is missing from project area list")
|
||||
area = matches[0]
|
||||
if area.get("name") != "Demo AOI - Geel buildings":
|
||||
raise SystemExit(f"Unexpected demo AOI name: {area.get('name')}")
|
||||
geometry = area.get("geometry")
|
||||
if not geometry or geometry.get("type") not in {"Polygon", "MultiPolygon"}:
|
||||
raise SystemExit("Demo AOI does not expose map-ready GeoJSON geometry")
|
||||
PY
|
||||
|
||||
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" "${candidate_dataset_id}" "${reference_dataset_id}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path, candidate_id, reference_id = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
with open(path, "r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
items = payload["data"]["items"]
|
||||
ids = {item["id"]: item for item in items}
|
||||
if candidate_id not in ids:
|
||||
raise SystemExit("Candidate fixture dataset is missing from default workbench dataset list")
|
||||
if reference_id not in ids:
|
||||
raise SystemExit("Reference fixture dataset is missing from default workbench dataset list")
|
||||
if ids[candidate_id].get("status") != "ready" or ids[reference_id].get("status") != "ready":
|
||||
raise SystemExit("Expected 2/2 ready demo datasets")
|
||||
if ids[reference_id].get("dataset_role") != "reference":
|
||||
raise SystemExit("Default workbench reference dataset is not marked as reference")
|
||||
PY
|
||||
|
||||
curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/quality-checks" > "${TMP_DIR}/quality_checks.json"
|
||||
require_json_data "${TMP_DIR}/quality_checks.json"
|
||||
"${PYTHON_BIN}" - "${TMP_DIR}/quality_checks.json" "${quality_check_id}" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path, quality_check_id = sys.argv[1], sys.argv[2]
|
||||
with open(path, "r", encoding="utf-8") as handle:
|
||||
payload = json.load(handle)
|
||||
items = payload["data"]["items"]
|
||||
matches = [item for item in items if item["id"] == quality_check_id]
|
||||
if not matches:
|
||||
raise SystemExit("Seeded QA/QC check is missing from default workbench quality list")
|
||||
quality_check = matches[0]
|
||||
if quality_check.get("status") != "ok":
|
||||
raise SystemExit(f"Unexpected default workbench QA/QC status: {quality_check.get('status')}")
|
||||
metrics = {metric["metric_key"]: metric.get("metric_value") for metric in quality_check.get("metrics", [])}
|
||||
for key in ("precision", "recall", "f1", "mean_iou"):
|
||||
if key not in metrics:
|
||||
raise SystemExit(f"Default workbench QA/QC metric is missing: {key}")
|
||||
PY
|
||||
|
||||
echo "Workbench default state verification passed"
|
||||
echo "Project: GeoIntel Demo - Building QA"
|
||||
echo "Area: Demo AOI - Geel buildings"
|
||||
echo "Datasets: 2/2 ready"
|
||||
echo "QA/QC: seeded check is available"
|
||||
Reference in New Issue
Block a user