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.
144 lines
8.3 KiB
Bash
Executable File
144 lines
8.3 KiB
Bash
Executable File
#!/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 ]
|