#[cfg(test)]
mod execution_mode_schema_validation_tests {
use crate::model::app::load_app_from_yaml;
use crate::model::choice::Choice;
use crate::model::common::ExecutionMode;
use crate::model::muxbox::MuxBox;
use crate::validation::SchemaValidator;
use std::fs;
use tempfile::NamedTempFile;
fn create_execution_mode_yaml(
muxbox_execution_mode: &str,
choice_execution_mode: &str,
) -> String {
format!(
r#"
app:
layouts:
- id: 'test_layout'
title: 'ExecutionMode Test Layout'
children:
- id: 'test_muxbox'
title: 'Test MuxBox'
position: {{x1: "10%", y1: "10%", x2: "90%", y2: "90%"}}
execution_mode: '{}'
script: ['echo "test script"']
choices:
- id: 'test_choice'
content: 'Test Choice'
execution_mode: '{}'
script: 'echo "test choice script"'
"#,
muxbox_execution_mode, choice_execution_mode
)
}
fn create_legacy_fields_yaml(thread: bool, pty: bool) -> String {
format!(
r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {{x1: "10%", y1: "10%", x2: "90%", y2: "90%"}}
thread: {}
pty: {}
script: ['echo "test"']
choices:
- id: 'test_choice'
content: 'Test Choice'
thread: {}
pty: {}
script: 'echo "choice test"'
"#,
thread, pty, thread, pty
)
}
fn create_mixed_fields_yaml() -> String {
r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {x1: "10%", y1: "10%", x2: "90%", y2: "90%"}
execution_mode: 'Thread'
thread: true
pty: false
script: ['echo "test"']
choices:
- id: 'test_choice'
content: 'Test Choice'
execution_mode: 'Pty'
thread: false
pty: true
script: 'echo "choice test"'
"#
.to_string()
}
#[test]
fn test_execution_mode_immediate_validation() {
let yaml_content = create_execution_mode_yaml("Immediate", "Immediate");
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"ExecutionMode 'immediate' should validate successfully: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Immediate);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Immediate);
}
#[test]
fn test_execution_mode_thread_validation() {
let yaml_content = create_execution_mode_yaml("Thread", "Thread");
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"ExecutionMode 'thread' should validate successfully: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Thread);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Thread);
}
#[test]
fn test_execution_mode_pty_validation() {
let yaml_content = create_execution_mode_yaml("Pty", "Pty");
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"ExecutionMode 'pty' should validate successfully: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Pty);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Pty);
}
#[test]
fn test_execution_mode_invalid_value_rejection() {
let yaml_content = r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {x1: "10%", y1: "10%", x2: "90%", y2: "90%"}
execution_mode: 'invalid_mode'
script: ['echo "test"']
"#;
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_err(),
"Invalid ExecutionMode value should be rejected"
);
let error_msg = result.err().unwrap().to_string();
assert!(
error_msg.contains("JSON Schema validation failed")
|| error_msg.contains("execution_mode"),
"Error should mention execution_mode validation failure: {}",
error_msg
);
}
#[test]
fn test_legacy_thread_field_backward_compatibility() {
let yaml_content = create_execution_mode_yaml("Thread", "Thread");
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"ExecutionMode fields should load successfully: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Thread);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Thread);
}
#[test]
fn test_legacy_pty_field_backward_compatibility() {
let yaml_content = create_execution_mode_yaml("Pty", "Pty");
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"ExecutionMode PTY should load successfully: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Pty);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Pty);
}
#[test]
fn test_legacy_both_false_defaults_to_immediate() {
let yaml_content = create_legacy_fields_yaml(false, false);
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"Legacy fields both false should default to immediate: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Immediate);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Immediate);
}
#[test]
fn test_pty_takes_precedence_over_thread() {
let yaml_content = create_execution_mode_yaml("Pty", "Pty");
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"ExecutionMode PTY should load successfully: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Pty);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Pty);
}
#[test]
fn test_execution_mode_schema_validator_deprecation_warnings() {
let mut validator = SchemaValidator::new();
use crate::model::common::InputBounds;
let mut muxbox = MuxBox {
id: "test_muxbox".to_string(),
position: InputBounds {
x1: "10%".to_string(),
y1: "10%".to_string(),
x2: "90%".to_string(),
y2: "90%".to_string(),
},
execution_mode: ExecutionMode::Thread,
..Default::default()
};
muxbox.choices = Some(vec![Choice {
id: "test_choice".to_string(),
content: Some("Test Choice".to_string()),
execution_mode: ExecutionMode::Pty,
..Default::default()
}]);
let result = validator.validate_muxbox(&muxbox, "test_muxbox");
assert!(
result.is_ok(),
"ExecutionMode fields should validate successfully in unified architecture"
);
}
#[test]
fn test_conflicting_execution_mode_and_legacy_fields() {
let yaml_content = r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {x1: "10%", y1: "10%", x2: "90%", y2: "90%"}
execution_mode: 'Immediate'
thread: true
pty: false
script: ['echo "test"']
"#;
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
if result.is_ok() {
let mut validator = SchemaValidator::new();
let app = result.unwrap();
let validation_result = validator.validate_app(&app);
if validation_result.is_err() {
let errors = validation_result.unwrap_err();
let error_messages: Vec<String> = errors.iter().map(|e| e.to_string()).collect();
let combined = error_messages.join("; ");
assert!(
combined.contains("DEPRECATION WARNING"),
"Should contain deprecation warnings: {}",
combined
);
}
}
}
#[test]
fn test_mixed_execution_mode_and_legacy_fields_consistency() {
let yaml_content = create_mixed_fields_yaml();
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"Mixed fields should load with proper ExecutionMode values: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Thread);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Pty);
}
#[test]
fn test_execution_mode_default_behavior() {
let yaml_content = r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {x1: "10%", y1: "10%", x2: "90%", y2: "90%"}
script: ['echo "test"']
choices:
- id: 'test_choice'
content: 'Test Choice'
script: 'echo "choice test"'
"#;
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"YAML without ExecutionMode should default to Immediate: {:?}",
result.err()
);
let app = result.unwrap();
let muxbox = &app.layouts[0].children.as_ref().unwrap()[0];
assert_eq!(muxbox.execution_mode, ExecutionMode::Immediate);
let choice = &muxbox.choices.as_ref().unwrap()[0];
assert_eq!(choice.execution_mode, ExecutionMode::Immediate);
}
#[test]
fn test_choice_execution_mode_validation() {
let yaml_content = r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {x1: "10%", y1: "10%", x2: "90%", y2: "90%"}
choices:
- id: 'immediate_choice'
content: 'Immediate Choice'
execution_mode: 'Immediate'
script: 'echo "immediate"'
- id: 'thread_choice'
content: 'Thread Choice'
execution_mode: 'Thread'
script: 'echo "thread"'
- id: 'pty_choice'
content: 'PTY Choice'
execution_mode: 'Pty'
script: 'echo "pty"'
"#;
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_ok(),
"Multiple choice ExecutionModes should validate successfully: {:?}",
result.err()
);
let app = result.unwrap();
let choices = &app.layouts[0].children.as_ref().unwrap()[0]
.choices
.as_ref()
.unwrap();
assert_eq!(choices[0].execution_mode, ExecutionMode::Immediate);
assert_eq!(choices[1].execution_mode, ExecutionMode::Thread);
assert_eq!(choices[2].execution_mode, ExecutionMode::Pty);
}
#[test]
fn test_execution_mode_case_sensitivity() {
let yaml_content = r#"
app:
layouts:
- id: 'test_layout'
children:
- id: 'test_muxbox'
position: {x1: "10%", y1: "10%", x2: "90%", y2: "90%"}
execution_mode: 'IMMEDIATE'
script: ['echo "test"']
"#;
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
fs::write(&temp_file, yaml_content).expect("Failed to write temp file");
let result = load_app_from_yaml(temp_file.path().to_str().unwrap());
assert!(
result.is_err(),
"ExecutionMode should be case-sensitive and reject uppercase values"
);
}
#[test]
fn test_comprehensive_schema_validation_integration() {
let mut validator = SchemaValidator::new();
let yaml_content = r#"
app:
layouts:
- id: 'comprehensive_test'
children:
- id: 'comprehensive_muxbox'
position: {x1: "0%", y1: "0%", x2: "100%", y2: "100%"}
execution_mode: 'Thread'
script: ['echo "comprehensive test"']
choices:
- id: 'comprehensive_choice'
content: 'Comprehensive Choice'
execution_mode: 'Pty'
script: 'echo "comprehensive choice"'
"#;
let result = validator.validate_app_yaml(yaml_content);
match result {
Ok(_) => {
}
Err(errors) => {
let error_messages: Vec<String> = errors.iter().map(|e| e.to_string()).collect();
let combined = error_messages.join("; ");
if combined.contains("Failed to load schema file")
|| combined.contains("No such file")
{
} else {
panic!(
"Schema validation should accept ExecutionMode fields: {}",
combined
);
}
}
}
}
}