#!/usr/bin/env bash
# Failure-injection coverage for durable deploy queue acknowledgement.
set -uo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
DEPLOY_SCRIPT="$ROOT/packaging/deploy-local.sh"
TESTROOT="${XDG_CACHE_HOME:-$HOME/.cache}/overdeck/tests/deploy-queue-ack/run-$$-$RANDOM"
mkdir -p "$TESTROOT"
trap 'rm -rf "$TESTROOT"' EXIT
fails=0
check() { if (( $1 == 0 )); then printf 'ok - %s\n' "$2"; else printf 'FAIL - %s\n' "$2"; fails=$((fails + 1)); fi; }

FINALIZER="$TESTROOT/finalizer.sh"
sed -n '/^publish_deploy_stamp() {/,/^}/p' "$DEPLOY_SCRIPT" >"$FINALIZER"
sed -n '/^acknowledge_deploy_requests() {/,/^}/p' "$DEPLOY_SCRIPT" >>"$FINALIZER"
sed -n '/^mark_degraded() {/,/^}/p' "$DEPLOY_SCRIPT" >>"$FINALIZER"
sed -n '/^finalize_deploy_success() {/,/^}/p' "$DEPLOY_SCRIPT" >>"$FINALIZER"

FULL_SHA=0123456789abcdef0123456789abcdef01234567
run_injection() (
  local name=$1 injection=$2 dir="$TESTROOT/$1"
  mkdir -p "$dir/queue"
  : >"$dir/queue/req-covered-a"
  : >"$dir/queue/req-covered-b"
  : >"$dir/queue/req-late"
  queue_snapshot=("$dir/queue/req-covered-a" "$dir/queue/req-covered-b")
  DEPLOY_STAMP="$dir/stamp"
  printf 'previous\n' >"$DEPLOY_STAMP"
  deployment_sha=$FULL_SHA
  OVERDECK_DEPLOY_TARGET_SHA=$FULL_SHA
  DEGRADED_REASONS=()
  source "$FINALIZER"
  acknowledge_deferred_deploy_requests() { return 0; }
  fail() { printf '%s\n' "$1" >"$dir/failure"; exit 1; }
  od-live-report-refresh() { return 0; }
  deploy_tree_readonly() { return 0; }
  main_checkout_ff_sync() { return 0; }
  case "$injection" in
    live-report) od-live-report-refresh() { return 1; } ;;
    identity) deployment_sha=short ;;
    stamp) publish_deploy_stamp() { return 1; } ;;
    tree-lock) deploy_tree_readonly() { return 1; } ;;
    main-sync) main_checkout_ff_sync() { return 1; } ;;
    acknowledge) acknowledge_deploy_requests() { return 1; } ;;
    success) ;;
  esac
  finalize_deploy_success
  printf '%s\n' "${DEGRADED_REASONS[*]:-}" >"$dir/degraded"
)

assert_failed_covered() {
  local name=$1 expected_step=$2 dir="$TESTROOT/$1"
  [[ -e "$dir/queue/req-covered-a" && -e "$dir/queue/req-covered-b" \
     && -e "$dir/queue/req-late" && "$(cat "$dir/failure" 2>/dev/null)" == "$expected_step" ]]
}

run_injection live-report live-report; rc=$?
[[ "$rc" -eq 0 && "$(<"$TESTROOT/live-report/stamp")" == "$FULL_SHA" \
   && ! -e "$TESTROOT/live-report/queue/req-covered-a" \
   && ! -e "$TESTROOT/live-report/queue/req-covered-b" \
   && -e "$TESTROOT/live-report/queue/req-late" \
   && "$(<"$TESTROOT/live-report/degraded")" == live-report-refresh-failed ]]
check $? 'live-report failure degrades the completed deploy and acknowledges covered requests'

run_injection identity identity; rc=$?
(( rc != 0 )) && assert_failed_covered identity deploy-stamp-failed
check $? 'invalid target identity keeps every covered request'

run_injection stamp stamp; rc=$?
(( rc != 0 )) && assert_failed_covered stamp deploy-stamp-failed
check $? 'stamp publication failure keeps every covered request'
[[ "$(cat "$TESTROOT/stamp/stamp")" == previous ]]
check $? 'stamp publication failure preserves the old stamp'

run_injection tree-lock tree-lock; rc=$?
(( rc != 0 )) && assert_failed_covered tree-lock deploy-tree-lock-failed
check $? 'release-tree lock failure keeps every covered request'
[[ "$(cat "$TESTROOT/tree-lock/stamp")" == "$FULL_SHA" ]]
check $? 'tree-lock failure occurs after atomic success-stamp publication'

run_injection acknowledge acknowledge; rc=$?
(( rc != 0 )) && assert_failed_covered acknowledge deploy-queue-drain-failed
check $? 'acknowledgement failure leaves requests available to retrigger'

run_injection main-sync main-sync; rc=$?
[[ "$rc" -eq 0 && ! -e "$TESTROOT/main-sync/queue/req-covered-a" \
   && ! -e "$TESTROOT/main-sync/queue/req-covered-b" \
   && -e "$TESTROOT/main-sync/queue/req-late" ]]
check $? 'fail-open main-checkout sync does not block durable commit or broaden coverage'

run_injection success success; rc=$?
[[ "$rc" -eq 0 && "$(cat "$TESTROOT/success/stamp")" == "$FULL_SHA" \
   && ! -e "$TESTROOT/success/queue/req-covered-a" \
   && ! -e "$TESTROOT/success/queue/req-covered-b" \
   && -e "$TESTROOT/success/queue/req-late" ]]
check $? 'success stamps full SHA, acknowledges snapshot, and preserves late request'

python3 - "$DEPLOY_SCRIPT" <<'PY'
import sys
text = open(sys.argv[1], encoding='utf-8').read()
def ordered(block, *needles):
    positions = [block.index(needle) for needle in needles]
    assert positions == sorted(positions), (needles, positions)
prelock = text[text.index('if [[ -d "$DEPLOY/.git" ]]'):text.index('# The lock is taken')]
ordered(prelock, 'prelock_sha=', 'prelock_queued=', 'acknowledge_deploy_requests', 'deploy_state finished docs-only')
locked_docs = text[text.index('if stamp_sha=$(docs_only_since_stamp HEAD)'):text.index('bash "${DEPLOY}/modules/gptbridge/install.sh"')]
ordered(locked_docs, 'docs_only_sha=', 'deploy_tree_readonly', 'acknowledge_deploy_requests', 'deploy_state finished docs-only')
finalizer = text[text.index('finalize_deploy_success() {'):text.index('\n}', text.index('finalize_deploy_success() {'))]
ordered(finalizer, 'od-live-report-refresh', 'publish_deploy_stamp', 'deploy_tree_readonly', 'main_checkout_ff_sync', 'acknowledge_deploy_requests')
tail = text[text.rindex('sha=$(git rev-parse --short HEAD)'):]
ordered(tail, 'finalize_deploy_success', 'deploy_state finished "$terminal_status"', 'notify_owner')
PY
check $? 'production paths acknowledge only after their fail-closed commit boundary'

(( fails == 0 )) || { printf '%s check(s) failed\n' "$fails" >&2; exit 1; }
printf 'deploy-queue-ack: all checks passed\n'
