use super::*;
use crate::events::{Event, EventReceiver};
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::timeout;
fn create_test_executor() -> (CommandExecutor, EventReceiver) {
let (sender, receiver) = mpsc::unbounded_channel();
let executor = CommandExecutor::new(sender, "cuenv".to_string());
(executor, receiver)
}
#[tokio::test]
async fn test_command_executor_new() {
let (sender, _receiver) = mpsc::unbounded_channel();
let executor = CommandExecutor::new(sender, "cuenv".to_string());
assert!(matches!(executor, CommandExecutor { .. }));
assert_eq!(executor.package(), "cuenv");
}
#[tokio::test]
async fn test_execute_version_command() {
let (executor, mut receiver) = create_test_executor();
let handle = tokio::spawn(async move {
executor
.execute(Command::Version {
format: "simple".to_string(),
})
.await
});
let mut events = Vec::new();
while let Ok(Some(event)) = timeout(Duration::from_millis(1500), receiver.recv()).await {
let is_complete = matches!(&event, Event::CommandComplete { .. });
events.push(event);
if is_complete {
break;
}
}
let result = handle.await.unwrap();
assert!(result.is_ok());
assert!(
events
.iter()
.any(|e| matches!(e, Event::CommandStart { command } if command == "version"))
);
assert!(
events
.iter()
.any(|e| matches!(e, Event::CommandProgress { .. }))
);
assert!(events.iter().any(|e| matches!(e, Event::CommandComplete { command, success: true, .. } if command == "version")));
}
#[tokio::test]
async fn test_execute_version_progress_events() {
let (executor, mut receiver) = create_test_executor();
let handle = tokio::spawn(async move {
executor
.execute(Command::Version {
format: "simple".to_string(),
})
.await
});
let mut progress_events = Vec::new();
while let Ok(Some(event)) = timeout(Duration::from_millis(1500), receiver.recv()).await {
if let Event::CommandProgress {
progress, message, ..
} = event
{
progress_events.push((progress, message));
} else if matches!(event, Event::CommandComplete { .. }) {
break;
}
}
let result = handle.await.unwrap();
assert!(result.is_ok());
assert!(!progress_events.is_empty());
assert!(
progress_events
.iter()
.any(|(_, msg)| msg.contains("Initializing"))
);
assert!(
progress_events
.iter()
.any(|(_, msg)| msg.contains("Loading version info"))
);
assert!(
progress_events
.iter()
.any(|(_, msg)| msg.contains("Complete"))
);
let progress_values: Vec<f32> = progress_events.iter().map(|(p, _)| *p).collect();
assert!(progress_values.first().unwrap() <= progress_values.last().unwrap());
}
#[tokio::test]
async fn test_execute_env_print_emits_failure_on_invalid_path() {
let (executor, mut receiver) = create_test_executor();
let path = "/definitely-not-a-cuenv-project".to_string();
let package = "test-package".to_string();
let format = "json".to_string();
let handle = tokio::spawn(async move {
executor
.execute(Command::EnvPrint {
path,
package,
format,
environment: None,
})
.await
});
let mut events = Vec::new();
while let Ok(Some(event)) = timeout(Duration::from_millis(1500), receiver.recv()).await {
let is_complete = matches!(&event, Event::CommandComplete { .. });
events.push(event);
if is_complete {
break;
}
}
let result = handle.await.unwrap();
assert!(result.is_err());
assert!(
events
.iter()
.any(|e| matches!(e, Event::CommandStart { command } if command == "env print"))
);
assert!(events.iter().any(|e| matches!(
e,
Event::CommandComplete {
command,
success: false,
..
} if command == "env print"
)));
}
#[tokio::test]
async fn test_command_enum_variants() {
let version_cmd = Command::Version {
format: "simple".to_string(),
};
assert!(matches!(version_cmd, Command::Version { .. }));
let env_cmd = Command::EnvPrint {
path: "/test/path".to_string(),
package: "test-pkg".to_string(),
format: "yaml".to_string(),
environment: Some("production".to_string()),
};
if let Command::EnvPrint {
path,
package,
format,
environment,
} = env_cmd
{
assert_eq!(path, "/test/path");
assert_eq!(package, "test-pkg");
assert_eq!(format, "yaml");
assert_eq!(environment, Some("production".to_string()));
} else {
panic!("Expected EnvPrint variant");
}
}
#[tokio::test]
async fn test_send_event() {
let (sender, mut receiver) = mpsc::unbounded_channel();
let executor = CommandExecutor::new(sender, "cuenv".to_string());
executor.send_event(Event::CommandStart {
command: "test".to_string(),
});
let event = receiver.recv().await.unwrap();
assert!(matches!(event, Event::CommandStart { command } if command == "test"));
}
#[tokio::test]
async fn test_send_event_with_closed_channel() {
let (sender, receiver) = mpsc::unbounded_channel();
let executor = CommandExecutor::new(sender, "cuenv".to_string());
drop(receiver);
executor.send_event(Event::CommandStart {
command: "test".to_string(),
});
}
#[tokio::test]
async fn test_execute_version_command_flow() {
let (executor, mut receiver) = create_test_executor();
let handle = tokio::spawn(async move { executor.execute_version().await });
let mut has_start = false;
let mut has_progress = false;
let mut has_complete = false;
while let Ok(Some(event)) = timeout(Duration::from_millis(1500), receiver.recv()).await {
match event {
Event::CommandStart { command } if command == "version" => has_start = true,
Event::CommandProgress { command, .. } if command == "version" => {
has_progress = true;
}
Event::CommandComplete {
command,
success: true,
..
} if command == "version" => {
has_complete = true;
break;
}
_ => {}
}
}
let result = handle.await.unwrap();
assert!(result.is_ok());
assert!(has_start);
assert!(has_progress);
assert!(has_complete);
}
#[tokio::test]
async fn test_command_debug_trait() {
let cmd = Command::Version {
format: "simple".to_string(),
};
let debug_str = format!("{cmd:?}");
assert!(debug_str.contains("Version"));
let cmd = Command::EnvPrint {
path: "/path".to_string(),
package: "pkg".to_string(),
format: "json".to_string(),
environment: None,
};
let debug_str = format!("{cmd:?}");
assert!(debug_str.contains("EnvPrint"));
assert!(debug_str.contains("/path"));
assert!(debug_str.contains("pkg"));
assert!(debug_str.contains("json"));
}
#[tokio::test]
async fn test_command_clone_trait() {
let original = Command::Version {
format: "simple".to_string(),
};
let cloned = original.clone();
assert!(matches!(cloned, Command::Version { .. }));
let original = Command::EnvPrint {
path: "/test".to_string(),
package: "test".to_string(),
format: "toml".to_string(),
environment: Some("dev".to_string()),
};
let cloned = original.clone();
if let Command::EnvPrint {
path,
package,
format,
environment,
} = cloned
{
assert_eq!(path, "/test");
assert_eq!(package, "test");
assert_eq!(format, "toml");
assert_eq!(environment, Some("dev".to_string()));
} else {
panic!("Clone failed");
}
}