use super::*;
#[tokio::test]
async fn test_runner_chained_set_tasks() {
let output = run_workflow_from_yaml(&testdata("chained_set_tasks.yaml"), json!({}))
.await
.unwrap();
assert_eq!(output["tripled"], json!(60));
}
#[tokio::test]
async fn test_runner_set_tasks_with_then() {
let output = run_workflow_from_yaml(&testdata("set_tasks_with_then.yaml"), json!({}))
.await
.unwrap();
assert_eq!(output["result"], json!(90));
assert!(output.get("skipped").is_none());
}
#[tokio::test]
async fn test_runner_set_tasks_with_termination() {
let output = run_workflow_from_yaml(&testdata("set_tasks_with_termination.yaml"), json!({}))
.await
.unwrap();
assert_eq!(output["finalValue"], json!(20));
assert!(output.get("skipped").is_none());
}
#[tokio::test]
async fn test_runner_set_tasks_invalid_then() {
let result = run_workflow_from_yaml(&testdata("set_tasks_invalid_then.yaml"), json!({})).await;
assert!(result.is_err());
let err_msg = format!("{}", result.unwrap_err());
assert!(
err_msg.contains("not found"),
"expected 'not found' in error, got: {}",
err_msg
);
}
#[tokio::test]
async fn test_runner_simple_set_workflow() {
use std::collections::HashMap;
use swf_core::models::map::Map;
use swf_core::models::task::{
SetTaskDefinition, SetValue, TaskDefinition, TaskDefinitionFields,
};
let mut map = HashMap::new();
map.insert("greeting".to_string(), json!("hello"));
let set_task = TaskDefinition::Set(SetTaskDefinition {
set: SetValue::Map(map),
common: TaskDefinitionFields::new(),
});
let entries = vec![("task1".to_string(), set_task)];
let workflow = WorkflowDefinition {
do_: Map { entries },
..WorkflowDefinition::default()
};
let runner = WorkflowRunner::new(workflow).unwrap();
let output = runner.run(json!({})).await.unwrap();
assert_eq!(output["greeting"], json!("hello"));
}
#[tokio::test]
async fn test_runner_set_listen_to_any() {
use crate::events::{CloudEvent, InMemoryEventBus};
let bus = Arc::new(InMemoryEventBus::new());
let yaml_str = std::fs::read_to_string(testdata("set_listen_to_any.yaml")).unwrap();
let workflow: WorkflowDefinition = serde_yaml::from_str(&yaml_str).unwrap();
let bus_clone = bus.clone();
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
bus_clone
.publish(CloudEvent::new(
"com.example.event",
json!({"triggered": true}),
))
.await;
});
let runner = WorkflowRunner::new(workflow).unwrap().with_event_bus(bus);
let output = runner.run(json!({})).await.unwrap();
assert_eq!(output["triggered"], json!(true));
}
#[tokio::test]
async fn test_runner_set_then_exit() {
let output = run_workflow_from_yaml(&testdata("set_tasks_with_termination.yaml"), json!({}))
.await
.unwrap();
assert_eq!(output["finalValue"], json!(20));
assert!(output.get("skipped").is_none());
}
#[tokio::test]
async fn test_runner_set_then_continue() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-then-continue
version: '0.1.0'
do:
- task1:
set:
value: 10
then: continue
- task2:
set:
value: 20
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["value"], json!(20));
}
#[tokio::test]
async fn test_runner_set_preserves_and_adds() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-preserves-adds
version: '0.1.0'
do:
- step1:
set:
a: 1
b: "${ .b }"
- step2:
set:
a: "${ .a }"
b: "${ .b }"
c: 3
"#;
let output = run_workflow_yaml(&yaml_str, json!({"b": 2})).await.unwrap();
assert_eq!(output["a"], json!(1));
assert_eq!(output["b"], json!(2));
assert_eq!(output["c"], json!(3));
}
#[tokio::test]
async fn test_runner_set_null_and_bool() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-null-bool
version: '0.1.0'
do:
- setValues:
set:
nullVal: null
boolVal: true
falseVal: false
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert!(output["nullVal"].is_null());
assert_eq!(output["boolVal"], json!(true));
assert_eq!(output["falseVal"], json!(false));
}
#[tokio::test]
async fn test_runner_set_overwrite() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-overwrite
version: '0.1.0'
do:
- initialSet:
set:
counter: 1
name: first
- overwriteSet:
set:
counter: 2
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["counter"], json!(2));
}
#[tokio::test]
async fn test_runner_set_multiple_expr() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-multiple-expr
version: '0.1.0'
do:
- computeAll:
set:
sum: "${ .a + .b }"
product: "${ .a * .b }"
difference: "${ .a - .b }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"a": 10, "b": 3}))
.await
.unwrap();
assert_eq!(output["sum"], json!(13));
assert_eq!(output["product"], json!(30));
assert_eq!(output["difference"], json!(7));
}
#[tokio::test]
async fn test_runner_set_if_skip() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-if-skip
version: '0.1.0'
do:
- conditionalSet:
if: ${ .enabled }
set:
status: active
- alwaysSet:
set:
done: true
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner.run(json!({"enabled": false})).await.unwrap();
assert_eq!(output["done"], json!(true));
}
#[tokio::test]
async fn test_runner_set_if_enabled() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-if-enabled
version: '0.1.0'
do:
- conditionalSet:
if: ${ .enabled }
set:
status: active
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner.run(json!({"enabled": true})).await.unwrap();
assert_eq!(output["status"], json!("active"));
}
#[tokio::test]
async fn test_runner_set_then_continue_flow() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-then-continue-flow
version: '0.1.0'
do:
- step1:
set:
a: 1
then: continue
- step2:
set:
b: 2
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["b"], json!(2));
}
#[tokio::test]
async fn test_runner_set_then_goto_forward() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-then-goto-forward
version: '0.1.0'
do:
- step1:
set:
start: true
then: step3
- step2:
set:
skipped: true
- step3:
set:
end: true
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["end"], json!(true));
assert!(output.get("skipped").is_none());
}
#[tokio::test]
async fn test_runner_set_boolean_expr() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-boolean-expr
version: '0.1.0'
do:
- compute:
set:
isAdult: "${ .age >= 18 }"
canVote: "${ .age >= 18 and .citizen }"
isMinor: "${ .age < 18 }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"age": 25, "citizen": true}))
.await
.unwrap();
assert_eq!(output["isAdult"], json!(true));
assert_eq!(output["canVote"], json!(true));
assert_eq!(output["isMinor"], json!(false));
}
#[tokio::test]
async fn test_runner_set_multiple_fields() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-multi-fields
version: '0.1.0'
do:
- compute:
set:
x: 1
y: 2
product: "${ .a * .b }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"a": 3, "b": 4}))
.await
.unwrap();
assert_eq!(output["x"], json!(1));
assert_eq!(output["y"], json!(2));
assert_eq!(output["product"], json!(12));
}
#[tokio::test]
async fn test_runner_set_nested_object_construction() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-nested-object
version: '0.1.0'
do:
- build:
set:
user: "${ {name: .name, address: {city: .city, zip: .zip}} }"
name: "${ .name }"
city: "${ .city }"
zip: "${ .zip }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"name": "Alice", "city": "NYC", "zip": "10001"}))
.await
.unwrap();
assert_eq!(output["user"]["name"], json!("Alice"));
assert_eq!(output["user"]["address"]["city"], json!("NYC"));
assert_eq!(output["user"]["address"]["zip"], json!("10001"));
}
#[tokio::test]
async fn test_runner_set_output_as_transform() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-output-as-transform
version: '0.1.0'
do:
- build:
set:
x: 10
y: 20
output:
as: "${ {sum: .x + .y, x: .x, y: .y} }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["sum"], json!(30));
assert_eq!(output["x"], json!(10));
}
#[tokio::test]
async fn test_runner_set_null_value() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-null-value
version: '0.1.0'
do:
- setNull:
set:
x: null
y: "${ .y }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"y": 42}))
.await
.unwrap();
assert_eq!(output["x"], json!(null));
assert_eq!(output["y"], json!(42));
}
#[tokio::test]
async fn test_runner_set_preserves_unrelated_keys() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-preserve
version: '0.1.0'
do:
- setShape:
set:
shape: circle
size: ${ .configuration.size }
fill: ${ .configuration.fill }
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"configuration": {"size": "large", "fill": "red"},
"otherKey": "preserved"
}))
.await
.unwrap();
assert_eq!(output["shape"], json!("circle"));
assert_eq!(output["size"], json!("large"));
assert_eq!(output["fill"], json!("red"));
}
#[tokio::test]
async fn test_runner_set_dynamic_index() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-dynamic-index
version: '0.1.0'
do:
- setItem:
set:
item: '${.items[.index]}'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["a", "b", "c"], "index": 1}))
.await
.unwrap();
assert_eq!(output["item"], json!("b"));
}
#[tokio::test]
async fn test_runner_set_complex_nested_structures() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-complex-nested
version: '0.1.0'
do:
- buildShape:
set:
shape:
type: rectangle
width: '${.config.dimensions.width}'
height: '${.config.dimensions.height}'
color: '${.meta.color}'
area: '${.config.dimensions.width * .config.dimensions.height}'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"config": {
"dimensions": {
"width": 10,
"height": 5
}
},
"meta": {
"color": "blue"
}
}))
.await
.unwrap();
assert_eq!(output["shape"]["type"], json!("rectangle"));
assert_eq!(output["shape"]["width"], json!(10));
assert_eq!(output["shape"]["height"], json!(5));
assert_eq!(output["shape"]["color"], json!("blue"));
assert_eq!(output["shape"]["area"], json!(50));
}
#[tokio::test]
async fn test_runner_set_default_values() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-default-values
version: '0.1.0'
do:
- setDefault:
set:
value: '${.missingField // "defaultValue"}'
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["value"], json!("defaultValue"));
}
#[tokio::test]
async fn test_runner_set_static_values() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-static-values
version: '0.1.0'
do:
- setStatic:
set:
status: completed
count: 10
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["status"], json!("completed"));
assert_eq!(output["count"], json!(10));
}
#[tokio::test]
async fn test_runner_set_nested_structures() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-nested-structures
version: '0.1.0'
do:
- setOrder:
set:
orderDetails:
orderId: '${.order.id}'
itemCount: '${.order.items | length}'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"order": {
"id": 12345,
"items": ["item1", "item2"]
}
}))
.await
.unwrap();
assert_eq!(output["orderDetails"]["orderId"], json!(12345));
assert_eq!(output["orderDetails"]["itemCount"], json!(2));
}
#[tokio::test]
async fn test_runner_set_static_and_dynamic_values() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-static-dynamic
version: '0.1.0'
do:
- setMetrics:
set:
status: active
remaining: '${.config.threshold - .metrics.current}'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"config": { "threshold": 100 },
"metrics": { "current": 75 }
}))
.await
.unwrap();
assert_eq!(output["status"], json!("active"));
assert_eq!(output["remaining"], json!(25));
}
#[tokio::test]
async fn test_runner_set_missing_input_data() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-missing-input
version: '0.1.0'
do:
- setMissing:
set:
value: '${.missingField}'
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["value"], json!(null));
}
#[tokio::test]
async fn test_runner_set_conditional_hot_cold() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: conditional-hot-cold
version: '0.1.0'
do:
- setWeather:
set:
forecast: '${ if .temperature > 25 then "hot" else "cold" end }'
"#;
let workflow: WorkflowDefinition = serde_yaml::from_str(&yaml_str).unwrap();
let runner = WorkflowRunner::new(workflow.clone()).unwrap();
let output = runner.run(json!({"temperature": 30})).await.unwrap();
assert_eq!(output["forecast"], json!("hot"));
let runner = WorkflowRunner::new(workflow).unwrap();
let output = runner.run(json!({"temperature": 15})).await.unwrap();
assert_eq!(output["forecast"], json!("cold"));
}
#[tokio::test]
async fn test_runner_set_deeply_nested() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-deep-nested
version: '0.1.0'
do:
- buildStructure:
set:
level1:
level2:
level3:
value: "${ .input * 2 }"
name: deep
"#;
let output = run_workflow_yaml(&yaml_str, json!({"input": 21}))
.await
.unwrap();
assert_eq!(output["level1"]["level2"]["level3"]["value"], json!(42));
assert_eq!(output["level1"]["level2"]["level3"]["name"], json!("deep"));
}
#[tokio::test]
async fn test_runner_set_array_construction() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-array
version: '0.1.0'
do:
- buildArray:
set:
items: '${ [.a, .b, .c] }'
"#;
let output = run_workflow_yaml(&yaml_str, json!({"a": 1, "b": 2, "c": 3}))
.await
.unwrap();
assert_eq!(output["items"], json!([1, 2, 3]));
}
#[tokio::test]
async fn test_runner_set_object_merge() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-merge
version: '0.1.0'
do:
- mergeObjects:
set:
result: '${ (.base + .override) }'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"base": {"name": "Alice", "age": 30},
"override": {"age": 31, "city": "NYC"}
}))
.await
.unwrap();
assert_eq!(output["result"]["name"], json!("Alice"));
assert_eq!(output["result"]["age"], json!(31));
assert_eq!(output["result"]["city"], json!("NYC"));
}
#[tokio::test]
async fn test_runner_set_then_listen() {
use crate::events::{CloudEvent, InMemoryEventBus};
let bus = Arc::new(InMemoryEventBus::new());
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: set-listen
version: '0.1.0'
do:
- doSomethingBeforeEvent:
set:
name: javierito
- callDoctor:
listen:
to:
any: []
"#;
let workflow: WorkflowDefinition = serde_yaml::from_str(&yaml_str).unwrap();
let bus_clone = bus.clone();
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
bus_clone
.publish(CloudEvent::new(
"com.example.doctor",
json!({"doctor": "available"}),
))
.await;
});
let runner = WorkflowRunner::new(workflow).unwrap().with_event_bus(bus);
let output = runner.run(json!({})).await.unwrap();
assert_eq!(output["doctor"], json!("available"));
}
#[tokio::test]
async fn test_runner_set_output_and_export_combined() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: output-export-combined
version: '1.0.0'
do:
- updateData:
set:
updated:
userId: '${ .userId + "_tested" }'
username: '${ .username + "_tested" }'
export:
as: '.'
- useContext:
set:
fromExport: '${ $context }'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"userId": "user1", "username": "test"}))
.await
.unwrap();
assert_eq!(
output["fromExport"]["updated"]["userId"],
json!("user1_tested")
);
assert_eq!(
output["fromExport"]["updated"]["username"],
json!("test_tested")
);
}
#[tokio::test]
async fn test_runner_set_output_as_then_export_as() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: output-as-then-export-as
version: '1.0.0'
do:
- compute:
set:
value: 42
extra: 'metadata'
output:
as: .value
export:
as: '.'
- check:
set:
exported: '${ $context }'
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output, json!({"exported": 42}));
}
#[tokio::test]
async fn test_e2e_chained_set_tasks() {
let yaml_str = r#"
document:
name: chained-workflow
dsl: '1.0.0'
namespace: default
version: '1.0.0'
do:
- task1:
set:
baseValue: 10
- task2:
set:
doubled: "${ .baseValue * 2 }"
- task3:
set:
tripled: "${ .doubled * 3 }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["tripled"], json!(60));
}
#[tokio::test]
async fn test_e2e_set_tasks_with_then_goto() {
let yaml_str = r#"
document:
name: then-workflow
dsl: '1.0.0'
namespace: default
version: '1.0.0'
do:
- task1:
set:
value: 30
then: task3
- task2:
set:
skipped: true
- task3:
set:
result: "${ .value * 3 }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["result"], json!(90));
assert!(output.get("skipped").is_none(), "task2 should be skipped");
}
#[tokio::test]
async fn test_e2e_set_tasks_with_then_end() {
let yaml_str = r#"
document:
name: termination-workflow
dsl: '1.0.0'
namespace: default
version: '1.0.0'
do:
- task1:
set:
finalValue: 20
then: end
- task2:
set:
skipped: true
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert_eq!(output["finalValue"], json!(20));
assert!(
output.get("skipped").is_none(),
"task2 should be skipped by then:end"
);
}