cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix(test): fail closed on shell test discovery

Dami committed 3ad952a
Validate the complete deterministic scripts/tests inventory before executing any test. Reject non-executable, unreadable, symlinked, malformed, and empty inventories explicitly, and cover the behavior with Bash 3.2-compatible regression fixtures.
commit
3ad952a7cea1a75524a1b88886bb7764fcd4e174
parent
bb4cb785c453c30ae9245131ae9d154a9d0c0a34
author
Dami <dami.etoile@gmail.com>
3 files changed, +161 -3
Ascripts/run-shell-tests.sh61++++++++++++++++++
Ascripts/tests/shell-test-discovery.sh99++++++++++++++++++++++++++++++
Mscripts/verify-demo-build.sh4+-
A scripts/run-shell-tests.sh +61 -0
@@ -0,0 +1,61 @@
1+#!/usr/bin/env bash2+3+set -euo pipefail4+5+LC_ALL=C6+export LC_ALL7+8+test_dir="${1:-}"9+if [[ -z "$test_dir" ]]; then10+ printf 'usage: %s TEST_DIRECTORY\n' "${0##*/}" >&211+ exit 212+fi13+if [[ -L "$test_dir" || ! -d "$test_dir" ]]; then14+ printf 'shell test directory must be a real directory: %s\n' "$test_dir" >&215+ exit 116+fi17+if [[ ! -r "$test_dir" || ! -x "$test_dir" ]]; then18+ printf 'shell test directory must be readable and searchable: %s\n' "$test_dir" >&219+ exit 120+fi21+22+# Bash expands this array in LC_ALL=C order. dotglob makes a test such as23+# `.release-contract.sh` part of the same inventory, while nullglob lets us24+# diagnose an empty inventory instead of trying to execute a literal glob.25+shopt -s dotglob nullglob26+shell_tests=("$test_dir"/*.sh)27+shopt -u dotglob nullglob28+29+if (( ${#shell_tests[@]} == 0 )); then30+ printf 'no shell tests were discovered in: %s\n' "$test_dir" >&231+ exit 132+fi33+34+# Validate the complete inventory before running its first entry. This keeps a35+# malformed or non-executable test late in the sort order from producing a36+# partially successful gate.37+for shell_test in "${shell_tests[@]}"; do38+ printf 'shell_test_discovered=%s\n' "$shell_test"39+ if [[ "$shell_test" == *$'\n'* || "$shell_test" == *$'\r'* ]]; then40+ printf 'shell test path contains an unsafe line break: %q\n' "$shell_test" >&241+ exit 142+ fi43+ if [[ -L "$shell_test" || ! -f "$shell_test" ]]; then44+ printf 'shell test entry must be a regular, non-symlink file: %s\n' \45+ "$shell_test" >&246+ exit 147+ fi48+ if [[ ! -r "$shell_test" ]]; then49+ printf 'shell test entry is not readable: %s\n' "$shell_test" >&250+ exit 151+ fi52+ if [[ ! -x "$shell_test" ]]; then53+ printf 'shell test entry is not executable: %s\n' "$shell_test" >&254+ exit 155+ fi56+done57+58+for shell_test in "${shell_tests[@]}"; do59+ printf 'shell_test_start=%s\n' "$shell_test"60+ bash "$shell_test"61+done
A scripts/tests/shell-test-discovery.sh +99 -0
@@ -0,0 +1,99 @@
1+#!/usr/bin/env bash2+3+set -euo pipefail4+5+script_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"6+repo_dir="$(CDPATH='' cd -- "$script_dir/../.." && pwd -P)"7+runner="$repo_dir/scripts/run-shell-tests.sh"8+temp_parent="${TMPDIR:-/tmp}"9+temp_parent="${temp_parent%/}"10+temp_root="$(mktemp -d "$temp_parent/goq-shell-test-discovery.XXXXXX")"11+12+cleanup() {13+ local status=$?14+ trap - EXIT INT TERM HUP15+ case "$temp_root" in16+ "$temp_parent"/goq-shell-test-discovery.??????) rm -rf -- "$temp_root" ;;17+ esac18+ exit "$status"19+}20+trap cleanup EXIT INT TERM HUP21+22+write_passing_test() {23+ local path="$1"24+ local marker="$2"25+ printf '%s\n' \26+ '#!/usr/bin/env bash' \27+ 'set -euo pipefail' \28+ "printf '%s\\n' '$marker' >>\"\$GOQ_TEST_MARKERS\"" \29+ >"$path"30+ chmod 0755 "$path"31+}32+33+assert_failed_with() {34+ local expected="$1"35+ local output_path="$2"36+ shift 237+38+ if "$@" >"$output_path" 2>&1; then39+ printf 'command unexpectedly succeeded: %s\n' "$*" >&240+ exit 141+ fi42+ if ! grep -Fq -- "$expected" "$output_path"; then43+ printf 'failure did not contain expected text: %s\n' "$expected" >&244+ sed -n '1,120p' "$output_path" >&245+ exit 146+ fi47+}48+49+ordered_dir="$temp_root/ordered"50+ordered_markers="$temp_root/ordered.markers"51+mkdir "$ordered_dir"52+write_passing_test "$ordered_dir/zeta.sh" zeta53+write_passing_test "$ordered_dir/.hidden.sh" hidden54+write_passing_test "$ordered_dir/alpha.sh" alpha55+GOQ_TEST_MARKERS="$ordered_markers" "$runner" "$ordered_dir" \56+ >"$temp_root/ordered.out"57+printf '%s\n' hidden alpha zeta >"$temp_root/ordered.expected"58+cmp "$temp_root/ordered.expected" "$ordered_markers"59+for expected_path in \60+ "$ordered_dir/.hidden.sh" \61+ "$ordered_dir/alpha.sh" \62+ "$ordered_dir/zeta.sh"63+do64+ grep -Fq "shell_test_discovered=$expected_path" "$temp_root/ordered.out"65+ grep -Fq "shell_test_start=$expected_path" "$temp_root/ordered.out"66+done67+68+nonexec_dir="$temp_root/nonexec"69+nonexec_markers="$temp_root/nonexec.markers"70+mkdir "$nonexec_dir"71+write_passing_test "$nonexec_dir/alpha.sh" should-not-run72+printf '%s\n' '#!/usr/bin/env bash' 'exit 0' >"$nonexec_dir/zeta.sh"73+chmod 0644 "$nonexec_dir/zeta.sh"74+assert_failed_with \75+ "shell test entry is not executable: $nonexec_dir/zeta.sh" \76+ "$temp_root/nonexec.out" \77+ env GOQ_TEST_MARKERS="$nonexec_markers" "$runner" "$nonexec_dir"78+grep -Fq "shell_test_discovered=$nonexec_dir/zeta.sh" "$temp_root/nonexec.out"79+[[ ! -e "$nonexec_markers" ]]80+81+symlink_dir="$temp_root/symlink"82+symlink_markers="$temp_root/symlink.markers"83+mkdir "$symlink_dir"84+write_passing_test "$symlink_dir/alpha.sh" should-not-run85+ln -s alpha.sh "$symlink_dir/zeta.sh"86+assert_failed_with \87+ "shell test entry must be a regular, non-symlink file: $symlink_dir/zeta.sh" \88+ "$temp_root/symlink.out" \89+ env GOQ_TEST_MARKERS="$symlink_markers" "$runner" "$symlink_dir"90+[[ ! -e "$symlink_markers" ]]91+92+empty_dir="$temp_root/empty"93+mkdir "$empty_dir"94+assert_failed_with \95+ "no shell tests were discovered in: $empty_dir" \96+ "$temp_root/empty.out" \97+ "$runner" "$empty_dir"98+99+printf 'shell_test_discovery_tests=ok\n'
M scripts/verify-demo-build.sh +1 -3