mod common;
use common::init_test_logging;
use rich_rust::interactive::{Confirm, Select};
use rich_rust::prelude::*;
use std::io::{self, Cursor};
use std::sync::Arc;
fn interactive_console() -> Console {
Console::builder()
.width(80)
.force_terminal(true)
.markup(true)
.build()
}
fn non_interactive_console() -> Console {
Console::builder()
.width(80)
.force_terminal(false)
.markup(true)
.build()
}
fn input(s: &str) -> Cursor<Vec<u8>> {
Cursor::new(s.as_bytes().to_vec())
}
#[test]
fn test_prompt_basic_text_input() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Name");
let mut reader = input("Alice\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "Alice");
}
#[test]
fn test_prompt_default_on_empty_input() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Name").default("Bob");
let mut reader = input("\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "Bob");
}
#[test]
fn test_prompt_user_input_overrides_default() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Name").default("Bob");
let mut reader = input("Charlie\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "Charlie");
}
#[test]
fn test_prompt_allow_empty() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Optional").allow_empty(true);
let mut reader = input("\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "");
}
#[test]
fn test_prompt_trims_trailing_whitespace() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Input");
let mut reader = input("hello \n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "hello");
}
#[test]
fn test_prompt_validation_retry() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Age").validate(|s: &str| {
s.parse::<u32>()
.map(|_| ())
.map_err(|_| "Enter a number".to_string())
});
let mut reader = input("abc\n25\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "25");
}
#[test]
fn test_prompt_validation_passes() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Email").validate(|s: &str| {
if s.contains('@') {
Ok(())
} else {
Err("Must contain @".to_string())
}
});
let mut reader = input("user@example.com\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "user@example.com");
}
#[test]
fn test_prompt_non_interactive_returns_default() {
init_test_logging();
let console = non_interactive_console();
let prompt = Prompt::new("Name").default("DefaultUser");
let result = prompt.ask(&console).unwrap();
assert_eq!(result, "DefaultUser");
}
#[test]
fn test_prompt_non_interactive_no_default_errors() {
init_test_logging();
let console = non_interactive_console();
let prompt = Prompt::new("Name");
let result = prompt.ask(&console);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"prompt requires an interactive console"
);
}
#[test]
fn test_prompt_eof_on_empty_stream() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Input");
let mut reader = input("");
let result = prompt.ask_from(&console, &mut reader);
assert!(result.is_err());
assert_eq!(result.unwrap_err().to_string(), "prompt input reached EOF");
}
#[test]
fn test_prompt_max_length_exceeded() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Input").max_length(10);
let mut reader = input("This is way too long\n");
let result = prompt.ask_from(&console, &mut reader);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_input_too_long());
assert_eq!(err.input_limit(), Some(10));
}
#[test]
fn test_prompt_max_length_within_limit() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Input").max_length(100);
let mut reader = input("Short\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "Short");
}
#[test]
fn test_integer_prompt_valid() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Count").validate(|s: &str| {
s.parse::<i64>()
.map(|_| ())
.map_err(|_| "Enter a valid integer".to_string())
});
let mut reader = input("42\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "42");
}
#[test]
fn test_integer_prompt_retry_then_valid() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Count").validate(|s: &str| {
s.parse::<i64>()
.map(|_| ())
.map_err(|_| "Enter a valid integer".to_string())
});
let mut reader = input("abc\n3.14\n99\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "99");
}
#[test]
fn test_integer_prompt_negative() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Temperature").validate(|s: &str| {
s.parse::<i64>()
.map(|_| ())
.map_err(|_| "Enter a valid integer".to_string())
});
let mut reader = input("-10\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "-10");
}
#[test]
fn test_float_prompt_valid() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Price").validate(|s: &str| {
s.parse::<f64>()
.map(|_| ())
.map_err(|_| "Enter a valid number".to_string())
});
let mut reader = input("3.14\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "3.14");
}
#[test]
fn test_float_prompt_retry() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Weight").validate(|s: &str| {
s.parse::<f64>()
.map(|_| ())
.map_err(|_| "Enter a valid number".to_string())
});
let mut reader = input("heavy\n72.5\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "72.5");
}
#[test]
fn test_confirm_yes() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Proceed?");
let mut reader = input("y\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(result);
}
#[test]
fn test_confirm_no() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Proceed?");
let mut reader = input("n\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(!result);
}
#[test]
fn test_confirm_yes_full() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Continue?");
let mut reader = input("yes\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(result);
}
#[test]
fn test_confirm_no_full() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Continue?");
let mut reader = input("no\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(!result);
}
#[test]
fn test_confirm_true_and_one() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Q?");
let mut reader = input("true\n");
assert!(confirm.ask_from(&console, &mut reader).unwrap());
let confirm = Confirm::new("Q?");
let mut reader = input("1\n");
assert!(confirm.ask_from(&console, &mut reader).unwrap());
}
#[test]
fn test_confirm_false_and_zero() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Q?");
let mut reader = input("false\n");
assert!(!confirm.ask_from(&console, &mut reader).unwrap());
let confirm = Confirm::new("Q?");
let mut reader = input("0\n");
assert!(!confirm.ask_from(&console, &mut reader).unwrap());
}
#[test]
fn test_confirm_default_on_empty() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Proceed?").default(true);
let mut reader = input("\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(result);
}
#[test]
fn test_confirm_default_false_on_empty() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Delete?").default(false);
let mut reader = input("\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(!result);
}
#[test]
fn test_confirm_invalid_then_valid() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Proceed?");
let mut reader = input("maybe\ny\n");
let result = confirm.ask_from(&console, &mut reader).unwrap();
assert!(result);
}
#[test]
fn test_confirm_case_insensitive() {
init_test_logging();
let console = interactive_console();
let confirm = Confirm::new("Q?");
let mut reader = input("Y\n");
assert!(confirm.ask_from(&console, &mut reader).unwrap());
let confirm = Confirm::new("Q?");
let mut reader = input("N\n");
assert!(!confirm.ask_from(&console, &mut reader).unwrap());
let confirm = Confirm::new("Q?");
let mut reader = input("YES\n");
assert!(confirm.ask_from(&console, &mut reader).unwrap());
}
#[test]
fn test_confirm_non_interactive_returns_default() {
init_test_logging();
let console = non_interactive_console();
let confirm = Confirm::new("Q?").default(true);
let result = confirm.ask(&console).unwrap();
assert!(result);
}
#[test]
fn test_confirm_non_interactive_no_default_errors() {
init_test_logging();
let console = non_interactive_console();
let confirm = Confirm::new("Q?");
let result = confirm.ask(&console);
assert!(result.is_err());
}
#[test]
fn test_select_by_text() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Color").choices(["red", "green", "blue"]);
let mut reader = input("green\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "green");
}
#[test]
fn test_select_by_number() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Fruit").choices(["apple", "banana", "cherry"]);
let mut reader = input("2\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "banana");
}
#[test]
fn test_select_default_on_empty() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Size")
.choices(["small", "medium", "large"])
.default("medium");
let mut reader = input("\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "medium");
}
#[test]
fn test_select_invalid_then_valid() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Color").choices(["red", "green", "blue"]);
let mut reader = input("purple\n1\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "red");
}
#[test]
fn test_select_case_insensitive() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Pick").choices(["Red", "Green", "Blue"]);
let mut reader = input("red\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "Red");
}
#[test]
fn test_select_no_choices_error() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Pick");
let mut reader = input("anything\n");
let result = select.ask_from(&console, &mut reader);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("No choices"));
}
#[test]
fn test_select_non_interactive_returns_default() {
init_test_logging();
let console = non_interactive_console();
let select = Select::new("Pick").choices(["a", "b", "c"]).default("b");
let result = select.ask(&console).unwrap();
assert_eq!(result, "b");
}
#[test]
fn test_select_non_interactive_no_default_errors() {
init_test_logging();
let console = non_interactive_console();
let select = Select::new("Pick").choices(["a", "b", "c"]);
let result = select.ask(&console);
assert!(result.is_err());
}
#[test]
fn test_select_boundary_numbers() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Pick").choices(["first", "middle", "last"]);
let mut reader = input("1\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "first");
let select = Select::new("Pick").choices(["first", "middle", "last"]);
let mut reader = input("3\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "last");
}
#[test]
fn test_select_out_of_range_number() {
init_test_logging();
let console = interactive_console();
let select = Select::new("Pick").choices(["a", "b"]);
let mut reader = input("5\na\n");
let result = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "a");
}
#[test]
fn test_pager_default_construction() {
init_test_logging();
let pager = Pager::default();
let _ = pager;
}
#[test]
fn test_pager_custom_command() {
init_test_logging();
let pager = Pager::default().command("less");
let _ = pager;
}
#[test]
fn test_pager_allow_color() {
init_test_logging();
let pager = Pager::default().allow_color(true);
let _ = pager;
let pager2 = Pager::default().allow_color(false);
let _ = pager2;
}
#[test]
fn test_status_non_interactive() {
init_test_logging();
let console = Arc::new(non_interactive_console());
let status = Status::new(&console, "Loading...").unwrap();
status.update("Still loading...");
drop(status);
}
#[test]
fn test_status_update_message() {
init_test_logging();
let console = Arc::new(non_interactive_console());
let status = Status::new(&console, "First message").unwrap();
status.update("Second message");
status.update("Third message");
drop(status);
}
#[test]
fn test_status_drop_safety() {
init_test_logging();
let console = Arc::new(non_interactive_console());
{
let status = Status::new(&console, "Temporary").unwrap();
let _ = status;
}
}
#[test]
fn test_prompt_error_display() {
init_test_logging();
assert_eq!(
PromptError::NotInteractive.to_string(),
"prompt requires an interactive console"
);
assert_eq!(PromptError::Eof.to_string(), "prompt input reached EOF");
assert_eq!(
PromptError::Validation("bad input".into()).to_string(),
"bad input"
);
let io_err = PromptError::Io(io::Error::new(io::ErrorKind::BrokenPipe, "pipe broke"));
assert!(io_err.to_string().contains("pipe broke"));
}
#[test]
fn test_prompt_error_input_too_long() {
init_test_logging();
let err = PromptError::InputTooLong {
limit: 100,
received: 200,
};
assert!(err.is_input_too_long());
assert_eq!(err.input_limit(), Some(100));
assert!(err.to_string().contains("200"));
assert!(err.to_string().contains("100"));
}
#[test]
fn test_prompt_error_std_error_trait() {
init_test_logging();
use std::error::Error;
let err = PromptError::NotInteractive;
assert!(err.source().is_none());
let io_err = PromptError::Io(io::Error::other("test"));
assert!(io_err.source().is_some());
}
#[test]
fn test_prompt_error_from_io() {
init_test_logging();
let io_err = io::Error::new(io::ErrorKind::UnexpectedEof, "eof");
let prompt_err: PromptError = io_err.into();
assert!(prompt_err.to_string().contains("eof"));
}
#[test]
fn test_prompt_no_markup() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Input").markup(false);
let mut reader = input("test\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "test");
}
#[test]
fn test_prompt_hide_default() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Name").default("Bob").show_default(false);
let mut reader = input("\n");
let result = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(result, "Bob");
}
#[test]
fn test_full_interactive_workflow() {
init_test_logging();
let console = interactive_console();
let prompt = Prompt::new("Name");
let mut reader = input("Alice\n");
let name = prompt.ask_from(&console, &mut reader).unwrap();
assert_eq!(name, "Alice");
let select = Select::new("Role").choices(["admin", "user", "guest"]);
let mut reader = input("2\n");
let role = select.ask_from(&console, &mut reader).unwrap();
assert_eq!(role, "user");
let confirm = Confirm::new("Create account?").default(true);
let mut reader = input("y\n");
let confirmed = confirm.ask_from(&console, &mut reader).unwrap();
assert!(confirmed);
}
#[test]
fn test_non_interactive_all_defaults() {
init_test_logging();
let console = non_interactive_console();
let name = Prompt::new("Name").default("System").ask(&console).unwrap();
assert_eq!(name, "System");
let role = Select::new("Role")
.choices(["admin", "user"])
.default("user")
.ask(&console)
.unwrap();
assert_eq!(role, "user");
let confirmed = Confirm::new("Proceed?")
.default(true)
.ask(&console)
.unwrap();
assert!(confirmed);
}