mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 17:34:04 +01:00
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
250 lines
8.8 KiB
Bash
Executable File
250 lines
8.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lib/tests/services_harness.sh — shared fixture for the check_and_enable_services
|
|
# split.
|
|
#
|
|
# Sourced, not executed. Every command the checks reach for — systemctl,
|
|
# guardctl, lsattr, chattr, logger, sudo, find, wc — is a PATH shim recording
|
|
# its invocation into $DEV/calls, so a test asserts on what the code TRIED to
|
|
# do rather than on the state of this machine. Nothing here touches a real unit,
|
|
# a real /etc file, or a real installer.
|
|
#
|
|
# The paths the checks probe (/usr/bin/pacman, /etc/hosts, ...) are prefixed
|
|
# with $SYSROOT in every lib, so this harness points SERVICES_ROOT at a fixture
|
|
# tree under the tmpdir. A test declares "pretend /etc/hosts has 200 lines" by
|
|
# actually creating $SYSROOT/etc/hosts with 200 lines -- the checks then run
|
|
# their real logic against a filesystem this process owns.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
LIB_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
# shellcheck source=services_assert.sh
|
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/services_assert.sh"
|
|
|
|
# --- fake external tools ----------------------------------------------------
|
|
|
|
# systemctl: `is-enabled`/`is-active <unit>` succeed only when $DEV/enabled or
|
|
# $DEV/active lists the unit. Every other subcommand (restart, ...) just
|
|
# records. `--user --machine=...` is the user_systemctl shape and is answered
|
|
# from the same two files, prefixed "user:".
|
|
cat >"${FAKE_BIN}/systemctl" <<'SYSTEMCTLSHIM'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
DEV="${SERVICES_TEST_DEV}"
|
|
printf '%s\n' "systemctl $*" >>"${DEV}/calls"
|
|
prefix=""
|
|
args=()
|
|
for a in "$@"; do
|
|
case "$a" in
|
|
--user) prefix="user:" ;;
|
|
--machine=*) ;;
|
|
*) args+=("$a") ;;
|
|
esac
|
|
done
|
|
verb="${args[0]:-}"
|
|
unit="${args[1]:-}"
|
|
case "$verb" in
|
|
is-enabled) grep -qxF "${prefix}${unit}" "${DEV}/enabled" 2>/dev/null ;;
|
|
is-active) grep -qxF "${prefix}${unit}" "${DEV}/active" 2>/dev/null ;;
|
|
*) exit 0 ;;
|
|
esac
|
|
SYSTEMCTLSHIM
|
|
chmod +x "${FAKE_BIN}/systemctl"
|
|
|
|
# guardctl file-guard status <name>: prints the two lines
|
|
# guard_lib_instance_healthy greps for, but only for instances listed in
|
|
# $DEV/guard_healthy. An instance in $DEV/guard_degraded prints a status whose
|
|
# path unit is inactive; anything else exits 1 (unregistered).
|
|
cat >"${FAKE_BIN}/guardctl" <<'GUARDCTLSHIM'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
DEV="${SERVICES_TEST_DEV}"
|
|
printf '%s\n' "guardctl $*" >>"${DEV}/calls"
|
|
name="${3:-}"
|
|
if grep -qxF "$name" "${DEV}/guard_healthy" 2>/dev/null; then
|
|
printf 'path unit: active\ntarget attrs: ----i---------e---- /etc/%s\n' "$name"
|
|
exit 0
|
|
fi
|
|
if grep -qxF "$name" "${DEV}/guard_degraded" 2>/dev/null; then
|
|
printf 'path unit: inactive\ntarget attrs: ------------------ /etc/%s\n' "$name"
|
|
exit 0
|
|
fi
|
|
exit 1
|
|
GUARDCTLSHIM
|
|
chmod +x "${FAKE_BIN}/guardctl"
|
|
|
|
# lsattr: prints an attribute string for paths listed in $DEV/immutable,
|
|
# otherwise the all-dashes "no attributes" form.
|
|
cat >"${FAKE_BIN}/lsattr" <<'LSATTRSHIM'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
DEV="${SERVICES_TEST_DEV}"
|
|
target="${1:-}"
|
|
if grep -qxF "$target" "${DEV}/immutable" 2>/dev/null; then
|
|
printf '%s %s\n' '----i---------e----' "$target"
|
|
else
|
|
printf '%s %s\n' '------------------e----' "$target"
|
|
fi
|
|
exit 0
|
|
LSATTRSHIM
|
|
chmod +x "${FAKE_BIN}/lsattr"
|
|
|
|
for tool in chattr logger sudo systemd-resolved; do
|
|
cat >"${FAKE_BIN}/${tool}" <<SIMPLESHIM
|
|
#!/usr/bin/env bash
|
|
printf '%s\n' "${tool} \$*" >>"\${SERVICES_TEST_DEV}/calls"
|
|
exit 0
|
|
SIMPLESHIM
|
|
chmod +x "${FAKE_BIN}/${tool}"
|
|
done
|
|
|
|
# shellcheck source=services_path.sh
|
|
. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/services_path.sh"
|
|
|
|
present_command() { # <name>...
|
|
local name
|
|
for name in "$@"; do
|
|
printf '#!/usr/bin/env bash\nexit 0\n' >"${FAKE_BIN}/${name}"
|
|
chmod +x "${FAKE_BIN}/${name}"
|
|
done
|
|
}
|
|
|
|
absent_command() { # <name>...
|
|
local name
|
|
for name in "$@"; do
|
|
rm -f "${FAKE_BIN}/${name}"
|
|
done
|
|
}
|
|
|
|
export SERVICES_TEST_DEV="${DEV}"
|
|
export PATH="${FAKE_BIN}:${REAL_BIN}"
|
|
|
|
# --- fixture filesystem -----------------------------------------------------
|
|
|
|
# Every absolute path the check libs probe is prefixed with $SYSROOT, which they
|
|
# read from SERVICES_ROOT. Pointing it at the tmpdir is what makes the
|
|
# file-existence branches reachable: the checks run their real logic, against a
|
|
# tree this process created. SERVICES_ROOT is deliberately un-defaulted in the
|
|
# libs, so a test file that forgets this line dies instead of touching /etc.
|
|
readonly SYSROOT_DIR="${TEST_TMPDIR}/sysroot"
|
|
export SERVICES_ROOT="${SYSROOT_DIR}"
|
|
|
|
mkdir -p \
|
|
"${SYSROOT_DIR}/usr/bin" \
|
|
"${SYSROOT_DIR}/usr/local/bin" \
|
|
"${SYSROOT_DIR}/etc/systemd/system" \
|
|
"${SYSROOT_DIR}/etc/pacman.d/hooks" \
|
|
"${SYSROOT_DIR}/var/lib"
|
|
|
|
# Create a file under the fixture root, making its parent as needed.
|
|
# `sysfile etc/hosts 200` writes 200 filler lines, which is how the
|
|
# StevenBlack-list line-count check is driven.
|
|
sysfile() { # <relative-path> [line-count]
|
|
local target="${SYSROOT_DIR}/$1"
|
|
mkdir -p "$(dirname "$target")"
|
|
if [[ -n "${2:-}" ]]; then
|
|
seq 1 "$2" >"$target"
|
|
else
|
|
: >"$target"
|
|
fi
|
|
}
|
|
|
|
# Remove a file or directory from the fixture root.
|
|
sysrm() { # <relative-path>...
|
|
local rel
|
|
for rel in "$@"; do
|
|
rm -rf "${SYSROOT_DIR:?}/${rel}"
|
|
done
|
|
}
|
|
|
|
# --- globals the libs read --------------------------------------------------
|
|
|
|
RED=$'\033[0;31m'
|
|
GREEN=$'\033[0;32m'
|
|
YELLOW=$'\033[0;33m'
|
|
BLUE=$'\033[0;34m'
|
|
CYAN=$'\033[0;36m'
|
|
NC=$'\033[0m'
|
|
|
|
DRY_RUN=0
|
|
STATUS_ONLY=0
|
|
ISSUES_FOUND=0
|
|
FIXES_APPLIED=0
|
|
declare -A SERVICE_STATUS=()
|
|
declare -a MISSING_SCRIPTS=()
|
|
|
|
# Installer paths point into the tmpdir so a test controls whether the repair
|
|
# path finds its script (present) or reports it missing (absent).
|
|
#
|
|
# Exported because the check libs that read them are sourced by the individual
|
|
# test files rather than by this harness, so standalone shellcheck sees only
|
|
# the write here and reports SC2034. They genuinely cross a process boundary
|
|
# too (the require_root probe runs as a child), so export is the accurate
|
|
# declaration rather than a way to quiet the warning.
|
|
export PACMAN_WRAPPER_INSTALL="${TEST_TMPDIR}/install_pacman_wrapper.sh"
|
|
export MAKEPKG_WRAPPER_INSTALL="${TEST_TMPDIR}/install_makepkg_wrapper.sh"
|
|
export PACMAN_WRAPPER_MANIFEST="${TEST_TMPDIR}/pacman-source.sha256"
|
|
export MAKEPKG_WRAPPER_MANIFEST="${TEST_TMPDIR}/makepkg-source.sha256"
|
|
export MIDNIGHT_SHUTDOWN_SCRIPT="${TEST_TMPDIR}/setup_midnight_shutdown.sh"
|
|
export PERIODIC_SYSTEM_SCRIPT="${TEST_TMPDIR}/setup_periodic_system.sh"
|
|
export HOSTS_INSTALL_SCRIPT="${TEST_TMPDIR}/hosts_install.sh"
|
|
export GUARD_LIB_MIGRATE_SCRIPT="${TEST_TMPDIR}/migrate_hosts_guard.sh"
|
|
export COMPULSIVE_BLOCK_SCRIPT="${TEST_TMPDIR}/block_compulsive_opening.sh"
|
|
export LEECHBLOCK_SCRIPT="${TEST_TMPDIR}/install_leechblock.sh"
|
|
export REMOVE_GUEST_MODE_SCRIPT="${TEST_TMPDIR}/remove_guest_mode.sh"
|
|
export VBOX_HOSTS_SCRIPT="${TEST_TMPDIR}/enforce_vbox_hosts.sh"
|
|
export WORKOUT_LOCKER_INSTALL_SCRIPT="${TEST_TMPDIR}/install_systemd.sh"
|
|
export WORKOUT_LOCKER_SCRIPT="${TEST_TMPDIR}/screen_lock.py"
|
|
|
|
# Create an installer stub that records being run, so "did the repair path fire"
|
|
# is observable without any real installation happening.
|
|
make_installer() { # <path>
|
|
printf '#!/usr/bin/env bash\nprintf "%%s\\n" "ran %s $*" >>"%s/calls"\nexit 0\n' \
|
|
"$(basename "$1")" "${DEV}" >"$1"
|
|
chmod +x "$1"
|
|
}
|
|
|
|
# shellcheck source=../services_common.sh
|
|
. "${LIB_DIR}/services_common.sh"
|
|
|
|
# Stage a fully healthy blocking stack: a long hosts file, the guard-lib trio
|
|
# active, both pacman hooks, and a resolved.conf that reads /etc/hosts.
|
|
stage_hosts_ok() {
|
|
sysfile etc/hosts 200
|
|
sysfile etc/hosts.stevenblack 200
|
|
printf '%s\n' "${SERVICES_ROOT}/etc/hosts" >"${DEV}/immutable"
|
|
printf 'hosts: files resolve dns\n' >"${SERVICES_ROOT}/etc/nsswitch.conf"
|
|
printf '[Resolve]\nReadEtcHosts=yes\n' >"${SERVICES_ROOT}/etc/systemd/resolved.conf"
|
|
printf '%s\n' hosts nsswitch resolved >"${DEV}/guard_healthy"
|
|
sysfile etc/pacman.d/hooks/10-guard-lib-unlock-all.hook
|
|
sysfile etc/pacman.d/hooks/90-guard-lib-relock-all.hook
|
|
}
|
|
|
|
# reset_state — return every mutable global and every $DEV fact to "nothing has
|
|
# happened yet", so each test group starts from a known machine.
|
|
reset_state() {
|
|
rm -f "${DEV:?}"/* 2>/dev/null || true
|
|
rm -rf "${SYSROOT_DIR:?}"
|
|
mkdir -p \
|
|
"${SYSROOT_DIR}/usr/bin" \
|
|
"${SYSROOT_DIR}/usr/local/bin" \
|
|
"${SYSROOT_DIR}/etc/systemd/system" \
|
|
"${SYSROOT_DIR}/etc/pacman.d/hooks" \
|
|
"${SYSROOT_DIR}/var/lib"
|
|
: >"${DEV}/calls"
|
|
DRY_RUN=0
|
|
STATUS_ONLY=0
|
|
ISSUES_FOUND=0
|
|
FIXES_APPLIED=0
|
|
SERVICE_STATUS=()
|
|
MISSING_SCRIPTS=()
|
|
# Staged fake executables are part of the machine a test describes, so they
|
|
# reset too. Without this a `present_command discord` leaks into every later
|
|
# group, which makes a forgetful test pass for the wrong reason.
|
|
rm -f "${FAKE_BIN:?}"/beeper "${FAKE_BIN}"/signal-desktop "${FAKE_BIN}"/discord \
|
|
"${FAKE_BIN}"/thorium-browser "${FAKE_BIN}"/chromium \
|
|
"${FAKE_BIN}"/google-chrome "${FAKE_BIN}"/brave-browser \
|
|
"${FAKE_BIN}"/VBoxManage
|
|
}
|
|
reset_state
|