#!/bin/sh
# One dispatcher; every fake in this directory is a symlink to it.
#
# The old version of this file did two things wrong, and both let real bugs
# through:
#
#   it always succeeded    so no test could ever reach an installer's failure
#                          path -- the apt and npm one-at-a-time retries, the
#                          "did not install" report, and cmd_install's non-zero
#                          exit were all unreachable, and all three survived
#                          being deleted outright.
#   it failed OPEN         the fakes were PREPENDED to $PATH, so a package
#                          manager with no fake here fell through to the real
#                          one. An audit of this suite invoked the host's real
#                          `brew`. A fake that is missing must be an ERROR, not
#                          a silent hand-off to the machine.
#
# The second is fixed by seal.sh, which builds a directory holding exactly the
# tools a run may touch -- fakes for anything that installs, downloads or needs
# root, real binaries for the pure ones -- and runs dotup under `env -i` with
# that directory as the WHOLE of PATH. Anything not listed is command-not-found.
#
# ---------------------------------------------------------------- failure ---
# FAKE_FAIL is a comma-separated list of injections:
#
#   FAKE_FAIL=brew              brew exits 1 every time
#   FAKE_FAIL=apt-get:batch     apt-get fails only when asked for >1 package,
#                               which is what a real "unable to locate package"
#                               inside a batch looks like: the batch dies and
#                               the one-at-a-time retry is the only thing that
#                               saves the other thirty.
#   FAKE_FAIL=npm:batch         the same shape for npm.
#   FAKE_FAIL=curl:deb          curl fails only for the .deb download.
#
# Several may be combined: FAKE_FAIL=brew,apt-get:batch
set -u
me=${0##*/}
printf '%s %s\n' "$me" "$*" >> "${DOTUP_TEST_LOG:?DOTUP_TEST_LOG unset}"

# The mode this run injects for THIS tool: "always", a tool-specific word, or
# empty for "behave".
mode=
_oldifs=$IFS
IFS=,
for _e in ${FAKE_FAIL:-}; do
	case $_e in
	"$me")   mode=always; break ;;
	"$me":*) mode=${_e#*:}; break ;;
	esac
done
IFS=$_oldifs
[ "$mode" = always ] && exit 1

# Count the package-ish arguments of a batch call: everything after the
# subcommand that is not a flag and not the flag's value.
count_pkgs() {
	n=0
	for a in "$@"; do
		case $a in
		-*|install|update|show|get|tool|-g|-y|--*) continue ;;
		*=*) continue ;;
		esac
		n=$((n + 1))
	done
	printf '%s\n' "$n"
}

case $me in
apt-get)
	case ${1:-} in update) exit 0 ;; esac
	[ "$mode" = batch ] && [ "$(count_pkgs "$@")" -gt 1 ] && exit 1
	exit 0 ;;
npm)
	[ "$mode" = batch ] && [ "$(count_pkgs "$@")" -gt 1 ] && exit 1
	exit 0 ;;
apt-cache)
	# Stand in for a real apt cache. Two names are special, and both are real
	# behaviours of stock Ubuntu that the installer has to tell apart:
	#
	#   gh         genuinely absent -- `apt-cache policy` prints nothing. The
	#              manifest's real example of a name that falls through to brew.
	#   docker-ce  present in the cache but with `Candidate: (none)`, because
	#              something else Conflicts/Replaces it. `apt-cache show` exits
	#              0 for this, which is why the old probe kept it in the batch
	#              and let apt refuse all thirty packages at once.
	case ${1:-} in
	policy)
		case ${2:-} in
		gh) exit 0 ;;
		docker-ce) printf '%s:\n  Installed: (none)\n  Candidate: (none)\n' "$2"; exit 0 ;;
		esac
		printf '%s:\n  Installed: (none)\n  Candidate: 1.0-fake\n' "$2"; exit 0 ;;
	esac
	case ${2:-} in gh) exit 100 ;; esac
	printf 'Package: %s\n' "${2:-}"; exit 0 ;;
unzip)
	# `unzip -oq <zip> -d <dir>` -- produce the one member ensure_bws looks for.
	d=; prev=
	for a in "$@"; do [ "$prev" = -d ] && { d=$a; break; }; prev=$a; done
	[ -n "$d" ] || exit 1
	mkdir -p "$d" && printf '#!/bin/sh\necho "bws 2.1.0"\n' > "$d/bws" \
		&& chmod 755 "$d/bws" && exit 0
	exit 1 ;;
dpkg)
	# install_deb fills %a in a gh: asset name from this.
	case ${1:-} in --print-architecture) echo amd64 ;; esac
	exit 0 ;;
chezmoi)
	# `chezmoi init --apply --source DIR -c CFG URL`. Produce the shape the
	# caller checks for -- a source tree with a .git in it -- and NOTHING else,
	# at the caller's umask, so a missing `chmod -R go-rwx` is visible as a mode.
	src=; prev=
	for a in "$@"; do [ "$prev" = --source ] && { src=$a; break; }; prev=$a; done
	[ -n "$src" ] || exit 0
	mkdir -p "$src/.git" && : > "$src/.git/config" && : > "$src/README" || exit 1
	exit 0 ;;
curl) . "$(dirname "$0")/_curl" ;;
fzf)
	# The picker's own fzf, and the only fake here whose ARGV is the thing
	# under test. `--bind` strings are built across a dozen continued lines, so
	# one stray comment between them silently truncates the command and the
	# picker draws with two binds instead of seven. Dump argv one argument per
	# line -- a bind that lost its continuation cannot masquerade as one that
	# survived -- and behave like an accept: read the piped rows, print nothing.
	case ${1:-} in --version) echo "${FAKE_FZF_VERSION:-9.9.9} (fake)"; exit 0 ;; esac
	[ -n "${FAKE_FZF_ARGV:-}" ] && printf '%s\n' "$@" > "$FAKE_FZF_ARGV"
	cat > /dev/null
	exit "${FAKE_FZF_RC:-0}" ;;
esac
exit 0
