#!/usr/bin/env bash
set -uo pipefail

LIB="$(cd "$(dirname "$0")" && pwd)/provision-deps.sh"
PASS=0
FAIL=0

ok() { PASS=$((PASS + 1)); printf '  ok   %s\n' "$1"; }
bad() { FAIL=$((FAIL + 1)); printf '  FAIL %s\n     %s\n' "$1" "$2"; }

run_detect() {
  bash "$LIB" detect "$1"
}

run_provision() {
  bash "$LIB" provision "$1"
}

run_repair() {
  bash "$LIB" repair "$1"
}

run_warm() {
  bash "$LIB" warm "$1"
}

cache_entry_count() {
  local repo_root="$1"
  find "$repo_root/.runplan-cache/deps" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' '
}

cache_entry_dirs() {
  local repo_root="$1"
  find "$repo_root/.runplan-cache/deps" -mindepth 1 -maxdepth 1 -type d | sort
}

cache_entry_path_for_workspace() {
  local repo_root="$1"
  local workspace="$2"
  local lockfile_path="$workspace/package-lock.json"
  node - "$workspace" "$lockfile_path" <<'EOF'
const crypto = require("crypto");
const fs = require("fs");

try {
  const [, , workspace, lockfilePath] = process.argv;
  const packageJson = JSON.parse(fs.readFileSync(`${workspace}/package.json`, "utf8"));
  const packageManager = typeof packageJson.packageManager === "string" && packageJson.packageManager ? packageJson.packageManager : "npm";
  const hash = crypto.createHash("sha256");
  hash.update(String(packageManager));
  hash.update("\0");
  hash.update(process.version);
  hash.update("\0");
  hash.update(process.arch);
  hash.update("\0");
  hash.update(fs.readFileSync(lockfilePath));
  process.stdout.write(hash.digest("hex"));
} catch (error) {
  process.exit(1);
}
EOF
}

set_cache_mtime() {
  local path="$1"
  local stamp="$2"
  touch -t "$stamp" "$path"
}

write_payload_bytes() {
  local path="$1"
  local size="$2"
  node - "$path" "$size" <<'EOF'
const fs = require("fs");

try {
  const [, , targetPath, sizeRaw] = process.argv;
  const size = Number(sizeRaw);
  fs.writeFileSync(targetPath, Buffer.alloc(size, "x"));
} catch (error) {
  process.exit(1);
}
EOF
}

init_git_repo() {
  local repo_root="$1"
  git -C "$repo_root" init -q
}

cleanup_tmpdir() {
  local tmpdir="$1"
  chmod -R u+w "$tmpdir" 2>/dev/null || true
  rm -rf "$tmpdir"
}

write_package_json() {
  local dir="$1"
  local package_manager="${2:-}"

  if [[ -n "$package_manager" ]]; then
    cat >"$dir/package.json" <<EOF
{
  "name": "fixture",
  "packageManager": "$package_manager"
}
EOF
  else
    cat >"$dir/package.json" <<'EOF'
{
  "name": "fixture"
}
EOF
  fi
}

write_stub_installer() {
  local dir="$1"
  local name="$2"
  cat >"$dir/$name" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$PWD" > .install-cwd
printf '%s\n' "$*" > .install-args
mkdir -p node_modules
printf 'cached' > node_modules/from-cache.txt
EOF
  chmod +x "$dir/$name"
}

write_logging_manager() {
  local dir="$1"
  local name="$2"
  cat >"$dir/$name" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s|%s|%s\n' "$0" "$PWD" "$*" >> "${RUNPLAN_MANAGER_LOG:?}"
EOF
  chmod +x "$dir/$name"
}

echo "provision-deps.sh tests:"

if [[ -x "$LIB" ]]; then
  ok "script exists and is executable"
else
  bad "script exists and is executable" "missing or non-executable: $LIB"
fi

tmpdir=$(mktemp -d)
write_package_json "$tmpdir" "pnpm@9.0.0"
touch "$tmpdir/yarn.lock"
out="$(run_detect "$tmpdir" 2>/dev/null)" || rc=$?
rc="${rc:-0}"
if [[ "$rc" -eq 0 && "$out" == "pnpm" ]]; then
  ok "packageManager beats conflicting yarn.lock"
else
  bad "packageManager beats conflicting yarn.lock" "exit=$rc out=$out"
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
init_git_repo "$tmpdir"
mkdir -p "$tmpdir/apps/web"
write_package_json "$tmpdir" "pnpm@10.33.0"
write_package_json "$tmpdir/apps/web"
touch "$tmpdir/pnpm-lock.yaml"
out="$(run_detect "$tmpdir/apps/web" 2>/dev/null)" || rc=$?
rc="${rc:-0}"
if [[ "$rc" -eq 0 && "$out" == "pnpm" ]]; then
  ok "detects repository packageManager from nested workspace"
else
  bad "detects repository packageManager from nested workspace" "exit=$rc out=$out"
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
write_package_json "$tmpdir"
touch "$tmpdir/pnpm-lock.yaml" "$tmpdir/package-lock.json"
out="$(run_detect "$tmpdir" 2>/dev/null)" || rc=$?
rc="${rc:-0}"
if [[ "$rc" -eq 0 && "$out" == "pnpm" ]]; then
  ok "lockfile precedence prefers pnpm over npm"
else
  bad "lockfile precedence prefers pnpm over npm" "exit=$rc out=$out"
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
write_package_json "$tmpdir"
touch "$tmpdir/yarn.lock"
out="$(run_detect "$tmpdir" 2>/dev/null)" || rc=$?
rc="${rc:-0}"
if [[ "$rc" -eq 0 && "$out" == "yarn" ]]; then
  ok "detects yarn from lockfile"
else
  bad "detects yarn from lockfile" "exit=$rc out=$out"
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
write_package_json "$tmpdir" "pnpm@9.0.0"
touch "$tmpdir/pnpm-lock.yaml"
if run_repair "$tmpdir" >/dev/null 2>&1; then
  bad "repair refuses online work without RUNPLAN_ALLOW_ONLINE" "expected failure"
else
  rc=$?
  if [[ "$rc" -eq 2 ]]; then
    ok "repair refuses online work without RUNPLAN_ALLOW_ONLINE"
  else
    bad "repair refuses online work without RUNPLAN_ALLOW_ONLINE" "exit=$rc"
  fi
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/bin"
write_package_json "$tmpdir" "pnpm@9.0.0"
touch "$tmpdir/pnpm-lock.yaml"
write_logging_manager "$tmpdir/bin" "pnpm"
manager_log="$tmpdir/manager.log"
out="$(RUNPLAN_ALLOW_ONLINE=1 RUNPLAN_MANAGER_LOG="$manager_log" PATH="$tmpdir/bin:$PATH" run_repair "$tmpdir" 2>/dev/null)" || rc=$?
rc="${rc:-0}"
if [[ "$rc" -eq 0 ]] \
  && grep -q "|$tmpdir|install --lockfile-only$" "$manager_log" \
  && grep -q "|$tmpdir|install --force --frozen-lockfile$" "$manager_log" \
  && grep -qx "repaired: lockfile" <<<"$out" \
  && grep -qx "repaired: store" <<<"$out"; then
  ok "repair invokes pnpm lockfile-only install then store warm"
else
  bad "repair invokes pnpm lockfile-only install then store warm" "exit=$rc out=$out log=$(cat "$manager_log" 2>/dev/null)"
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/bin"
write_package_json "$tmpdir" "pnpm@9.0.0"
touch "$tmpdir/pnpm-lock.yaml"
write_logging_manager "$tmpdir/bin" "pnpm"
manager_log="$tmpdir/manager.log"
out="$(RUNPLAN_ALLOW_ONLINE=1 RUNPLAN_MANAGER_LOG="$manager_log" PATH="$tmpdir/bin:$PATH" run_warm "$tmpdir" 2>/dev/null)" || rc=$?
rc="${rc:-0}"
if [[ "$rc" -eq 0 ]] \
  && grep -q "|$tmpdir|install --force --frozen-lockfile$" "$manager_log" \
  && ! grep -q "lockfile-only" "$manager_log" \
  && [[ "$out" == "repaired: store" ]]; then
  ok "warm invokes pnpm store warm without touching lockfile"
else
  bad "warm invokes pnpm store warm without touching lockfile" "exit=$rc out=$out log=$(cat "$manager_log" 2>/dev/null)"
fi
cleanup_tmpdir "$tmpdir"
unset rc

tmpdir=$(mktemp -d)
if run_detect "$tmpdir" >/dev/null 2>&1; then
  bad "detect fails closed when package manager is unknown" "expected failure"
else
  rc=$?
  if [[ "$rc" -eq 17 ]]; then
    ok "detect fails closed when package manager is unknown"
  else
    bad "detect fails closed when package manager is unknown" "exit=$rc"
  fi
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/bin"
cat >"$tmpdir/bin/npm" <<'EOF'
#!/usr/bin/env bash
exit 9
EOF
chmod +x "$tmpdir/bin/npm"
touch "$tmpdir/package-lock.json"
if PATH="$tmpdir/bin:$PATH" run_provision "$tmpdir" >/dev/null 2>&1; then
  bad "provision maps install failure to exit 17" "expected failure"
else
  rc=$?
  if [[ "$rc" -eq 17 ]]; then
    ok "provision maps install failure to exit 17"
  else
    bad "provision maps install failure to exit 17" "exit=$rc"
  fi
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
mkdir -p "$tmpdir/bin"
write_package_json "$tmpdir" "pnpm@9.0.0"
touch "$tmpdir/pnpm-lock.yaml"
cat >"$tmpdir/bin/pnpm" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >> "${RUNPLAN_MANAGER_LOG:?}"
if [[ "$*" == "store prune" ]]; then
  exit 0
fi
attempt_file="${RUNPLAN_MANAGER_LOG}.attempt"
if [[ ! -f "$attempt_file" ]]; then
  touch "$attempt_file"
  echo 'ERR_PNPM_TARBALL_INTEGRITY Got unexpected checksum' >&2
  exit 1
fi
mkdir -p node_modules
EOF
chmod +x "$tmpdir/bin/pnpm"
manager_log="$tmpdir/manager.log"
if RUNPLAN_ALLOW_ONLINE=1 RUNPLAN_MANAGER_LOG="$manager_log" PATH="$tmpdir/bin:$PATH" run_provision "$tmpdir" >/dev/null 2>&1 \
  && [[ "$(grep -c '^install --frozen-lockfile$' "$manager_log")" -eq 1 ]] \
  && [[ "$(grep -c '^install --no-frozen-lockfile --fix-lockfile$' "$manager_log")" -eq 1 ]] \
  && [[ "$(grep -c '^store prune$' "$manager_log")" -eq 1 ]]; then
  ok "online pnpm provision prunes stale metadata and repairs stale lockfile integrity"
else
  rc=$?
  bad "online pnpm provision prunes stale metadata and repairs stale lockfile integrity" "exit=$rc log=$(cat "$manager_log" 2>/dev/null)"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
init_git_repo "$repo_root"
write_package_json "$workspace" "pnpm@9.0.0"
touch "$workspace/pnpm-lock.yaml"
write_stub_installer "$tmpdir/bin" "pnpm"
if RUNPLAN_ALLOW_ONLINE=1 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  install_args="$(cat "$workspace/.install-args" 2>/dev/null || true)"
  if [[ "$install_args" == "install --frozen-lockfile" ]]; then
    ok "explicit online provision keeps lockfile immutable without offline mode"
  else
    bad "explicit online provision keeps lockfile immutable without offline mode" "args=${install_args:-<empty>}"
  fi
else
  rc=$?
  bad "explicit online provision keeps lockfile immutable without offline mode" "unexpected exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3}
EOF
cat >"$tmpdir/bin/npm" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf 'up to date, audited 1 package\n'
EOF
chmod +x "$tmpdir/bin/npm"
if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  if [[ -d "$workspace/node_modules" && ! -L "$workspace/node_modules" ]]; then
    ok "zero-dependency install provisions an empty node_modules"
  else
    bad "zero-dependency install provisions an empty node_modules" "node_modules missing or symlinked"
  fi
else
  rc=$?
  bad "zero-dependency install provisions an empty node_modules" "exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3}
EOF
write_stub_installer "$tmpdir/bin" "npm"
if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  cache_root="$repo_root/.runplan-cache/deps"
  materialized_file="$(find "$cache_root" -path '*/node_modules/from-cache.txt' | head -n 1)"
  if [[ -d "$workspace/node_modules" && ! -L "$workspace/node_modules" ]]; then
    ok "provision materializes cached node_modules into workspace"
  else
    bad "provision materializes cached node_modules into workspace" "node_modules missing or symlinked"
  fi

  if [[ -f "$workspace/node_modules/from-cache.txt" ]]; then
    ok "cached node_modules content is reachable from workspace"
  else
    bad "cached node_modules content is reachable from workspace" "missing from-cache.txt"
  fi

  install_args="$(find "$cache_root" -path '*/.install-args' -exec cat {} \; 2>/dev/null | head -n 1)"
  if [[ "$install_args" == "ci --offline" ]]; then
    ok "npm provision uses offline frozen install flags"
  else
    bad "npm provision uses offline frozen install flags" "args=${install_args:-<empty>}"
  fi

  if [[ -n "$materialized_file" && ! -w "$(dirname "$materialized_file")" ]]; then
    ok "cache entry is read-only after populate"
  else
    bad "cache entry is read-only after populate" "node_modules still writable"
  fi

  leftover_tmp="$(find "$cache_root" -mindepth 1 -maxdepth 1 -type d -name '.tmp-*' | head -n 1)"
  if [[ -z "$leftover_tmp" ]]; then
    ok "atomic populate leaves no temp cache directories"
  else
    bad "atomic populate leaves no temp cache directories" "leftover=$leftover_tmp"
  fi

  cache_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
  cache_entry="$repo_root/.runplan-cache/deps/$cache_key"
  chmod -R u+w "$cache_entry"
  rm -f "$cache_entry/.runplan-cache-complete" "$cache_entry/node_modules/from-cache.txt"
  if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1 \
    && [[ -f "$workspace/node_modules/from-cache.txt" ]]; then
    ok "incomplete cache entries are repopulated before materialization"
  else
    rc=$?
    bad "incomplete cache entries are repopulated before materialization" "exit=$rc"
  fi
else
  rc=$?
  bad "provision materializes cached node_modules into workspace" "unexpected exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"1.0.0"}}}
EOF
write_stub_installer "$tmpdir/bin" "npm"
if RUNPLAN_CACHE_GC_KEEP=2 RUNPLAN_CACHE_GC_MAX_BYTES=1048576 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  first_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
  first_entry="$repo_root/.runplan-cache/deps/$first_key"
  set_cache_mtime "$first_entry" 202401010101
  cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"2.0.0"}}}
EOF
  if RUNPLAN_CACHE_GC_KEEP=2 RUNPLAN_CACHE_GC_MAX_BYTES=1048576 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
    second_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
    second_entry="$repo_root/.runplan-cache/deps/$second_key"
    set_cache_mtime "$second_entry" 202402020202
    cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"3.0.0"}}}
EOF
    if RUNPLAN_CACHE_GC_KEEP=2 RUNPLAN_CACHE_GC_MAX_BYTES=1048576 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
      third_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
      third_entry="$repo_root/.runplan-cache/deps/$third_key"
      count="$(cache_entry_count "$repo_root")"
      if [[ "$count" == "2" && ! -d "$first_entry" && -d "$second_entry" && -d "$third_entry" ]]; then
        ok "cache GC drops least-recently-used entries beyond keep count"
      else
        bad "cache GC drops least-recently-used entries beyond keep count" "count=$count entries=$(cache_entry_dirs "$repo_root" | tr '\n' ' ')"
      fi
    else
      rc=$?
      bad "cache GC drops least-recently-used entries beyond keep count" "third provision exit=$rc"
    fi
  else
    rc=$?
    bad "cache GC drops least-recently-used entries beyond keep count" "second provision exit=$rc"
  fi
else
  rc=$?
  bad "cache GC drops least-recently-used entries beyond keep count" "first provision exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"1.0.0"}}}
EOF
write_stub_installer "$tmpdir/bin" "npm"
if RUNPLAN_CACHE_GC_KEEP=10 RUNPLAN_CACHE_GC_MAX_BYTES=250 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  first_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
  first_entry="$repo_root/.runplan-cache/deps/$first_key"
  chmod -R u+w "$first_entry"
  write_payload_bytes "$first_entry/node_modules/payload.bin" 400
  chmod -R a-w "$first_entry"
  set_cache_mtime "$first_entry" 202401010101
  cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"2.0.0"}}}
EOF
  if RUNPLAN_CACHE_GC_KEEP=10 RUNPLAN_CACHE_GC_MAX_BYTES=250 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
    second_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
    second_entry="$repo_root/.runplan-cache/deps/$second_key"
    count="$(cache_entry_count "$repo_root")"
    if [[ "$count" == "1" && ! -d "$first_entry" && -d "$second_entry" ]]; then
      ok "cache GC enforces byte cap after sorting by recency"
    else
      bad "cache GC enforces byte cap after sorting by recency" "count=$count entries=$(cache_entry_dirs "$repo_root" | tr '\n' ' ')"
    fi
  else
    rc=$?
    bad "cache GC enforces byte cap after sorting by recency" "second provision exit=$rc"
  fi
else
  rc=$?
  bad "cache GC enforces byte cap after sorting by recency" "first provision exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"1.0.0"}}}
EOF
write_stub_installer "$tmpdir/bin" "npm"
if RUNPLAN_CACHE_GC_KEEP=1 RUNPLAN_CACHE_GC_MAX_BYTES=120 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  first_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
  first_entry="$repo_root/.runplan-cache/deps/$first_key"
  chmod -R u+w "$first_entry"
  write_payload_bytes "$first_entry/node_modules/payload.bin" 300
  chmod -R a-w "$first_entry"
  set_cache_mtime "$first_entry" 202401010101
  cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"2.0.0"}}}
EOF
  if RUNPLAN_CACHE_GC_KEEP=0 RUNPLAN_CACHE_GC_MAX_BYTES=1 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
    second_key="$(cache_entry_path_for_workspace "$repo_root" "$workspace")"
    second_entry="$repo_root/.runplan-cache/deps/$second_key"
    count="$(cache_entry_count "$repo_root")"
    if [[ "$count" == "1" && ! -d "$first_entry" && -d "$second_entry" ]]; then
      ok "cache GC never evicts the live workspace key"
    else
      bad "cache GC never evicts the live workspace key" "count=$count entries=$(cache_entry_dirs "$repo_root" | tr '\n' ' ')"
    fi
  else
    rc=$?
    bad "cache GC never evicts the live workspace key" "second provision exit=$rc"
  fi
else
  rc=$?
  bad "cache GC never evicts the live workspace key" "first provision exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3}
EOF
write_stub_installer "$tmpdir/bin" "npm"
mkdir -p "$repo_root/.runplan-cache/deps"
mkdir -p "$repo_root/.runplan-cache/deps/orphan.tmp.123/node_modules"
printf 'stale' >"$repo_root/.runplan-cache/deps/orphan.tmp.123/node_modules/stale.txt"
if RUNPLAN_CACHE_GC_KEEP=5 RUNPLAN_CACHE_GC_MAX_BYTES=1048576 PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  if [[ ! -e "$repo_root/.runplan-cache/deps/orphan.tmp.123" ]]; then
    ok "cache GC sweeps orphan temp directories"
  else
    bad "cache GC sweeps orphan temp directories" "leftover=$repo_root/.runplan-cache/deps/orphan.tmp.123"
  fi
else
  rc=$?
  bad "cache GC sweeps orphan temp directories" "unexpected exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"1.0.0"}}}
EOF
write_stub_installer "$tmpdir/bin" "npm"
if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  mapfile -t cache_targets < <(find "$repo_root/.runplan-cache/deps" -path '*/node_modules' | sort)
  first_target="${cache_targets[0]:-}"
  first_count="$(cache_entry_count "$repo_root")"
  cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3,"packages":{"":{"version":"2.0.0"}}}
EOF
  if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
    mapfile -t cache_targets < <(find "$repo_root/.runplan-cache/deps" -path '*/node_modules' | sort)
    second_target="${cache_targets[1]:-}"
    second_count="$(cache_entry_count "$repo_root")"
    if [[ "$first_target" != "$second_target" && "$first_count" == "1" && "$second_count" == "2" ]]; then
      ok "cache key changes when lockfile content changes"
    else
      bad "cache key changes when lockfile content changes" "first=$first_target second=$second_target counts=$first_count/$second_count"
    fi
  else
    rc=$?
    bad "cache key changes when lockfile content changes" "second provision exit=$rc"
  fi
else
  rc=$?
  bad "cache key changes when lockfile content changes" "first provision exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace"
cat >"$workspace/package-lock.json" <<'EOF'
{"name":"fixture","lockfileVersion":3}
EOF
write_stub_installer "$tmpdir/bin" "npm"
cat >"$tmpdir/bin/cp" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
echo "$*" >> "${RUNPLAN_CP_LOG:?}"
if [[ "${1:-}" == "--reflink=always" ]]; then
  exit 1
fi
if [[ "${1:-}" == "-al" ]]; then
  /bin/cp -a "$2" "$3"
  exit 0
fi
exec /bin/cp "$@"
EOF
chmod +x "$tmpdir/bin/cp"
cp_log="$tmpdir/cp.log"
if RUNPLAN_CP_LOG="$cp_log" PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  if grep -q '^--reflink=always ' "$cp_log" && grep -q '^-al ' "$cp_log"; then
    ok "materialize falls back from reflink to hardlink-copy probe"
  else
    bad "materialize falls back from reflink to hardlink-copy probe" "log=$(cat "$cp_log" 2>/dev/null)"
  fi

  : >"$cp_log"
  rm -rf "$workspace/node_modules"
  if RUNPLAN_CP_LOG="$cp_log" PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
    if grep -q '^-al ' "$cp_log" && ! grep -q '^--reflink=always ' "$cp_log"; then
      ok "materialize probe result is memoized after first success"
    else
      bad "materialize probe result is memoized after first success" "log=$(cat "$cp_log" 2>/dev/null)"
    fi
  else
    rc=$?
    bad "materialize probe result is memoized after first success" "second provision exit=$rc"
  fi
else
  rc=$?
  bad "materialize falls back from reflink to hardlink-copy probe" "unexpected exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace" "pnpm@9.0.0"
touch "$workspace/pnpm-lock.yaml"
write_stub_installer "$tmpdir/bin" "pnpm"
if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  if [[ ! -d "$repo_root/.runplan-cache/deps" && -d "$workspace/node_modules" && ! -L "$workspace/node_modules" ]]; then
    ok "pnpm delegates to native store without repo cache linkage"
  else
    bad "pnpm delegates to native store without repo cache linkage" "cache=$(find "$repo_root/.runplan-cache" -mindepth 1 2>/dev/null | head -n 1) node_modules=$(test -e "$workspace/node_modules" && stat -c %F "$workspace/node_modules")"
  fi

  install_args="$(cat "$workspace/.install-args" 2>/dev/null || true)"
  if [[ "$install_args" == "install --frozen-lockfile --offline" ]]; then
    ok "pnpm provision keeps native offline immutable install flags"
  else
    bad "pnpm provision keeps native offline immutable install flags" "args=${install_args:-<empty>}"
  fi
else
  rc=$?
  bad "pnpm delegates to native store without repo cache linkage" "unexpected exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

tmpdir=$(mktemp -d)
repo_root="$tmpdir/repo"
workspace="$repo_root/packages/app"
mkdir -p "$workspace/.yarn/cache" "$tmpdir/bin"
mkdir -p "$repo_root"
init_git_repo "$repo_root"
write_package_json "$workspace" "yarn@4.1.0"
touch "$workspace/yarn.lock"
cat >"$workspace/.yarnrc.yml" <<'EOF'
nodeLinker: pnp
EOF
cat >"$tmpdir/bin/yarn" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$PWD" > .install-cwd
printf '%s\n' "$*" > .install-args
printf 'pnp' > .pnp.cjs
EOF
chmod +x "$tmpdir/bin/yarn"
if PATH="$tmpdir/bin:$PATH" run_provision "$workspace" >/dev/null 2>&1; then
  if [[ ! -d "$repo_root/.runplan-cache/deps" && ! -e "$workspace/node_modules" && -f "$workspace/.pnp.cjs" ]]; then
    ok "yarn berry delegates to native store without assuming node_modules"
  else
    bad "yarn berry delegates to native store without assuming node_modules" "cache=$(find "$repo_root/.runplan-cache" -mindepth 1 2>/dev/null | head -n 1) node_modules=$(test -e "$workspace/node_modules" && stat -c %F "$workspace/node_modules") pnp=$(test -f "$workspace/.pnp.cjs" && echo yes || echo no)"
  fi

  install_args="$(cat "$workspace/.install-args" 2>/dev/null || true)"
  if [[ "$install_args" == "install --immutable --offline" ]]; then
    ok "yarn berry provision keeps native immutable install flags"
  else
    bad "yarn berry provision keeps native immutable install flags" "args=${install_args:-<empty>}"
  fi
else
  rc=$?
  bad "yarn berry delegates to native store without assuming node_modules" "unexpected exit=$rc"
fi
cleanup_tmpdir "$tmpdir"

if bash "$LIB" nope >/dev/null 2>&1; then
  bad "invalid subcommand exits 2" "expected failure"
else
  rc=$?
  if [[ "$rc" -eq 2 ]]; then
    ok "invalid subcommand exits 2"
  else
    bad "invalid subcommand exits 2" "exit=$rc"
  fi
fi

if grep -qxF '.runplan-cache/' .gitignore; then
  ok ".gitignore excludes .runplan-cache/"
else
  bad ".gitignore excludes .runplan-cache/" "$(cat .gitignore)"
fi

echo
echo "PASS=$PASS FAIL=$FAIL"
[[ "$FAIL" -eq 0 ]]
