Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error message when run_command_async fails to find a command #1831

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion test-helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub mod mock_cassandra;
pub mod shotover_process;
mod test_tracing;

use anyhow::{anyhow, Result};
use anyhow::{anyhow, Error, Result};
use std::path::Path;
use subprocess::{Exec, Redirection};

Expand Down Expand Up @@ -38,16 +38,76 @@ pub fn run_command(command: &str, args: &[&str]) -> Result<String> {
}
}

fn command_not_found_error(command: &str, args: &[&str]) -> Error {
// Maps a command to its associated dependency; however, if the name of the command and dependency is the same, we just use the command name.
// Currently, the only use case is mapping `npm` to its dependency `nodejs`.
let dependency = match command {
"npm" => "nodejs",
_ => command,
};

let args_part = if !args.is_empty() {
format!(" {}", args.join(" "))
} else {
String::new()
};

anyhow!(
"Attempted to run the command `{}{}` but {} does not exist. Have you installed {}?",
command,
args_part,
command,
dependency
)
}

pub async fn run_command_async(current_dir: &Path, command: &str, args: &[&str]) {
let output = tokio::process::Command::new(command)
.args(args)
.current_dir(current_dir)
.kill_on_drop(true)
.status()
.await
.map_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
return command_not_found_error(command, args);
}

anyhow!(e)
})
.unwrap();

if !output.success() {
panic!("command {command} {args:?} failed. See above output.")
}
}

#[cfg(test)]
mod tests {
use super::*;
use futures_util::FutureExt;

#[tokio::test]
async fn test_run_command_async_not_found_message() {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let command = "shotover_non_existent_command";
let args = &["arg1", "arg2"];
let result = std::panic::AssertUnwindSafe(run_command_async(dir, command, args))
.catch_unwind()
.await;

assert!(result.is_err(), "Expected a panic but none occurred");

if let Err(panic_info) = result {
if let Some(error_message) = panic_info.downcast_ref::<String>() {
assert!(
error_message.contains("Attempted to run the command `shotover_non_existent_command arg1 arg2` but shotover_non_existent_command does not exist. Have you installed shotover_non_existent_command?"),
"Error message did not contain the expected NotFound error got: {}",
error_message
);
} else {
panic!("Panic payload was not a string: {:?}", panic_info);
}
}
}
}
Loading