test(adr-077): dispatch invariants + ci-full wiring
bond-dispatch-test.sh asserts the two-path gate: bond-only activates, bare refuses, a repo_kind project activates identically with/without a carrier (the Hard bond-path-is-purely-additive invariant), and a bond rescues a non-matching repo_kind. Wired into ci-full via ci-bond-dispatch (skips when no data dir, same pattern as the smoke tests).
This commit is contained in:
parent
e22a66dee8
commit
a163e0d23b
2 changed files with 139 additions and 1 deletions
|
|
@ -34,7 +34,7 @@ help:
|
|||
@echo " just clean - Clean build artifacts"
|
||||
|
||||
# Run all CI checks
|
||||
ci-full: ci-lint-rust ci-fmt-toml ci-lint-toml ci-lint-nickel ci-lint-markdown ci-lint-prose ci-lint-adr-refs ci-test ci-secrets-error-fixtures ci-secrets-smoke ci-vault-lock-smoke ci-audit ci-audit-protocol ci-check-config-sync ci-docs
|
||||
ci-full: ci-lint-rust ci-fmt-toml ci-lint-toml ci-lint-nickel ci-lint-markdown ci-lint-prose ci-lint-adr-refs ci-test ci-bond-dispatch ci-secrets-error-fixtures ci-secrets-smoke ci-vault-lock-smoke ci-audit ci-audit-protocol ci-check-config-sync ci-docs
|
||||
@echo "✅ All CI checks passed!"
|
||||
|
||||
# ==============================================================================
|
||||
|
|
@ -126,6 +126,23 @@ ci-test-coverage:
|
|||
@echo "📊 Running tests with coverage..."
|
||||
cargo llvm-cov --all-features --lcov --output-path lcov.info
|
||||
|
||||
# adr-077 two-path domain-activation dispatch invariants (repo_kind ⊕ bond).
|
||||
# Needs an installed data dir (domains/rustelo); skipped on workers without one,
|
||||
# same skip-when-unavailable behavior as the smoke tests below.
|
||||
ci-bond-dispatch:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
dd=""
|
||||
for c in "$HOME/Library/Application Support/ontoref" "$HOME/.local/share/ontoref"; do
|
||||
[[ -d "$c/domains/rustelo" ]] && { dd="$c"; break; }
|
||||
done
|
||||
if [[ -z "$dd" ]]; then
|
||||
echo "🔗 ci-bond-dispatch: skipped (no installed data dir — run just install-daemon)"
|
||||
exit 0
|
||||
fi
|
||||
echo "🔗 Running adr-077 bond dispatch invariants..."
|
||||
bash scripts/bond-dispatch-test.sh
|
||||
|
||||
# End-to-end credential chain smoke (ADR-017 + ADR-019).
|
||||
# Pushes a throwaway vault to ZOT, pulls it back from an isolated HOME, decrypts.
|
||||
# Skipped automatically when ZOT credentials are not exported — keeps `ci-full`
|
||||
|
|
|
|||
121
scripts/bond-dispatch-test.sh
Executable file
121
scripts/bond-dispatch-test.sh
Executable file
|
|
@ -0,0 +1,121 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# adr-077 dispatch invariants — behavioural test for the two-path domain
|
||||
# activation gate in install/ontoref-global (_dispatch_domain).
|
||||
#
|
||||
# Asserts, against the SOURCE wrapper (not the installed copy):
|
||||
# T1 bond path — a bond-only consumer (carrier, NO manifest) activates.
|
||||
# T2 refusal — a bare project (no manifest, no carrier) is refused.
|
||||
# T3 additive Hard — a repo_kind-matching project activates IDENTICALLY with
|
||||
# and without a carrier present (constraint
|
||||
# `bond-path-is-purely-additive`: the carrier never alters
|
||||
# an outcome the repo_kind path already granted).
|
||||
# T4 bond rescues — a manifest whose repo_kind does NOT match is refused
|
||||
# without a carrier, and activates WITH one.
|
||||
#
|
||||
# "Activated" = the dispatch passed the gate and proceeded to run the domain's
|
||||
# commands.nu; the gate's refusal line is absent. The command itself may then
|
||||
# fail on its own logic (the fixtures are not real rustelo sites) — that failure
|
||||
# is BEYOND the gate and is exactly what proves the gate let it through.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
WRAPPER="install/ontoref-global"
|
||||
DOMAIN="rustelo" # a real installed domain with repo_kinds.txt = RusteloApp
|
||||
REFUSAL="not available for this project"
|
||||
|
||||
# ── locate a data dir (ONTOREF_ROOT) so domains/ + dispatcher resolve ─────────
|
||||
DD=""
|
||||
for candidate in "$HOME/Library/Application Support/ontoref" "$HOME/.local/share/ontoref"; do
|
||||
if [[ -d "$candidate/domains/$DOMAIN" ]]; then DD="$candidate"; break; fi
|
||||
done
|
||||
if [[ -z "$DD" ]]; then
|
||||
echo "bond-dispatch-test: no installed data dir with domains/$DOMAIN — run 'just install-daemon' first" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
echo "== adr-077 dispatch invariants =="
|
||||
echo " wrapper=$WRAPPER data_dir=$DD domain=$DOMAIN"
|
||||
echo
|
||||
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
|
||||
# Run the source wrapper against a project root; echo combined output.
|
||||
run_dispatch() {
|
||||
local root="$1"
|
||||
ONTOREF_ROOT="$DD" ONTOREF_PROJECT_ROOT="$root" bash "$WRAPPER" "$DOMAIN" routes 2>&1 || true
|
||||
}
|
||||
|
||||
# assert_activated <label> <project_root>
|
||||
assert_activated() {
|
||||
local label="$1" root="$2" out
|
||||
out="$(run_dispatch "$root")"
|
||||
if echo "$out" | grep -qF "$REFUSAL"; then
|
||||
echo " FAIL $label — gate REFUSED (expected activation)"; FAIL=$((FAIL + 1))
|
||||
else
|
||||
echo " pass $label — gate passed"; PASS=$((PASS + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
# assert_refused <label> <project_root>
|
||||
assert_refused() {
|
||||
local label="$1" root="$2" out
|
||||
out="$(run_dispatch "$root")"
|
||||
if echo "$out" | grep -qF "$REFUSAL"; then
|
||||
echo " pass $label — gate refused"; PASS=$((PASS + 1))
|
||||
else
|
||||
echo " FAIL $label — gate ACTIVATED (expected refusal)"; FAIL=$((FAIL + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
# ── fixtures ──────────────────────────────────────────────────────────────────
|
||||
make_carrier() {
|
||||
local root="$1"
|
||||
mkdir -p "$root/.domains-ontoref/$DOMAIN"
|
||||
cat > "$root/.domains-ontoref/$DOMAIN/bonds.ncl" <<EOF
|
||||
let s = import "bond.ncl" in
|
||||
{
|
||||
domain = "$DOMAIN",
|
||||
bonds = [
|
||||
{ id = "test-to-$DOMAIN", from = "test", to = "$DOMAIN", axis = 'Dependency, state = 'Linked, dep_kind = 'Conformist, mutual = false, plane = "$DOMAIN" },
|
||||
],
|
||||
} | s.BondSet
|
||||
EOF
|
||||
}
|
||||
|
||||
make_manifest() {
|
||||
local root="$1" kind="$2"
|
||||
mkdir -p "$root/.ontoref/ontology"
|
||||
printf 'let m = import "manifest" in\n{ project = "t", repo_kind = %s }\n' "'$kind" \
|
||||
> "$root/.ontoref/ontology/manifest.ncl"
|
||||
}
|
||||
|
||||
# T1 — bond-only consumer (carrier, no manifest)
|
||||
BOND_ONLY="$TMP/bond-only"; mkdir -p "$BOND_ONLY"; make_carrier "$BOND_ONLY"
|
||||
assert_activated "T1 bond-only (carrier, no manifest)" "$BOND_ONLY"
|
||||
|
||||
# T2 — bare project (no manifest, no carrier)
|
||||
BARE="$TMP/bare"; mkdir -p "$BARE"
|
||||
assert_refused "T2 bare (no manifest, no carrier)" "$BARE"
|
||||
|
||||
# T3 — additive Hard: repo_kind match, with vs without carrier → both activate
|
||||
MANAGED="$TMP/managed"; make_manifest "$MANAGED" "RusteloApp"
|
||||
assert_activated "T3a managed repo_kind, no carrier" "$MANAGED"
|
||||
make_carrier "$MANAGED"
|
||||
assert_activated "T3b managed repo_kind, WITH carrier (additive)" "$MANAGED"
|
||||
|
||||
# T4 — bond rescues a non-matching repo_kind
|
||||
WRONG="$TMP/wrong-kind"; make_manifest "$WRONG" "SomeOtherKind"
|
||||
assert_refused "T4a wrong repo_kind, no carrier" "$WRONG"
|
||||
make_carrier "$WRONG"
|
||||
assert_activated "T4b wrong repo_kind, WITH carrier (bond rescues)" "$WRONG"
|
||||
|
||||
echo
|
||||
echo "== summary: $PASS passed, $FAIL failed =="
|
||||
[[ "$FAIL" -eq 0 ]] || exit 1
|
||||
Loading…
Add table
Reference in a new issue