#!/usr/bin/env bash
# Fixture test for lib/deckctl/system-units.sh: root content installs only from
# module-declared paths, and every undeclared or unresolvable path fails closed.
set -euo pipefail
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
FIX=$(mktemp -d "${TMPDIR:-/tmp}/deckctl-system-units-test-XXXXXX")
trap 'rm -rf "$FIX"' EXIT
pass=0; fail=0
ok()  { echo "  PASS: $1"; pass=$((pass+1)); }
bad() { echo "  FAIL: $1"; fail=$((fail+1)); }

mk_sandbox() { # $1=dest
  mkdir -p "$1/lib/deckctl" "$1/modules"
  cp "$ROOT/lib/deckctl/system-units.sh" "$1/lib/deckctl/"
}

mk_module() { # $1=sandbox $2=name $3=descriptor-json-body
  local mdir="$1/modules/$2"
  mkdir -p "$mdir"
  printf '%s\n' "$3" >"$mdir/deck.module.json"
}

# SUDO=: makes install a no-op echo target; --dry-run keeps every action printed only.
run_units() { # $1=sandbox
  SUDO=: bash "$1/lib/deckctl/system-units.sh" --dry-run 2>&1
}

targets() { grep '^DRY: : install' | sed 's/.* //' | sort; }

# 1. declared paths install; a sibling directory the module does not declare is
#    not silently skipped but refused.
sb="$FIX/declared"; mk_sandbox "$sb"
mk_module "$sb" alpha '{"schema_version":1,"name":"alpha","kind":"config","system_units":["systemd/system"],"system_bin":["system-bin"]}'
mkdir -p "$sb/modules/alpha/systemd/system" "$sb/modules/alpha/system-bin"
touch "$sb/modules/alpha/systemd/system/a.service" "$sb/modules/alpha/system-bin/a-helper"
got=$(run_units "$sb" | targets)
want=$(printf '%s\n' /etc/systemd/system/a.service /usr/local/bin/a-helper | sort)
[[ "$got" == "$want" ]] && ok "declared paths install to their targets" || bad "declared paths: got [$got] want [$want]"

# 2. an existing unit directory no module declares must abort, not install nothing.
sb="$FIX/undeclared"; mk_sandbox "$sb"
mk_module "$sb" beta '{"schema_version":1,"name":"beta","kind":"config"}'
mkdir -p "$sb/modules/beta/systemd/system"
touch "$sb/modules/beta/systemd/system/b.service"
if out=$(run_units "$sb"); then
  bad "undeclared unit directory should abort (exit 0, output: $out)"
else
  case "$out" in *"not declared in its module"*) ok "undeclared unit directory aborts" ;;
    *) bad "undeclared unit directory aborted with wrong message: $out" ;; esac
fi

# 3. a module with no deck.module.json contributes nothing and is not scanned as
#    an implicit source.
sb="$FIX/nodescriptor"; mk_sandbox "$sb"
mkdir -p "$sb/modules/gamma/systemd/system"
touch "$sb/modules/gamma/systemd/system/g.service"
if out=$(run_units "$sb"); then
  bad "descriptorless module with unit dir should abort (output: $out)"
else
  case "$out" in *"not declared in its module"*) ok "descriptorless module aborts rather than installing" ;;
    *) bad "descriptorless module aborted with wrong message: $out" ;; esac
fi

# 4. a declared path that does not exist fails closed.
sb="$FIX/missing"; mk_sandbox "$sb"
mk_module "$sb" delta '{"schema_version":1,"name":"delta","kind":"config","system_units":["systemd/system"]}'
if out=$(run_units "$sb"); then
  bad "missing declared path should abort (output: $out)"
else
  case "$out" in *"does not exist"*) ok "missing declared path aborts" ;;
    *) bad "missing declared path aborted with wrong message: $out" ;; esac
fi

# 5. buildbox host-config is root content for a builder and must never be
#    installable onto this workstation.
sb="$FIX/hostconfig"; mk_sandbox "$sb"
mk_module "$sb" buildbox '{"schema_version":1,"name":"buildbox","kind":"config","system_units":["host-config/systemd/system"]}'
mkdir -p "$sb/modules/buildbox/host-config/systemd/system"
touch "$sb/modules/buildbox/host-config/systemd/system/box.service"
if out=$(run_units "$sb"); then
  bad "host-config declaration should abort (output: $out)"
else
  case "$out" in *"buildbox harden path"*) ok "host-config declaration is refused" ;;
    *) bad "host-config declaration aborted with wrong message: $out" ;; esac
fi

# 6. path escapes are refused before any install runs.
sb="$FIX/escape"; mk_sandbox "$sb"
mk_module "$sb" eps '{"schema_version":1,"name":"eps","kind":"config","system_units":["../../etc"]}'
if out=$(run_units "$sb"); then
  bad "path escape should abort (output: $out)"
else
  case "$out" in *"relative path inside the module"*) ok "path escape is refused" ;;
    *) bad "path escape aborted with wrong message: $out" ;; esac
fi

# 7. the real repo declares every unit directory it carries.
if out=$(SUDO=: bash "$ROOT/lib/deckctl/system-units.sh" --dry-run 2>&1); then
  ok "repo modules declare all their root content"
else
  bad "repo modules fail the opt-in check: $out"
fi

echo "system-units: $pass passed, $fail failed"
[[ $fail -eq 0 ]]
