From 601b21e04e993db6dba9f47a10b1e6d221992389 Mon Sep 17 00:00:00 2001 From: kirillDevPro <113171057+kirillDevPro@users.noreply.github.com> Date: Fri, 25 Sep 2026 08:19:37 +0200 Subject: [PATCH] test(shell): cover kind fallback and shell arguments ShellBuilder asks ShellKind which program to run, how to spell a variable, and which flags to pass. The existing tests only reach Nushell, Fish, and sh quoting. Cmd and PowerShell rewrite variables differently and do not take -i, and an unknown program name on Windows falls back to PowerShell rather than POSIX. --- crates/moon-util/src/shell.rs | 3 + crates/moon-util/src/shell/shell_tests.rs | 82 +++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 crates/moon-util/src/shell/shell_tests.rs diff --git a/crates/moon-util/src/shell.rs b/crates/moon-util/src/shell.rs index e9b6d05..35ead2d 100644 --- a/crates/moon-util/src/shell.rs +++ b/crates/moon-util/src/shell.rs @@ -693,6 +693,9 @@ impl ShellKind { } } +#[cfg(test)] +mod shell_tests; + #[cfg(test)] mod tests { use super::*; diff --git a/crates/moon-util/src/shell/shell_tests.rs b/crates/moon-util/src/shell/shell_tests.rs new file mode 100644 index 0000000..c937034 --- /dev/null +++ b/crates/moon-util/src/shell/shell_tests.rs @@ -0,0 +1,82 @@ +use super::ShellKind; + +/// Catches `ShellKind::new` sending an unknown program to POSIX on Windows, +/// so a task would look for `sh` and fail to start a shell. +#[test] +fn unknown_program_falls_back_to_powershell_only_on_windows() { + assert_eq!(ShellKind::new("cmd.exe", false), ShellKind::Cmd); + assert_eq!( + ShellKind::new("powershell.exe", true), + ShellKind::PowerShell + ); + assert_eq!(ShellKind::new("pwsh", false), ShellKind::Pwsh); + assert_eq!(ShellKind::new("bash", false), ShellKind::Posix); + assert_eq!(ShellKind::new("fancy", true), ShellKind::PowerShell); + assert_eq!(ShellKind::new("fancy", false), ShellKind::Posix); +} + +/// Catches `to_cmd_variable` rewriting `${VAR:-default}` into a percent name, +/// so cmd would look up `VAR:-default` instead of keeping the default text. +#[test] +fn cmd_rewrites_variables_but_leaves_default_substitutions() { + assert_eq!(ShellKind::Cmd.to_shell_variable("$FOO"), "%FOO%"); + assert_eq!(ShellKind::Cmd.to_shell_variable("${FOO}"), "%FOO%"); + assert_eq!( + ShellKind::Cmd.to_shell_variable("${FOO:-bar}"), + "${FOO:-bar}" + ); + assert_eq!(ShellKind::Cmd.to_shell_variable("plain"), "plain"); +} + +/// Catches `to_powershell_variable` leaving `$FOO` as a local variable, so a +/// PowerShell task would miss the environment value the caller set. +#[test] +fn powershell_rewrites_variables_onto_the_env_drive() { + assert_eq!(ShellKind::PowerShell.to_shell_variable("$FOO"), "$env:FOO"); + assert_eq!(ShellKind::Pwsh.to_shell_variable("${BAR}"), "$env:BAR"); + assert_eq!( + ShellKind::PowerShell.to_shell_variable("${FOO:-bar}"), + "${FOO:-bar}" + ); + assert_eq!(ShellKind::Pwsh.to_shell_variable("plain"), "plain"); +} + +/// Catches `args_for_shell` dropping the quotes around a cmd `/C` payload, so +/// cmd would split `echo hi` on the space and run a different command. +#[test] +fn cmd_args_quote_the_command_and_ignore_interactive() { + let command = "echo hi".to_owned(); + let expected = vec!["/S".to_owned(), "/C".to_owned(), "\"echo hi\"".to_owned()]; + assert_eq!( + ShellKind::Cmd.args_for_shell(true, command.clone()), + expected + ); + assert_eq!(ShellKind::Cmd.args_for_shell(false, command), expected); +} + +/// Catches `args_for_shell` passing `-c` or `-i` to Windows PowerShell, so +/// the process would reject the flag and the task would not run. +#[test] +fn powershell_args_use_capital_c_without_an_interactive_flag() { + let command = "echo hi".to_owned(); + let expected = vec!["-C".to_owned(), "echo hi".to_owned()]; + assert_eq!( + ShellKind::PowerShell.args_for_shell(true, command.clone()), + expected + ); + assert_eq!(ShellKind::Pwsh.args_for_shell(false, command), expected); +} + +/// Catches `args_for_shell` always passing `-i` to a POSIX shell, so a +/// non-interactive task would start an interactive shell and wait on stdin. +#[test] +fn posix_args_add_interactive_only_when_requested() { + assert_eq!( + ShellKind::Posix.args_for_shell(false, "echo hi".to_owned()), + vec!["-c".to_owned(), "echo hi".to_owned()] + ); + assert_eq!( + ShellKind::Fish.args_for_shell(true, "echo hi".to_owned()), + vec!["-i".to_owned(), "-c".to_owned(), "echo hi".to_owned()] + ); +}