#!/usr/bin/env bash # Network & Devices reads real NetworkManager and BlueZ state. # # Both of the bugs this contract exists to prevent were silent. Neither logged # anything; both produced a page that looked fine and told the user something # false: # # * the device lookups used enum names that do not exist # (NetworkDeviceType.Wifi rather than DeviceType.Wifi), so they returned # null and the page reported "No Wi-Fi adapter" on a machine whose Wi-Fi was # connected; # * signalStrength is 0.0-1.0, not a percentage, so thresholds written for # 0-100 put every network including the connected one in the bottom bucket. # # So this compares what the service resolves against what NetworkManager itself # reports, rather than merely checking the service does not crash. set -euo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" harness="$repo_dir/config/dot/quickshell/connectivity-harness.qml" fail() { printf 'connectivity contract: %s\n' "$1" >&2 exit 1 } command -v nmcli >/dev/null || fail 'nmcli is needed to check the service against reality' run() { qs -p "$harness" "$@"; } harness_pid="" cleanup() { run ipc call connectivity-test setActive false >/dev/null 2>&1 || true # By PID: never `pkill -f connectivity-harness`, which also matches the # shell running this script. [[ -n "$harness_pid" ]] && kill "$harness_pid" >/dev/null 2>&1 || true } trap cleanup EXIT qs -p "$harness" --daemonize >/dev/null for _ in $(seq 1 40); do run ipc show 2>/dev/null | rg -q '^target connectivity-test$' && break sleep 0.1 done run ipc show 2>/dev/null | rg -q '^target connectivity-test$' || fail 'test IPC target did not start' harness_pid="$(run list | awk '/Process ID:/ { print $3; exit }')" # Scanning only runs while the page says it is visible. run ipc call connectivity-test setActive true >/dev/null sleep 3 state="$(run ipc call connectivity-test status)" # ── Devices the service finds must match the ones NetworkManager reports ───── nm_wifi="$(nmcli -t -f DEVICE,TYPE device | awk -F: '$2 == "wifi" { print $1; exit }')" nm_wired="$(nmcli -t -f DEVICE,TYPE,STATE device | awk -F: '$2 == "ethernet" && $3 == "connected" { print $1; exit }')" if [[ -n "$nm_wifi" ]]; then [[ "$(jq -r .wifiDevice <<<"$state")" == "$nm_wifi" ]] \ || fail "NetworkManager reports Wi-Fi device '$nm_wifi' but the service found '$(jq -r .wifiDevice <<<"$state")'" fi if [[ -n "$nm_wired" ]]; then [[ "$(jq -r .wiredConnected <<<"$state")" == "true" ]] \ || fail "NetworkManager reports '$nm_wired' connected but the service says it is not" fi # ── Signal strength is a ratio, and the labels must reflect that ───────────── while IFS='|' read -r value expect; do got="$(run ipc call connectivity-test labelFor "$value")" [[ "$got" == "$expect" ]] || fail "signal $value labelled '$got', expected '$expect'" done <<'CASES' 1.0|Excellent 0.85|Excellent 0.6|Good 0.4|Fair 0.1|Weak 0.0|No signal CASES # If a network is connected, it must not be described as the weakest possible # thing -- that was the visible symptom of reading the ratio as a percentage. active_ssid="$(jq -r .activeSsid <<<"$state")" if [[ -n "$active_ssid" ]]; then strength="$(jq -r .activeStrength <<<"$state")" awk -v s="$strength" 'BEGIN { exit !(s >= 0 && s <= 1) }' \ || fail "signalStrength $strength is outside 0.0-1.0; the label buckets assume a ratio" fi # ── Bluetooth ──────────────────────────────────────────────────────────────── if [[ "$(bluetoothctl list 2>/dev/null | wc -l)" -gt 0 ]]; then [[ "$(jq -r .adapter <<<"$state")" == "true" ]] \ || fail 'an adapter is present but the service did not find it' fi trap - EXIT cleanup printf 'connectivity contract: PASS\n'