227 lines
8.2 KiB
Bash
227 lines
8.2 KiB
Bash
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
# Copyright (C) 2026 Bryan Joshua Pedini
|
|
|
|
# tests/e2e/helpers.bash - shared helpers for the E2E suite (sourced by tests/e2e/*.bats).
|
|
# Runs as root inside a distro container prepared by tests/e2e/entrypoint.sh.
|
|
# shellcheck shell=bash
|
|
|
|
load '../vendor/bats-support/load'
|
|
load '../vendor/bats-assert/load'
|
|
|
|
STATE_DIR=/var/lib/skeptical-updater
|
|
STATE_FILE=$STATE_DIR/trusted.tsv
|
|
FAMILY=${SU_FAMILY:?SU_FAMILY not set (run through tests/e2e/entrypoint.sh)}
|
|
case $FAMILY in
|
|
apt | pacman) BE=$FAMILY ;;
|
|
rpm) if command -v zypper >/dev/null 2>&1; then BE=zypper; else BE=dnf; fi ;;
|
|
*) BE=unknown ;;
|
|
esac
|
|
export STATE_DIR STATE_FILE FAMILY BE
|
|
unset SKEPTICAL_UPDATER_STATE_DIR NO_COLOR
|
|
|
|
# shellcheck source=/dev/null
|
|
pretty_name() { (. /etc/os-release && printf '%s' "$PRETTY_NAME"); }
|
|
|
|
# pkg_ver NAME -> upstream version of the installed package (1.0 / 2.0) or "absent"
|
|
pkg_ver() {
|
|
local v=''
|
|
case $BE in
|
|
apt)
|
|
v=$(dpkg-query -W -f '${db:Status-Abbrev} ${Version}' "$1" 2>/dev/null) || v=''
|
|
# ${db:Status-Abbrev}: 1st char = selection (i, h = hold, ...), 2nd = state (i = installed)
|
|
if [[ ${v:1:1} == i ]]; then v=${v##* }; else v=''; fi
|
|
;;
|
|
pacman)
|
|
v=$(pacman -Q "$1" 2>/dev/null) || v=''
|
|
v=${v#* }
|
|
v=${v%-*}
|
|
;;
|
|
dnf | zypper) v=$(rpm -q --qf '%{VERSION}' "$1" 2>/dev/null) || v='' ;;
|
|
esac
|
|
printf '%s' "${v:-absent}"
|
|
}
|
|
|
|
versions() { printf 'a=%s b=%s c=%s' "$(pkg_ver su-dummy-a)" "$(pkg_ver su-dummy-b)" "$(pkg_ver su-dummy-c)"; }
|
|
|
|
# holds_snapshot: the complete native hold/lock/ignore/exclude state, as text
|
|
holds_snapshot() {
|
|
case $BE in
|
|
apt) apt-mark showhold | sort ;;
|
|
pacman)
|
|
echo "--- ignorepkg"
|
|
cat /etc/pacman.d/ignorepkg
|
|
echo "--- pacman-conf IgnorePkg"
|
|
pacman-conf IgnorePkg
|
|
;;
|
|
dnf) cat /etc/dnf/dnf.conf ;;
|
|
zypper) if [[ -e /etc/zypp/locks ]]; then cat /etc/zypp/locks; else echo "(no /etc/zypp/locks)"; fi ;;
|
|
esac
|
|
}
|
|
|
|
# hold_c: a pre-existing, admin-made hold/lock/IgnorePkg/exclude on su-dummy-c
|
|
hold_c() {
|
|
case $BE in
|
|
apt) apt-mark hold su-dummy-c >/dev/null ;;
|
|
pacman) echo 'IgnorePkg = su-dummy-c' >/etc/pacman.d/ignorepkg ;;
|
|
dnf) sed -i '/^\[main\]/a excludepkgs=su-dummy-c' /etc/dnf/dnf.conf ;;
|
|
zypper) zypper --non-interactive --quiet addlock su-dummy-c >/dev/null ;;
|
|
esac
|
|
}
|
|
|
|
e2e_setup_file() {
|
|
# remember the pristine configuration once per container
|
|
case $BE in
|
|
pacman)
|
|
# 1.0 packages for e2e_reset (re-running the fixture would rebuild the repo with new
|
|
# bytes in the same second, leaving the already synced database stale)
|
|
if [[ ! -d /srv/su-old-pacman ]]; then
|
|
mkdir -p /srv/su-old-pacman
|
|
(
|
|
# shellcheck source=fixtures/pacman.sh
|
|
. "$BATS_TEST_DIRNAME/fixtures/pacman.sh"
|
|
fx_build_tar /srv/su-old-pacman su-dummy-a 1.0 'Skeptical-updater dummy package A' su-dummy-b
|
|
fx_build_tar /srv/su-old-pacman su-dummy-b 1.0 'Skeptical-updater dummy package B (dependency of A)'
|
|
fx_build_tar /srv/su-old-pacman su-dummy-c 1.0 'Skeptical-updater dummy package C (independent)'
|
|
)
|
|
fi
|
|
;;
|
|
dnf) [[ -e /srv/su-orig-dnf.conf ]] || cp -p /etc/dnf/dnf.conf /srv/su-orig-dnf.conf ;;
|
|
zypper)
|
|
rm -f /etc/zypp/zypp.conf.d/50-su-e2e.conf /etc/zypp/locks-su-e2e
|
|
if [[ ! -e /srv/su-orig-locks.state ]]; then
|
|
if [[ -e /etc/zypp/locks ]]; then
|
|
cp -p /etc/zypp/locks /srv/su-orig-locks
|
|
echo present >/srv/su-orig-locks.state
|
|
else
|
|
echo absent >/srv/su-orig-locks.state
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# e2e_reset: back to "su-dummy-{a,b,c} 1.0 installed, 2.0 available, no holds, no statefile"
|
|
e2e_reset() {
|
|
rm -rf -- "$STATE_DIR"
|
|
case $BE in
|
|
apt)
|
|
local -a held=()
|
|
mapfile -t held < <(apt-mark showhold)
|
|
((${#held[@]} == 0)) || apt-mark unhold "${held[@]}" >/dev/null
|
|
dpkg --force-confnew -i /srv/su-old/*.deb </dev/null >/dev/null 2>&1
|
|
;;
|
|
pacman)
|
|
: >/etc/pacman.d/ignorepkg
|
|
pacman -U --noconfirm /srv/su-old-pacman/*.pkg.tar.zst >/dev/null 2>&1
|
|
pacman -D --asexplicit su-dummy-a su-dummy-c >/dev/null
|
|
pacman -D --asdeps su-dummy-b >/dev/null
|
|
;;
|
|
dnf | zypper)
|
|
if [[ $BE == dnf ]]; then
|
|
cp -p /srv/su-orig-dnf.conf /etc/dnf/dnf.conf
|
|
else
|
|
rm -f /etc/zypp/zypp.conf.d/50-su-e2e.conf /etc/zypp/locks-su-e2e
|
|
fi
|
|
if [[ $BE == dnf ]]; then
|
|
:
|
|
elif [[ $(cat /srv/su-orig-locks.state) == present ]]; then
|
|
cp -p /srv/su-orig-locks /etc/zypp/locks
|
|
else
|
|
rm -f /etc/zypp/locks
|
|
fi
|
|
rpm -Uvh --oldpackage --replacepkgs /var/tmp/su-fixture/RPMS/*/su-dummy-[abc]-1.0-1.*.rpm >/dev/null 2>&1
|
|
;;
|
|
esac
|
|
# su-dummy-b is a dependency of su-dummy-a (installed as such), on every backend
|
|
case $BE in
|
|
apt) apt-mark auto su-dummy-orphan su-dummy-b >/dev/null ;;
|
|
dnf) dnf -q mark remove su-dummy-b >/dev/null 2>&1 ;;
|
|
zypper) grep -qx su-dummy-b /var/lib/zypp/AutoInstalled 2>/dev/null || echo su-dummy-b >>/var/lib/zypp/AutoInstalled ;;
|
|
esac
|
|
[[ $(versions) == "a=1.0 b=1.0 c=1.0" && $(pkg_ver su-dummy-orphan) == 1.0 ]] || {
|
|
echo "e2e_reset: unexpected state: $(versions) orphan=$(pkg_ver su-dummy-orphan)" >&2
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# the native upgrade command line printed by --dry-run (without exclusions)
|
|
native_upgrade_regex() {
|
|
case $BE in
|
|
apt) printf '%s' '^\+ apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold -o DPkg::Lock::Timeout=60 --with-new-pkgs upgrade$' ;;
|
|
pacman) printf '%s' '^\+ pacman -Su --noconfirm$' ;; # after the reviewed databases were installed
|
|
dnf) printf '%s' '^\+ (dnf|yum) -y --setopt=skip_if_unavailable=0 --setopt=\\\*\.metadata_expire=-1 upgrade$' ;;
|
|
zypper) printf '%s' '^\+ zypper --non-interactive --no-refresh (update|dist-upgrade)$' ;;
|
|
esac
|
|
}
|
|
|
|
# rpm_publish NAME VERSION: build NAME-VERSION-1 (arch-specific for su-dummy-c, like the fixture)
|
|
# with the fixture's rpmbuild tree and add it to the su-dummy repository
|
|
rpm_publish() {
|
|
local name=$1 ver=$2 top=/var/tmp/su-fixture repo=/var/lib/su-dummy-repo spec
|
|
spec=$top/SPECS/$name-$ver.spec
|
|
{
|
|
echo "Name: $name"
|
|
echo "Version: $ver"
|
|
echo "Release: 1"
|
|
echo "Summary: skeptical-updater dummy $name (v$ver)"
|
|
echo "License: MIT"
|
|
[[ $name == su-dummy-c ]] || echo "BuildArch: noarch"
|
|
echo "%global debug_package %{nil}"
|
|
echo "%description"
|
|
echo "Dummy package $name version $ver."
|
|
echo "%install"
|
|
echo "mkdir -p %{buildroot}/usr/share/su-dummy"
|
|
echo "echo '$name $ver' > %{buildroot}/usr/share/su-dummy/$name"
|
|
echo "%files"
|
|
echo "/usr/share/su-dummy/$name"
|
|
} >"$spec"
|
|
rpmbuild -bb --quiet --define "_topdir $top" "$spec" >/dev/null 2>&1
|
|
cp "$top"/RPMS/*/"$name-$ver"-1.*.rpm "$repo"/
|
|
createrepo_c --quiet --update "$repo" >/dev/null
|
|
}
|
|
rpm_unpublish() {
|
|
rm -f /var/lib/su-dummy-repo/"$1-$2"-1.*.rpm
|
|
createrepo_c --quiet --update /var/lib/su-dummy-repo >/dev/null
|
|
}
|
|
|
|
# pacman_sync_snapshot: the real sync database directory, with full timestamps
|
|
pacman_sync_snapshot() { ls -la --time-style=full-iso /var/lib/pacman/sync 2>&1; }
|
|
|
|
# pacman_publish NAME VERSION / pacman_unpublish: add a package to the fixture's [su-dummy] repo,
|
|
# and rebuild that repo with only the fixture's 2.0 packages again
|
|
pacman_publish() {
|
|
(
|
|
# shellcheck source=fixtures/pacman.sh
|
|
. "$BATS_TEST_DIRNAME/fixtures/pacman.sh"
|
|
fx_build_tar /srv/su-repo "$1" "$2" "Skeptical-updater dummy package $1 $2"
|
|
)
|
|
(cd /srv/su-repo && repo-add --quiet su-dummy.db.tar.gz "./$1-$2-1-any.pkg.tar.zst")
|
|
}
|
|
pacman_unpublish() {
|
|
(cd /srv/su-repo && rm -f su-dummy.db* su-dummy.files* &&
|
|
for f in ./*.pkg.tar.zst; do case $f in ./su-dummy-[abc]-2.0-1-any.pkg.tar.zst) ;; *) rm -f "$f" ;; esac; done &&
|
|
repo-add --quiet su-dummy.db.tar.gz ./su-dummy-a-2.0-1-any.pkg.tar.zst ./su-dummy-b-2.0-1-any.pkg.tar.zst ./su-dummy-c-2.0-1-any.pkg.tar.zst)
|
|
}
|
|
|
|
# session_start ARGS...: run skeptical-updater ARGS in the background with a FIFO as stdin and wait
|
|
# for its prompt; sets SESSION_PID and SESSION_FD (write the input to it; fd 3 belongs to bats)
|
|
session_start() {
|
|
mkfifo "$BATS_TEST_TMPDIR/in"
|
|
skeptical-updater "$@" <"$BATS_TEST_TMPDIR/in" >"$BATS_TEST_TMPDIR/out" 2>&1 &
|
|
SESSION_PID=$!
|
|
exec {SESSION_FD}>"$BATS_TEST_TMPDIR/in"
|
|
for _ in $(seq 1 600); do
|
|
grep -q '^<enter> upgrade' "$BATS_TEST_TMPDIR/out" && return 0
|
|
sleep 0.1
|
|
done
|
|
return 1
|
|
}
|
|
# session_finish INPUT: send INPUT, close stdin, wait; sets SESSION_RC (read by the .bats files)
|
|
# shellcheck disable=SC2034
|
|
session_finish() {
|
|
printf '%s\n' "$1" >&"$SESSION_FD"
|
|
exec {SESSION_FD}>&-
|
|
SESSION_RC=0
|
|
wait "$SESSION_PID" || SESSION_RC=$?
|
|
}
|