fix agent networking
shellcheck / shellcheck (push) Failing after 17s

This commit is contained in:
2026-11-02 08:57:45 -07:00
parent 6bc4fdc880
commit 448c8b43f1
13 changed files with 1684 additions and 8 deletions
+218
View File
@@ -0,0 +1,218 @@
#!/usr/bin/env bats
load test_helper
# The mDNS helper and the configure items built on it (ai-servers, storage),
# with fake avahi-browse, curl and smbclient. Nothing here touches the network.
setup() {
common_setup
export COREDESKTOP="$REPO"
AVAHI="$BATS_TEST_TMPDIR/avahi"
# What avahi-browse --parsable prints, including this machine (basement),
# an IPv6 copy of a service, and an unresolved (+) line
cat > "$AVAHI" << 'EOF'
+;enp5s0;IPv4;llm;_ssh._tcp;local
=;enp5s0;IPv4;llm;_ssh._tcp;local;llm.local;192.168.0.5;22;
=;enp5s0;IPv6;llm;_ssh._tcp;local;llm.local;fe80::1;22;
=;enp5s0;IPv4;llm;_sftp-ssh._tcp;local;llm.local;192.168.0.5;22;
=;enp5s0;IPv4;basement;_ssh._tcp;local;basement.local;192.168.0.106;22;
=;enp5s0;IPv4;PI-STAR;_smb._tcp;local;pi-star.local;192.168.0.102;445;
=;enp5s0;IPv4;HP\032LaserJet\032MFP\032M234dw\032\0403BE90A\041\032\064\032lab;_ipp._tcp;local;lab.local;192.168.0.22;631;"rp=printers/HP"
EOF
export AVAHI
stub avahi-browse 'cat "$AVAHI"'
stub hostnamectl 'echo basement'
confirm_yes() { return 0; }
confirm() { return 0; }
unsandboxed() { echo "unsandboxed $*" >> "$STUB_LOG"; }
export -f confirm_yes confirm unsandboxed
}
# --- lib/mdns.sh --------------------------------------------------------------------
@test "mdns_unescape decodes Avahi's decimal escapes" {
source "$REPO/configure.d/lib/mdns.sh"
[ "$(mdns_unescape 'HP\032LaserJet\032\0403BE90A\041\032\064\032lab')" = "HP LaserJet (3BE90A) @ lab" ]
[ "$(mdns_unescape 'a\.b')" = "a.b" ]
[ "$(mdns_unescape plain)" = "plain" ]
}
@test "mdns_services lists each resolved IPv4 service once, with its name decoded" {
source "$REPO/configure.d/lib/mdns.sh"
run mdns_services
[ "$status" -eq 0 ]
[ "${#lines[@]}" -eq 5 ]
[[ "$output" == *"HP LaserJet MFP M234dw (3BE90A) @ lab|_ipp._tcp|lab.local|192.168.0.22|631|\"rp=printers/HP\""* ]]
[[ "$output" != *"fe80"* ]]
}
@test "mdns_hosts lists the other machines, not this one" {
source "$REPO/configure.d/lib/mdns.sh"
run mdns_hosts
[ "$status" -eq 0 ]
[ "$output" = "$(printf 'lab.local\nllm.local\npi-star.local')" ]
}
# --- configure ai-servers --------------------------------------------------------------
setup_ai() {
source "$REPO/configure.d/50-ai-servers.sh"
# llm.local:11434 is Ollama with two models; nothing else answers
stub curl 'case "$*" in
*http://llm.local:11434/v1/models*) echo "{\"data\": [{\"id\": \"qwen3-coder:30b\"}, {\"id\": \"gemma4:12b\"}]}" ;;
*http://llm.local:11434/api/version*) echo "{\"version\": \"0.14.0\"}" ;;
*) exit 7 ;;
esac'
}
@test "find_servers asks each machine on the LLM ports and lists the ones that answer" {
setup_ai
run find_servers
[ "$status" -eq 0 ]
[ "$output" = "llm.local:11434|2" ]
[ "$(server_kind llm.local:11434)" = Ollama ]
}
@test "sandbox_allow adds a server once, keeping what's there" {
setup_ai
mkdir -p "$(dirname "$SANDBOX_CONF")"
echo '{"network": {"allowedDomains": ["docs.example.com"]}, "filesystem": {"allowWrite": ["~/notes"]}}' > "$SANDBOX_CONF"
sandbox_allow llm.local:11434
sandbox_allow llm.local:11434
[ "$(jq -c '.network.allowedDomains' "$SANDBOX_CONF")" = '["docs.example.com","llm.local:11434"]' ]
[ "$(jq -c '.filesystem.allowWrite' "$SANDBOX_CONF")" = '["~/notes"]' ]
sandbox_disallow llm.local:11434
[ "$(jq -c '.network.allowedDomains' "$SANDBOX_CONF")" = '["docs.example.com"]' ]
}
@test "sandbox_allow leaves a file that isn't valid JSON alone" {
setup_ai
mkdir -p "$(dirname "$SANDBOX_CONF")"
echo 'not json' > "$SANDBOX_CONF"
run sandbox_allow llm.local:11434
[ "$status" -ne 0 ]
[ "$(cat "$SANDBOX_CONF")" = "not json" ]
}
@test "claude_allow starts from the default domains when Claude has no list, and keeps their order" {
setup_ai
mkdir -p "$HOME/.claude"
echo '{"model": "opus"}' > "$CLAUDE_SETTINGS"
claude_allow llm.local:11434
[ "$(jq -c '.sandbox.network.allowedDomains[0:2]' "$CLAUDE_SETTINGS")" = '["anthropic.com","*.anthropic.com"]' ]
[ "$(jq -r '.sandbox.network.allowedDomains[-1]' "$CLAUDE_SETTINGS")" = llm.local:11434 ]
[ "$(jq -r '.model' "$CLAUDE_SETTINGS")" = opus ]
claude_disallow llm.local:11434
run jq -e '.sandbox.network.allowedDomains | index("llm.local:11434")' "$CLAUDE_SETTINGS"
[ "$status" -ne 0 ]
}
@test "claude_allow does nothing without Claude's settings" {
setup_ai
claude_allow llm.local:11434
[ ! -e "$CLAUDE_SETTINGS" ]
}
@test "opencode_add adds the server's models as a provider, and opencode_remove takes it out" {
setup_ai
opencode_add llm.local:11434 Ollama gemma4:12b > /dev/null
[ "$(opencode_provider llm.local:11434)" = llm-11434 ]
[ "$(jq -r '.provider["llm-11434"].options.baseURL' "$OPENCODE_CONF")" = "http://llm.local:11434/v1" ]
[ "$(jq -r '.provider["llm-11434"].name' "$OPENCODE_CONF")" = "Ollama on llm.local" ]
[ "$(jq -c '.provider["llm-11434"].models | keys' "$OPENCODE_CONF")" = '["gemma4:12b","qwen3-coder:30b"]' ]
[ "$(jq -r '.model' "$OPENCODE_CONF")" = "llm-11434/gemma4:12b" ]
opencode_remove llm.local:11434
[ "$(jq -c '.provider' "$OPENCODE_CONF")" = '{}' ]
[ "$(jq -r '.model // "none"' "$OPENCODE_CONF")" = none ]
}
@test "hermes_use points Hermes at the server's OpenAI API" {
setup_ai
hermes_use llm.local:11434 gemma4:12b
called unsandboxed hermes config set model.provider custom
called unsandboxed hermes config set model.base_url http://llm.local:11434/v1
called unsandboxed hermes config set model.default gemma4:12b
}
@test "add_server allows the server and remembers it; remove_server undoes it" {
setup_ai
mkdir -p "$(dirname "$AI_SERVERS_CONF")"
touch "$AI_SERVERS_CONF"
stub systemctl 'exit 3'
run add_server llm.local:11434 <<< "2"
[ "$status" -eq 0 ]
[ "$(cat "$AI_SERVERS_CONF")" = llm.local:11434 ]
jq -e '.network.allowedDomains | index("llm.local:11434")' "$SANDBOX_CONF"
run remove_server llm.local:11434
[ "$status" -eq 0 ]
[ ! -s "$AI_SERVERS_CONF" ]
run jq -e '.network.allowedDomains | index("llm.local:11434")' "$SANDBOX_CONF"
[ "$status" -ne 0 ]
}
@test "add_server refuses an address that isn't an LLM server" {
setup_ai
run add_server lab.local:11434 <<< "1"
[ "$status" -eq 1 ]
[[ "$output" == *"doesn't answer like an LLM server"* ]]
[ ! -e "$SANDBOX_CONF" ]
}
# --- configure storage -------------------------------------------------------------------
@test "find_storage lists SMB and SFTP servers, not this machine" {
source "$REPO/configure.d/60-storage.sh"
run find_storage
[ "$status" -eq 0 ]
[ "$output" = "$(printf 'sftp|llm.local|llm\nsmb|pi-star.local|PI-STAR')" ]
}
@test "smb_shares lists disk shares, without admin shares, and passes the password outside the arguments" {
source "$REPO/configure.d/60-storage.sh"
stub smbclient 'echo "PASSWD=$PASSWD" >> "$STUB_LOG"
printf "Disk|files|Shared files\nDisk|C\$|Default share\nIPC|IPC\$|IPC Service\nDisk|My Music|\n"'
run smb_shares nas.local me secret
[ "$output" = "$(printf 'files\nMy Music')" ]
called smbclient -L //nas.local -U me -g
called PASSWD=secret
}
@test "smb_fstab_line mounts as you on first use, with \\040 for spaces" {
source "$REPO/configure.d/60-storage.sh"
run smb_fstab_line "//nas.local/My Files" /mnt/nas-my-files /etc/coredesktop/smb/nas-my-files
[[ "$output" == "//nas.local/My\\040Files /mnt/nas-my-files cifs credentials=/etc/coredesktop/smb/nas-my-files,uid=$(id -u),gid=$(id -g),"* ]]
[[ "$output" == *",x-systemd.automount,"*" 0 0" ]]
run smb_fstab_line //nas.local/pub /mnt/nas-pub ""
[[ "$output" == *" cifs guest,"* ]]
}
@test "mount_point names the folder after the host and share, avoiding ones in use" {
source "$REPO/configure.d/60-storage.sh"
[ "$(mount_point Bats-Test-NAS.local 'My Files')" = /mnt/bats-test-nas-my-files ]
[ "$(mount_point bats-test-host.local '')" = /mnt/bats-test-host ]
mkdir -p "$(dirname "$STORAGE_CONF")"
echo "sftp|me@bats-test-host.local:|/mnt/bats-test-host" > "$STORAGE_CONF"
[ "$(mount_point bats-test-host.local '')" = /mnt/bats-test-host-2 ]
}
@test "sftp_unit runs sftp-mount as a user service" {
source "$REPO/configure.d/60-storage.sh"
run sftp_unit "me@brain.local:/srv/my data" /mnt/brain-my-data
[[ "$output" == *'ExecStart=%h/.local/share/coredesktop/system/sftp-mount "me@brain.local:/srv/my data" "/mnt/brain-my-data"'* ]]
[[ "$output" == *"WantedBy=default.target"* ]]
[ "$(sftp_unit_name /mnt/brain-my-data)" = coredesktop-sftp-brain-my-data.service ]
}
@test "enter_and_add reads smb:// and sftp:// addresses" {
source "$REPO/configure.d/60-storage.sh"
add_smb() { echo "smb [$1] [$2]"; }
add_sftp() { echo "sftp [$1] [$2] [$3]"; }
[ "$(enter_and_add <<< 'smb://nas.local/My Files' | tail -n1)" = "smb [nas.local] [My Files]" ]
[ "$(enter_and_add <<< 'sftp://brain.local' | tail -n1)" = "sftp [brain.local] [] []" ]
[ "$(enter_and_add <<< 'sftp://me@brain.local/srv/data' | tail -n1)" = "sftp [brain.local] [me] [/srv/data]" ]
run enter_and_add <<< 'smb://nas.local'
[ "$status" -eq 1 ]
}
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env bats
load test_helper
# system/setup-printers.sh with fake CUPS and Avahi, set up like a network
# with a printer reached directly (M234dw), the same printer shared by "lab",
# and a USB printer shared by "gateway". $QUEUES is what lpstat -v lists.
M234_URI='dnssd://HP%20LaserJet%20MFP%20M234dw%20(3BE90A)._ipp._tcp.local/?uuid=abf23253'
LAB_URI='dnssd://HP%20LaserJet%20MFP%20M234dw%20(3BE90A)%20%40%20lab._ipp._tcp.local/cups?uuid=c5d876d6'
P1102_URI='dnssd://HP_LaserJet_Professional_P1102w%20%40%20gateway._ipp._tcp.local/cups?uuid=4da44ae6'
setup() {
common_setup
SCRIPT="$REPO/system/setup-printers.sh"
export LPINFO="network $M234_URI
network $LAB_URI
network $P1102_URI"
export QUEUES="" ADDED_QUEUES="$BATS_TEST_TMPDIR/added"
: > "$ADDED_QUEUES"
AVAHI="$BATS_TEST_TMPDIR/avahi"
cat > "$AVAHI" << 'EOF'
=;enp5s0;IPv4;HP\032LaserJet\032MFP\032M234dw\032\0403BE90A\041;_ipp._tcp;local;NPI3BE90A.local;192.168.0.114;631;"txtvers=1" "rp=ipp/print"
=;enp5s0;IPv4;HP\032LaserJet\032MFP\032M234dw\032\0403BE90A\041\032\064\032lab;_ipp._tcp;local;lab.local;192.168.0.22;631;"rp=printers/HP-LaserJet-MFP-M232-M237"
=;enp5s0;IPv4;HP_LaserJet_Professional_P1102w\032\064\032gateway;_ipp._tcp;local;gateway.local;192.168.0.39;631;"rp=printers/HP_LaserJet_Professional_P1102w"
EOF
export AVAHI
stub avahi-browse 'cat "$AVAHI"'
stub hostnamectl 'echo basement'
stub lpinfo 'echo "$LPINFO"'
stub lpstat 'case "$1" in
-r) echo "scheduler is running" ;;
-v) [ -n "$QUEUES" ] || { echo "lpstat: No destinations added." >&2; exit 1; }
echo "$QUEUES" ;;
-a) grep -qx "$2" "$ADDED_QUEUES" && echo "$2 accepting requests since now" ;;
-d) echo "no system default destination" ;;
esac'
# Fails for URIs matching $LPADMIN_FAIL; records the queues it adds
stub lpadmin 'if [ "$1" = -p ]; then
[ -n "$LPADMIN_FAIL" ] && [[ "$5" == $LPADMIN_FAIL ]] && { echo "lpadmin: Unable to connect" >&2; exit 1; }
echo "$2" >> "$ADDED_QUEUES"
fi'
stub sudo '"$@"'
stub pacman 'exit 1'
}
@test "the dry run plans only the printer without a queue or a direct equivalent" {
QUEUES="device for M234dw: ipp://NPI3BE90A.local:631/ipp/print"
run bash "$SCRIPT" --no-deps --dry-run --all
[ "$status" -eq 0 ]
[[ "$output" == *"HP LaserJet MFP M234dw (3BE90A)"*"direct"*"already set up as M234dw"* ]]
[[ "$output" == *"HP LaserJet MFP M234dw (3BE90A) @ lab"*"shared by lab"*"same printer as 1, reached directly"* ]]
[[ "$output" == *"would run: sudo lpadmin -p HP_LaserJet_Professional_P1102w_gateway -E -v"* ]]
[[ "$output" == *"Would add:"$'\n'" HP_LaserJet_Professional_P1102w_gateway"* ]]
not_called sudo
not_called lpadmin
}
@test "--all adds each printer once with IPP Everywhere, preferring direct ones" {
run bash "$SCRIPT" --no-deps --all
[ "$status" -eq 0 ]
called lpadmin -p HP_LaserJet_MFP_M234dw_3BE90A -E -v "$M234_URI" -m everywhere
called lpadmin -p HP_LaserJet_Professional_P1102w_gateway -E -v "$P1102_URI" -m everywhere
[ "$(grep -c '^lpadmin -p' "$STUB_LOG")" -eq 2 ]
[[ "$output" == *"Added:"$'\n'" HP_LaserJet_MFP_M234dw_3BE90A"$'\n'" HP_LaserJet_Professional_P1102w_gateway"* ]]
}
@test "a second run adds nothing" {
QUEUES="device for HP_LaserJet_MFP_M234dw_3BE90A: $M234_URI
device for HP_LaserJet_Professional_P1102w_gateway: $P1102_URI"
run bash "$SCRIPT" --no-deps --all
[ "$status" -eq 0 ]
not_called lpadmin
[[ "$output" == *"Added:"$'\n'" none"* ]]
}
@test "a printer can still be chosen through the computer sharing it" {
run bash "$SCRIPT" --no-deps <<< "2"
[ "$status" -eq 0 ]
called lpadmin -p HP_LaserJet_MFP_M234dw_3BE90A_lab -E -v "$LAB_URI" -m everywhere
[ "$(grep -c '^lpadmin -p' "$STUB_LOG")" -eq 1 ]
}
@test "n adds nothing" {
run bash "$SCRIPT" --no-deps <<< "n"
[ "$status" -eq 0 ]
not_called lpadmin
}
@test "a queue name in use for another printer gets a number" {
QUEUES="device for HP_LaserJet_Professional_P1102w_gateway: ipp://elsewhere.local/ipp/print"
run bash "$SCRIPT" --no-deps <<< "3"
[ "$status" -eq 0 ]
called lpadmin -p HP_LaserJet_Professional_P1102w_gateway_2 -E -v "$P1102_URI" -m everywhere
}
@test "when the mDNS address fails, it tries the printer's IP address" {
export LPADMIN_FAIL='dnssd://*'
run bash "$SCRIPT" --no-deps <<< "3"
[ "$status" -eq 0 ]
called lpadmin -p HP_LaserJet_Professional_P1102w_gateway -E -v ipp://192.168.0.39:631/printers/HP_LaserJet_Professional_P1102w -m everywhere
}
@test "a printer that can't be added is reported, and the rest are still added" {
export LPADMIN_FAIL='*P1102w*'
run bash "$SCRIPT" --no-deps --all
[ "$status" -eq 1 ]
[[ "$output" == *"Added:"$'\n'" HP_LaserJet_MFP_M234dw_3BE90A"* ]]
[[ "$output" == *"Failed:"$'\n'" HP_LaserJet_Professional_P1102w @ gateway: lpadmin: Unable to connect"* ]]
[[ "$output" == *"hplip"* ]]
}
@test "--default makes a new queue the default" {
run bash "$SCRIPT" --no-deps --all --default HP_LaserJet_MFP_M234dw_3BE90A
[ "$status" -eq 0 ]
called lpadmin -d HP_LaserJet_MFP_M234dw_3BE90A
[[ "$output" == *"Default printer: HP_LaserJet_MFP_M234dw_3BE90A"* ]]
}
@test "--default with a queue that doesn't exist fails without changing the default" {
run bash "$SCRIPT" --no-deps --all --default Nope
[ "$status" -eq 1 ]
uncalled lpadmin -d Nope
[[ "$output" == *"there's no queue named Nope"* ]]
}
@test "it stops when CUPS isn't running" {
stub lpstat 'echo "scheduler is not running"'
run bash "$SCRIPT" --no-deps --all
[ "$status" -eq 1 ]
[[ "$output" == *"CUPS isn't running"* ]]
not_called lpinfo
}
@test "it says so when no printers are found" {
export LPINFO=""
stub sleep 'exit 0'
run bash "$SCRIPT" --no-deps --all
[ "$status" -eq 0 ]
[[ "$output" == *"No printers found"* ]]
[ "$(grep -c '^lpinfo ' "$STUB_LOG")" -eq 3 ]
}
@test "unknown options are refused" {
run bash "$SCRIPT" --bogus
[ "$status" -eq 2 ]
}
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bats
load test_helper
# sandbox/agent runs a fake agent through a fake srt, which sets NO_PROXY the
# way the real one does, saves the settings it was given and runs the command.
setup() {
common_setup
SANDBOX="$HOME/.local/share/coredesktop/sandbox"
mkdir -p "$SANDBOX/bin" "$SANDBOX/config" "$BATS_TEST_TMPDIR/project"
cp "$REPO/sandbox/config/base.json" "$SANDBOX/config/"
ln -s "$REPO/sandbox/agent" "$SANDBOX/bin/fakeagent"
export XDG_RUNTIME_DIR="$BATS_TEST_TMPDIR/run"
mkdir -p "$XDG_RUNTIME_DIR"
export SETTINGS_COPY="$BATS_TEST_TMPDIR/settings.json"
stub srt 'cp "$2" "$SETTINGS_COPY"
while [ "$1" != "--" ]; do shift; done
shift
export NO_PROXY=localhost,127.0.0.1,::1,10.0.0.0/8,192.168.0.0/16 no_proxy=localhost,192.168.0.0/16
exec "$@"'
stub fakeagent 'echo "NO_PROXY=$NO_PROXY no_proxy=$no_proxy args=$*"'
cd "$BATS_TEST_TMPDIR/project"
}
@test "an agent connects directly only to loopback, so allowed LAN addresses go through the proxy" {
run "$SANDBOX/bin/fakeagent" one "two words"
[ "$status" -eq 0 ]
[ "$output" = "NO_PROXY=localhost,127.0.0.1,::1 no_proxy=localhost,127.0.0.1,::1 args=one two words" ]
}
@test "a server allowed in your own base.json is in the agent's settings" {
mkdir -p "$HOME/.config/coredesktop/sandbox"
echo '{"network": {"allowedDomains": ["llm.local:11434"]}}' > "$HOME/.config/coredesktop/sandbox/base.json"
run "$SANDBOX/bin/fakeagent"
[ "$status" -eq 0 ]
jq -e '.network.allowedDomains | index("llm.local:11434") and index("github.com")' "$SETTINGS_COPY"
}
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bats
load test_helper
# version runs from a fake checkout with fake git, uname, pacman and
# checkupdates. The running kernel's modules never exist here, so the kernel
# line always also says a newer one is installed.
setup() {
common_setup
CD="$HOME/.local/share/coredesktop"
mkdir -p "$CD"
cp "$REPO/version" "$REPO/utils.sh" "$CD/"
stub git 'case "$3" in
rev-parse) exit 0 ;;
log) echo "2026.10.31 (6bc4fdc)" ;;
fetch) exit 1 ;;
esac'
stub uname 'echo "${KERNEL:-9.9.1-arch1-1}"'
# pacman -Q <package>: the versions in $INSTALLED, "name version" per line
stub pacman 'grep "^$2 " <<< "$INSTALLED" || exit 1'
stub checkupdates 'printf "%s" "$PENDING"; [ -n "$PENDING" ] || exit 2'
export INSTALLED="mesa 1:26.2.6-1" PENDING=""
}
@test "version shows the kernel and Mesa, with nothing newer to install" {
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" == *"CoreDesktop 2026.10.31 (6bc4fdc)"* ]]
[[ "$output" == *"Kernel 9.9.1-arch1-1 (newer kernel installed, reboot to use it)"$'\n'* ]]
[[ "$output" == *"Video driver Mesa 26.2.6"$'\n'* ]]
[[ "$output" != *"is available"* ]]
}
@test "version suggests update for a newer kernel and video driver" {
export PENDING=$'linux 9.9.1.arch1-1 -> 9.9.2.arch1-1\nmesa 1:26.2.6-1 -> 1:26.3.0-1\n'
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" == *"Kernel 9.9.1-arch1-1"*", 9.9.2 is available (run update)"* ]]
[[ "$output" == *"Video driver Mesa 26.2.6, 26.3.0 is available (run update)"* ]]
}
@test "version shows NVIDIA's driver over Mesa, and ignores a rebuild of the same version" {
export INSTALLED=$'mesa 1:26.2.6-1\nnvidia-utils 615.78.08-1'
export PENDING=$'nvidia-utils 615.78.08-1 -> 615.78.08-2\n'
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" == *"Video driver NVIDIA 615.78.08"$'\n'* ]]
[[ "$output" != *"is available"* ]]
}
@test "version checks the kernel package that's running, e.g. linux-lts" {
export KERNEL=6.12.50-1-lts
export PENDING=$'linux 9.9.1.arch1-1 -> 9.9.2.arch1-1\nlinux-lts 6.12.50-1 -> 6.12.51-1\n'
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" == *"6.12.51 is available"* ]]
[[ "$output" != *"9.9.2"* ]]
}
@test "version leaves out what's available when it can't check" {
rm "$STUB_BIN/checkupdates"
stub checkupdates 'exit 1'
export PENDING=$'linux 9.9.1.arch1-1 -> 9.9.2.arch1-1\n'
run bash "$CD/version"
[ "$status" -eq 0 ]
[[ "$output" == *"Kernel 9.9.1-arch1-1"* ]]
[[ "$output" != *"is available"* ]]
}