#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
python3 scripts/test_capy_doc_generators.py
python3 scripts/check_capy_doc_links.py --self-test
python3 scripts/build_site_highlighter.py --check
node scripts/test_site_highlighter.js
python3 scripts/test_doc_example_results.py
python3 scripts/test_run_doc_examples.py
python3 scripts/test_doc_example_contexts.py
python3 scripts/run_doc_examples.py --check
host="${BEARER_TEST_HTTP_HOST:-bearer.openfu.com}"
base="${BEARER_TEST_HTTP_BASE:-http://127.0.0.1}/examples/capy-reference"
reference_dir=site/examples/capy-reference
[[ -f "$reference_dir/index.capy" && -f "$reference_dir/style.css" ]]
pages=(index syntax types ownership interop status)
targets=(install-and-first-program source-structure-and-syntax values-and-types functions-and-closures units-components-and-exports errors-testing-and-style)
for index in "${!pages[@]}"; do
path="$base/${pages[$index]}.capy"
headers=$(curl -fsS --max-time 30 -o /dev/null -D - -H "Host: $host" "$path" | tr -d '\r')
[[ "$headers" == *"HTTP/1.1 302"* && "$headers" == *"Location: /doc/guide/${targets[$index]}/"* ]] || { echo "Capy reference redirect mismatch for $path" >&2; exit 1; }
done
doc_base="${BEARER_TEST_HTTP_BASE:-http://127.0.0.1}/doc/"
headers=$(curl -fsS --max-time 30 -o /dev/null -D - -H "Host: $host" "${doc_base}type/dvalue/" | tr -d "\r")
[[ "$headers" == *"HTTP/1.1 301"* && "$headers" == *"Location: /doc/type/any/"* ]] || { echo "Dynamic value documentation redirect failed" >&2; exit 1; }
python3 - <<'PY'
from pathlib import Path
root = Path.cwd()
if (root / "site/doc/components/doc_page.capy").exists():
raise SystemExit("Documentation retains the dynamic detail component")
if 'component_render("/doc/components/doc_' in (root / "scripts/generate_capy_docs.py").read_text():
raise SystemExit("Documentation generator retains dynamic detail components")
for path in (root / "site/doc/content").glob("*/*.capy"):
lines = [line.strip() for line in path.read_text().splitlines() if line.strip()]
if len(lines) < 3 or lines[0] != "function COMPONENT(props : any)" or lines[1] != "{" or lines[-1] != "}":
raise SystemExit(f"Documentation route has an unexpected shape: {path}")
if any(not line.startswith("print(") for line in lines[2:-1]):
raise SystemExit(f"Documentation route does not print static detail HTML directly: {path}")
for component in (root / "site/doc/components/index.capy", root / "site/doc/components/all.capy"):
text = component.read_text()
for heading in ("API Functions", "Types", "Handlers", "How-to", "Learn Capy"):
if f">{heading}<" not in text:
raise SystemExit(f"Documentation listing lacks {heading}: {component}")
if "Clear a dynamic value" in text:
raise SystemExit(f"Documentation listing uses an API prose title: {component}")
search = (root / "site/doc/components/search.capy").read_text()
if 'class=\\\"search-kind\\\"' not in search:
raise SystemExit("Documentation search does not label result kinds")
PY
status() { curl -sS --max-time 60 -o /dev/null -w '%{http_code}' -H "Host: $host" "$1"; }
for path in api/zzzznotreal/ how-to/zzzznotreal/ api/component-exists/ api/component-2/ api/string-2/ '?''p=0''_String'; do
[[ "$(status "$doc_base$path")" == 404 ]] || { echo "Old or unknown documentation URL did not return 404: $path" >&2; exit 1; }
done
for path in api/component/ api/string/ type/string/ type/any/ type/request/ handler/render/ handler/component/ handler/cli/; do
[[ "$(status "$doc_base$path")" == 200 ]] || { echo "Canonical documentation URL did not return 200: $path" >&2; exit 1; }
done
python3 - "$doc_base" "$host" <<'PY'
import html
from pathlib import Path
import re
import sys
import urllib.request
base, host = sys.argv[1:]
for source in sorted(Path("site/doc/source/how-to").glob("*.txt")):
title = re.search(r"(?m)^:title\n([^\n]+)", source.read_text())
title = title[1] if title else source.stem
title = html.escape(title, quote=False).replace('"', """)
url = base + "how-to/" + source.stem + "/"
request = urllib.request.Request(url, headers={"Host": host})
with urllib.request.urlopen(request, timeout=60) as response:
body = response.read().decode()
if ">" + title + "" not in body:
raise SystemExit(f"How-to page did not render its title: {url}")
print("All how-to documentation pages rendered")
PY
component_page=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}api/component/")
[[ "$component_page" == *">component"* ]] || { echo "Component documentation page did not render" >&2; exit 1; }
route_gate="site/doc/content/how-to/route-file-gate.capy"
trap 'rm -f "$route_gate"' EXIT
cat >"$route_gate" <<'EOF'
function COMPONENT(props : any) {
print("