#[cfg(test)]
mod tests {
use std::collections::BTreeSet;
use std::fs;
use std::path::PathBuf;
const HANDLER_COMMANDS: &[&str] = &[
"available_ports",
"managed_ports",
"cancel_read",
"close",
"close_all",
"force_close",
"open",
"capabilities",
"watch",
"unwatch",
"watch_ports",
"unwatch_ports",
"read",
"read_binary",
"write",
"write_binary",
"set_baud_rate",
"set_data_bits",
"set_flow_control",
"set_parity",
"set_stop_bits",
"set_timeout",
"write_request_to_send",
"write_data_terminal_ready",
"read_clear_to_send",
"read_data_set_ready",
"read_ring_indicator",
"read_carrier_detect",
"bytes_to_read",
"bytes_to_write",
"clear_buffer",
"set_break",
"clear_break",
"set_log_level",
"get_log_level",
"exchange",
"exchange_binary",
"cancel_exchange",
"at",
"at_phases",
"send_sms_pdu",
"configure_at_session",
"enable_mux",
"open_mux_channel",
"disable_mux",
];
fn repo_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
fn permission_command_names() -> BTreeSet<String> {
let dir = repo_root().join("permissions/autogenerated/commands");
fs::read_dir(&dir)
.unwrap_or_else(|e| panic!("read {}: {e}", dir.display()))
.filter_map(|e| e.ok())
.filter_map(|e| {
let name = e.file_name().into_string().ok()?;
name.strip_suffix(".toml").map(str::to_string)
})
.collect()
}
fn guest_js_invoke_commands() -> BTreeSet<String> {
let js = fs::read_to_string(repo_root().join("guest-js/serial-port.ts"))
.expect("guest-js/serial-port.ts");
let needle = "plugin:serialplugin|";
let mut out = BTreeSet::new();
let mut rest = js.as_str();
while let Some(i) = rest.find(needle) {
let after = &rest[i + needle.len()..];
let end = after
.find(|c: char| !c.is_ascii_alphanumeric() && c != '_')
.unwrap_or(after.len());
out.insert(after[..end].to_string());
rest = &after[end..];
}
out
}
#[test]
fn handler_commands_match_permission_files() {
let perms = permission_command_names();
let handler: BTreeSet<_> = HANDLER_COMMANDS.iter().map(|s| (*s).to_string()).collect();
assert_eq!(
handler, perms,
"mismatch generate_handler! ↔ permissions/autogenerated/commands"
);
}
#[test]
fn guest_js_invokes_exactly_handler_commands() {
let js = guest_js_invoke_commands();
let handler: BTreeSet<_> = HANDLER_COMMANDS.iter().map(|s| (*s).to_string()).collect();
assert_eq!(js, handler, "mismatch guest-js invoke ↔ generate_handler!");
}
#[test]
fn default_permission_set_covers_all_allow_commands() {
let default = fs::read_to_string(repo_root().join("permissions/default.toml"))
.expect("permissions/default.toml");
for cmd in HANDLER_COMMANDS {
let allow = format!("allow-{}", cmd.replace('_', "-"));
let present = default.lines().any(|line| {
let trimmed = line.trim().trim_end_matches(',');
trimmed == format!("\"{allow}\"")
});
assert!(present, "default.toml missing {allow} for command {cmd}");
}
}
}