diff --git a/crates/jarvis-core/src/commands.rs b/crates/jarvis-core/src/commands.rs index c250cd0..ae1c133 100644 --- a/crates/jarvis-core/src/commands.rs +++ b/crates/jarvis-core/src/commands.rs @@ -266,6 +266,112 @@ pub fn list_paths(commands: &[JCommandsList]) -> Vec<&Path> { commands.iter().map(|x| x.path.as_path()).collect() } +#[cfg(test)] +mod tests { + use super::*; + + fn resources_commands_dir() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent().unwrap() + .parent().unwrap() + .join("resources/commands") + } + + #[test] + fn every_command_toml_parses() { + let cmds_dir = resources_commands_dir(); + assert!(cmds_dir.is_dir(), "missing {}", cmds_dir.display()); + + let mut failures = Vec::new(); + let mut packs_seen = 0; + + for entry in fs::read_dir(&cmds_dir).expect("read_dir") { + let entry = entry.expect("entry"); + let toml_file = entry.path().join("command.toml"); + if !toml_file.exists() { + continue; + } + packs_seen += 1; + let body = fs::read_to_string(&toml_file).expect("read"); + if let Err(e) = toml::from_str::(&body) { + failures.push(format!("{}: {}", toml_file.display(), e)); + } + } + + assert!(packs_seen > 0, "no command packs found in {}", cmds_dir.display()); + assert!( + failures.is_empty(), + "parse failures ({}):\n{}", + failures.len(), + failures.join("\n") + ); + } + + #[test] + fn lua_command_scripts_exist() { + let cmds_dir = resources_commands_dir(); + let mut missing = Vec::new(); + + for entry in fs::read_dir(&cmds_dir).expect("read_dir") { + let entry = entry.expect("entry"); + let pack_path = entry.path(); + let toml_file = pack_path.join("command.toml"); + if !toml_file.exists() { + continue; + } + let body = fs::read_to_string(&toml_file).expect("read"); + let pack: JCommandsList = toml::from_str(&body).expect("parse"); + for cmd in &pack.commands { + if cmd.cmd_type != "lua" { + continue; + } + let script = if cmd.script.is_empty() { + "script.lua".to_string() + } else { + cmd.script.clone() + }; + let script_path = pack_path.join(&script); + if !script_path.exists() { + missing.push(format!( + "{} -> {} (missing)", + cmd.id, + script_path.display() + )); + } + } + } + + assert!(missing.is_empty(), "missing Lua scripts:\n{}", missing.join("\n")); + } + + #[test] + fn every_command_has_phrases() { + let cmds_dir = resources_commands_dir(); + let mut empty = Vec::new(); + + for entry in fs::read_dir(&cmds_dir).expect("read_dir") { + let toml_file = entry.expect("entry").path().join("command.toml"); + if !toml_file.exists() { + continue; + } + let body = fs::read_to_string(&toml_file).expect("read"); + let pack: JCommandsList = toml::from_str(&body).expect("parse"); + for cmd in &pack.commands { + // structural commands (terminate, stop_chaining) may omit phrases + if cmd.cmd_type == "terminate" || cmd.cmd_type == "stop_chaining" { + continue; + } + let total: usize = cmd.phrases.values().map(|v| v.len()).sum(); + if total == 0 { + empty.push(cmd.id.clone()); + } + } + } + + assert!(empty.is_empty(), "commands with no phrases: {:?}", empty); + } +} + #[cfg(feature = "lua")] fn execute_lua_command( cmd_path: &PathBuf,