#!/usr/bin/env bash
# Self-test for release-version-assert.sh.
#
# WHY THIS EXISTS. The assert it covers runs in the TAG-TRIGGERED release
# workflow: there is exactly one run per release, it happens after the decision
# to release has been made, and it cannot be rehearsed. An assert nobody can
# exercise before that run is an assert whose first execution is also its first
# test — and this one's failure mode is silent, because an assert that never
# compares anything exits green and reads like a pass.
#
# So the matrix below is deliberately half NEGATIVE control. It is not enough to
# show a matching version passes; it has to show that a STALE binary reds, that
# a missing artifact reds rather than being skipped, and that an empty asset list
# reds rather than reporting success over nothing.
#
# The stub "binaries" are shell scripts that echo a version string, so this runs
# anywhere — including the Windows dev box where it was written. What it does NOT
# cover is the real `spt --version` output shape; that is why the assert takes the
# LAST field rather than pattern-matching, and why it echoes the raw line.
#
# Run: bash .github/ci/release-version-assert-selftest.sh
# [unit->REQ-RELEASE-VERSION-TAG-ASSERT]
set -uo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
UNDER_TEST="$HERE/release-version-assert.sh"

fails=0
checks=0

# Builds a throwaway dist/ of stub binaries and runs the assert against it.
#   $1 label · $2 tag · $3 version the stubs report · $4 want_rc
#   $5 substring the output must contain ("" = no requirement)
#   $6 assets to CREATE (default: both)
#   $7 SPT_RELEASE_ASSERT_ASSETS override ("-" = unset)
run_case() {
  local label="$1" tag="$2" reported="$3" want_rc="$4" want_sub="$5"
  local create="${6:-spt-x86_64-linux spt-x86_64-linux-musl}"
  # `${7--}`, not `${7:--}`: an EMPTY override is a real case (it is how the
  # empty-asset-list arm is driven), so only an UNSET $7 may fall back.
  local override="${7--}"

  local work dist rc out
  work="$(mktemp -d)"
  dist="$work/dist"
  mkdir -p "$dist"
  for a in $create; do
    printf '#!/usr/bin/env bash\nprintf "%%s\\n" "%s"\n' "$reported" >"$dist/$a"
    chmod -x "$dist/$a" 2>/dev/null || true # download-artifact drops the bit
  done

  if [ "$override" = "-" ]; then
    out="$(cd "$work" && GITHUB_REF_NAME="$tag" RUNNER_TEMP="$work/tmp" \
      bash "$UNDER_TEST" dist 2>&1)"
  else
    out="$(cd "$work" && GITHUB_REF_NAME="$tag" RUNNER_TEMP="$work/tmp" \
      SPT_RELEASE_ASSERT_ASSETS="$override" bash "$UNDER_TEST" dist 2>&1)"
  fi
  rc=$?
  rm -rf "$work"

  checks=$((checks + 1))
  local ok=1
  [ "$rc" = "$want_rc" ] || ok=0
  if [ -n "$want_sub" ]; then
    case "$out" in *"$want_sub"*) ;; *) ok=0 ;; esac
  fi

  if [ "$ok" = 1 ]; then
    printf '  ok   %-44s rc=%s\n' "$label" "$rc"
  else
    printf '  FAIL %-44s rc=%s (want %s)\n' "$label" "$rc" "$want_rc"
    [ -n "$want_sub" ] && printf '       wanted substring: %s\n' "$want_sub"
    printf '       ---- output ----\n'
    printf '       %s\n' "$out"
    fails=$((fails + 1))
  fi
}

# --- the happy path --------------------------------------------------------
run_case "tag matches binary -> pass" v0.52.0 "spt 0.52.0" 0 "2/2"
run_case "prerelease tag matches" v0.52.0-rc1 "spt 0.52.0-rc1" 0 "2/2"
run_case "bare version, no name prefix" v0.52.0 "0.52.0" 0 "2/2"

# --- the defect this exists for --------------------------------------------
# A tag pushed without bumping the workspace version: the binary self-reports
# the PREVIOUS release.
run_case "stale binary (unbumped) -> fail" v0.52.0 "spt 0.51.0" 1 \
  "does not match tag"
run_case "stale binary names both values" v0.52.0 "spt 0.51.0" 1 \
  "reports version '0.51.0' but the tag is 'v0.52.0'"
run_case "stale binary names the fix" v0.52.0 "spt 0.51.0" 1 \
  "bump \`[workspace.package] version\`"
run_case "binary ahead of tag -> fail" v0.51.0 "spt 0.52.0" 1 \
  "does not match tag"

# --- degenerate inputs that must not read as a pass ------------------------
run_case "missing artifact -> fail" v0.52.0 "spt 0.52.0" 1 \
  "Release asset missing" "spt-x86_64-linux"
run_case "empty asset list -> fail" v0.52.0 "spt 0.52.0" 1 \
  "the asset list is empty" "spt-x86_64-linux spt-x86_64-linux-musl" ""
run_case "narrowed asset list is announced" v0.52.0 "spt 0.52.0" 0 \
  "NON-DEFAULT asset list in play" "spt-x86_64-linux" "spt-x86_64-linux"

# A binary that will not execute is a distinct failure from one that reports the
# wrong version, and must not be swallowed by `set -e` before its diagnostic.
work="$(mktemp -d)"
mkdir -p "$work/dist"
for a in spt-x86_64-linux spt-x86_64-linux-musl; do
  printf '#!/usr/bin/env bash\necho boom >&2\nexit 3\n' >"$work/dist/$a"
done
out="$(cd "$work" && GITHUB_REF_NAME=v0.52.0 RUNNER_TEMP="$work/tmp" \
  bash "$UNDER_TEST" dist 2>&1)"
rc=$?
rm -rf "$work"
checks=$((checks + 1))
case "$rc:$out" in
  1:*"will not run"*) printf '  ok   %-44s rc=%s\n' "unrunnable binary -> named failure" "$rc" ;;
  *)
    printf '  FAIL %-44s rc=%s (want 1 + "will not run")\n' "unrunnable binary -> named failure" "$rc"
    printf '       %s\n' "$out"
    fails=$((fails + 1))
    ;;
esac

# The tag is not optional: without it the assert has nothing to compare against
# and must refuse rather than compare against an empty string.
checks=$((checks + 1))
work="$(mktemp -d)"
mkdir -p "$work/dist"
out="$(cd "$work" && env -u GITHUB_REF_NAME bash "$UNDER_TEST" dist 2>&1)"
rc=$?
rm -rf "$work"
if [ "$rc" -ne 0 ]; then
  printf '  ok   %-44s rc=%s\n' "no tag -> refuse" "$rc"
else
  printf '  FAIL %-44s rc=%s (want non-zero)\n' "no tag -> refuse" "$rc"
  printf '       %s\n' "$out"
  fails=$((fails + 1))
fi

# --- result ----------------------------------------------------------------
# A zero-assertion run reads identically to a clean one, so assert the COUNT.
if [ "$checks" -lt 12 ]; then
  echo "release-version-assert-selftest: only $checks assertions ran — the matrix was truncated" >&2
  exit 1
fi

if [ "$fails" -ne 0 ]; then
  echo "release-version-assert-selftest: $fails/$checks assertions FAILED" >&2
  exit 1
fi
echo "release-version-assert-selftest: $checks/$checks assertions passed"
