test(peripherals): add unit tests for peripheral module configuration and listing

Add tests for list_configured_boards() covering enabled/disabled states and
empty/non-empty board configurations. Add test verifying create_peripheral_tools()
returns empty when peripherals are disabled. Addresses audit finding CRITICAL-1
for the untested peripherals module — covers all non-hardware-gated logic paths.

Fix pre-existing Windows build errors in config/schema.rs: make non-unix
sync_directory async and gate unix-only imports behind #[cfg(unix)].

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Alex Gorevski 2026-02-19 13:28:22 -08:00
parent bec1dc7b8c
commit 673697a43e
2 changed files with 77 additions and 3 deletions

View file

@ -7,7 +7,9 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{OnceLock, RwLock}; use std::sync::{OnceLock, RwLock};
use tokio::fs::{self, File, OpenOptions}; use tokio::fs::{self, OpenOptions};
#[cfg(unix)]
use tokio::fs::File;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
const SUPPORTED_PROXY_SERVICE_KEYS: &[&str] = &[ const SUPPORTED_PROXY_SERVICE_KEYS: &[&str] = &[
@ -3373,14 +3375,16 @@ async fn sync_directory(path: &Path) -> Result<()> {
} }
#[cfg(not(unix))] #[cfg(not(unix))]
fn sync_directory(_path: &Path) -> Result<()> { async fn sync_directory(_path: &Path) -> Result<()> {
Ok(()) Ok(())
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::{fs::Permissions, os::unix::fs::PermissionsExt, path::PathBuf}; #[cfg(unix)]
use std::{fs::Permissions, os::unix::fs::PermissionsExt};
use std::path::PathBuf;
use tokio::sync::{Mutex, MutexGuard}; use tokio::sync::{Mutex, MutexGuard};
use tokio::test; use tokio::test;
use tokio_stream::wrappers::ReadDirStream; use tokio_stream::wrappers::ReadDirStream;

View file

@ -231,3 +231,73 @@ pub async fn create_peripheral_tools(config: &PeripheralsConfig) -> Result<Vec<B
pub async fn create_peripheral_tools(_config: &PeripheralsConfig) -> Result<Vec<Box<dyn Tool>>> { pub async fn create_peripheral_tools(_config: &PeripheralsConfig) -> Result<Vec<Box<dyn Tool>>> {
Ok(Vec::new()) Ok(Vec::new())
} }
#[cfg(test)]
mod tests {
use super::*;
use crate::config::{PeripheralBoardConfig, PeripheralsConfig};
#[test]
fn list_configured_boards_when_disabled_returns_empty() {
let config = PeripheralsConfig {
enabled: false,
boards: vec![PeripheralBoardConfig {
board: "nucleo-f401re".into(),
transport: "serial".into(),
path: Some("/dev/ttyACM0".into()),
baud: 115_200,
}],
datasheet_dir: None,
};
let result = list_configured_boards(&config);
assert!(result.is_empty(), "disabled peripherals should return no boards");
}
#[test]
fn list_configured_boards_when_enabled_with_boards() {
let config = PeripheralsConfig {
enabled: true,
boards: vec![
PeripheralBoardConfig {
board: "nucleo-f401re".into(),
transport: "serial".into(),
path: Some("/dev/ttyACM0".into()),
baud: 115_200,
},
PeripheralBoardConfig {
board: "rpi-gpio".into(),
transport: "native".into(),
path: None,
baud: 115_200,
},
],
datasheet_dir: None,
};
let result = list_configured_boards(&config);
assert_eq!(result.len(), 2);
assert_eq!(result[0].board, "nucleo-f401re");
assert_eq!(result[1].board, "rpi-gpio");
}
#[test]
fn list_configured_boards_when_enabled_but_no_boards() {
let config = PeripheralsConfig {
enabled: true,
boards: vec![],
datasheet_dir: None,
};
let result = list_configured_boards(&config);
assert!(result.is_empty(), "enabled with no boards should return empty");
}
#[tokio::test]
async fn create_peripheral_tools_returns_empty_when_disabled() {
let config = PeripheralsConfig {
enabled: false,
boards: vec![],
datasheet_dir: None,
};
let tools = create_peripheral_tools(&config).await.unwrap();
assert!(tools.is_empty(), "disabled peripherals should produce no tools");
}
}