Private
Public Access
Keep the compile-time generic marker as *. Update the compiler, stdlib, fixtures, documentation and editor grammars without a dynamic type alias. Preserve native identifiers and historical work and patch records. Replace the port_spawn environment argument with cwd/env options. Report failed directory changes through EXIT 127 with cwd_failed. Compile every API example and add a complete Pi RPC service example. Service examples compile without execution or output blocks. Update ABI versions and audited golden artifacts.
118 lines
7.9 KiB
Bash
Executable File
118 lines
7.9 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/"
|
|
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 + "</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 : any) {
|
|
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 : any = {}) 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/any/'* && "$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"
|