Files
rose-ash/lib/gitea/conformance.sh
giles 83a8a2f8db sx-gitea Phase 3: wire — smart-HTTP protocol over native CIDs (TDD, 272/272)
lib/gitea/wire.sx: git-style pkt-line framing (byte-compatible hex4
lengths + flush sections); object closure walker (commits/trees/blobs/
tags) with missing-object detection; wants/haves pack negotiation.
Objects travel as '<cid> <serialized-sx>' pkt lines — receivers re-derive
the CID from the bytes, so packs are tamper-evident by construction.

Server endpoints: GET info/refs (read-gated advertisement incl. '@ HEAD'
symref line), POST git-upload-pack (read), POST git-receive-pack (write;
401/403/404 like the rest of the API) with per-ref command application:
create/update/delete via ref-CAS, fast-forward enforcement on heads/*,
closure-completeness check, stale detection, heads|tags-only.

Client: gitea/remote over any dream app fn — ls-remote, clone! (sets
HEAD + default-branch, cleans up on unreachable remote), mirror fetch!,
push!/push-delete! with local pack computation. Suite syncs two
in-memory forges end to end: clone, incremental fetch, push, non-ff
rejection + recovery, branch create/delete, tag push, private-repo
credentialed round trip.

sx-parse comes from spec/parser.sx on the OCaml server host — added to
the conformance load order. Also merged loops/git (git-wire export/
import adapters, 267/267) for future stock-git interop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:35:40 +00:00

169 lines
4.6 KiB
Bash

#!/usr/bin/env bash
# lib/gitea/conformance.sh — run sx-gitea test suites, emit scoreboard.json + scoreboard.md.
#
# Usage:
# bash lib/gitea/conformance.sh # run all suites
# bash lib/gitea/conformance.sh -v # also print failure details
set -uo pipefail
cd "$(git rev-parse --show-toplevel)"
SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
if [ ! -x "$SX_SERVER" ]; then
SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
fi
if [ ! -x "$SX_SERVER" ]; then
echo "ERROR: sx_server.exe not found." >&2
exit 1
fi
VERBOSE="${1:-}"
# suite name | pass counter | fail counter | failures list
SUITES=(
"repo|gitea-repo-pass|gitea-repo-fail|gitea-repo-fails"
"access|gitea-access-pass|gitea-access-fail|gitea-access-fails"
"wire|gitea-wire-pass|gitea-wire-fail|gitea-wire-fails"
)
OUT_JSON="lib/gitea/scoreboard.json"
OUT_MD="lib/gitea/scoreboard.md"
# Library load order: kernel stdlib, persist, artdag canon, sx-git, dream
# (types/router/middleware/error/html/json/api), then the gitea modules.
MODULES=(
"spec/stdlib.sx"
"spec/parser.sx"
"lib/r7rs.sx"
"lib/persist/event.sx"
"lib/persist/backend.sx"
"lib/persist/log.sx"
"lib/persist/kv.sx"
"lib/artdag/dag.sx"
"lib/git/object.sx"
"lib/git/ref.sx"
"lib/git/dag.sx"
"lib/git/worktree.sx"
"lib/git/diff.sx"
"lib/git/merge.sx"
"lib/git/porcelain.sx"
"lib/datalog/tokenizer.sx"
"lib/datalog/parser.sx"
"lib/datalog/unify.sx"
"lib/datalog/db.sx"
"lib/datalog/builtins.sx"
"lib/datalog/aggregates.sx"
"lib/datalog/strata.sx"
"lib/datalog/eval.sx"
"lib/datalog/api.sx"
"lib/datalog/magic.sx"
"lib/acl/schema.sx"
"lib/acl/facts.sx"
"lib/acl/engine.sx"
"lib/dream/types.sx"
"lib/dream/router.sx"
"lib/dream/middleware.sx"
"lib/dream/error.sx"
"lib/dream/html.sx"
"lib/dream/json.sx"
"lib/dream/auth.sx"
"lib/dream/api.sx"
"lib/gitea/repo.sx"
"lib/gitea/access.sx"
"lib/gitea/web.sx"
"lib/gitea/wire.sx"
)
run_suite() {
local suite=$1 passvar=$2 failvar=$3 failsvar=$4
local file="lib/gitea/tests/${suite}.sx"
local TMP
TMP=$(mktemp)
{
echo "(epoch 1)"
for M in "${MODULES[@]}"; do echo "(load \"$M\")"; done
echo "(epoch 2)"
echo "(load \"${file}\")"
echo "(epoch 3)"
echo "(eval \"(list ${passvar} ${failvar})\")"
echo "(epoch 4)"
echo "(eval \"(inspect ${failsvar})\")"
} > "$TMP"
local OUTPUT
OUTPUT=$(timeout 600 "$SX_SERVER" < "$TMP" 2>/dev/null)
rm -f "$TMP"
local LINE
LINE=$(echo "$OUTPUT" | awk '/^\(ok-len 3 / {getline; print; exit}')
if [ -z "$LINE" ]; then
LINE=$(echo "$OUTPUT" | grep -E '^\(ok 3 \([0-9]+ [0-9]+\)\)' | tail -1 \
| sed -E 's/^\(ok 3 //; s/\)$//')
fi
local P F
P=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\1/')
F=$(echo "$LINE" | sed -E 's/^\(([0-9]+) ([0-9]+)\).*/\2/')
P=${P:-0}
F=${F:-0}
if [ -n "$VERBOSE" ] && [ "$F" != "0" ]; then
echo " --- ${suite} failures ---" >&2
echo "$OUTPUT" | awk '/^\(ok(-len)? 4 /,0' | head -40 >&2
fi
echo "${P} ${F}"
}
declare -A SUITE_PASS
declare -A SUITE_FAIL
TOTAL_PASS=0
TOTAL_FAIL=0
echo "Running sx-gitea conformance suite..." >&2
for entry in "${SUITES[@]}"; do
IFS='|' read -r s passvar failvar failsvar <<< "$entry"
read -r p f < <(run_suite "$s" "$passvar" "$failvar" "$failsvar")
SUITE_PASS[$s]=$p
SUITE_FAIL[$s]=$f
TOTAL_PASS=$((TOTAL_PASS + p))
TOTAL_FAIL=$((TOTAL_FAIL + f))
printf " %-12s %d/%d\n" "$s" "$p" "$((p+f))" >&2
done
{
printf '{\n'
printf ' "suites": {\n'
first=1
for entry in "${SUITES[@]}"; do
IFS='|' read -r s _ _ _ <<< "$entry"
if [ $first -eq 0 ]; then printf ',\n'; fi
printf ' "%s": {"pass": %d, "fail": %d}' "$s" "${SUITE_PASS[$s]}" "${SUITE_FAIL[$s]}"
first=0
done
printf '\n },\n'
printf ' "total_pass": %d,\n' "$TOTAL_PASS"
printf ' "total_fail": %d,\n' "$TOTAL_FAIL"
printf ' "total": %d\n' "$((TOTAL_PASS + TOTAL_FAIL))"
printf '}\n'
} > "$OUT_JSON"
{
printf '# sx-gitea Conformance Scoreboard\n\n'
printf '_Generated by `lib/gitea/conformance.sh`_\n\n'
printf '| Suite | Pass | Fail | Total |\n'
printf '|-------|-----:|-----:|------:|\n'
for entry in "${SUITES[@]}"; do
IFS='|' read -r s _ _ _ <<< "$entry"
p=${SUITE_PASS[$s]}
f=${SUITE_FAIL[$s]}
printf '| %s | %d | %d | %d |\n' "$s" "$p" "$f" "$((p+f))"
done
printf '| **Total** | **%d** | **%d** | **%d** |\n' "$TOTAL_PASS" "$TOTAL_FAIL" "$((TOTAL_PASS + TOTAL_FAIL))"
} > "$OUT_MD"
echo "Wrote $OUT_JSON and $OUT_MD" >&2
echo "Total: $TOTAL_PASS pass, $TOTAL_FAIL fail" >&2
[ "$TOTAL_FAIL" -eq 0 ]