226 lines
5.2 KiB
Rust
226 lines
5.2 KiB
Rust
|
|
use std::collections::HashMap;
|
||
|
|
use std::time::Duration;
|
||
|
|
|
||
|
|
use wfe_core::models::error_behavior::ErrorBehavior;
|
||
|
|
use wfe_yaml::load_workflow_from_str;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn single_step_produces_one_workflow_step() {
|
||
|
|
let yaml = r#"
|
||
|
|
workflow:
|
||
|
|
id: single
|
||
|
|
version: 1
|
||
|
|
steps:
|
||
|
|
- name: hello
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: echo hello
|
||
|
|
"#;
|
||
|
|
let compiled = load_workflow_from_str(yaml, &HashMap::new()).unwrap();
|
||
|
|
// The definition should have exactly 1 main step.
|
||
|
|
let main_steps: Vec<_> = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.filter(|s| s.name.as_deref() == Some("hello"))
|
||
|
|
.collect();
|
||
|
|
assert_eq!(main_steps.len(), 1);
|
||
|
|
assert_eq!(main_steps[0].id, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn two_sequential_steps_wired_correctly() {
|
||
|
|
let yaml = r#"
|
||
|
|
workflow:
|
||
|
|
id: sequential
|
||
|
|
version: 1
|
||
|
|
steps:
|
||
|
|
- name: step-a
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: echo a
|
||
|
|
- name: step-b
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: echo b
|
||
|
|
"#;
|
||
|
|
let compiled = load_workflow_from_str(yaml, &HashMap::new()).unwrap();
|
||
|
|
|
||
|
|
let step_a = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("step-a"))
|
||
|
|
.unwrap();
|
||
|
|
let step_b = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("step-b"))
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
// step-a should have an outcome pointing to step-b.
|
||
|
|
assert_eq!(step_a.outcomes.len(), 1);
|
||
|
|
assert_eq!(step_a.outcomes[0].next_step, step_b.id);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn parallel_block_produces_container_with_children() {
|
||
|
|
let yaml = r#"
|
||
|
|
workflow:
|
||
|
|
id: parallel-wf
|
||
|
|
version: 1
|
||
|
|
steps:
|
||
|
|
- name: parallel-group
|
||
|
|
parallel:
|
||
|
|
- name: task-a
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: echo a
|
||
|
|
- name: task-b
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: echo b
|
||
|
|
"#;
|
||
|
|
let compiled = load_workflow_from_str(yaml, &HashMap::new()).unwrap();
|
||
|
|
|
||
|
|
let container = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("parallel-group"))
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
container.step_type.contains("SequenceStep"),
|
||
|
|
"Container should be a SequenceStep, got: {}",
|
||
|
|
container.step_type
|
||
|
|
);
|
||
|
|
assert_eq!(container.children.len(), 2);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn on_failure_creates_compensation_step() {
|
||
|
|
let yaml = r#"
|
||
|
|
workflow:
|
||
|
|
id: compensation-wf
|
||
|
|
version: 1
|
||
|
|
steps:
|
||
|
|
- name: deploy
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: deploy.sh
|
||
|
|
on_failure:
|
||
|
|
name: rollback
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: rollback.sh
|
||
|
|
"#;
|
||
|
|
let compiled = load_workflow_from_str(yaml, &HashMap::new()).unwrap();
|
||
|
|
|
||
|
|
let deploy = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("deploy"))
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
assert!(deploy.compensation_step_id.is_some());
|
||
|
|
assert_eq!(deploy.error_behavior, Some(ErrorBehavior::Compensate));
|
||
|
|
|
||
|
|
let rollback = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("rollback"))
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
assert_eq!(deploy.compensation_step_id, Some(rollback.id));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn error_behavior_maps_correctly() {
|
||
|
|
let yaml = r#"
|
||
|
|
workflow:
|
||
|
|
id: retry-wf
|
||
|
|
version: 1
|
||
|
|
error_behavior:
|
||
|
|
type: retry
|
||
|
|
interval: 5s
|
||
|
|
max_retries: 10
|
||
|
|
steps:
|
||
|
|
- name: step1
|
||
|
|
type: shell
|
||
|
|
config:
|
||
|
|
run: echo hi
|
||
|
|
error_behavior:
|
||
|
|
type: suspend
|
||
|
|
"#;
|
||
|
|
let compiled = load_workflow_from_str(yaml, &HashMap::new()).unwrap();
|
||
|
|
|
||
|
|
assert_eq!(
|
||
|
|
compiled.definition.default_error_behavior,
|
||
|
|
ErrorBehavior::Retry {
|
||
|
|
interval: Duration::from_secs(5),
|
||
|
|
max_retries: 10,
|
||
|
|
}
|
||
|
|
);
|
||
|
|
|
||
|
|
let step = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("step1"))
|
||
|
|
.unwrap();
|
||
|
|
assert_eq!(step.error_behavior, Some(ErrorBehavior::Suspend));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn anchors_compile_correctly() {
|
||
|
|
let yaml = r#"
|
||
|
|
workflow:
|
||
|
|
id: anchor-wf
|
||
|
|
version: 1
|
||
|
|
steps:
|
||
|
|
- name: build
|
||
|
|
type: shell
|
||
|
|
config: &default_config
|
||
|
|
shell: bash
|
||
|
|
timeout: 5m
|
||
|
|
run: cargo build
|
||
|
|
|
||
|
|
- name: test
|
||
|
|
type: shell
|
||
|
|
config: *default_config
|
||
|
|
"#;
|
||
|
|
let compiled = load_workflow_from_str(yaml, &HashMap::new()).unwrap();
|
||
|
|
|
||
|
|
// Should have 2 main steps + factories.
|
||
|
|
let build_step = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("build"))
|
||
|
|
.unwrap();
|
||
|
|
let test_step = compiled
|
||
|
|
.definition
|
||
|
|
.steps
|
||
|
|
.iter()
|
||
|
|
.find(|s| s.name.as_deref() == Some("test"))
|
||
|
|
.unwrap();
|
||
|
|
|
||
|
|
// Both should have step_config.
|
||
|
|
assert!(build_step.step_config.is_some());
|
||
|
|
assert!(test_step.step_config.is_some());
|
||
|
|
|
||
|
|
// Build should wire to test.
|
||
|
|
assert_eq!(build_step.outcomes.len(), 1);
|
||
|
|
assert_eq!(build_step.outcomes[0].next_step, test_step.id);
|
||
|
|
|
||
|
|
// Test uses the same config via alias - shell should be bash.
|
||
|
|
let test_config: wfe_yaml::executors::shell::ShellConfig =
|
||
|
|
serde_json::from_value(test_step.step_config.clone().unwrap()).unwrap();
|
||
|
|
assert_eq!(test_config.run, "cargo build");
|
||
|
|
assert_eq!(test_config.shell, "bash", "shell should be inherited from YAML anchor alias");
|
||
|
|
}
|