#[allow(clippy::duplicate_mod)]
#[path = "helpers.rs"]
mod helpers;
#[allow(unused_imports)]
use helpers::fixtures::{MockAccessLevel, MockHandler, MockIo, TEST_TREE};
#[allow(unused_imports)]
use nut_shell::Shell;
#[test]
#[cfg(all(feature = "completion", not(feature = "authentication")))]
fn test_tab_completion_single_match() {
let mut shell = helpers::create_test_shell();
helpers::type_input(&mut shell, "ech");
shell.io_mut().clear_output();
shell.process_char('\t').unwrap();
let completion_output = shell.io_mut().output();
assert!(
completion_output.contains('o'),
"Tab should complete 'ech' to 'echo': {}",
completion_output
);
let output = helpers::execute_command(&mut shell, " completion_test");
assert!(output.contains("completion_test"));
}
#[test]
#[cfg(all(feature = "completion", not(feature = "authentication")))]
fn test_tab_with_no_input() {
let mut shell = helpers::create_test_shell();
shell.io_mut().clear_output();
helpers::press_tab(&mut shell);
let output = shell.io_mut().output();
helpers::assert_contains_all(&output, &["echo", "system"]);
}
#[test]
#[cfg(all(feature = "completion", not(feature = "authentication")))]
fn test_tab_with_no_matches() {
let mut shell = helpers::create_test_shell();
helpers::type_input(&mut shell, "xyz");
shell.io_mut().clear_output();
helpers::press_tab(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains('\x07'),
"Should emit bell character when no matches found"
);
}
#[test]
#[cfg(all(feature = "completion", not(feature = "authentication")))]
fn test_tab_completion_in_subdirectory() {
let mut shell = helpers::create_test_shell();
helpers::execute_command(&mut shell, "system");
helpers::type_input(&mut shell, "st");
shell.io_mut().clear_output();
helpers::press_tab(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains("atus") || output.contains("status"),
"Should complete 'status' from 'st' in /system: {}",
output
);
}
#[test]
#[cfg(all(feature = "completion", not(feature = "authentication")))]
fn test_tab_completes_directory_with_slash() {
let mut shell = helpers::create_test_shell();
helpers::type_input(&mut shell, "syst");
shell.io_mut().clear_output();
helpers::press_tab(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains("em/"),
"Directory completion should append '/': {}",
output
);
}
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn setup_shell_with_history(
commands: &[&str],
) -> Shell<'static, MockAccessLevel, MockIo, MockHandler, nut_shell::config::DefaultConfig> {
let mut shell = helpers::create_test_shell();
for cmd in commands {
helpers::execute_command(&mut shell, cmd);
}
shell.io_mut().clear_output();
shell
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_navigation_up() {
let mut shell = setup_shell_with_history(&["echo first", "echo second"]);
shell.process_char('\x1b').unwrap(); shell.process_char('[').unwrap();
shell.process_char('A').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("echo second"));
shell.io_mut().clear_output();
shell.process_char('\n').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("second"));
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_navigation_up_multiple() {
let mut shell = setup_shell_with_history(&["echo first", "echo second"]);
shell.process_char('\x1b').unwrap();
shell.process_char('[').unwrap();
shell.process_char('A').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("echo second"));
shell.io_mut().clear_output();
shell.process_char('\x1b').unwrap();
shell.process_char('[').unwrap();
shell.process_char('A').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("echo first"));
shell.io_mut().clear_output();
shell.process_char('\n').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("first"));
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_navigation_down() {
let mut shell = setup_shell_with_history(&["echo first", "echo second", "echo third"]);
shell.process_char('\x1b').unwrap();
shell.process_char('[').unwrap();
shell.process_char('A').unwrap();
shell.io_mut().clear_output();
shell.process_char('\x1b').unwrap();
shell.process_char('[').unwrap();
shell.process_char('A').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("echo second"));
shell.io_mut().clear_output();
shell.process_char('\x1b').unwrap();
shell.process_char('[').unwrap();
shell.process_char('B').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("echo third"));
shell.io_mut().clear_output();
shell.process_char('\n').unwrap();
let output = shell.io_mut().output();
assert!(output.contains("third"));
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_empty_buffer() {
let mut shell = helpers::create_test_shell();
shell.io_mut().clear_output();
helpers::press_up_arrow(&mut shell);
let output = shell.io_mut().output();
assert!(
output.is_empty() || output.trim().is_empty(),
"Up arrow on empty history should do nothing"
);
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_up_at_oldest() {
let mut shell = helpers::create_test_shell();
helpers::execute_command(&mut shell, "echo first");
helpers::execute_command(&mut shell, "echo second");
helpers::press_up_arrow(&mut shell);
helpers::press_up_arrow(&mut shell);
shell.io_mut().clear_output();
helpers::press_up_arrow(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains("echo first"),
"Should stay at oldest command: {}",
output
);
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_down_at_newest() {
let mut shell = helpers::create_test_shell();
helpers::execute_command(&mut shell, "echo test");
helpers::press_up_arrow(&mut shell);
shell.io_mut().clear_output();
helpers::press_down_arrow(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains("\r") || output.contains("\x1b"),
"Down at newest should clear buffer: {}",
output
);
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_after_failed_command() {
let mut shell = helpers::create_test_shell();
helpers::execute_command(&mut shell, "nonexistent");
helpers::execute_command(&mut shell, "echo valid");
shell.io_mut().clear_output();
helpers::press_up_arrow(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains("echo valid"),
"Should recall last command (even after failed command): {}",
output
);
}
#[test]
#[cfg(all(feature = "history", not(feature = "authentication")))]
fn test_history_edit_recalled_command() {
let mut shell = helpers::create_test_shell();
helpers::execute_command(&mut shell, "echo original");
helpers::press_up_arrow(&mut shell);
shell.io_mut().clear_output();
helpers::press_backspace_n(&mut shell, 8);
helpers::type_input(&mut shell, "modified");
shell.io_mut().clear_output();
helpers::press_enter(&mut shell);
let output = shell.io_mut().output();
assert!(
output.contains("modified"),
"Should execute edited command: {}",
output
);
}
#[tokio::test]
#[cfg(all(feature = "async", not(feature = "authentication")))]
async fn test_async_command_execution() {
let io = MockIo::new();
let handler = MockHandler;
let mut shell = Shell::new(&TEST_TREE, handler, io);
shell.activate().unwrap();
shell.io_mut().clear_output();
for c in "system\n".chars() {
shell.process_char_async(c).await.unwrap();
}
for c in "async-wait\n".chars() {
shell.process_char_async(c).await.unwrap();
}
let output = shell.io_mut().output();
assert!(
output.contains("Waited 100ms"),
"Async command should execute. Output: {}",
output
);
}
#[tokio::test]
#[cfg(all(feature = "async", not(feature = "authentication")))]
async fn test_async_command_with_arguments() {
let io = MockIo::new();
let handler = MockHandler;
let mut shell = Shell::new(&TEST_TREE, handler, io);
shell.activate().unwrap();
shell.io_mut().clear_output();
for c in "system\n".chars() {
shell.process_char_async(c).await.unwrap();
}
shell.io_mut().clear_output();
for c in "async-wait 250\n".chars() {
shell.process_char_async(c).await.unwrap();
}
let output = shell.io_mut().output();
assert!(output.contains("Waited 250ms"));
}