feat: advance login workflow and integration gates
This commit is contained in:
107
scripts/test-cli.sh
Executable file
107
scripts/test-cli.sh
Executable file
@@ -0,0 +1,107 @@
|
||||
#!/bin/sh
|
||||
# Exercise the non-interactive CLI contract without authenticating a user.
|
||||
#
|
||||
# This suite intentionally uses --preview. It validates TOML parsing, command
|
||||
# line validation, build metadata, and the preflight path without requiring a
|
||||
# PAM secret, a controlling VT, or a running display server.
|
||||
set -eu
|
||||
|
||||
binary=${1:-target/debug/latchd}
|
||||
|
||||
if [ ! -x "$binary" ]; then
|
||||
echo "test-cli: binary is not executable: $binary" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
workdir=$(mktemp -d "${TMPDIR:-/tmp}/latchd-cli.XXXXXX")
|
||||
trap 'rm -rf "$workdir"' EXIT HUP INT TERM
|
||||
|
||||
config="$workdir/config.toml"
|
||||
cat >"$config" <<'TOML'
|
||||
tty = 2
|
||||
main_log_path = "/tmp/latchd-test.log"
|
||||
client_log_path = "/tmp/latchd-test.client.log"
|
||||
pam_service = "latchd"
|
||||
system_shell = "/bin/sh"
|
||||
initial_path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
[x11]
|
||||
display = ":99"
|
||||
server_path = "/usr/bin/X"
|
||||
xauth_path = "/usr/bin/xauth"
|
||||
server_timeout_secs = 5
|
||||
server_log_path = "/tmp/latchd-test.xorg.log"
|
||||
sessions_path = "/nonexistent/latchd-test/xsessions"
|
||||
scripts_path = "/nonexistent/latchd-test/wms"
|
||||
|
||||
[wayland]
|
||||
sessions_path = "/nonexistent/latchd-test/wayland-sessions"
|
||||
scripts_path = "/nonexistent/latchd-test/wayland"
|
||||
|
||||
[environment_switcher]
|
||||
include_tty_shell = false
|
||||
max_display_length = 18
|
||||
|
||||
[power_controls]
|
||||
entries = []
|
||||
|
||||
[accounts]
|
||||
minimum_uid = 1000
|
||||
excluded_users = []
|
||||
enumerate = true
|
||||
faillock_path = "/usr/sbin/faillock"
|
||||
faillock_config = "/etc/security/faillock.conf"
|
||||
|
||||
[visual]
|
||||
background_enabled = false
|
||||
animations = false
|
||||
notifications = false
|
||||
reduced_motion = true
|
||||
max_wallpaper_bytes = 1048576
|
||||
TOML
|
||||
|
||||
assert_contains() {
|
||||
needle=$1
|
||||
haystack=$2
|
||||
grep -F -- "$needle" "$haystack" >/dev/null || {
|
||||
echo "test-cli: expected '$needle' in $haystack" >&2
|
||||
cat "$haystack" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
info="$workdir/build-info"
|
||||
"$binary" --build-info >"$info"
|
||||
assert_contains "version=" "$info"
|
||||
assert_contains "commit=" "$info"
|
||||
assert_contains "epoch=" "$info"
|
||||
|
||||
version="$workdir/version"
|
||||
"$binary" --version >"$version"
|
||||
assert_contains "latchd" "$version"
|
||||
|
||||
valid="$workdir/valid"
|
||||
"$binary" --preview --no-tui --no-log --config "$config" >"$valid"
|
||||
assert_contains "configuration valid" "$valid"
|
||||
|
||||
shown="$workdir/show-config"
|
||||
"$binary" --preview --show-config --no-log --config "$config" >"$shown"
|
||||
assert_contains "tty = 2" "$shown"
|
||||
assert_contains "pam_service" "$shown"
|
||||
|
||||
unknown="$workdir/unknown.toml"
|
||||
printf 'unknown = true\n' >"$unknown"
|
||||
if "$binary" --preview --no-tui --no-log --config "$unknown" >"$workdir/unknown.out" 2>&1; then
|
||||
echo "test-cli: unknown configuration field was accepted" >&2
|
||||
exit 1
|
||||
fi
|
||||
assert_contains "unknown" "$workdir/unknown.out"
|
||||
|
||||
invalid_tty="$workdir/invalid-tty"
|
||||
if "$binary" --preview --no-tui --no-log --config "$config" --tty 0 >"$invalid_tty" 2>&1; then
|
||||
echo "test-cli: --tty 0 was accepted" >&2
|
||||
exit 1
|
||||
fi
|
||||
assert_contains "--tty must be greater than zero" "$invalid_tty"
|
||||
|
||||
echo "test-cli: PASS (CLI metadata, config validation, preflight, and safe preview paths)"
|
||||
146
scripts/test-system-e2e.sh
Executable file
146
scripts/test-system-e2e.sh
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/bin/sh
|
||||
# Probe host capabilities needed for a real login-manager deployment.
|
||||
#
|
||||
# This script never reports an unavailable capability as a pass. On an
|
||||
# ordinary container it prints SKIP and exits successfully; --strict turns
|
||||
# every SKIP into a failure for a prepared VM or privileged runner.
|
||||
set -u
|
||||
|
||||
binary=${1:-}
|
||||
strict=0
|
||||
if [ "${1:-}" = "--strict" ]; then
|
||||
strict=1
|
||||
shift
|
||||
binary=${1:-}
|
||||
fi
|
||||
|
||||
failures=0
|
||||
skips=0
|
||||
passes=0
|
||||
|
||||
pass() {
|
||||
passes=$((passes + 1))
|
||||
printf 'PASS: %s\n' "$*"
|
||||
}
|
||||
|
||||
skip() {
|
||||
skips=$((skips + 1))
|
||||
printf 'SKIP: %s\n' "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
failures=$((failures + 1))
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
need_command() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
if [ -n "$binary" ]; then
|
||||
if [ ! -x "$binary" ]; then
|
||||
fail "latchd binary is not executable: $binary"
|
||||
else
|
||||
pass "latchd binary is available"
|
||||
fi
|
||||
else
|
||||
skip "latchd binary not supplied; capability-only probe"
|
||||
fi
|
||||
|
||||
# This is deliberately a runtime capability check, not an authentication
|
||||
# claim. Authentication needs a disposable VM user and a test PAM policy.
|
||||
if need_command ldconfig && ldconfig -p 2>/dev/null | grep -q 'libpam\.so'; then
|
||||
pass "PAM runtime library is available"
|
||||
else
|
||||
skip "PAM runtime library is unavailable"
|
||||
fi
|
||||
if [ -r /etc/pam.d/latchd ]; then
|
||||
pass "latchd PAM policy is installed"
|
||||
else
|
||||
skip "latchd PAM policy is not installed (authentication E2E requires a provisioned VM)"
|
||||
fi
|
||||
if need_command faillock; then
|
||||
pass "faillock helper is available"
|
||||
else
|
||||
skip "faillock helper is unavailable"
|
||||
fi
|
||||
|
||||
# A real VT is a character device and must be allocated by the host/VM. A
|
||||
# pseudo-terminal from CI is not equivalent and is intentionally not accepted.
|
||||
if [ -c /dev/tty0 ] && [ -d /sys/class/tty/tty0 ]; then
|
||||
pass "Linux virtual terminal device is available"
|
||||
else
|
||||
skip "Linux virtual terminal is unavailable (container or non-console host)"
|
||||
fi
|
||||
|
||||
# Xvfb is a genuine X server and is safe to exercise without host display
|
||||
# access. If it is absent, this is a skip rather than a synthetic success.
|
||||
if need_command Xvfb; then
|
||||
display_number=$((100 + ($$ % 500)))
|
||||
display=":$display_number"
|
||||
xlog=${TMPDIR:-/tmp}/latchd-xvfb.$$.log
|
||||
Xvfb "$display" -screen 0 640x480x24 -nolisten tcp >"$xlog" 2>&1 &
|
||||
xvfb_pid=$!
|
||||
cleanup_xvfb() {
|
||||
kill "$xvfb_pid" >/dev/null 2>&1 || true
|
||||
wait "$xvfb_pid" >/dev/null 2>&1 || true
|
||||
rm -f "$xlog"
|
||||
}
|
||||
trap cleanup_xvfb EXIT HUP INT TERM
|
||||
ready=0
|
||||
i=0
|
||||
while [ "$i" -lt 20 ]; do
|
||||
if [ -S "/tmp/.X11-unix/X$display_number" ] && kill -0 "$xvfb_pid" 2>/dev/null; then
|
||||
ready=1
|
||||
break
|
||||
fi
|
||||
i=$((i + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
if [ "$ready" -eq 1 ]; then
|
||||
pass "X11 server starts in an isolated display ($display)"
|
||||
else
|
||||
fail "Xvfb did not start; see $xlog"
|
||||
fi
|
||||
cleanup_xvfb
|
||||
trap - EXIT HUP INT TERM
|
||||
else
|
||||
skip "Xvfb is unavailable"
|
||||
fi
|
||||
|
||||
# A Wayland socket is meaningful only when a compositor owns it. Do not claim
|
||||
# Wayland support merely because a client library or executable is installed.
|
||||
wayland_socket=
|
||||
if [ -n "${WAYLAND_DISPLAY:-}" ] && [ -n "${XDG_RUNTIME_DIR:-}" ]; then
|
||||
case "$WAYLAND_DISPLAY" in
|
||||
/*) wayland_socket=$WAYLAND_DISPLAY ;;
|
||||
*) wayland_socket=$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY ;;
|
||||
esac
|
||||
fi
|
||||
if [ -n "$wayland_socket" ] && [ -S "$wayland_socket" ]; then
|
||||
pass "Wayland compositor socket is available ($wayland_socket)"
|
||||
else
|
||||
skip "Wayland compositor socket is unavailable (requires a compositor-backed VM/session)"
|
||||
fi
|
||||
|
||||
# systemd-logind is not normally PID 1 in a Docker container. loginctl output
|
||||
# is checked only when the manager and seat are actually reachable.
|
||||
if need_command loginctl && [ -d /run/systemd/system ]; then
|
||||
if loginctl --no-pager show-seat seat0 >/dev/null 2>&1; then
|
||||
pass "systemd-logind seat0 is reachable"
|
||||
else
|
||||
fail "systemd-logind is present but seat0 is not reachable"
|
||||
fi
|
||||
else
|
||||
skip "systemd-logind seat0 is unavailable"
|
||||
fi
|
||||
|
||||
printf 'E2E summary: pass=%s skip=%s fail=%s\n' "$passes" "$skips" "$failures"
|
||||
if [ "$failures" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
if [ "$strict" -eq 1 ] && [ "$skips" -ne 0 ]; then
|
||||
echo "E2E strict mode: unavailable capabilities are errors" >&2
|
||||
exit 2
|
||||
fi
|
||||
exit 0
|
||||
Reference in New Issue
Block a user