From 96bff65797da0e0392bef44069514c8907d5fe26 Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Sun, 7 Jun 2026 11:43:57 +0200 Subject: [PATCH 1/2] test(tui): add command parity harness --- crates/tui/src/commands/mod.rs | 201 ++++++++++++++++++++++++++ crates/tui/src/tui/command_palette.rs | 52 +++++++ 2 files changed, 253 insertions(+) diff --git a/crates/tui/src/commands/mod.rs b/crates/tui/src/commands/mod.rs index 59ea42874..130c598cd 100644 --- a/crates/tui/src/commands/mod.rs +++ b/crates/tui/src/commands/mod.rs @@ -1270,6 +1270,127 @@ mod tests { } } + #[test] + fn command_registry_metadata_is_complete_and_palette_safe() { + for command in COMMANDS { + assert!(!command.name.is_empty(), "command name must not be empty"); + assert_eq!( + command.name.trim(), + command.name, + "/{} command name must not need trimming", + command.name + ); + assert!( + command + .name + .chars() + .all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit()), + "/{} command names must stay lowercase ASCII", + command.name + ); + + let expected_usage_prefix = format!("/{}", command.name); + assert!( + command.usage.starts_with(&expected_usage_prefix), + "/{} usage must start with its canonical slash command, got {:?}", + command.name, + command.usage + ); + + let description = command.description_for(Locale::En); + assert!( + !description.trim().is_empty(), + "/{} must have non-empty English help text", + command.name + ); + + let palette_command = command.palette_command(); + assert!( + palette_command.starts_with(&expected_usage_prefix), + "/{} palette command must use the canonical command, got {:?}", + command.name, + palette_command + ); + assert_eq!( + palette_command.ends_with(' '), + command.requires_argument(), + "/{} palette command spacing must match argument requirement", + command.name + ); + + for alias in command.aliases { + assert!( + !alias.trim().is_empty(), + "/{} alias must not be empty", + command.name + ); + assert_eq!( + alias.trim(), + *alias, + "/{} alias /{alias} must not need trimming", + command.name + ); + assert!( + !alias.starts_with('/'), + "/{} alias /{alias} must be stored without a slash", + command.name + ); + assert!( + !alias.chars().any(char::is_whitespace), + "/{} alias /{alias} must not contain whitespace", + command.name + ); + } + } + } + + #[test] + fn command_info_resolves_canonical_names_and_aliases() { + for command in COMMANDS { + for lookup in [command.name.to_string(), format!("/{}", command.name)] { + let resolved = get_command_info(&lookup) + .unwrap_or_else(|| panic!("{lookup:?} should resolve to /{}", command.name)); + assert_eq!(resolved.name, command.name); + } + + for alias in command.aliases { + for lookup in [alias.to_string(), format!("/{alias}")] { + let resolved = get_command_info(&lookup).unwrap_or_else(|| { + panic!("{lookup:?} should resolve to /{}", command.name) + }); + assert_eq!(resolved.name, command.name); + } + } + } + } + + #[test] + fn every_registered_command_has_a_help_topic() { + for command in COMMANDS { + let mut app = create_test_app(); + let result = execute(&format!("/help {}", command.name), &mut app); + assert!( + !result.is_error, + "/help {} returned an error: {result:?}", + command.name + ); + let message = result + .message + .unwrap_or_else(|| panic!("/help {} should return text", command.name)); + assert!( + message.contains(command.name), + "/help {} should mention the command name, got {message:?}", + command.name + ); + assert!( + message.contains(command.usage), + "/help {} should include usage {:?}, got {message:?}", + command.name, + command.usage + ); + } + } + #[test] fn context_command_opens_inspector_and_keeps_ctx_alias() { let context = COMMANDS @@ -1534,6 +1655,86 @@ mod tests { name == "restore" } + #[test] + fn slash_parser_preserves_arguments_after_the_command_name() { + let mut app = create_test_app(); + let result = execute("/agent 2 review this carefully", &mut app); + assert!(!result.is_error); + let Some(AppAction::SendMessage(message)) = result.action else { + panic!("expected /agent to send a model instruction"); + }; + assert!(message.contains(r#"prompt: "review this carefully""#)); + assert!(message.contains("max_depth: 2")); + + let mut app = create_test_app(); + let result = execute(" /relay ship command harness ", &mut app); + assert!(!result.is_error); + let Some(AppAction::SendMessage(message)) = result.action else { + panic!("expected /relay to send a model instruction"); + }; + assert!(message.contains("Requested relay focus: ship command harness")); + + let mut app = create_test_app(); + let result = execute("/rlm 3 inspect this corpus", &mut app); + assert!(!result.is_error); + let Some(AppAction::SendMessage(message)) = result.action else { + panic!("expected /rlm to send a model instruction"); + }; + assert!(message.contains(r#"content: "inspect this corpus""#)); + assert!(message.contains("sub_rlm_max_depth: 3")); + } + + #[test] + fn representative_command_groups_keep_dispatch_surfaces() { + let mut app = create_test_app(); + let help = execute("/help clear", &mut app) + .message + .expect("/help clear should return text"); + assert!(help.contains("clear")); + assert!(help.contains("/clear")); + + let mut app = create_test_app(); + let result = execute("/config", &mut app); + assert!(matches!(result.action, Some(AppAction::OpenConfigView))); + + let mut app = create_test_app(); + let result = execute("/relay command boundary", &mut app); + assert!(!result.is_error); + assert!(matches!( + result.action, + Some(AppAction::SendMessage(message)) + if message.contains("Requested relay focus: command boundary") + )); + + let mut app = create_test_app(); + let note_help = execute("/note help", &mut app) + .message + .expect("/note help should return text"); + assert!(note_help.contains("Usage: /note")); + + let mut app = create_test_app(); + let result = execute("/hunt ship layer 2 | budget: 100", &mut app); + assert!(!result.is_error); + assert_eq!(app.hunt.quarry.as_deref(), Some("ship layer 2")); + assert_eq!(app.hunt.token_budget, Some(100)); + + let (mut app, _tmpdir, _guard) = create_isolated_test_app(); + let skills = execute("/skills", &mut app) + .message + .expect("/skills should return text"); + assert!(skills.contains("Skills location:")); + + let mut app = create_test_app(); + let result = execute("/task list", &mut app); + assert!(matches!(result.action, Some(AppAction::TaskList))); + + let mut app = create_test_app(); + let tokens = execute("/tokens", &mut app) + .message + .expect("/tokens should return text"); + assert!(tokens.contains("deepseek-v4-pro")); + } + /// Smoke test: every entry in `COMMANDS` must dispatch to a real handler. /// A dispatch miss surfaces as the fall-through `Unknown command:` error /// message in `execute`. This catches the case where a new command is diff --git a/crates/tui/src/tui/command_palette.rs b/crates/tui/src/tui/command_palette.rs index 1eef0f4b2..cfffcee38 100644 --- a/crates/tui/src/tui/command_palette.rs +++ b/crates/tui/src/tui/command_palette.rs @@ -1146,6 +1146,58 @@ mod tests { assert!(!command_labels.contains(&"/deepseek")); } + #[test] + fn command_palette_has_one_entry_for_every_registered_command() { + let tmp = TempDir::new().expect("tempdir"); + let skills_dir = tmp.path().join("skills"); + let mcp_config_path = tmp.path().join("mcp.json"); + let entries = build_entries( + Locale::En, + skills_dir.as_path(), + tmp.path(), + mcp_config_path.as_path(), + None, + ); + + let command_entries = entries + .iter() + .filter(|entry| entry.section == PaletteSection::Command) + .collect::>(); + assert_eq!(command_entries.len(), commands::COMMANDS.len()); + + for command in commands::COMMANDS { + let label = format!("/{}", command.name); + let matching = command_entries + .iter() + .filter(|entry| entry.label == label) + .collect::>(); + assert_eq!( + matching.len(), + 1, + "expected one palette entry for /{}", + command.name + ); + + let entry = matching[0]; + assert_eq!(entry.command, command.palette_command()); + assert!( + entry + .description + .contains(command.description_for(Locale::En)), + "/{} palette description should include command help text", + command.name + ); + if command.requires_argument() { + assert!( + entry.description.contains(command.usage), + "/{} palette description should include usage {:?}", + command.name, + command.usage + ); + } + } + } + #[test] fn command_palette_inserts_model_command_for_argument_entry() { let entries = build_entries( From acaae1c2e5e12115b23a53b904d1e0b9947d7d6a Mon Sep 17 00:00:00 2001 From: Paulo Aboim Pinto Date: Sun, 7 Jun 2026 12:24:13 +0200 Subject: [PATCH 2/2] test(tui): address command harness review --- crates/tui/src/commands/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/tui/src/commands/mod.rs b/crates/tui/src/commands/mod.rs index 130c598cd..5478ee4ac 100644 --- a/crates/tui/src/commands/mod.rs +++ b/crates/tui/src/commands/mod.rs @@ -1318,7 +1318,7 @@ mod tests { command.name ); - for alias in command.aliases { + for &alias in command.aliases { assert!( !alias.trim().is_empty(), "/{} alias must not be empty", @@ -1326,7 +1326,7 @@ mod tests { ); assert_eq!( alias.trim(), - *alias, + alias, "/{} alias /{alias} must not need trimming", command.name ); @@ -1353,7 +1353,7 @@ mod tests { assert_eq!(resolved.name, command.name); } - for alias in command.aliases { + for &alias in command.aliases { for lookup in [alias.to_string(), format!("/{alias}")] { let resolved = get_command_info(&lookup).unwrap_or_else(|| { panic!("{lookup:?} should resolve to /{}", command.name) @@ -1366,8 +1366,8 @@ mod tests { #[test] fn every_registered_command_has_a_help_topic() { + let mut app = create_test_app(); for command in COMMANDS { - let mut app = create_test_app(); let result = execute(&format!("/help {}", command.name), &mut app); assert!( !result.is_error,