Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions tests/helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,38 @@ teardown_test_env() {
# Ensure cleanup on unexpected exit (set -e abort, signal, etc.)
trap 'teardown_test_env' EXIT

# ---------------------------------------------------------------------------
# Scenario callers discard setup.sh output (`>/dev/null 2>&1`), so a setup
# failure used to leave no trace in the CI log — only "(setup failed)" or, when
# the caller has no `|| rc=$?`, a bare set -e abort of the whole runner. Keep a
# copy of the runner's original stdout on fd 3 and print the tail of a failed
# setup.sh run there; the caller's fd 1/2 redirections do not affect fd 3.
# ---------------------------------------------------------------------------
if ! { true >&3; } 2>/dev/null; then
exec 3>&1
fi
_TEST_SETUP_LOG_TAIL="${_TEST_SETUP_LOG_TAIL:-20}"

Comment on lines +120 to +121
# _run_setup_logged [setup.sh args...]
# Runs setup.sh, replays its combined output on stdout (so callers that
# capture or grep the output keep working) and, on a non-zero exit, prints the
# last $_TEST_SETUP_LOG_TAIL lines to fd 3. Returns the exit code of setup.sh.
_run_setup_logged() {
local log rc=0
log="$(mktemp)"
bash "$PROJECT_DIR/setup.sh" "$@" >"$log" 2>&1 || rc=$?
if [[ "$rc" -ne 0 ]]; then
{
printf ' [setup.sh exited %s: %s] last %s lines:\n' \
"$rc" "$*" "$_TEST_SETUP_LOG_TAIL"
tail -n "$_TEST_SETUP_LOG_TAIL" "$log" | sed 's/^/ | /'
} >&3
fi
cat "$log"
rm -f "$log"
return "$rc"
}

# ---------------------------------------------------------------------------
# run_setup - Run setup.sh with given args in the test environment
#
Expand All @@ -115,14 +147,14 @@ trap 'teardown_test_env' EXIT
# Returns the exit code of setup.sh
# ---------------------------------------------------------------------------
run_setup() {
bash "$PROJECT_DIR/setup.sh" --non-interactive --language=en "$@" 2>&1
_run_setup_logged --non-interactive --language=en "$@"
}

# ---------------------------------------------------------------------------
# run_setup_update - Run setup.sh in update mode
# ---------------------------------------------------------------------------
run_setup_update() {
bash "$PROJECT_DIR/setup.sh" --update --non-interactive "$@" 2>&1
_run_setup_logged --update --non-interactive "$@"
}

# ---------------------------------------------------------------------------
Expand Down
90 changes: 90 additions & 0 deletions tests/unit/test-helpers-run-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash
# tests/unit/test-helpers-run-setup.sh - run_setup / run_setup_update diagnostics
#
# Sourced by run-unit-tests.sh (helpers.sh already loaded). Uses a fake
# setup.sh under a private PROJECT_DIR so nothing real is deployed.

_hrs_tmp="$(mktemp -d)"
mkdir -p "$_hrs_tmp/project"
cat > "$_hrs_tmp/project/setup.sh" <<'EOF'
#!/bin/bash
for i in 1 2 3 4 5; do printf 'line %s\n' "$i"; done
printf 'args: %s\n' "$*" >&2
exit "${HRS_FAKE_RC:-0}"
EOF

{
test_name="helpers: run_setup replays setup.sh output on stdout and prints a failure tail on fd 3"
_hrs_rc=0
(
# shellcheck disable=SC2034 # read by run_setup / run_setup_update
PROJECT_DIR="$_hrs_tmp/project"
_TEST_SETUP_LOG_TAIL=3
export HRS_FAKE_RC=7
run_setup --profile=minimal \
>"$_hrs_tmp/stdout" 2>"$_hrs_tmp/stderr" 3>"$_hrs_tmp/fd3"
) || _hrs_rc=$?
if [[ "$_hrs_rc" -eq 7 ]] \
&& grep -q '^line 1$' "$_hrs_tmp/stdout" \
&& grep -q '^args: --non-interactive --language=en --profile=minimal$' \
"$_hrs_tmp/stdout" \
&& [[ ! -s "$_hrs_tmp/stderr" ]] \
&& grep -q 'setup.sh exited 7: --non-interactive --language=en --profile=minimal' \
"$_hrs_tmp/fd3" \
&& grep -q '| line 4$' "$_hrs_tmp/fd3" \
&& grep -q '| args: ' "$_hrs_tmp/fd3" \
&& ! grep -q 'line 1' "$_hrs_tmp/fd3"; then
pass "$test_name"
else
fail "$test_name"
fi
}

{
test_name="helpers: run_setup stays quiet on fd 3 when setup.sh succeeds"
rm -f "$_hrs_tmp/stdout" "$_hrs_tmp/stderr" "$_hrs_tmp/fd3"
_hrs_rc=0
(
# shellcheck disable=SC2034 # read by run_setup / run_setup_update
PROJECT_DIR="$_hrs_tmp/project"
export HRS_FAKE_RC=0
run_setup --profile=minimal \
>"$_hrs_tmp/stdout" 2>"$_hrs_tmp/stderr" 3>"$_hrs_tmp/fd3"
) || _hrs_rc=$?
if [[ "$_hrs_rc" -eq 0 ]] \
&& grep -q '^line 5$' "$_hrs_tmp/stdout" \
&& [[ ! -s "$_hrs_tmp/stderr" ]] \
&& [[ ! -s "$_hrs_tmp/fd3" ]]; then
pass "$test_name"
else
fail "$test_name"
fi
}

{
test_name="helpers: run_setup_update passes --update --non-interactive and keeps the caller's output capture intact"
rm -f "$_hrs_tmp/stdout" "$_hrs_tmp/fd3"
_hrs_rc=0
_hrs_out=""
(
# shellcheck disable=SC2034 # read by run_setup / run_setup_update
PROJECT_DIR="$_hrs_tmp/project"
export HRS_FAKE_RC=3
# The same shape scenarios use: capture the output while fd 3 goes elsewhere.
_out="$(run_setup_update --dry-run 2>&1 3>"$_hrs_tmp/fd3")" || _rc=$?
printf '%s' "$_out" > "$_hrs_tmp/stdout"
exit "${_rc:-0}"
) || _hrs_rc=$?
if [[ "$_hrs_rc" -eq 3 ]] \
&& grep -q '^args: --update --non-interactive --dry-run$' "$_hrs_tmp/stdout" \
&& ! grep -q 'setup.sh exited' "$_hrs_tmp/stdout" \
&& grep -q 'setup.sh exited 3: --update --non-interactive --dry-run' \
"$_hrs_tmp/fd3"; then
pass "$test_name"
else
fail "$test_name"
fi
}

rm -rf "$_hrs_tmp"
unset _hrs_tmp _hrs_rc _hrs_out