#!/usr/bin/env bash
# Tests for init/oosh — source-safety contract. After the thin-bootstrap
# rewrite, init/oosh is a flat top-to-bottom installer with destructive
# paths (sudo re-exec, mv $OOSH_DIR $HOME/oosh, state-machine handoff).
# When sourced (e.g. by callers that just want to inspect its functions),
# those paths must NOT fire — they have wiped user oosh trees in the past.
#
# This file pins the contract:
#   1. BASH_SOURCE[0] != $0   → guard fires, source returns cleanly
#   2. OOSH_NO_AUTORUN=1      → guard fires, even if BASH_SOURCE detection
#                                is defeated (eval-strip loaders)
#   3. set -e does NOT leak into the sourcing shell
#   4. die() function is exposed to the sourcing shell
#
# The grep/structure assertions for the file body live in test.install.
TEST_CATEGORY=core

level=$1
if [ -z "$level" ]; then
  level=1
else
  shift
fi
echo "starting: ${BASH_SOURCE[@]##*/} <LOG_LEVEL=$level>"

source this
source test.suite

log.level $level

INIT_SCRIPT="$OOSH_DIR/init/oosh"

# ─────────────────────────────────────────────────────────────────────────
# T-OOSH-AUTORUN-GUARD-PRESENT: the guard itself is in the file
# ─────────────────────────────────────────────────────────────────────────
# Pin the literal contract so the guard can't be silently removed during
# a future refactor. Mirrors the grep-style assertions in test.install.
test.case $level "T-OOSH-AUTORUN-GUARD-PRESENT: init/oosh has source-vs-execute guard" \
  echo "(grep)"
if grep -qE 'BASH_SOURCE\[0\].*!=.*\$0' "$INIT_SCRIPT" \
   && grep -qE 'OOSH_NO_AUTORUN' "$INIT_SCRIPT"; then
  expect.pass "init/oosh has BASH_SOURCE check and OOSH_NO_AUTORUN sentinel"
else
  expect.fail "init/oosh missing source-vs-execute guard — sourcing it from tests will fire the installer"
fi

# ─────────────────────────────────────────────────────────────────────────
# T-OOSH-NO-AUTORUN-SENTINEL: OOSH_NO_AUTORUN=1 source returns cleanly
# ─────────────────────────────────────────────────────────────────────────
# Run in a subshell so any guard regression that re-fires the installer
# can't replace this test process via `exec sudo`. Inspect captured output
# for the smoking-gun strings the installer prints before its destructive
# steps (sudo re-exec banner, clone message, handoff message).
test.case $level "T-OOSH-NO-AUTORUN-SENTINEL: OOSH_NO_AUTORUN=1 source returns cleanly" \
  echo "(subshell source)"
guardOutput=$(OOSH_NO_AUTORUN=1 bash -c "source '$INIT_SCRIPT' && echo SOURCED-OK" 2>&1)
guardRc=$?
if [ "$guardRc" -eq 0 ] \
   && printf '%s\n' "$guardOutput" | grep -q '^SOURCED-OK$' \
   && ! printf '%s\n' "$guardOutput" | grep -qE 're-executing as root|Pre-cloning|Cloning .* into|Handing off to ossh'; then
  expect.pass "OOSH_NO_AUTORUN=1 source returned 0 with no installer side effects"
else
  expect.fail "guard failed: rc=$guardRc output=$(printf '%q' "$guardOutput")"
fi

# ─────────────────────────────────────────────────────────────────────────
# T-OOSH-SOURCE-DETECTION: bare source (no env var) also returns cleanly
# ─────────────────────────────────────────────────────────────────────────
# When the file is sourced rather than executed, BASH_SOURCE[0] is the
# absolute file path while $0 is bash's invocation arg. They differ, so
# the guard fires without needing the explicit OOSH_NO_AUTORUN sentinel.
test.case $level "T-OOSH-SOURCE-DETECTION: bare source (BASH_SOURCE != \$0) returns cleanly" \
  echo "(subshell source)"
sourceOutput=$(bash -c "source '$INIT_SCRIPT' && echo SOURCED-OK" 2>&1)
sourceRc=$?
if [ "$sourceRc" -eq 0 ] \
   && printf '%s\n' "$sourceOutput" | grep -q '^SOURCED-OK$' \
   && ! printf '%s\n' "$sourceOutput" | grep -qE 're-executing as root|Pre-cloning|Cloning .* into|Handing off to ossh'; then
  expect.pass "bare source returned 0 with no installer side effects"
else
  expect.fail "BASH_SOURCE guard failed: rc=$sourceRc output=$(printf '%q' "$sourceOutput")"
fi

# ─────────────────────────────────────────────────────────────────────────
# T-OOSH-NO-ERREXIT-LEAK: sourcing does NOT enable errexit in the caller
# ─────────────────────────────────────────────────────────────────────────
# init/oosh has `set -e` at the top — but it must sit AFTER the auto-run
# guard so sourced callers don't get errexit forced into their shell. If
# `set -e` leaks into test.suite, the next failing command would abort the
# entire test run. Run the check in a subshell whose initial errexit state
# is OFF; verify it's still OFF after sourcing.
test.case $level "T-OOSH-NO-ERREXIT-LEAK: sourcing does not enable errexit in caller" \
  echo "(subshell errexit check)"
errexitState=$(bash -c "set +e; source '$INIT_SCRIPT'; case \$- in *e*) echo ON ;; *) echo OFF ;; esac" 2>&1)
if [ "$errexitState" = "OFF" ]; then
  expect.pass "errexit remained OFF in caller after sourcing init/oosh"
else
  expect.fail "errexit leaked into sourcing shell (got: '$errexitState') — would break subsequent tests"
fi

# ─────────────────────────────────────────────────────────────────────────
# T-OOSH-DIE-EXPOSED: sourcing exposes die() to the caller
# ─────────────────────────────────────────────────────────────────────────
# die() is defined BEFORE the guard so it remains available to anyone who
# sources init/oosh — the only function the file currently exports.
test.case $level "T-OOSH-DIE-EXPOSED: die() is defined and reachable after sourcing" \
  echo "(subshell type check)"
dieType=$(bash -c "source '$INIT_SCRIPT' && type -t die" 2>&1)
if [ "$dieType" = "function" ]; then
  expect.pass "die() is a function after sourcing init/oosh"
else
  expect.fail "die() not exposed (got type: '$dieType')"
fi

test.suite.save.results
