testsAndMisc/linux_configuration/features/lib/tests/test_rpi_nc_ca.sh
Krzysztof kuhy Rudnicki 4f2b0ccdf7 Dissolve linux_configuration/scripts/ and single_use/
Two levels of pure taxonomy carrying no information, and a 2-directory
cap leaves no room for them. Categories now sit directly under
linux_configuration/: features, fixes, utils, misc, fresh-install,
installers, diagnostics, gaming, boot_recovery, periodic_background, lib,
meta, tests. A script's own lib/ and tests/ are exempt from the cap, so
those stay put.

Depth violations 248 -> 139; roughly 40 of the remainder are inside
digital_wellbeing and hosts, deferred until the guard-lib repoint.

The moves are the easy half. What breaks is every path that counted
levels, and three classes of it had to be found by resolving paths rather
than reading diffs:

  - 52 relative `source` lines for lib/common.sh and lib/android.sh. The
    correct prefix differs per file: 40 were ../../ and 12 were ../../../,
    so a blanket replace would have fixed one set and broken the other.
  - REPO_ROOT in 7 fixes/lib/tests harnesses and setup_syncyomi_guard.sh,
    each walking up four levels from a directory now two levels shallower.
    This caused a real regression -- the fixes suite went red with
    "log_warn: command not found" -- caught by running it and comparing
    against the pre-move baseline worktree.
  - conftest.py's sys.path entries, which took the pytest gate down with
    14 collection errors, and the `# shellcheck source=` directives, which
    took shellcheck down with SC1091.

16 symlinks in $HOME broke, including the entire i3blocks bar. All
repointed, and cpu_monitor/memory/disk/time/volume were run to confirm
they emit real output rather than merely resolving.

Verified: 1798 tests pass at 100% coverage, 8 of 9 lib/tests suites pass
(the ninth needs the coverage jail, identically at baseline), pre-commit
is clean, and the two untracked secret configs are still ignored after
their gitignore paths changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 18:53:30 +02:00

209 lines
8.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# Tests for rpi_nc_ca.sh — installing the Pi's CA into the client's trust stores.
#
# The subject sudo-copies into /etc/ca-certificates, /usr/local/share and
# /etc/pki, and appends to /etc/hosts. Those writes are the point, so they run
# for real inside the jail, which bind-mounts every one of those paths to a
# throwaway dir. See jail_args beside this file.
#
# The five OS branches are selected by SEEDING the marker file each one tests
# for (/etc/arch-release, /etc/debian_version, ...) inside the jailed /etc.
# That is only safe because /etc is bind-mounted: on a real host this would
# make the machine claim to be a different distribution.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./features_harness.sh
. "$SCRIPT_DIR/features_harness.sh"
# --- Refuse to run outside the jail -----------------------------------------
# The suite seeds /etc/arch-release and friends and appends to /etc/hosts.
# An unwritable /etc means we are NOT contained.
if [[ ! -w /etc ]]; then
printf 'REFUSING: this suite must run under shell_coverage_jail.sh\n' >&2
exit 1
fi
_t_setup_env
trap _t_teardown EXIT
# Globals the entry script defines above its source line.
PI_PASSWORD="hunter2"
PI_USER="pi"
PI_HOSTNAME="raspberrypi"
log_info() { printf '[INFO] %s\n' "$*"; }
log_success() { printf '[OK] %s\n' "$*"; }
log_warning() { printf '[WARN] %s\n' "$*"; }
die() {
printf '[DIE] %s\n' "$*" >&2
exit 1
}
discover_raspberry_pi() { printf '10.0.0.7\n'; }
# sshpass writes the CA to stdout, which the subject redirects into the file.
# A record-only stub would leave an EMPTY file and the subject would die --
# a stub must materialise what the real tool produces.
_t_ssh_emits_ca() {
cat >"$TEST_TMPDIR/bin/sshpass" <<'SSHPASS'
#!/usr/bin/env bash
printf -- '-----BEGIN CERTIFICATE-----\nFAKECA\n-----END CERTIFICATE-----\n'
SSHPASS
chmod +x "$TEST_TMPDIR/bin/sshpass"
}
# shellcheck source=../rpi_nc_ca.sh
. "$FEATURES_LIB_DIR/rpi_nc_ca.sh"
[[ -n $PI_HOSTNAME && -n $PI_USER ]] ||
die "fixture globals are empty; the suite would assert nothing"
# --- show_help --------------------------------------------------------------
help_out="$(show_help)"
_t_has "$help_out" 'Usage:' "show_help prints a usage line"
_t_has "$help_out" 'raspberry_pi_nextcloud.sh' "show_help names the entry script"
# --- phase_install_ca: refuses without a password ---------------------------
no_pw_out="$(
PI_PASSWORD=""
phase_install_ca 2>&1
)" || true
_t_has "$no_pw_out" 'PI_PASSWORD not set' "phase_install_ca dies when PI_PASSWORD is unset"
# --- phase_install_ca: refuses when the Pi cannot be found ------------------
no_pi_out="$(
discover_raspberry_pi() { printf '\n'; }
phase_install_ca 2>&1
)" || true
_t_has "$no_pi_out" 'Failed to discover' "phase_install_ca dies when the Pi is undiscoverable"
# --- phase_install_ca: refuses on an empty CA download ----------------------
_t_stub sshpass
empty_out="$(phase_install_ca 2>&1)" || true
_t_has "$empty_out" 'Failed to download CA' "phase_install_ca dies when the downloaded CA is empty"
# --- phase_install_ca: the Arch branch --------------------------------------
_t_ssh_emits_ca
_t_stub trust
_t_stub certutil
_t_stub curl
rm -f /etc/debian_version /etc/redhat-release
: >/etc/arch-release
mkdir -p /etc/ca-certificates/trust-source/anchors
arch_out="$(phase_install_ca 2>&1)"
_t_has "$arch_out" 'Detected Arch Linux' "the Arch branch is selected by /etc/arch-release"
_t_file_has /etc/ca-certificates/trust-source/anchors/nextcloud-ca.crt 'FAKECA' \
"the Arch branch copies the CA into the system anchor dir"
_t_called 'trust' "the Arch branch runs trust extract-compat"
_t_file_has /etc/hosts 'raspberrypi' "the hostname is appended to /etc/hosts"
# --- phase_install_ca: /etc/hosts is not duplicated on a second run ---------
second_out="$(phase_install_ca 2>&1)"
_t_eq "1" "$(grep -c 'raspberrypi' /etc/hosts)" "a second run does not duplicate the /etc/hosts entry"
_t_has "$second_out" 'already in /etc/hosts' "the second run reports the entry already exists"
# --- phase_install_ca: the Debian branch ------------------------------------
rm -f /etc/arch-release
: >/etc/debian_version
mkdir -p /usr/local/share/ca-certificates
_t_stub update-ca-certificates
deb_out="$(phase_install_ca 2>&1)"
_t_has "$deb_out" 'Detected Debian/Ubuntu' "the Debian branch is selected by /etc/debian_version"
_t_file_has /usr/local/share/ca-certificates/nextcloud-ca.crt 'FAKECA' \
"the Debian branch copies the CA into /usr/local/share"
_t_called 'update-ca-certificates' "the Debian branch runs update-ca-certificates"
# --- phase_install_ca: the RHEL branch --------------------------------------
rm -f /etc/debian_version
: >/etc/redhat-release
mkdir -p /etc/pki/ca-trust/source/anchors
_t_stub update-ca-trust
rhel_out="$(phase_install_ca 2>&1)"
_t_has "$rhel_out" 'Detected RHEL/Fedora' "the RHEL branch is selected by /etc/redhat-release"
_t_file_has /etc/pki/ca-trust/source/anchors/nextcloud-ca.crt 'FAKECA' \
"the RHEL branch copies the CA into the pki anchor dir"
_t_called 'update-ca-trust' "the RHEL branch runs update-ca-trust"
# --- phase_install_ca: the unknown-OS branch --------------------------------
rm -f /etc/redhat-release
unknown_out="$(phase_install_ca 2>&1)"
_t_has "$unknown_out" 'Unknown OS' "an unrecognised OS warns instead of failing"
# --- phase_install_ca: the macOS branch -------------------------------------
# Selected by `uname` rather than a marker file, so the stub is what picks it.
_t_stub security
cat >"$TEST_TMPDIR/bin/uname" <<'UNAME'
#!/usr/bin/env bash
printf 'Darwin\n'
UNAME
chmod +x "$TEST_TMPDIR/bin/uname"
mac_out="$(phase_install_ca 2>&1)"
_t_has "$mac_out" 'Detected macOS' "the macOS branch is selected when uname reports Darwin"
_t_has "$mac_out" 'system keychain' "the macOS branch reports the keychain install"
_t_unstub uname
# --- phase_install_ca: certutil reports the CA is ALREADY installed ---------
# `certutil -L` emitting a line containing "Nextcloud" takes the else branch.
cat >"$TEST_TMPDIR/bin/certutil" <<'CERTUTIL'
#!/usr/bin/env bash
printf 'Nextcloud Home CA CT,C,C\n'
exit 0
CERTUTIL
chmod +x "$TEST_TMPDIR/bin/certutil"
already_out="$(phase_install_ca 2>&1)"
_t_has "$already_out" 'already installed in Chrome/Chromium' \
"an existing NSS entry is reported rather than re-added"
# --- phase_install_ca: certutil FAILS to add the CA -------------------------
# -L must succeed-but-not-match (so the add is attempted) while -A fails.
cat >"$TEST_TMPDIR/bin/certutil" <<'CERTUTIL'
#!/usr/bin/env bash
for arg in "$@"; do
if [[ $arg == -A ]]; then
exit 1
fi
done
exit 0
CERTUTIL
chmod +x "$TEST_TMPDIR/bin/certutil"
cannot_out="$(phase_install_ca 2>&1)"
_t_has "$cannot_out" 'Could not install in Chrome/Chromium' \
"a failing certutil -A warns instead of aborting"
# --- phase_install_ca: the Firefox loop, all three outcomes -----------------
# A profile dir must exist for the loop body to run at all; the glob otherwise
# yields a literal unmatched pattern that fails the -d test.
mkdir -p ~/.mozilla/firefox/abcd.default
# certutil -A still fails here (stubbed above), so nothing installs.
ff_fail_out="$(phase_install_ca 2>&1)"
_t_has "$ff_fail_out" 'Could not install in Firefox' \
"a profile whose certutil -A fails reports the manual-import warning"
# -L succeeds but does not match, and -A now succeeds: the CA is added.
_t_stub certutil
ff_add_out="$(phase_install_ca 2>&1)"
_t_has "$ff_add_out" 'CA installed in Firefox' "a successful certutil -A reports the Firefox install"
# -L matches "Nextcloud": already present, so the add is skipped.
cat >"$TEST_TMPDIR/bin/certutil" <<'CERTUTIL'
#!/usr/bin/env bash
printf 'Nextcloud Home CA CT,C,C\n'
exit 0
CERTUTIL
chmod +x "$TEST_TMPDIR/bin/certutil"
ff_have_out="$(phase_install_ca 2>&1)"
_t_has "$ff_have_out" 'CA installed in Firefox' "an already-present Firefox CA still reports success"
# --- phase_install_ca: HTTPS verification succeeds --------------------------
cat >"$TEST_TMPDIR/bin/curl" <<'CURL'
#!/usr/bin/env bash
printf 'installed\n'
CURL
chmod +x "$TEST_TMPDIR/bin/curl"
verified_out="$(phase_install_ca 2>&1)"
_t_has "$verified_out" 'HTTPS connection verified' "a status.php reporting 'installed' verifies the connection"
_t_report "test_rpi_nc_ca.sh"