346 lines
8.2 KiB
Bash
Executable File
346 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
HELPER_PATH=$(cd "$SCRIPT_DIR/.." && pwd)/run-review.sh
|
|
|
|
fail() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_file_contains() {
|
|
local file=$1
|
|
local pattern=$2
|
|
if ! grep -qF "$pattern" "$file"; then
|
|
echo "Expected pattern not found: $pattern" >&2
|
|
echo "--- $file ---" >&2
|
|
sed -n '1,200p' "$file" >&2 || true
|
|
fail "missing pattern in $file"
|
|
fi
|
|
}
|
|
|
|
assert_exit_code() {
|
|
local actual=$1
|
|
local expected=$2
|
|
if [[ "$actual" -ne "$expected" ]]; then
|
|
fail "expected exit code $expected, got $actual"
|
|
fi
|
|
}
|
|
|
|
assert_nonzero_exit() {
|
|
local actual=$1
|
|
if [[ "$actual" -eq 0 ]]; then
|
|
fail "expected non-zero exit code"
|
|
fi
|
|
}
|
|
|
|
make_command() {
|
|
local file=$1
|
|
local body=$2
|
|
cat >"$file" <<EOF
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
$body
|
|
EOF
|
|
chmod +x "$file"
|
|
}
|
|
|
|
run_helper() {
|
|
local command_file=$1
|
|
local stdout_file=$2
|
|
local stderr_file=$3
|
|
local status_file=$4
|
|
shift 4
|
|
|
|
set +e
|
|
"$HELPER_PATH" \
|
|
--command-file "$command_file" \
|
|
--stdout-file "$stdout_file" \
|
|
--stderr-file "$stderr_file" \
|
|
--status-file "$status_file" \
|
|
"$@"
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
return "$exit_code"
|
|
}
|
|
|
|
run_helper_allowing_success_file() {
|
|
local command_file=$1
|
|
local stdout_file=$2
|
|
local stderr_file=$3
|
|
local status_file=$4
|
|
local success_file=$5
|
|
shift 5
|
|
|
|
set +e
|
|
"$HELPER_PATH" \
|
|
--command-file "$command_file" \
|
|
--stdout-file "$stdout_file" \
|
|
--stderr-file "$stderr_file" \
|
|
--status-file "$status_file" \
|
|
--success-file "$success_file" \
|
|
"$@"
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
return "$exit_code"
|
|
}
|
|
|
|
test_delayed_success() {
|
|
local dir=$1
|
|
local command_file=$dir/delayed-success.sh
|
|
local stdout_file=$dir/delayed-success.stdout
|
|
local stderr_file=$dir/delayed-success.stderr
|
|
local status_file=$dir/delayed-success.status
|
|
|
|
make_command "$command_file" '
|
|
sleep 2
|
|
printf "VERDICT: APPROVED\n"
|
|
'
|
|
|
|
if run_helper "$command_file" "$stdout_file" "$stderr_file" "$status_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 5 \
|
|
--stall-warning-seconds 3 \
|
|
--hard-timeout-seconds 10; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_exit_code "$exit_code" 0
|
|
assert_file_contains "$stdout_file" "VERDICT: APPROVED"
|
|
assert_file_contains "$status_file" "state=completed"
|
|
}
|
|
|
|
test_soft_timeout_continues() {
|
|
local dir=$1
|
|
local command_file=$dir/soft-timeout.sh
|
|
local stdout_file=$dir/soft-timeout.stdout
|
|
local stderr_file=$dir/soft-timeout.stderr
|
|
local status_file=$dir/soft-timeout.status
|
|
|
|
make_command "$command_file" '
|
|
sleep 3
|
|
printf "completed after soft timeout\n"
|
|
'
|
|
|
|
if run_helper "$command_file" "$stdout_file" "$stderr_file" "$status_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 1 \
|
|
--stall-warning-seconds 2 \
|
|
--hard-timeout-seconds 8; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_exit_code "$exit_code" 0
|
|
assert_file_contains "$stdout_file" "completed after soft timeout"
|
|
assert_file_contains "$status_file" "state=completed"
|
|
}
|
|
|
|
test_in_progress_heartbeats() {
|
|
local dir=$1
|
|
local command_file=$dir/in-progress-heartbeats.sh
|
|
local stdout_file=$dir/in-progress-heartbeats.stdout
|
|
local stderr_file=$dir/in-progress-heartbeats.stderr
|
|
local status_file=$dir/in-progress-heartbeats.status
|
|
|
|
make_command "$command_file" '
|
|
sleep 3
|
|
printf "finished with heartbeat coverage\n"
|
|
'
|
|
|
|
if run_helper "$command_file" "$stdout_file" "$stderr_file" "$status_file" \
|
|
--poll-seconds 1 \
|
|
--heartbeat-seconds 1 \
|
|
--soft-timeout-seconds 5 \
|
|
--stall-warning-seconds 4 \
|
|
--hard-timeout-seconds 10; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_exit_code "$exit_code" 0
|
|
assert_file_contains "$stdout_file" "finished with heartbeat coverage"
|
|
assert_file_contains "$status_file" "state=in-progress"
|
|
assert_file_contains "$status_file" "In progress 1"
|
|
assert_file_contains "$status_file" "In progress 2"
|
|
}
|
|
|
|
test_nonzero_failure() {
|
|
local dir=$1
|
|
local command_file=$dir/nonzero-failure.sh
|
|
local stdout_file=$dir/nonzero-failure.stdout
|
|
local stderr_file=$dir/nonzero-failure.stderr
|
|
local status_file=$dir/nonzero-failure.status
|
|
|
|
make_command "$command_file" '
|
|
printf "boom\n" >&2
|
|
exit 7
|
|
'
|
|
|
|
if run_helper "$command_file" "$stdout_file" "$stderr_file" "$status_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 5 \
|
|
--stall-warning-seconds 3 \
|
|
--hard-timeout-seconds 10; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_exit_code "$exit_code" 7
|
|
assert_file_contains "$stderr_file" "boom"
|
|
assert_file_contains "$status_file" "state=failed"
|
|
}
|
|
|
|
test_empty_output_is_terminal() {
|
|
local dir=$1
|
|
local command_file=$dir/empty-output.sh
|
|
local stdout_file=$dir/empty-output.stdout
|
|
local stderr_file=$dir/empty-output.stderr
|
|
local status_file=$dir/empty-output.status
|
|
|
|
make_command "$command_file" '
|
|
sleep 1
|
|
exit 0
|
|
'
|
|
|
|
if run_helper "$command_file" "$stdout_file" "$stderr_file" "$status_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 5 \
|
|
--stall-warning-seconds 3 \
|
|
--hard-timeout-seconds 10; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_nonzero_exit "$exit_code"
|
|
assert_file_contains "$status_file" "state=completed-empty-output"
|
|
}
|
|
|
|
test_success_file_allows_empty_stdout() {
|
|
local dir=$1
|
|
local command_file=$dir/success-file.sh
|
|
local stdout_file=$dir/success-file.stdout
|
|
local stderr_file=$dir/success-file.stderr
|
|
local status_file=$dir/success-file.status
|
|
local success_file=$dir/review-output.md
|
|
|
|
make_command "$command_file" "
|
|
printf 'review body from redirected file\\n' > \"$success_file\"
|
|
exit 0
|
|
"
|
|
|
|
if run_helper_allowing_success_file "$command_file" "$stdout_file" "$stderr_file" "$status_file" "$success_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 5 \
|
|
--stall-warning-seconds 3 \
|
|
--hard-timeout-seconds 10; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_exit_code "$exit_code" 0
|
|
assert_file_contains "$success_file" "review body from redirected file"
|
|
assert_file_contains "$status_file" "state=completed"
|
|
}
|
|
|
|
test_signal_cleanup() {
|
|
local dir=$1
|
|
local command_file=$dir/signal-child.sh
|
|
local stdout_file=$dir/signal-child.stdout
|
|
local stderr_file=$dir/signal-child.stderr
|
|
local status_file=$dir/signal-child.status
|
|
local child_pid_file=$dir/child.pid
|
|
|
|
make_command "$command_file" "
|
|
printf '%s\n' \"\$\$\" > \"$child_pid_file\"
|
|
sleep 30
|
|
"
|
|
|
|
set +e
|
|
"$HELPER_PATH" \
|
|
--command-file "$command_file" \
|
|
--stdout-file "$stdout_file" \
|
|
--stderr-file "$stderr_file" \
|
|
--status-file "$status_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 5 \
|
|
--stall-warning-seconds 2 \
|
|
--hard-timeout-seconds 10 &
|
|
local helper_pid=$!
|
|
set -e
|
|
|
|
sleep 2
|
|
kill -TERM "$helper_pid"
|
|
|
|
set +e
|
|
wait "$helper_pid"
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
assert_nonzero_exit "$exit_code"
|
|
[[ -f "$child_pid_file" ]] || fail "child pid file was not written"
|
|
|
|
local child_pid
|
|
child_pid=$(cat "$child_pid_file")
|
|
sleep 1
|
|
if kill -0 "$child_pid" 2>/dev/null; then
|
|
fail "child process is still alive after helper termination"
|
|
fi
|
|
}
|
|
|
|
test_hard_timeout_escalation() {
|
|
local dir=$1
|
|
local command_file=$dir/hard-timeout.sh
|
|
local stdout_file=$dir/hard-timeout.stdout
|
|
local stderr_file=$dir/hard-timeout.stderr
|
|
local status_file=$dir/hard-timeout.status
|
|
|
|
make_command "$command_file" '
|
|
sleep 30
|
|
'
|
|
|
|
if run_helper "$command_file" "$stdout_file" "$stderr_file" "$status_file" \
|
|
--poll-seconds 1 \
|
|
--soft-timeout-seconds 2 \
|
|
--stall-warning-seconds 2 \
|
|
--hard-timeout-seconds 4; then
|
|
local exit_code=0
|
|
else
|
|
local exit_code=$?
|
|
fi
|
|
|
|
assert_exit_code "$exit_code" 81
|
|
assert_file_contains "$status_file" "state=needs-operator-decision"
|
|
}
|
|
|
|
main() {
|
|
[[ -x "$HELPER_PATH" ]] || fail "helper is not executable: $HELPER_PATH"
|
|
|
|
local tmp_dir
|
|
tmp_dir=$(mktemp -d)
|
|
trap "rm -rf '$tmp_dir'" EXIT
|
|
|
|
test_delayed_success "$tmp_dir"
|
|
test_soft_timeout_continues "$tmp_dir"
|
|
test_in_progress_heartbeats "$tmp_dir"
|
|
test_nonzero_failure "$tmp_dir"
|
|
test_empty_output_is_terminal "$tmp_dir"
|
|
test_success_file_allows_empty_stdout "$tmp_dir"
|
|
test_signal_cleanup "$tmp_dir"
|
|
test_hard_timeout_escalation "$tmp_dir"
|
|
|
|
echo "PASS: reviewer runtime smoke tests"
|
|
}
|
|
|
|
main "$@"
|