Files
2026-04-16 12:44:35 +01:00

255 lines
7.9 KiB
Rust

use std::fs;
use std::path::PathBuf;
use std::sync::{Mutex, OnceLock};
use std::time::{SystemTime, UNIX_EPOCH};
use redbear_netctl_console::backend::{
ConsoleBackend, FsBackend, IpMode, Profile, RuntimePaths, SecurityKind,
};
fn env_lock() -> &'static Mutex<()> {
static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
LOCK.get_or_init(|| Mutex::new(()))
}
fn temp_root(prefix: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let root = std::env::temp_dir().join(format!("{prefix}-{nanos}"));
fs::create_dir_all(&root).unwrap();
root
}
fn runtime_paths(
profile_dir: &PathBuf,
wifictl_root: &PathBuf,
netcfg_root: &PathBuf,
dhcpd_command: &str,
) -> RuntimePaths {
RuntimePaths {
profile_dir: profile_dir.clone(),
active_profile_path: profile_dir.join("active"),
wifictl_root: wifictl_root.clone(),
netcfg_root: netcfg_root.clone(),
dhcpd_command: dhcpd_command.to_string(),
dhcp_wait_timeout: std::time::Duration::from_millis(500),
dhcp_poll_interval: std::time::Duration::from_millis(10),
}
}
#[test]
fn saves_and_loads_profile_using_fake_roots() {
let _guard = env_lock()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let profile_dir = temp_root("rbos-netctl-console-profiles");
let wifictl_root = temp_root("rbos-netctl-console-wifictl");
let netcfg_root = temp_root("rbos-netctl-console-netcfg");
let backend = FsBackend::new(runtime_paths(
&profile_dir,
&wifictl_root,
&netcfg_root,
"/usr/bin/true",
));
let profile = Profile {
name: "wifi-open-bounded".to_string(),
description: "Wi-Fi bounded".to_string(),
interface: "wlan0".to_string(),
ssid: "demo-open".to_string(),
security: SecurityKind::Open,
key: String::new(),
ip_mode: IpMode::Bounded,
address: String::new(),
gateway: String::new(),
dns: String::new(),
};
backend.save_profile(&profile).unwrap();
let loaded = backend.load_profile("wifi-open-bounded").unwrap();
assert_eq!(loaded, profile);
assert_eq!(
backend.list_wifi_profiles().unwrap(),
vec!["wifi-open-bounded".to_string()]
);
}
#[test]
fn scan_writes_bounded_wifictl_flow_nodes() {
let _guard = env_lock()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let profile_dir = temp_root("rbos-netctl-console-scan-profiles");
let wifictl_root = temp_root("rbos-netctl-console-scan-wifictl");
let netcfg_root = temp_root("rbos-netctl-console-scan-netcfg");
fs::create_dir_all(wifictl_root.join("ifaces/wlan0")).unwrap();
fs::write(wifictl_root.join("ifaces/wlan0/status"), "scanning\n").unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-init-status"),
"transport_init=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/activation-status"),
"activation=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/scan-results"),
"ssid=demo-open security=open\nssid=demo-secure security=wpa2-psk\n",
)
.unwrap();
let backend = FsBackend::new(runtime_paths(
&profile_dir,
&wifictl_root,
&netcfg_root,
"/usr/bin/true",
));
let results = backend.scan("wlan0").unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].ssid, "demo-open");
assert_eq!(results[1].ssid, "demo-secure");
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/prepare")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/init-transport")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/activate-nic")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/scan")).unwrap(),
"1\n"
);
}
#[test]
fn connect_uses_wifictl_and_marks_active_profile() {
let _guard = env_lock()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let profile_dir = temp_root("rbos-netctl-console-connect-profiles");
let wifictl_root = temp_root("rbos-netctl-console-connect-wifictl");
let netcfg_root = temp_root("rbos-netctl-console-connect-netcfg");
fs::create_dir_all(wifictl_root.join("ifaces/wlan0")).unwrap();
fs::create_dir_all(netcfg_root.join("ifaces/wlan0/addr")).unwrap();
fs::write(
netcfg_root.join("ifaces/wlan0/addr/list"),
"Not configured\n",
)
.unwrap();
fs::write(wifictl_root.join("ifaces/wlan0/status"), "connected\n").unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/transport-init-status"),
"transport_init=ok\n",
)
.unwrap();
fs::write(
wifictl_root.join("ifaces/wlan0/activation-status"),
"activation=ok\n",
)
.unwrap();
let dhcp_script = profile_dir.join("fake-dhcpd.sh");
fs::write(
&dhcp_script,
format!(
"#!/usr/bin/env bash\nset -euo pipefail\nprintf '10.0.0.44/24\\n' > '{}/ifaces/wlan0/addr/list'\n",
netcfg_root.display()
),
)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&dhcp_script).unwrap().permissions();
perms.set_mode(0o755);
fs::set_permissions(&dhcp_script, perms).unwrap();
}
let backend = FsBackend::new(runtime_paths(
&profile_dir,
&wifictl_root,
&netcfg_root,
dhcp_script.to_str().unwrap(),
));
let profile = Profile {
name: "wifi-dhcp".to_string(),
description: "Wi-Fi DHCP".to_string(),
interface: "wlan0".to_string(),
ssid: "demo".to_string(),
security: SecurityKind::Wpa2Psk,
key: "secret".to_string(),
ip_mode: IpMode::Dhcp,
address: String::new(),
gateway: String::new(),
dns: String::new(),
};
let message = backend.connect(&profile).unwrap();
assert!(message.contains("applied wifi-dhcp"));
assert_eq!(
backend.active_profile_name().unwrap().as_deref(),
Some("wifi-dhcp")
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/ssid")).unwrap(),
"demo\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/security")).unwrap(),
"wpa2-psk\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/key")).unwrap(),
"secret\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/prepare")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/connect")).unwrap(),
"1\n"
);
assert_eq!(
fs::read_to_string(netcfg_root.join("ifaces/wlan0/addr/list")).unwrap(),
"10.0.0.44/24\n"
);
}
#[test]
fn disconnect_clears_active_profile_when_it_matches() {
let _guard = env_lock()
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let profile_dir = temp_root("rbos-netctl-console-disconnect-profiles");
let wifictl_root = temp_root("rbos-netctl-console-disconnect-wifictl");
let netcfg_root = temp_root("rbos-netctl-console-disconnect-netcfg");
fs::create_dir_all(wifictl_root.join("ifaces/wlan0")).unwrap();
let backend = FsBackend::new(runtime_paths(
&profile_dir,
&wifictl_root,
&netcfg_root,
"/usr/bin/true",
));
backend.set_active_profile("wifi-dhcp").unwrap();
let message = backend.disconnect(Some("wifi-dhcp"), "wlan0").unwrap();
assert!(message.contains("disconnected wlan0"));
assert_eq!(backend.active_profile_name().unwrap(), None);
assert_eq!(
fs::read_to_string(wifictl_root.join("ifaces/wlan0/disconnect")).unwrap(),
"1\n"
);
}