use mecha10_cli::dev::lifecycle_adapter::CliLifecycleManager;
use mecha10_cli::types::project::{NodeRef, NodesConfig, ProjectConfig, RobotConfig, TargetEntry, TargetsConfig};
fn create_test_targets_config() -> TargetsConfig {
TargetsConfig {
dev: Some(TargetEntry {
nodes: vec![NodeRef::from("listener"), NodeRef::from("speaker")],
platforms: None,
}),
simulation: Some(TargetEntry {
nodes: vec![
NodeRef::from("listener"),
NodeRef::from("speaker"),
NodeRef::from("camera"),
],
platforms: None,
}),
robot: Some(TargetEntry {
nodes: vec![
NodeRef::from("listener"),
NodeRef::from("speaker"),
NodeRef::from("imu"),
NodeRef::from("motor"),
],
platforms: None,
}),
remote: None,
rl: None,
}
}
fn create_project_config(targets: TargetsConfig) -> ProjectConfig {
ProjectConfig {
name: "test".to_string(),
version: "0.1.0".to_string(),
template_version: None,
robot: RobotConfig {
id: "test".to_string(),
name: "test".to_string(),
platform: None,
description: None,
},
simulation: None,
nodes: NodesConfig::new(),
targets,
behaviors: None,
docker: Default::default(),
environments: Default::default(),
actuator: Default::default(),
}
}
#[test]
fn test_from_project_config_with_targets() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let manager = CliLifecycleManager::from_project_config(&project_config);
assert!(manager.is_some());
let manager = manager.unwrap();
assert_eq!(manager.current_mode(), "dev");
}
#[test]
fn test_from_project_config_without_targets() {
let project_config = create_project_config(TargetsConfig::default());
let manager = CliLifecycleManager::from_project_config(&project_config);
assert!(manager.is_none());
}
#[test]
fn test_nodes_for_current_mode() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
let nodes = manager.nodes_for_current_mode();
assert_eq!(nodes.len(), 2);
assert!(nodes.contains(&"listener".to_string()));
assert!(nodes.contains(&"speaker".to_string()));
}
#[test]
fn test_mark_nodes_running() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
manager.mark_nodes_running(&["listener".to_string(), "speaker".to_string()]);
let diff = manager.calculate_mode_diff("dev").unwrap();
assert_eq!(diff.start.len(), 0);
assert_eq!(diff.stop.len(), 0);
}
#[test]
fn test_calculate_mode_diff_start_nodes() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
manager.mark_nodes_running(&["listener".to_string(), "speaker".to_string()]);
let diff = manager.calculate_mode_diff("simulation").unwrap();
assert_eq!(diff.start.len(), 1);
assert!(diff.start.contains(&"camera".to_string()));
assert_eq!(diff.stop.len(), 0);
}
#[test]
fn test_calculate_mode_diff_stop_nodes() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
manager.mark_nodes_running(&["listener".to_string(), "speaker".to_string(), "camera".to_string()]);
let diff = manager.calculate_mode_diff("dev").unwrap();
assert_eq!(diff.start.len(), 0);
assert_eq!(diff.stop.len(), 1);
assert!(diff.stop.contains(&"camera".to_string()));
}
#[test]
fn test_calculate_mode_diff_stops_nodes_not_in_target() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
manager.mark_nodes_running(&["listener".to_string(), "speaker".to_string(), "camera".to_string()]);
let diff = manager.calculate_mode_diff("robot").unwrap();
assert!(diff.start.contains(&"imu".to_string()));
assert!(diff.start.contains(&"motor".to_string()));
assert!(diff.stop.contains(&"camera".to_string()));
}
#[test]
fn test_calculate_mode_diff_keeps_common_node_running() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
manager.mark_nodes_running(&["listener".to_string(), "speaker".to_string()]);
let diff = manager.calculate_mode_diff("simulation").unwrap();
assert!(
!diff.stop.contains(&"listener".to_string()),
"listener is common to dev and simulation and must not be stopped, got stop={:?}",
diff.stop
);
assert!(
!diff.stop.contains(&"speaker".to_string()),
"speaker is common to dev and simulation and must not be stopped, got stop={:?}",
diff.stop
);
assert!(
!diff.start.contains(&"listener".to_string()),
"listener is already running and must not be restarted, got start={:?}",
diff.start
);
}
#[test]
fn test_change_mode() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
assert_eq!(manager.current_mode(), "dev");
let diff = manager.change_mode("simulation").unwrap();
assert_eq!(manager.current_mode(), "simulation");
assert_eq!(diff.start.len(), 3);
assert_eq!(diff.stop.len(), 0);
}
#[test]
fn test_change_mode_invalid() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
let result = manager.change_mode("invalid-mode");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("not found"));
}
#[test]
fn test_available_modes() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
let modes = manager.available_modes();
assert_eq!(modes.len(), 3);
assert!(modes.contains(&"dev"));
assert!(modes.contains(&"simulation"));
assert!(modes.contains(&"robot"));
}
#[test]
fn test_validate_valid_config() {
let config = create_test_targets_config();
let available_nodes = vec![
"listener".to_string(),
"speaker".to_string(),
"camera".to_string(),
"imu".to_string(),
"motor".to_string(),
];
let result = CliLifecycleManager::validate(&config, &available_nodes);
assert!(result.is_ok());
}
#[test]
fn test_validate_missing_node() {
let config = create_test_targets_config();
let available_nodes = vec!["listener".to_string(), "speaker".to_string()];
let result = CliLifecycleManager::validate(&config, &available_nodes);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("unknown node"));
}
#[test]
fn test_nodes_for_current_mode_distinguishes_keyed_instances() {
let config = TargetsConfig {
dev: Some(TargetEntry {
nodes: vec![
NodeRef::Keyed {
name: "@mecha10/camera".to_string(),
instance_id: Some("real".to_string()),
camera_id: None,
},
NodeRef::Keyed {
name: "@mecha10/camera".to_string(),
instance_id: Some("sim".to_string()),
camera_id: None,
},
],
platforms: None,
}),
simulation: None,
robot: None,
remote: None,
rl: None,
};
let project_config = create_project_config(config);
let manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
let nodes = manager.nodes_for_current_mode();
assert_eq!(nodes.len(), 2);
assert!(nodes.contains(&"@mecha10/camera::real".to_string()));
assert!(nodes.contains(&"@mecha10/camera::sim".to_string()));
}
#[test]
fn test_mark_nodes_stopped() {
let config = create_test_targets_config();
let project_config = create_project_config(config);
let mut manager = CliLifecycleManager::from_project_config(&project_config).unwrap();
manager.mark_nodes_running(&["listener".to_string(), "speaker".to_string()]);
manager.mark_nodes_stopped(&["listener".to_string()]);
let diff = manager.calculate_mode_diff("dev").unwrap();
assert_eq!(diff.start.len(), 1);
assert!(diff.start.contains(&"listener".to_string()));
}