Files
stack/scripts/test-foundation.sh
jason.woltje 8ebddd6f93 feat(foundation): offline synthetic scope/permission inspector (FI-FILBERT-8 APPROVED r6)
Rocko-authored, Filbert-reviewed inspector (r6 manifest
a4a44930...) with full review/build/verdict evidence under
docs/plans/reviews. 43/0 selftests, oracle zero-disagreement,
foundation checker PASS. Owner A9 acceptance recorded separately.
2026-09-07 14:06:35 -05:00

144 lines
8.3 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Offline selftests for the foundation synthetic inspector (charter candidate 3).
#
# No Docker, no network, no credentials, no live data: every check runs against
# checked-in synthetic fixtures or a disposable sandbox. Tests spawn the inspector
# as a subprocess; the inspector itself never spawns anything.
#
# scripts/test-foundation.sh full run
# NO_COLOR=1 scripts/test-foundation.sh plain output
set -uo pipefail
cd "$(dirname "$0")/.."
SANDBOX="$(mktemp -d)"
trap 'rm -rf "$SANDBOX"' EXIT
PASS=0
# Status colors: terminal-only, NO_COLOR-respecting; plain when piped.
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
C_OK=$'\033[0;32m'; C_FAIL=$'\033[0;31m'; C_RESET=$'\033[0m'
else
C_OK=""; C_FAIL=""; C_RESET=""
fi
FAIL=0
check() {
if [ "$2" = "0" ]; then PASS=$((PASS+1)); echo "${C_OK}OK${C_RESET} $1"; else FAIL=$((FAIL+1)); echo "${C_FAIL}FAIL${C_RESET} $1"; fi
}
# expect_exit NAME EXPECTED_RC -- command...
expect_exit() {
local name="$1" expected="$2"
shift 3 # name, expected, "--"
local rc
"$@" >/dev/null 2>&1
rc=$?
if [ "$rc" -eq "$expected" ]; then
PASS=$((PASS + 1))
echo "${C_OK}OK${C_RESET} $name (exit $rc)"
else
FAIL=$((FAIL + 1))
echo "${C_FAIL}FAIL${C_RESET} $name (exit $rc, expected $expected)"
fi
}
INSPECT="node scripts/foundation-inspect.mjs"
FIXTURES="scripts/foundation/fixtures"
DEMO="$FIXTURES/demo"
echo "toolchain: node $(node --version), python $(python3 --version 2>&1 | cut -d' ' -f2), jsonschema $(python3 -c 'import importlib.metadata as m; print(m.version("jsonschema"))' 2>/dev/null || echo missing)"
echo
# --- syntax ---
for f in scripts/foundation-inspect.mjs scripts/foundation/strict-json.mjs scripts/foundation/canonical.mjs \
scripts/foundation/resolve.mjs scripts/foundation/validate-record.mjs scripts/foundation/fixtures/build-fixtures.mjs \
scripts/foundation/*.test.mjs; do
node --check "$f" >/dev/null 2>&1
check "syntax: $f" $?
done
python3 -c 'import ast, sys; ast.parse(open(sys.argv[1], encoding="utf-8").read(), sys.argv[1])' scripts/foundation/verify-schema.py >/dev/null 2>&1
check "syntax: scripts/foundation/verify-schema.py (ast only; no bytecode written)" $?
# --- fixtures are exactly what the generator produces ---
node scripts/foundation/fixtures/build-fixtures.mjs "$SANDBOX/fx" >/dev/null 2>&1
check "fixture generator runs" $?
for d in bundles raw index.json; do
diff -r "$SANDBOX/fx/$d" "$FIXTURES/$d" >/dev/null 2>&1
check "checked-in fixtures/$d equal a fresh generation" $?
done
DEMO_OK=0
for f in "$SANDBOX"/fx/demo/*.json; do
cmp -s "$f" "$DEMO/$(basename "$f")" || DEMO_OK=1
done
check "checked-in demo bundles equal a fresh generation" $DEMO_OK
# --- unit, CLI, privacy, non-effect and fixture-index tests ---
node --test scripts/foundation/ >"$SANDBOX/node-test.log" 2>&1
NODE_RC=$?
check "node --test scripts/foundation/ ($(grep -E '^ pass' "$SANDBOX/node-test.log" | tr -d '\n' || echo 'summary missing'))" $NODE_RC
[ "$NODE_RC" -ne 0 ] && grep -E "^✖|AssertionError" "$SANDBOX/node-test.log" | head -20
# --- differential schema oracle (pinned hashes; explicit jsonschema 4.26.0; never installs or skips) ---
python3 scripts/foundation/verify-schema.py >"$SANDBOX/oracle.log" 2>&1
ORACLE_RC=$?
check "differential schema oracle: $(tail -1 "$SANDBOX/oracle.log")" $ORACLE_RC
grep -E "^(platform witness|node|schema column|profile column|profile refusals)" "$SANDBOX/oracle.log" | sed 's/^/ /'
grep -qE "^schema column: .*DISAGREEMENTS 0;" "$SANDBOX/oracle.log"
check "oracle: zero schema-column disagreements with the pinned checker" $?
grep -qE "^profile refusals asserted: [1-9][0-9]* " "$SANDBOX/oracle.log"
check "oracle: strict-only profile refusals are counted and asserted" $?
[ "$ORACLE_RC" -ne 0 ] && grep -E "problem|FAIL" "$SANDBOX/oracle.log" | head -20
# --- owner demo paths and exit codes (shell-level receipts) ---
expect_exit "demo: permitted read preview exits 0" 0 -- $INSPECT "$DEMO/demo-read-w1.json"
expect_exit "demo: permitted file.change preview exits 0" 0 -- $INSPECT "$DEMO/demo-file-change-src.json"
expect_exit "demo: assignment.change proposal is unresolved (exit 3)" 3 -- $INSPECT "$DEMO/demo-change-w1.json"
expect_exit "demo: revoked registration is refused (exit 3)" 3 -- $INSPECT "$DEMO/adm-registration-revoked.json"
expect_exit "demo: message is not authority (exit 3)" 3 -- $INSPECT "$DEMO/prop-message-is-not-authority.json"
expect_exit "usage: no arguments exits 2" 2 -- $INSPECT
expect_exit "io: missing file exits 4" 4 -- $INSPECT "$SANDBOX/does-not-exist.json"
expect_exit "io: directory exits 4" 4 -- $INSPECT "$SANDBOX"
ln -s "$DEMO/demo-read-w1.json" "$SANDBOX/link.json"
expect_exit "io: symlink exits 4 (O_NOFOLLOW)" 4 -- $INSPECT "$SANDBOX/link.json"
expect_exit "bound: oversize fixture exits 2" 2 -- $INSPECT "$FIXTURES/raw/raw-oversize-file.json"
expect_exit "profile: one final LF in a typed selection id is refused before admission (exit 2)" 2 -- $INSPECT "$FIXTURES/bundles/profile-selection-agent-id-one-final-lf.json"
expect_exit "profile: two final LFs fail the schema pattern itself (exit 2)" 2 -- $INSPECT "$FIXTURES/bundles/profile-id-record-id-two-final-lf.json"
expect_exit "profile: escaped newlines in free-form text stay allowed (exit 0)" 0 -- $INSPECT "$FIXTURES/bundles/profile-escaped-newline-free-text-allowed.json"
# (captured to a file first: under pipefail the inspector's own exit 2 would mask the python verdict)
$INSPECT --json "$FIXTURES/bundles/profile-selection-agent-id-one-final-lf.json" > "$SANDBOX/profile-refusal.json" 2>/dev/null
python3 -c 'import json,sys; r=json.load(open(sys.argv[1])); sys.exit(0 if r["result"]=="invalid" and r["reason"]=="invalid-request" and r["rule"]=="profile-pattern-mismatch" and r["selection"] is None and r["operation"] is None and "agent-a" not in json.dumps(r) else 1)' "$SANDBOX/profile-refusal.json"
check "profile refusal is invalid-request/profile-pattern-mismatch with selection and operation withheld, value not echoed" $?
$INSPECT "$DEMO/demo-read-w1.json" | head -1 | grep -q '^SYNTHETIC PREVIEW — NO LIVE EFFECTS$'
check "text output starts with the disclaimer" $?
$INSPECT --json "$DEMO/demo-read-w1.json" | python3 -c 'import json,sys; r=json.load(sys.stdin); sys.exit(0 if r["result"]=="allowed" and r["rule"] is None and "exit" not in r and list(r)==["disclaimer","preview","bundleVersion","authentication","declarations","result","reason","rule","selection","operation","proposal","diagnostic"] else 1)'
check "json output is valid JSON with result allowed and exactly the charter §7 fields" $?
$INSPECT --json "$DEMO/demo-read-w1.json" | cmp -s - "$DEMO/demo-read-w1.expected.json"
check "json golden matches byte-for-byte" $?
# --- non-effect: sandbox inventory unchanged across runs; canaries never printed ---
mkdir -p "$SANDBOX/home/.mosaic" "$SANDBOX/cwd"
printf '{"token":"CANARY-DO-NOT-PRINT-shell"}\n' > "$SANDBOX/home/.mosaic/credentials.json"
cp "$DEMO/demo-change-w1.json" "$SANDBOX/cwd/bundle.json"
inventory() { (cd "$SANDBOX" && find . -path ./node-test.log -prune -o -path ./oracle.log -prune -o -print0 | sort -z | xargs -0 stat -c '%n %F %s %a %u %g %i %Y' ; find . -type f ! -name '*.log' -print0 | sort -z | xargs -0 sha256sum); }
inventory > "$SANDBOX.before"
REPO="$PWD"; NODE_BIN="$(command -v node)"
OUT="$(cd "$SANDBOX/cwd" && env -i HOME="$SANDBOX/home" PATH=/nonexistent "$NODE_BIN" "$REPO/scripts/foundation-inspect.mjs" bundle.json 2>&1; \
env -i HOME="$SANDBOX/home" PATH=/nonexistent "$NODE_BIN" "$REPO/scripts/foundation-inspect.mjs" --json "$SANDBOX/home/.mosaic/credentials.json" 2>&1)"
printf '%s' "$OUT" | grep -q '^result: unresolved$'
check "sandboxed bundle run (env -i, PATH=/nonexistent) produced the unresolved proposal" $?
inventory > "$SANDBOX.after"
cmp -s "$SANDBOX.before" "$SANDBOX.after"
check "sandbox inventory (path/type/size/mode/uid/gid/inode/mtime/sha256) unchanged by runs" $?
rm -f "$SANDBOX.before" "$SANDBOX.after"
! printf '%s' "$OUT" | grep -q "CANARY-DO-NOT-PRINT"
check "canary never printed (bundle run and credential-file run)" $?
printf '%s' "$OUT" | grep -q '"rule": "shape-'
check "a non-bundle JSON file is refused at the shape gate, not read into output" $?
! printf '%s' "$OUT" | grep -q '"token"'
check "no field of the non-bundle file is echoed" $?
echo
echo "selftest: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]