#!/usr/bin/env bash
# Smoke tests for .semaphore/vms/batch-memo.  CI runs them before planning;
# to run them by hand:
#   .semaphore/vms/test-batch-memo.sh
#
# `artifact` is stubbed on PATH: its cache lives in a local directory and
# `pull workflow` serves a plan written by the test.
set -u
vms_dir="$(cd "$(dirname "$0")" && pwd)"
fails=0

check() {
  local desc="$1"
  shift
  if "$@"; then echo "PASS: $desc"; else
    echo "FAIL: $desc"
    fails=$((fails + 1))
  fi
}

# fails <cmd>... succeeds if <cmd> fails, keeping its stdout in $out.
fails() {
  ! "$@" >"$out" 2>/dev/null
}

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
mkdir -p "$work/bin" "$work/cache" "$work/workflow"
export STUB_CACHE="$work/cache" STUB_WORKFLOW="$work/workflow"
cat >"$work/bin/artifact" <<'STUB'
#!/usr/bin/env bash
set -e
case "$1 $2" in
  "cache store")
    key="$3"; shift 3
    mkdir -p "$(dirname "$STUB_CACHE/$key")"
    tar -cf "$STUB_CACHE/$key" -- "$@" ;;
  "cache restore")
    [ -f "$STUB_CACHE/$3" ] && tar -xf "$STUB_CACHE/$3" || echo "MISS $3"
    # <key>.fail marks an entry whose restore extracts it, then fails.
    [ ! -e "$STUB_CACHE/$3.fail" ] ;;
  "cache has_key")
    [ -f "$STUB_CACHE/$3" ] ;;
  "cache delete")
    rm -f "$STUB_CACHE/$3" ;;
  "pull workflow")
    [ -e "$STUB_WORKFLOW/$3" ] && cp -r "$STUB_WORKFLOW/$3" . ;;
  *) echo "unexpected: artifact $*" >&2; exit 1 ;;
esac
STUB
chmod +x "$work/bin/artifact"
export PATH="$work/bin:$PATH"
export CI_WORKFLOW_NAME=calico-1-pr-abcde CI_GIT_SHA=0123abc

# shellcheck source=batch-memo
source "$vms_dir/batch-memo"

write_plan() { # <scope> then "<group> <class> <key> <state>" lines on stdin
  mkdir -p "$STUB_WORKFLOW/felix-fv-plan"
  echo "$1" >"$STUB_WORKFLOW/felix-fv-plan/scope"
  cat >"$STUB_WORKFLOW/felix-fv-plan/lanes"
}

echo "== batch_memo_init"
rm -rf "$STUB_WORKFLOW/felix-fv-plan"
out="$work/out"
check "no plan: init fails" fails batch_memo_init 22.04-ipt

write_plan felix-fv-memo/pr-1 <<'EOF'
22.04-ipt nonbpf k1 miss
bpf-24.04-ipt-with-ut bpf k2 hit
EOF
batch_memo_init 22.04-ipt >/dev/null
check "lane miss: prefix set" [ "$BATCH_MEMO_PREFIX" = felix-fv-memo/pr-1/k1 ]
check "lane miss: not a lane hit" [ "$BATCH_MEMO_LANE_HIT" = false ]
batch_memo_init bpf-24.04-ipt-with-ut >/dev/null
check "lane hit" [ "$BATCH_MEMO_LANE_HIT" = true ]
batch_memo_init 26.04-unknown >/dev/null
check "unknown lane: memo off" [ -z "$BATCH_MEMO_PREFIX" ]

write_plan "" <<'EOF'
22.04-ipt nonbpf k1 miss
EOF
batch_memo_init 22.04-ipt >/dev/null
check "no scope (not a PR): memo off" [ -z "$BATCH_MEMO_PREFIX" ]
batch_memo_report "$work" 16 0 >"$out"
check "memo off: reported" grep -qE '^FV-MEMO-LANE lane=22.04-ipt outcome=off batches=16 cached=0 ran=16 result=pass .*reason=no-scope$' "$out"

echo "== skip, record, and restore"
export BATCH_MEMO_PREFIX=felix-fv-memo/pr-1/k1
run1="$work/run1/artifacts"
for b in ut 2; do
  mkdir -p "$run1/$b/report"
  echo "<testsuites/>" >"$run1/$b/report/felix_fv_$b.xml"
  echo "log" >"$run1/$b/test.log"
  batch_memo_record "$run1" "$b" >/dev/null
done
check "a recorded batch keeps only its reports" bash -c "! tar -tf '$STUB_CACHE/$BATCH_MEMO_PREFIX/batch-2' | grep -q test.log"
# Passed, but its reports never came off the VM.
mkdir -p "$run1/3"
batch_memo_record "$run1" 3 >/dev/null
check "a batch without reports is not recorded" [ ! -e "$STUB_CACHE/$BATCH_MEMO_PREFIX/batch-3" ]
# A cache entry without reports must not count as a pass.
mkdir -p "$work/bare/1" && (cd "$work/bare" && artifact cache store "$BATCH_MEMO_PREFIX/batch-1" 1)

run2="$work/run2/artifacts"
batches=(ut 1 2 3)
batch_memo_skip_cached "$run2" >/dev/null
check "cached batches are skipped" [ "${batches[*]}" = "1 3" ]
check "a restore without reports is cleared before the batch runs" [ ! -e "$run2/1" ]
batch_memo_report "$run2" 4 1 >"$out"
check "partial hit: reported" grep -qE '^FV-MEMO-LANE .* outcome=partial batches=4 cached=2 ran=2 result=fail ' "$out"
check "each skipped batch: reported" [ "$(grep -c '^FV-MEMO-BATCH .* cached-from=calico-1-pr-abcde@0123abc$' "$out")" = 2 ]
check "the lane line comes last" bash -c "tail -n1 '$out' | grep -q '^FV-MEMO-LANE '"
check "a skipped batch's report is restored" [ -f "$run2/2/report/felix_fv_2.xml" ]
check "a skipped batch says where it passed" grep -q "$CI_WORKFLOW_NAME" "$run2/ut/batch-memo-source"

batches=(4 5)
batch_memo_skip_cached "$run2" >/dev/null
check "nothing cached: every batch runs" [ "${batches[*]}" = "4 5" ]
batch_memo_report "$run2" 2 0 >"$out"
check "miss: reported" grep -qE '^FV-MEMO-LANE .* outcome=miss batches=2 cached=0 ran=2 result=pass ' "$out"

for b in 1 3; do
  mkdir -p "$run2/$b/report" && echo "<testsuites/>" >"$run2/$b/report/felix_fv_$b.xml"
done
batch_memo_record_lane "$run2" ut 1 2 3 4 >/dev/null
check "a lane with a batch lacking reports is not recorded" [ ! -e "$STUB_CACHE/$BATCH_MEMO_PREFIX/lane" ]
batch_memo_record_lane "$run2" ut 1 2 3 >/dev/null
run3="$work/run3/artifacts"
batch_memo_restore_lane "$run3" >"$out"
check "lane hit: reported" grep -qE '^FV-MEMO-LANE .* outcome=lane batches=4 cached=4 ran=0 result=pass ' "$out"
for b in ut 1 2 3; do
  check "a lane hit restores batch $b" [ -f "$run3/$b/report/felix_fv_$b.xml" ]
done

touch "$STUB_CACHE/$BATCH_MEMO_PREFIX/lane.fail"
run5="$work/run5/artifacts"
check "a lane restore that fails part way fails" fails batch_memo_restore_lane "$run5"
check "a lane that fails to restore is deleted" [ ! -e "$STUB_CACHE/$BATCH_MEMO_PREFIX/lane" ]
rm "$STUB_CACHE/$BATCH_MEMO_PREFIX/lane.fail"
batch_memo_record_lane "$run2" ut 1 2 3 >/dev/null

touch "$STUB_CACHE/$BATCH_MEMO_PREFIX/batch-2.fail"
run6="$work/run6/artifacts"
batches=(2)
batch_memo_skip_cached "$run6" >/dev/null
check "a batch restore that fails part way runs the batch" [ "${batches[*]}" = "2" ]
check "a batch restore that fails part way is cleared" [ ! -e "$run6/2" ]
rm "$STUB_CACHE/$BATCH_MEMO_PREFIX/batch-2.fail"

rm "$STUB_CACHE/$BATCH_MEMO_PREFIX/lane"
run4="$work/run4/artifacts"
check "a lane hit whose entry is gone fails" fails batch_memo_restore_lane "$run4"
check "a failed lane restore is reported" grep -qE '^FV-MEMO-LANE .* outcome=lane .*result=fail .*reason=restore-failed$' "$out"

echo
if [ "$fails" -ne 0 ]; then
  echo "$fails check(s) FAILED"
  exit 1
fi
echo "All checks passed"
