#[cfg(test)]
mod tests {
use crate::config::mapreduce::parse_mapreduce_workflow;
#[allow(unused_imports)]
use tempfile::TempDir;
#[test]
fn test_on_failure_parsing() {
let yaml = r#"
name: test-on-failure
mode: mapreduce
setup:
- shell: "exit 1"
on_failure:
fail_workflow: true
- shell: "echo 'second'"
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo test"
"#;
let config = parse_mapreduce_workflow(yaml).unwrap();
assert!(config.setup.is_some());
let setup = config.setup.unwrap();
assert_eq!(setup.commands.len(), 2);
let first_step = &setup.commands[0];
assert!(first_step.on_failure.is_some());
let on_failure = first_step.on_failure.as_ref().unwrap();
assert!(on_failure.should_fail_workflow());
}
#[tokio::test]
async fn test_setup_fails_on_shell_error() {
let yaml = r#"
name: test-shell-failure
mode: mapreduce
setup:
- shell: "exit 1" # This should fail and stop the workflow
- shell: "echo 'should not execute'" # This should never run
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo 'should not reach map phase'"
"#;
let config = parse_mapreduce_workflow(yaml).unwrap();
assert_eq!(config.name, "test-shell-failure");
}
#[tokio::test]
async fn test_on_failure_continue() {
let yaml = r#"
name: test-on-failure-continue
mode: mapreduce
setup:
- shell: "exit 1"
on_failure:
shell: "echo 'Handling failure'"
continue_workflow: true # Proposed: continue after handling
- shell: "echo 'This should execute'"
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo 'map phase'"
"#;
let _config = parse_mapreduce_workflow(yaml).unwrap();
}
#[tokio::test]
async fn test_on_failure_fail_workflow() {
let yaml = r#"
name: test-on-failure-fail
mode: mapreduce
setup:
- shell: "exit 1"
on_failure:
shell: "echo 'Handling failure'"
fail_workflow: true # Stop after handling
- shell: "echo 'This should NOT execute'"
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo 'should not reach map phase'"
"#;
let _config = parse_mapreduce_workflow(yaml).unwrap();
}
#[tokio::test]
async fn test_ignore_errors_flag() {
let _yaml = r#"
name: test-ignore-errors
mode: mapreduce
setup:
- shell: "exit 1"
ignore_errors: true # Proposed: simple flag to ignore failures
- shell: "echo 'This should execute despite error'"
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo 'map phase'"
"#;
}
#[tokio::test]
async fn test_on_failure_with_retry() {
let _yaml = r#"
name: test-retry-on-failure
mode: mapreduce
setup:
- shell: "test -f /tmp/test_marker || exit 1"
on_failure:
shell: "touch /tmp/test_marker && echo 'Created marker'"
retry_original: true # Proposed: retry the original command
max_retries: 2
- shell: "echo 'Should execute after retry succeeds'"
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo 'map phase'"
"#;
}
#[tokio::test]
async fn test_mixed_commands() {
let _yaml = r#"
name: test-mixed
mode: mapreduce
setup:
- shell: "echo 'First command succeeds'"
- shell: "exit 1"
ignore_errors: true
- shell: "echo 'Third command runs despite second failing'"
- shell: "exit 2" # This should fail the workflow
- shell: "echo 'This should NOT run'"
map:
input: dummy.json
json_path: "$[*]"
max_parallel: 1
agent_template:
commands:
- shell: "echo 'should not reach here'"
"#;
}
#[tokio::test]
async fn test_regular_workflow_shell_failure() {
let _yaml = r#"
- shell: "echo 'Starting workflow'"
- shell: "exit 1" # Should fail here
- shell: "echo 'Should not execute'"
"#;
}
#[tokio::test]
async fn test_shell_command_error_propagation() {
use crate::cook::orchestrator::ExecutionEnvironment;
use crate::cook::workflow::{CaptureOutput, WorkflowStep};
use std::sync::Arc;
let _step = WorkflowStep {
name: Some("failing-command".to_string()),
shell: Some("exit 42".to_string()),
claude: None,
test: None,
foreach: None,
write_file: None,
command: None,
handler: None,
capture: None,
capture_format: None,
capture_streams: Default::default(),
output_file: None,
timeout: None,
capture_output: CaptureOutput::Disabled,
on_failure: None,
retry: None,
on_success: None,
on_exit_code: Default::default(),
commit_required: false,
auto_commit: false,
commit_config: None,
validate: None,
step_validate: None,
skip_validation: false,
validation_timeout: None,
ignore_validation_failure: false,
working_dir: None,
env: Default::default(),
when: None,
};
let temp_dir = TempDir::new().unwrap();
let _env = ExecutionEnvironment {
working_dir: Arc::new(temp_dir.path().to_path_buf()),
project_dir: Arc::new(temp_dir.path().to_path_buf()),
worktree_name: None,
session_id: Arc::from("test-session"),
};
}
}