use gitbaby::command::cmd::Cmd; use std::path::PathBuf; macro_rules! unwrap_or_panic { ($expr:expr, $msg:literal) => { match $expr { Ok(v) => v, Err(e) => panic!("{}: {:?}", $msg, e), } }; ($expr:expr, $fmt:literal, $($arg:tt)*) => { match $expr { Ok(v) => v, Err(e) => panic!("{}: {:?}", format_args!($fmt, $($arg)*), e), } }; } #[tokio::test] async fn run_echo_and_capture_output() { let mut cmd = Cmd::new( "test:echo", "sh", vec!["-c".to_string(), "echo hello".to_string()], vec![], PathBuf::from("."), vec![], None, ); let out = unwrap_or_panic!(cmd.run().await, "run failed"); assert!(out.status.success(), "exit code should be 0"); assert_eq!(String::from_utf8_lossy(&out.stdout).trim(), "hello"); } #[tokio::test] async fn run_with_non_zero_exit_returns_output() { let mut cmd = Cmd::new( "test:false", "sh", vec!["-c".to_string(), "echo oops 1>&2; exit 7".to_string()], vec![], PathBuf::from("."), vec![], None, ); let res = cmd.run().await; match res { Ok(_) => panic!("run() with non-zero exit should fail"), Err(gitbaby::command::error::CmdError::NonZeroExit { code, stderr, .. }) => { assert_eq!(code, 7); assert_eq!(stderr.trim(), "oops"); } Err(e) => panic!("unexpected error: {:?}", e), } } #[tokio::test] async fn run_soft_keeps_non_zero_exit_in_output() { let mut cmd = Cmd::new( "test:false-soft", "sh", vec!["-c".to_string(), "echo oops 1>&2; exit 7".to_string()], vec![], PathBuf::from("."), vec![], None, ); let out = unwrap_or_panic!(cmd.run_soft().await, "run_soft failed"); assert_eq!(out.status.code(), Some(7)); assert_eq!(String::from_utf8_lossy(&out.stderr).trim(), "oops"); } #[tokio::test] async fn feed_writes_to_stdin() { let mut cmd = Cmd::new( "test:cat", "cat", vec![], vec![], PathBuf::from("."), vec![], None, ); unwrap_or_panic!(cmd.spawn().await, "spawn failed"); unwrap_or_panic!(cmd.feed(b"via-stdin").await, "feed failed"); let out = unwrap_or_panic!(cmd.run().await, "run failed"); assert_eq!(String::from_utf8_lossy(&out.stdout), "via-stdin"); } #[tokio::test] async fn env_vars_are_applied() { let mut cmd = Cmd::new( "test:env", "sh", vec!["-c".to_string(), "printf %s \"$GREETING\"".to_string()], vec![], PathBuf::from("."), vec![("GREETING".to_string(), "hi".to_string())], None, ); let out = unwrap_or_panic!(cmd.run().await, "run failed"); assert!(out.status.success()); assert_eq!(String::from_utf8_lossy(&out.stdout), "hi"); } #[tokio::test] async fn config_args_prepend_to_command_line() { let mut cmd = Cmd::new( "test:cfg", "echo", vec!["world".to_string()], vec!["ignored-no-flag-echo".to_string()], PathBuf::from("."), vec![], None, ); let rendered = format!("{cmd}"); assert_eq!(rendered, "echo ignored-no-flag-echo world"); let out = unwrap_or_panic!(cmd.run().await, "run failed"); assert_eq!( String::from_utf8_lossy(&out.stdout).trim(), "ignored-no-flag-echo world" ); } #[tokio::test] async fn timeout_kills_long_running_process() { let mut cmd = Cmd::new( "test:sleep", "sleep", vec!["5".to_string()], vec![], PathBuf::from("."), vec![], Some(1), ); let res = cmd.run().await; assert!(res.is_err(), "timeout should produce an error"); }