use assert_cmd::cargo_bin;
use std::process::Command;
type TestResult = Result<(), Box<dyn std::error::Error>>;
fn unique_temp_script_path(stem: &str) -> std::path::PathBuf {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|duration| duration.as_nanos())
.unwrap_or(0);
std::env::temp_dir().join(format!("{stem}_{}_{}.nu", std::process::id(), unique))
}
#[test]
fn help_shows_usage() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd.arg("--help").output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("Usage:"));
assert!(stdout.contains("--commands"));
Ok(())
}
#[test]
fn help_lists_all_flags() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd.arg("--help").output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let required_flags = [
"--help",
"--version",
"--interactive",
"--login",
"--commands",
"--execute",
"--include-path",
"--table-mode",
"--error-style",
"--no-newline",
"--no-config-file",
"--no-history",
"--no-std-lib",
"--config",
"--env-config",
"--log-level",
"--log-target",
"--log-include",
"--log-exclude",
"--stdin",
"--testbin",
"--experimental-options",
"--lsp",
"--ide-goto-def",
"--ide-hover",
"--ide-complete",
"--ide-check",
"--ide-ast",
];
for flag in required_flags {
assert!(stdout.contains(flag), "missing {flag}");
}
#[cfg(feature = "plugin")]
{
for flag in ["--plugin-config", "--plugins"] {
assert!(stdout.contains(flag), "missing {flag}");
}
}
#[cfg(feature = "mcp")]
{
assert!(stdout.contains("--mcp"), "missing --mcp");
}
Ok(())
}
#[test]
fn short_value_with_equals_runs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-c=print 1"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "1");
Ok(())
}
#[test]
fn version_flag_prints_version() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd.arg("--version").output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(!stdout.trim().is_empty());
Ok(())
}
#[test]
fn inline_short_value_is_rejected() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-cfoo"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("inline values"));
Ok(())
}
#[test]
fn unknown_flag_suggests_correction() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--comma"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Did you mean"));
Ok(())
}
#[test]
fn experimental_options_accepts_bracketed_list() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--experimental-options",
"[example=false, reorder-cell-paths=true, pipefail=true]",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn experimental_options_accepts_comma_list() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--experimental-options",
"example=false, reorder-cell-paths=true, pipefail=true",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn experimental_options_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--experimental-options",
"examples=false",
"-c",
"print 1",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("experimental"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid experimental options"));
Ok(())
}
#[test]
fn experimental_options_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--experimental-options"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid experimental options"));
Ok(())
}
#[test]
fn table_mode_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--table-mode", "rounde"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("table-mode"));
assert!(stderr.contains("Valid table modes") || stderr.contains("Did you mean"));
Ok(())
}
#[test]
fn table_mode_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-m"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid table modes"));
Ok(())
}
#[test]
fn table_mode_accepts_valid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--table-mode",
"rounded",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn login_flag_runs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-l", "-c", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn config_flag_accepts_path() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--config", "missing.nu", "--no-std-lib", "-c", "print 1"])
.output()?;
assert!(!output.status.success());
Ok(())
}
#[test]
fn env_config_flag_accepts_path() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--env-config",
"missing.nu",
"--no-std-lib",
"-c",
"print 1",
])
.output()?;
assert!(!output.status.success());
Ok(())
}
#[test]
fn include_path_accepts_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-I",
"lib",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn execute_flag_accepts_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-e", "print 1"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("STDIN is not a TTY"));
Ok(())
}
#[test]
fn interactive_and_login_flags_run() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-il", "-c", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn no_newline_flag_suppresses_newline() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--no-newline",
"-c",
"print 1",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim_end(), "1");
Ok(())
}
#[test]
fn no_history_flag_runs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-history", "--no-std-lib", "-c", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_flags_accept_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-level",
"info",
"--log-target",
"stdout",
"--log-include",
"warn",
"--log-exclude",
"info",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_level_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-level", "infos"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("log-level"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn log_target_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-target", "std"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("log-target"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid log targets"));
Ok(())
}
#[test]
fn log_include_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"verbose",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("log-include"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn log_exclude_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-exclude",
"verbose",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("log-exclude"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn log_level_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-level"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn log_target_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-target"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid log targets"));
Ok(())
}
#[test]
fn log_include_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-include"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn log_exclude_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-exclude"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn stdin_flag_runs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--stdin",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn testbin_flag_accepts_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--testbin", "cococo", "--no-std-lib", "-c", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn testbin_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--testbin", "cocooo"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("testbin"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid test bins"));
Ok(())
}
#[test]
fn testbin_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--testbin"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid test bins"));
Ok(())
}
#[test]
fn error_style_flag_accepts_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--error-style",
"plain",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn error_style_rejects_invalid_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--error-style", "fanc"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("error-style"));
assert!(stderr.contains("Did you mean") || stderr.contains("Valid error styles"));
Ok(())
}
#[test]
fn error_style_missing_value_lists_modes() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--error-style"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid error styles"));
Ok(())
}
#[test]
fn ide_flags_accept_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--ide-goto-def",
"0",
"--ide-hover",
"0",
"--ide-complete",
"0",
"--ide-check",
"0",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("ide") || stderr.contains("panicked"));
Ok(())
}
#[test]
fn ide_ast_flag_runs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--ide-ast",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn lsp_flag_accepts_run() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--lsp"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("disconnected channel"));
Ok(())
}
#[test]
fn mcp_flag_runs_when_enabled() -> TestResult {
#[cfg(feature = "mcp")]
{
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--mcp"])
.output()?;
assert!(output.status.success());
}
Ok(())
}
#[test]
fn plugin_flags_accept_paths_when_enabled() -> TestResult {
#[cfg(feature = "plugin")]
{
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--plugin-config",
"missing.nu",
"--plugins",
"missing-plugin",
"--no-std-lib",
"-c",
"print 1",
])
.output()?;
assert!(!output.status.success());
}
Ok(())
}
#[test]
fn plugins_requires_absolute_paths() -> TestResult {
#[cfg(feature = "plugin")]
{
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--plugins",
"nu_plugin_gstat",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("plugin"));
}
Ok(())
}
#[test]
fn login_shell_sets_dash_name() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd.arg("-c").arg("print 1").output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn double_dash_preserves_script_args() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd.args(["--help", "--", "--flag", "value"]).output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("Usage:"));
Ok(())
}
#[test]
fn log_include_accepts_single_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"error",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_include_accepts_multiple_values_space_separated() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"error",
"warn",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_include_accepts_comma_separated_no_brackets() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"error,warn",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_include_accepts_comma_separated_with_spaces() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"error, warn, info",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_include_accepts_bracketed_list() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"[error,warn]",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_include_accepts_bracketed_list_with_spaces() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"[error, warn, info]",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_include_rejects_invalid_level() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-include",
"invalid",
"-c",
"print 'test'",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Invalid value for `--log-include`"));
Ok(())
}
#[test]
fn log_exclude_accepts_single_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-exclude",
"debug",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_exclude_accepts_multiple_values_space_separated() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-exclude",
"debug",
"trace",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_exclude_accepts_comma_separated() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-exclude",
"debug,trace",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_exclude_accepts_bracketed_list() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-exclude",
"[debug, trace]",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn log_exclude_rejects_invalid_level() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-exclude",
"invalid",
"-c",
"print 'test'",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Invalid value for `--log-exclude`"));
Ok(())
}
#[test]
fn experimental_options_accepts_unquoted_bracketed_multivalue() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--experimental-options",
"[example=false,",
"reorder-cell-paths=true,",
"pipefail=true,",
"enforce-runtime-annotations=true]",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn experimental_options_accepts_all() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--experimental-options",
"all",
"-c",
"print 'test'",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn parses_combined_shorts_with_value_last() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-ilc", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn accepts_combined_shorts_without_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-il", "-c", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn accepts_split_shorts_for_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-i",
"-l",
"-c",
"print 1",
])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn accepts_group_then_value_flag() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-il", "-c", "print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn accepts_group_then_value_flag_with_equals() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-il", "-c=print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn missing_table_mode_lists_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-m"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid table modes"));
Ok(())
}
#[test]
fn missing_error_style_lists_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--error-style"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid error styles"));
Ok(())
}
#[test]
fn missing_testbin_lists_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--testbin"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid test bins"));
Ok(())
}
#[test]
fn rejects_invalid_testbin_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--testbin",
"cocooo",
"-c",
"print 1",
])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Did you mean") || stderr.contains("Valid test bins"));
Ok(())
}
#[test]
fn missing_log_level_lists_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-level"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid log levels"));
Ok(())
}
#[test]
fn missing_log_target_lists_values() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--log-target"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Valid log targets"));
Ok(())
}
#[test]
fn rejects_value_flag_not_last() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-cil"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("expects a value"));
Ok(())
}
#[test]
fn rejects_inline_short_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-cfoo"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("inline"));
Ok(())
}
#[test]
fn rejects_combined_inline_short_value() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-abcfoo"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("inline"));
Ok(())
}
#[test]
fn accepts_short_value_with_equals() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-c=print 1"])
.output()?;
assert!(output.status.success());
Ok(())
}
#[test]
fn suggests_unknown_flags() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "--comma", "ls"])
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(!output.status.success());
assert!(stderr.contains("Unknown flag"));
assert!(stderr.contains("Did you mean"));
Ok(())
}
#[test]
fn no_config_file_flag_prevents_config_loading() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-c", "print 'ok'"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn no_config_file_short_flag_works() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["-n", "--no-std-lib", "-c", "print 'ok'"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn no_std_lib_flag_prevents_std_loading() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-c", "print 'ok'"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn include_path_accepts_single_path() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--include-path",
".",
"-c",
"print 'ok'",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn include_path_short_flag_works() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-I",
".",
"-c",
"print 'ok'",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn include_path_accepts_multiple_paths_separated() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let paths = format!(".{sep}tests", sep = '\x1e');
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--include-path",
&paths,
"-c",
"print 'ok'",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn no_newline_with_commands_suppresses_final_newline() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--no-newline",
"-c",
"1 + 1",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
let trimmed = stdout.trim_end();
assert_eq!(trimmed, "2");
assert_eq!(stdout, trimmed); Ok(())
}
#[test]
fn no_newline_only_affects_result_not_print() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--no-newline",
"-c",
"print 'test'",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim_end(), "test");
Ok(())
}
#[test]
fn script_can_receive_arguments() -> TestResult {
let script_path = unique_temp_script_path("test_args_script");
std::fs::write(&script_path, "# This is a test script\nprint 'script ran'")?;
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
script_path.to_str().unwrap(),
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let _ = std::fs::remove_file(&script_path);
assert!(output.status.success());
assert!(stdout.contains("script ran"));
Ok(())
}
#[test]
fn script_path_can_have_args_after_it() -> TestResult {
let script_path = unique_temp_script_path("test_script_args2");
std::fs::write(&script_path, "print 'ok'")?;
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
script_path.to_str().unwrap(),
"--some-arg",
"value",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let _ = std::fs::remove_file(&script_path);
assert!(output.status.success());
assert!(stdout.contains("ok"));
Ok(())
}
#[test]
fn script_with_nu_flags_before_script_name() -> TestResult {
let script_path = unique_temp_script_path("test_flags_before");
std::fs::write(&script_path, "print 'flags work'")?;
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-level",
"error",
script_path.to_str().unwrap(),
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let _ = std::fs::remove_file(&script_path);
assert!(output.status.success());
assert!(stdout.contains("flags work"));
Ok(())
}
#[test]
fn combined_short_flags_work_with_commands() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd.args(["-nc", "print 'ok'"]).output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn table_mode_accepts_all_valid_modes() -> TestResult {
let modes = [
"basic",
"thin",
"light",
"compact",
"frameless",
"with_love",
"compact_double",
"default",
"rounded",
"reinforced",
"heavy",
"none",
"psql",
"markdown",
"dots",
"restructured",
"ascii_rounded",
"basic_compact",
];
for mode in modes {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--table-mode",
mode,
"-c",
"print 'ok'",
])
.output()?;
assert!(output.status.success(), "Failed for table mode: {}", mode);
}
Ok(())
}
#[test]
fn error_style_accepts_all_valid_styles() -> TestResult {
let styles = ["fancy", "plain", "short"];
for style in styles {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--error-style",
style,
"-c",
"print 'ok'",
])
.output()?;
assert!(output.status.success(), "Failed for error style: {}", style);
}
Ok(())
}
#[test]
fn log_level_accepts_all_valid_levels() -> TestResult {
let levels = ["error", "warn", "info", "debug", "trace"];
for level in levels {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-level",
level,
"--log-target",
"stderr",
"-c",
"print 'ok'",
])
.output()?;
assert!(output.status.success(), "Failed for log level: {}", level);
}
Ok(())
}
#[test]
fn log_target_accepts_all_valid_targets() -> TestResult {
let targets = ["stdout", "stderr", "mixed"];
for target in targets {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-target",
target,
"-c",
"print 'ok'",
])
.output()?;
assert!(output.status.success(), "Failed for log target: {}", target);
}
Ok(())
}
#[test]
fn log_file_without_target_fails() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-file",
"some.log",
"-c",
"print 'ok'",
])
.output()?;
assert!(
!output.status.success(),
"Expected failure when log-file is used without file target"
);
Ok(())
}
#[test]
fn log_file_target_needs_level() -> TestResult {
let temp = std::env::temp_dir().join("nu_cli_log.log");
let path = temp.to_str().unwrap();
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-target",
"file",
"--log-file",
path,
"-c",
"print 'ok'",
])
.output()?;
let _ = std::fs::remove_file(&temp);
assert!(
!output.status.success(),
"Expected failure when log-level is missing"
);
Ok(())
}
#[test]
fn log_target_file_with_log_file_succeeds() -> TestResult {
let temp = std::env::temp_dir().join("nu_cli_log.log");
let path = temp.to_str().unwrap();
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-target",
"file",
"--log-file",
path,
"--log-level",
"info",
"-c",
"print 'ok'",
])
.output()?;
let _ = std::fs::remove_file(&temp);
assert!(
output.status.success(),
"Expected success with log file path"
);
Ok(())
}
#[test]
fn log_target_file_without_log_file_fails() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--log-target",
"file",
"-c",
"print 'ok'",
])
.output()?;
assert!(
!output.status.success(),
"Expected failure when missing --log-file"
);
Ok(())
}
#[test]
fn include_path_sets_env_nu_lib_dirs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-I",
"/tmp/test",
"-c",
"$env.NU_LIB_DIRS | to nuon",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("/tmp/test"));
Ok(())
}
#[test]
fn include_path_appends_to_env_nu_lib_dirs() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-I",
"/tmp/append",
"-c",
"$env.NU_LIB_DIRS | to nuon",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("/tmp/append"));
#[cfg(windows)]
{
assert!(stdout.contains(r#"\scripts"#));
assert!(stdout.contains(r#"\completions"#));
}
#[cfg(not(windows))]
{
assert!(stdout.contains("/scripts"));
assert!(stdout.contains("/completions"));
}
Ok(())
}
#[test]
fn nu_lib_dirs_env_var_sets_env() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
cmd.env("NU_LIB_DIRS", "/tmp/envpath");
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-c",
"$env.NU_LIB_DIRS | to nuon",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("/tmp/envpath"));
Ok(())
}
#[test]
fn commands_flag_exits_after_execution() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-c", "print 'test'"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "test");
Ok(())
}
#[test]
fn stdin_flag_with_commands_receives_input() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let mut child = cmd
.args(["--no-config-file", "--no-std-lib", "--stdin", "-c", "$in"])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()?;
if let Some(mut stdin) = child.stdin.take() {
use std::io::Write;
stdin.write_all(b"test input")?;
}
let output = child.wait_with_output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert!(stdout.contains("test input"));
Ok(())
}
#[test]
fn bare_dash_is_rejected() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-"])
.output()?;
assert!(!output.status.success());
Ok(())
}
#[test]
fn long_flag_without_value_works() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--no-history",
"-c",
"print ok",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn long_flag_with_value_works() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"--table-mode",
"basic",
"-c",
"print ok",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn short_flag_without_value_works() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args(["--no-config-file", "--no-std-lib", "-l", "-c", "print ok"])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn short_flag_with_value_works() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-m",
"basic",
"-c",
"print ok",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}
#[test]
fn mixed_long_and_short_flags_work() -> TestResult {
let mut cmd = Command::new(cargo_bin!());
let output = cmd
.args([
"--no-config-file",
"--no-std-lib",
"-il",
"--table-mode",
"basic",
"--error-style",
"plain",
"-c",
"print ok",
])
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(output.status.success());
assert_eq!(stdout.trim(), "ok");
Ok(())
}