249 lines
8.3 KiB
Bash
Executable File
249 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Regression harness for the optional, identity-checked Gitea squash message.
|
|
|
|
set -u
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SUBJECT="$SCRIPT_DIR/pr-merge.sh"
|
|
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/pr-merge-message-field}"
|
|
ORIG_PATH="$PATH"
|
|
failures=0
|
|
|
|
rm -rf "$WORK_DIR"
|
|
mkdir -p "$WORK_DIR"
|
|
|
|
fail() {
|
|
echo "FAIL $1" >&2
|
|
failures=$((failures + 1))
|
|
}
|
|
|
|
make_case() {
|
|
local name="$1" case_dir
|
|
case_dir="$WORK_DIR/$name"
|
|
mkdir -p "$case_dir/bin" "$case_dir/agent"
|
|
cp "$SUBJECT" "$case_dir/pr-merge.sh"
|
|
chmod +x "$case_dir/pr-merge.sh"
|
|
|
|
cat > "$case_dir/detect-platform.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
detect_platform() { PLATFORM=gitea; printf 'gitea\n'; }
|
|
get_repo_owner() { printf 'acme\n'; }
|
|
get_repo_name() { printf 'widgets\n'; }
|
|
get_remote_host() { printf 'git.example.test\n'; }
|
|
get_gitea_token() { printf 'fixture-token\n'; }
|
|
get_gitea_basic_auth() { return 1; }
|
|
get_gitea_login_for_host() { return 1; }
|
|
SH
|
|
|
|
cat > "$case_dir/pr-metadata.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
cat <<'JSON'
|
|
{
|
|
"number": 42,
|
|
"title": "Preserve both branch authors",
|
|
"author": "poster",
|
|
"baseRefName": "main"
|
|
}
|
|
JSON
|
|
SH
|
|
|
|
cat > "$case_dir/ci-queue-wait.sh" <<'SH'
|
|
#!/usr/bin/env bash
|
|
exit 0
|
|
SH
|
|
|
|
cat > "$case_dir/bin/curl" <<'SH'
|
|
#!/usr/bin/env bash
|
|
set -eu
|
|
|
|
url=""
|
|
method="GET"
|
|
out_file=""
|
|
data=""
|
|
config=""
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-o)
|
|
out_file="$2"
|
|
shift 2
|
|
;;
|
|
-w)
|
|
shift 2
|
|
;;
|
|
-X)
|
|
method="$2"
|
|
shift 2
|
|
;;
|
|
-d|--data|--data-binary)
|
|
data="$2"
|
|
shift 2
|
|
;;
|
|
-K|--config)
|
|
if [[ "$2" == "-" ]]; then
|
|
config=$(cat)
|
|
fi
|
|
shift 2
|
|
;;
|
|
-H|--header|-u|--user)
|
|
if [[ "$2" == *"fixture-token"* ]]; then
|
|
: > "${MOSAIC_TEST_TOKEN_ARGV_MARKER:?}"
|
|
fi
|
|
shift 2
|
|
;;
|
|
http://*|https://*)
|
|
url="$1"
|
|
shift
|
|
;;
|
|
*)
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
printf '%s %s\n' "$method" "$url" >> "${MOSAIC_TEST_CURL_LOG:?}"
|
|
if [[ "$config" == *"Authorization: token fixture-token"* ]]; then
|
|
: > "${MOSAIC_TEST_AUTH_CONFIG_MARKER:?}"
|
|
fi
|
|
|
|
case "$url" in
|
|
*/pulls/42/commits*)
|
|
case "${MOSAIC_TEST_COMMITS_MODE:?}" in
|
|
verified)
|
|
body='[{"sha":"1111111111111111111111111111111111111111","commit":{"author":{"name":"Poster","email":"[email protected]"}},"author":{"login":"poster"}},{"sha":"2222222222222222222222222222222222222222","commit":{"author":{"name":"Alice","email":"[email protected]"}},"author":{"login":"alice"}}]'
|
|
;;
|
|
null-login)
|
|
body='[{"sha":"1111111111111111111111111111111111111111","commit":{"author":{"name":"Poster","email":"[email protected]"}},"author":{"login":"poster"}},{"sha":"3333333333333333333333333333333333333333","commit":{"author":{"name":"Unresolved Author","email":"[email protected]"}},"author":null}]'
|
|
;;
|
|
single)
|
|
body='[{"sha":"1111111111111111111111111111111111111111","commit":{"author":{"name":"Poster","email":"[email protected]"}},"author":{"login":"poster"}}]'
|
|
;;
|
|
*)
|
|
echo "unknown commits mode" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
code=200
|
|
;;
|
|
*/pulls/42/merge)
|
|
body='{}'
|
|
code=200
|
|
printf '%s' "$data" > "${MOSAIC_TEST_MERGE_PAYLOAD:?}"
|
|
;;
|
|
*/users/*)
|
|
body='{"message":"not found"}'
|
|
code=404
|
|
;;
|
|
*)
|
|
body='{"message":"unexpected URL"}'
|
|
code=500
|
|
;;
|
|
esac
|
|
|
|
if [[ -n "$out_file" ]]; then
|
|
printf '%s' "$body" > "$out_file"
|
|
else
|
|
printf '%s' "$body"
|
|
fi
|
|
printf '%s' "$code"
|
|
SH
|
|
|
|
chmod +x "$case_dir/detect-platform.sh" "$case_dir/pr-metadata.sh" \
|
|
"$case_dir/ci-queue-wait.sh" "$case_dir/bin/curl"
|
|
printf '%s\n' "$case_dir"
|
|
}
|
|
|
|
run_case() {
|
|
local case_dir="$1" mode="$2"
|
|
shift 2
|
|
MOSAIC_TEST_COMMITS_MODE="$mode" \
|
|
MOSAIC_TEST_CURL_LOG="$case_dir/curl.log" \
|
|
MOSAIC_TEST_MERGE_PAYLOAD="$case_dir/merge-payload.json" \
|
|
MOSAIC_TEST_TOKEN_ARGV_MARKER="$case_dir/token-in-argv" \
|
|
MOSAIC_TEST_AUTH_CONFIG_MARKER="$case_dir/auth-via-config" \
|
|
AGENT_WORK_ROOT="$case_dir/agent" \
|
|
PATH="$case_dir/bin:$ORIG_PATH" \
|
|
"$case_dir/pr-merge.sh" -n 42 "$@"
|
|
}
|
|
|
|
# Verified multi-author path: the non-poster trailer is built from one commit's
|
|
# linked author.login and that same commit's author email. No /users lookup.
|
|
verified_dir=$(make_case verified)
|
|
set +e
|
|
verified_output=$(run_case "$verified_dir" verified --co-author-trailers --escalate-to tl-mosaic 2>&1)
|
|
verified_rc=$?
|
|
set -e
|
|
if [[ "$verified_rc" -ne 0 ]]; then
|
|
fail "verified multi-author merge expected rc=0, got rc=$verified_rc: $verified_output"
|
|
elif [[ ! -s "$verified_dir/merge-payload.json" ]]; then
|
|
fail "verified multi-author merge did not reach the API payload"
|
|
else
|
|
python3 - "$verified_dir/merge-payload.json" <<'PY' || fail "verified payload did not preserve squash and exact message fields"
|
|
import json
|
|
import sys
|
|
payload = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
assert payload == {
|
|
"Do": "squash",
|
|
"MergeTitleField": "Preserve both branch authors",
|
|
"MergeMessageField": "Co-authored-by: alice <[email protected]>",
|
|
}, payload
|
|
PY
|
|
fi
|
|
[[ -e "$verified_dir/auth-via-config" ]] || fail "verified path did not authenticate curl through stdin config"
|
|
[[ ! -e "$verified_dir/token-in-argv" ]] || fail "verified path placed the Gitea token in curl argv"
|
|
if grep -q '/users/' "$verified_dir/curl.log" 2>/dev/null; then
|
|
fail "verified path performed a forbidden second /users lookup"
|
|
fi
|
|
|
|
# BLOCK path: a commit email exists but author.login is null. It must name both
|
|
# facts, name the escalation principal, and never reach the merge endpoint.
|
|
null_dir=$(make_case null-login)
|
|
set +e
|
|
null_output=$(run_case "$null_dir" null-login --co-author-trailers --escalate-to tl-mosaic 2>&1)
|
|
null_rc=$?
|
|
set -e
|
|
[[ "$null_rc" -ne 0 ]] || fail "null-login author expected a non-zero BLOCK"
|
|
[[ "$null_output" == *"BLOCK"* ]] || fail "null-login author omitted BLOCK diagnostic"
|
|
[[ "$null_output" == *"author.login=NULL"* ]] || fail "null-login author omitted the null provider fact"
|
|
[[ "$null_output" == *"[email protected]"* ]] || fail "null-login author omitted the commit email fact"
|
|
[[ "$null_output" == *"tl-mosaic"* ]] || fail "null-login author omitted the named escalation principal"
|
|
[[ ! -e "$null_dir/merge-payload.json" ]] || fail "null-login BLOCK still reached the merge API"
|
|
|
|
# Negative control: ordinary single-author merge remains byte-for-byte payload
|
|
# compatible and hardcoded to squash, with no optional message fields.
|
|
single_dir=$(make_case single)
|
|
set +e
|
|
single_output=$(run_case "$single_dir" single 2>&1)
|
|
single_rc=$?
|
|
set -e
|
|
if [[ "$single_rc" -ne 0 ]]; then
|
|
fail "ordinary single-author merge expected rc=0, got rc=$single_rc: $single_output"
|
|
elif [[ ! -s "$single_dir/merge-payload.json" ]]; then
|
|
fail "ordinary single-author merge did not reach the API payload"
|
|
else
|
|
python3 - "$single_dir/merge-payload.json" <<'PY' || fail "ordinary single-author payload changed"
|
|
import json
|
|
import sys
|
|
payload = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
assert payload == {"Do": "squash"}, payload
|
|
PY
|
|
fi
|
|
[[ -e "$single_dir/auth-via-config" ]] || fail "ordinary path did not authenticate curl through stdin config"
|
|
[[ ! -e "$single_dir/token-in-argv" ]] || fail "ordinary path placed the Gitea token in curl argv"
|
|
|
|
# Squash is not defaultable: an explicit non-squash method must remain refused.
|
|
method_dir=$(make_case method-refusal)
|
|
set +e
|
|
method_output=$(run_case "$method_dir" single -m merge 2>&1)
|
|
method_rc=$?
|
|
set -e
|
|
[[ "$method_rc" -ne 0 ]] || fail "non-squash method unexpectedly passed"
|
|
[[ "$method_output" == *"enforces squash merge only"* ]] || fail "non-squash refusal lost its policy diagnostic"
|
|
[[ ! -e "$method_dir/merge-payload.json" ]] || fail "non-squash refusal reached the merge API"
|
|
|
|
if [[ "$failures" -ne 0 ]]; then
|
|
echo "pr-merge message-field regression failed ($failures assertions)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "pr-merge message-field regression passed (verified, BLOCK, and unchanged squash control)"
|