Private
Public Access
116 lines
7.7 KiB
Bash
Executable File
116 lines
7.7 KiB
Bash
Executable File
#!/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_capy_api_outputs.py --self-test
|
|
python3 scripts/test_capy_api_outputs.py --verify
|
|
|
|
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/"
|
|
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 : dval)" 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/dvalue/ 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 + "</h2>" 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</h2>"* ]] || { 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 : dval) {
|
|
print("<main>route file gate</main>")
|
|
}
|
|
EOF
|
|
route_added=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}how-to/route-file-gate/")
|
|
[[ "$route_added" == *"route file gate"* ]] || { echo "A documentation page file did not reach its route" >&2; exit 1; }
|
|
rm -f "$route_gate"
|
|
[[ "$(status "${doc_base}how-to/route-file-gate/")" == 404 ]] || { echo "A deleted documentation page file did not return 404" >&2; exit 1; }
|
|
|
|
guide=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}guide/dynamic-values/")
|
|
[[ "$guide" == *"8. Dynamic values"* && "$guide" == *"One object, many names"* && "$guide" == *'class="guide-navigation"'* ]] || { echo "Capy guide page omitted content or navigation" >&2; exit 1; }
|
|
constructor=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}api/string/")
|
|
[[ "$constructor" == *"function string(value : as string, opt : dval = {}) string"* && "$constructor" != *"DOC EXAMPLE ERROR"* ]] || { echo "Capy constructor API page omitted its signature" >&2; exit 1; }
|
|
contract=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}api/http-request/")
|
|
[[ "$contract" == *'<h3>Description</h3>'* && "$contract" == *'class="related-link" href="/doc/api/socket-connect/">socket_connect</a>'* ]] || { echo "Structured API contract sections or related links are incomplete" >&2; exit 1; }
|
|
index=$(curl -fsS --max-time 60 -H "Host: $host" "$doc_base")
|
|
[[ "$index" != *'>String()<'* && "$index" != *'>COMPONENT()<'* && "$index" != *'>CLI()<'* ]] || { echo "Type or handler index entry rendered as a function" >&2; exit 1; }
|
|
[[ "$index" != *"route-file-gate"* ]] || { echo "Documentation index retained a deleted page" >&2; exit 1; }
|
|
singlepage=$(curl -fsS --max-time 180 -H "Host: $host" "${doc_base}all/")
|
|
[[ "$singlepage" == *'/doc/guide/install-and-first-program/'* && "$singlepage" == *'/doc/how-to/read-request-context/'* && "$singlepage" == *'/doc/type/dvalue/'* && "$singlepage" == *'/doc/api/http-request/'* && "$singlepage" == *'>How-to</h3>'* ]] || { echo "Combined documentation route list omitted canonical links or the how-to section" >&2; exit 1; }
|
|
search=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}search/?q=http_request")
|
|
[[ "$search" == *'/doc/api/http-request/'* && "$search" == *'http_request'* ]] || { echo "Documentation search omitted a structured API page" >&2; exit 1; }
|
|
how_to_search=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}search/?q=request")
|
|
[[ "$how_to_search" == *'/doc/how-to/read-request-context/'* && "$how_to_search" == *'search-how-to'* && "$how_to_search" == *'>How-to</h3>'* ]] || { echo "Documentation search omitted the how-to section" >&2; exit 1; }
|
|
landing=$(curl -fsS --max-time 60 -H "Host: $host" "${BEARER_TEST_HTTP_BASE:-http://127.0.0.1}/info/")
|
|
all_wrapper=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}all/index.capy")
|
|
search_wrapper=$(curl -fsS --max-time 60 -H "Host: $host" "${doc_base}search/index.capy")
|
|
for page in landing index component_page singlepage search all_wrapper search_wrapper; do
|
|
[[ "${!page}" == *'rel="icon" type="image/png" href="/favicon.png"'* ]] || { echo "$page omitted the favicon" >&2; exit 1; }
|
|
done
|
|
curl -fsS --max-time 30 -H "Host: $host" "${BEARER_TEST_HTTP_BASE:-http://127.0.0.1}/favicon.png" | cmp - site/favicon.png
|
|
scripts/test_capy_guide_examples.sh
|
|
echo "Capy reference pages and canonical documentation routes passed"
|