126 lines
5.6 KiB
Bash
Executable File
126 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Red-first acceptance checks for #1050. This file is committed before the
|
|
# installer implementation. Do not weaken these properties to make it green.
|
|
|
|
set -uo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
TMP="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-install-state-test.XXXXXX")"
|
|
trap 'rm -rf "$TMP"' EXIT
|
|
failures=0
|
|
|
|
fail_case() { printf '[test] FAIL: %s\n' "$*" >&2; failures=$((failures + 1)); }
|
|
pass_case() { printf '[test] PASS: %s\n' "$*"; }
|
|
|
|
fingerprint() {
|
|
local dir="$1"
|
|
if [[ ! -d "$dir" ]]; then printf 'ABSENT\n'; return; fi
|
|
(
|
|
cd "$dir" || exit 1
|
|
find . -mindepth 1 -printf '%P|%y|%m|%u|%g|%l\n' | LC_ALL=C sort
|
|
find . -type f -print0 | LC_ALL=C sort -z | xargs -0 -r sha256sum
|
|
) | sha256sum | awk '{print $1}'
|
|
}
|
|
|
|
make_fake_npm() {
|
|
local bin="$1"
|
|
mkdir -p "$bin"
|
|
cat > "$bin/npm" <<'FAKE'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
case "${1:-} ${2:-} ${3:-}" in
|
|
'view @mosaicstack/mosaic@next version') echo '0.0.50-next.999' ;;
|
|
'view @mosaicstack/gateway@next version') echo '0.0.7-next.999' ;;
|
|
'view @mosaicstack/mosaic version') echo '0.0.49' ;;
|
|
'ls -g --depth=0'|'ls -g --json') echo '{"dependencies":{"@mosaicstack/mosaic":{"version":"0.0.50-next.999"},"@mosaicstack/gateway":{"version":"0.0.7-next.999"}}}' ;;
|
|
ls*) echo '{"dependencies":{"@mosaicstack/mosaic":{"version":"0.0.50-next.999"},"@mosaicstack/gateway":{"version":"0.0.7-next.999"}}}' ;;
|
|
*) echo "unexpected fake npm command: $*" >&2; exit 1 ;;
|
|
esac
|
|
FAKE
|
|
chmod 0755 "$bin/npm"
|
|
}
|
|
|
|
printf '[test] case: --check enumerates exactly P0-P8, discriminates, and mutates nothing\n'
|
|
check_home="$TMP/check-home"
|
|
check_bin="$TMP/check-bin"
|
|
mkdir -p "$check_home/.config/mosaic/skills/alpha" "$check_home/.npm-global/bin" "$check_bin"
|
|
printf '# framework\n' > "$check_home/.config/mosaic/AGENTS.md"
|
|
printf '# skill\n' > "$check_home/.config/mosaic/skills/alpha/SKILL.md"
|
|
cat > "$check_home/.npm-global/bin/mosaic" <<'CLI'
|
|
#!/usr/bin/env bash
|
|
printf '0.0.50-next.999\n'
|
|
CLI
|
|
chmod 0755 "$check_home/.npm-global/bin/mosaic"
|
|
make_fake_npm "$check_bin"
|
|
before="$(fingerprint "$check_home")"
|
|
set +e
|
|
HOME="$check_home" MOSAIC_HOME="$check_home/.config/mosaic" MOSAIC_PREFIX="$check_home/.npm-global" \
|
|
MOSAIC_NO_COLOR=1 PATH="$check_bin:/usr/local/bin:/usr/bin:/bin" \
|
|
bash "$ROOT/tools/install.sh" --check --next >"$TMP/check.log" 2>&1
|
|
check_status=$?
|
|
set -e
|
|
after="$(fingerprint "$check_home")"
|
|
|
|
[[ "$before" == "$after" ]] && pass_case '--check left the complete HOME fingerprint unchanged' \
|
|
|| fail_case "--check mutated HOME (before=$before after=$after)"
|
|
[[ "$check_status" -ne 0 ]] && pass_case '--check exited non-zero for failed P4/P5/P8 predicates' \
|
|
|| fail_case '--check returned zero on the deliberately broken host'
|
|
|
|
phase_rows=0
|
|
for phase in P0 P1 P2 P3 P4 P5 P6 P7 P8; do
|
|
count="$(grep -Ec "^\[$phase\] (PASS|FAIL):" "$TMP/check.log" || true)"
|
|
[[ "$count" -eq 1 ]] || fail_case "$phase expected exactly one PASS/FAIL row, got $count"
|
|
phase_rows=$((phase_rows + count))
|
|
done
|
|
[[ "$phase_rows" -eq 9 ]] && pass_case '--check emitted exactly nine P0-P8 result rows' \
|
|
|| fail_case "--check emitted $phase_rows canonical rows instead of 9"
|
|
grep -q '^\[P3\] PASS:.*0\.0\.50-next\.999' "$TMP/check.log" \
|
|
&& pass_case 'P3 preserves the absolute-path exact-version discriminator' \
|
|
|| fail_case 'P3 did not PASS with the exact resolved next-lane version'
|
|
grep -q '^\[P4\] FAIL: NOT-MEASURED / UNDECLARED:' "$TMP/check.log" \
|
|
&& pass_case 'P4 refuses fabricated precision when no shipped-set declaration exists' \
|
|
|| fail_case 'P4 did not report the declared-set population as NOT-MEASURED / UNDECLARED'
|
|
for phase in P5 P8; do
|
|
grep -q "^\[$phase\] FAIL:" "$TMP/check.log" \
|
|
&& pass_case "$phase remains an attributable expected RED" \
|
|
|| fail_case "$phase did not report its own expected failure"
|
|
done
|
|
|
|
printf '[test] case: per-phase P2-P8 fault injection restores representative host mutations\n'
|
|
for phase in P2 P3 P4 P5 P6 P7 P8; do
|
|
home="$TMP/fault-$phase/home"
|
|
state="$TMP/fault-$phase/state"
|
|
mkdir -p "$home/.config/mosaic" "$home/.npm-global/bin" "$home/.claude" "$state"
|
|
printf 'operator-framework-sentinel\n' > "$home/.config/mosaic/operator.txt"
|
|
printf '@scope:registry=https://pre.example.invalid/\n' > "$home/.npmrc"
|
|
printf 'old-cli\n' > "$home/.npm-global/bin/mosaic"
|
|
printf '{"hooks":{"safe":true}}\n' > "$home/.claude/settings.json"
|
|
before="$(fingerprint "$home")"
|
|
set +e
|
|
HOME="$home" MOSAIC_HOME="$home/.config/mosaic" MOSAIC_PREFIX="$home/.npm-global" \
|
|
MOSAIC_INSTALL_STATE_DIR="$state" MOSAIC_INSTALL_FAULT_AFTER="$phase" \
|
|
MOSAIC_NO_COLOR=1 bash "$ROOT/tools/install.sh" --state-machine-self-test \
|
|
>"$TMP/fault-$phase.log" 2>&1
|
|
status=$?
|
|
set -e
|
|
after="$(fingerprint "$home")"
|
|
[[ "$status" -ne 0 ]] || fail_case "$phase injected fault returned zero"
|
|
grep -q "phase=$phase" "$TMP/fault-$phase.log" \
|
|
|| fail_case "$phase fault transcript did not name the injected phase"
|
|
[[ "$before" == "$after" ]] \
|
|
&& pass_case "$phase rollback restored framework/npmrc/prefix/runtime representative state" \
|
|
|| fail_case "$phase rollback mismatch (before=$before after=$after)"
|
|
if find "$state" -type f -exec grep -l '"status"[[:space:]]*:[[:space:]]*"in-progress"' {} + 2>/dev/null | grep -q .; then
|
|
fail_case "$phase left a journal in-progress"
|
|
else
|
|
pass_case "$phase left no journal falsely in-progress"
|
|
fi
|
|
done
|
|
|
|
if [[ "$failures" -ne 0 ]]; then
|
|
printf '[test] install state-machine acceptance RED: %d failed assertion(s)\n' "$failures" >&2
|
|
printf '[test] --check transcript: %s\n' "$TMP/check.log" >&2
|
|
exit 1
|
|
fi
|
|
printf '[test] installer state-machine acceptance passed\n'
|