use super::*;
#[tokio::test]
async fn test_runner_for_colors() {
let output = run_workflow_from_yaml(
&testdata("for_colors.yaml"),
json!({"colors": ["red", "green", "blue"], "processed": {"colors": [], "indexes": []}}),
)
.await
.unwrap();
assert_eq!(
output["processed"]["colors"],
json!(["red", "green", "blue"])
);
assert_eq!(output["processed"]["indexes"], json!([0, 1, 2]));
}
#[tokio::test]
async fn test_runner_for_sum_numbers() {
let output = run_workflow_from_yaml(
&testdata("for_sum_numbers.yaml"),
json!({"numbers": [1, 2, 3, 4, 5], "total": 0}),
)
.await
.unwrap();
assert_eq!(output["result"], json!(15));
}
#[tokio::test]
async fn test_runner_for_nested_loops() {
let output = run_workflow_from_yaml(
&testdata("for_nested_loops.yaml"),
json!({"fruits": ["apple", "banana"], "colors": ["red", "green"], "matrix": []}),
)
.await
.unwrap();
assert_eq!(
output["matrix"],
json!([
["apple", "red"],
["apple", "green"],
["banana", "red"],
["banana", "green"]
])
);
}
#[tokio::test]
async fn test_runner_for_collect() {
let output = run_workflow_from_yaml(&testdata("for_collect.yaml"), json!({"input": [1, 2, 3]}))
.await
.unwrap();
assert_eq!(output["output"], json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_sum() {
let output = run_workflow_from_yaml(
&testdata("for_sum.yaml"),
json!({"input": [1, 2, 3], "counter": 0}),
)
.await
.unwrap();
assert_eq!(output, json!(6));
}
#[tokio::test]
async fn test_runner_for_while() {
let output = run_workflow_from_yaml(
&testdata("for_while.yaml"),
json!({"numbers": [3, 5, 7, 9], "total": 0}),
)
.await
.unwrap();
assert_eq!(output["total"], json!(15));
}
#[tokio::test]
async fn test_runner_for_custom_each_at() {
let output = run_workflow_from_yaml(
&testdata("for_custom_each_at.yaml"),
json!({"items": ["apple", "banana", "cherry"], "results": []}),
)
.await
.unwrap();
assert_eq!(
output["results"],
json!([
{"name": "apple", "index": 0},
{"name": "banana", "index": 1},
{"name": "cherry", "index": 2}
])
);
}
#[tokio::test]
async fn test_runner_for_custom_at() {
let output = run_workflow_from_yaml(
&testdata("for_custom_at.yaml"),
json!({"items": ["apple", "banana", "cherry"], "result": []}),
)
.await
.unwrap();
assert_eq!(
output["result"],
json!([
{"idx": 0, "item": "apple"},
{"idx": 1, "item": "banana"},
{"idx": 2, "item": "cherry"}
])
);
}
#[tokio::test]
async fn test_runner_for_export_context() {
let output = run_workflow_from_yaml(&testdata("for_collect.yaml"), json!({"input": [1, 2, 3]}))
.await
.unwrap();
assert_eq!(output["output"], json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_sum_output_as_counter() {
let output = run_workflow_from_yaml(
&testdata("for_sum.yaml"),
json!({"input": [10, 20, 30], "counter": 0}),
)
.await
.unwrap();
assert_eq!(output, json!(60));
}
#[tokio::test]
async fn test_runner_for_while_stops() {
let output = run_workflow_from_yaml(
&testdata("for_while.yaml"),
json!({"numbers": [3, 5, 7, 9], "total": 0}),
)
.await
.unwrap();
assert_eq!(output["total"], json!(15));
}
#[tokio::test]
async fn test_runner_for_object_collect() {
let output = run_workflow_from_yaml(
&testdata("for_object_collect.yaml"),
json!({"items": ["apple", "banana", "cherry"], "result": []}),
)
.await
.unwrap();
assert_eq!(
output["result"],
json!([
{"name": "apple", "position": 1},
{"name": "banana", "position": 2},
{"name": "cherry", "position": 3}
])
);
}
#[tokio::test]
async fn test_runner_for_filter_collect() {
let output = run_workflow_from_yaml(
&testdata("for_filter_collect.yaml"),
json!({"numbers": [1, 2, 3, 4, 5, 6], "evens": []}),
)
.await
.unwrap();
assert_eq!(output["evens"], json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_conditional_collect() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-conditional-collect
version: '0.1.0'
do:
- processItems:
for:
in: ${ .items }
each: item
do:
- addEven:
set:
evens: "${ if $item % 2 == 0 then [.evens[]] + [$item] else .evens end }"
odds: "${ if $item % 2 != 0 then [.odds[]] + [$item] else .odds end }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": [1, 2, 3, 4, 5], "evens": [], "odds": []}))
.await
.unwrap();
assert_eq!(output["evens"], json!([2, 4]));
assert_eq!(output["odds"], json!([1, 3, 5]));
}
#[tokio::test]
async fn test_runner_for_within_do() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-within-do
version: '0.1.0'
do:
- outerBlock:
do:
- loopTask:
for:
in: ${ .numbers }
each: num
do:
- accumulate:
set:
sum: "${ .sum + $num }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"numbers": [10, 20, 30], "sum": 0}))
.await
.unwrap();
assert_eq!(output["sum"], json!(60));
}
#[tokio::test]
async fn test_runner_for_output_as_extract() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-output-as-extract
version: '0.1.0'
do:
- sumNumbers:
for:
in: ${ .numbers }
each: n
do:
- addNum:
set:
total: "${ .total + $n }"
output:
as: .total
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"numbers": [1, 2, 3, 4, 5], "total": 0}))
.await
.unwrap();
assert_eq!(output, json!(15));
}
#[tokio::test]
async fn test_runner_for_export_accumulate() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-export-accumulate
version: '0.1.0'
do:
- processItems:
for:
in: ${ .items }
each: item
do:
- addItem:
set:
collected: "${ [.collected[]] + [$item] }"
export:
as: '${ {collected: .collected} }'
- reportResult:
set:
allItems: "${ $context.collected }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["a", "b", "c"], "collected": []}))
.await
.unwrap();
assert_eq!(output["allItems"], json!(["a", "b", "c"]));
}
#[tokio::test]
async fn test_runner_for_while_output_as() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-while-output-as
version: '0.1.0'
do:
- limitedLoop:
for:
in: ${ .items }
each: item
while: ${ .total <= 10 }
do:
- accumulate:
set:
total: "${ .total + $item }"
output:
as: .total
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": [3, 5, 7, 9], "total": 0}))
.await
.unwrap();
assert_eq!(output, json!(15));
}
#[tokio::test]
async fn test_runner_for_empty_collection() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-empty
version: '0.1.0'
do:
- loopTask:
for:
in: ${ .items }
each: item
do:
- processItem:
set:
processed: "${ [.processed[]] + [$item] }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": [], "processed": []}))
.await
.unwrap();
assert_eq!(output["processed"], json!([]));
}
#[tokio::test]
async fn test_runner_for_null_collection() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-null
version: '0.1.0'
do:
- loopTask:
for:
in: ${ .missing }
each: item
do:
- processItem:
set:
processed: true
"#;
let output = run_workflow_yaml(&yaml_str, json!({})).await.unwrap();
assert!(output.get("processed").is_none());
}
#[tokio::test]
async fn test_runner_for_single_element() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-single-element
version: '0.1.0'
do:
- loopTask:
for:
in: ${ .items }
each: item
do:
- processItem:
set:
result: "${ $item * 2 }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"items": [21]}))
.await
.unwrap();
assert_eq!(output["result"], json!(42));
}
#[tokio::test]
async fn test_runner_for_with_at_variable() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-with-at
version: '0.1.0'
do:
- processItems:
for:
in: ${ .items }
each: fruit
at: idx
do:
- addEntry:
set:
result: "${ [.result[]] + [{name: $fruit, index: $idx}] }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["apple", "banana"], "result": []}))
.await
.unwrap();
assert_eq!(
output["result"],
json!([
{"name": "apple", "index": 0},
{"name": "banana", "index": 1}
])
);
}
#[tokio::test]
async fn test_runner_for_while_custom_vars() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-while-custom-vars
version: '0.1.0'
do:
- limitedLoop:
for:
in: ${ .numbers }
each: num
at: pos
while: ${ .sum < 20 }
do:
- accumulate:
set:
sum: "${ .sum + $num }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"numbers": [5, 8, 12, 3], "sum": 0}))
.await
.unwrap();
assert_eq!(output["sum"], json!(25));
}
#[tokio::test]
async fn test_runner_for_object_keys() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-object-keys
version: '0.1.0'
do:
- iterateKeys:
for:
in: ${ [.config | to_entries[] | .key] }
each: key
do:
- processKey:
set:
keys: "${ [.keys[]] + [$key] }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"config": {"host": "localhost", "port": 8080}, "keys": []}))
.await
.unwrap();
let keys = output["keys"].as_array().unwrap();
assert_eq!(keys.len(), 2);
assert!(keys.contains(&json!("host")));
assert!(keys.contains(&json!("port")));
}
#[tokio::test]
async fn test_runner_for_output_as_array() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-output-as-array
version: '0.1.0'
do:
- collectDoubled:
for:
in: ${ .items }
each: n
do:
- double:
set:
result: "${ [.result[]] + [$n * 2] }"
output:
as: .result
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": [1, 2, 3], "result": []}))
.await
.unwrap();
assert_eq!(output, json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_with_output_as_extract() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-output-as-extract
version: '0.1.0'
do:
- sumLoop:
for:
in: "${ .numbers }"
each: num
output:
as: "${ .total }"
do:
- addNum:
set:
total: "${ .total + $num }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"numbers": [1, 2, 3, 4, 5], "total": 0}))
.await
.unwrap();
assert_eq!(output, json!(15));
}
#[tokio::test]
async fn test_runner_for_nested_do_export() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-nested-do-export
version: '0.1.0'
do:
- processItems:
for:
in: "${ .items }"
each: item
do:
- transform:
set:
processed: "${ .processed + [$item] }"
export:
as: "${ .processed }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["a", "b", "c"], "processed": []}))
.await
.unwrap();
assert_eq!(output["processed"], json!(["a", "b", "c"]));
}
#[tokio::test]
async fn test_runner_for_with_try_catch_early_exit() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-try-catch-early-exit
version: '0.1.0'
do:
- processItems:
for:
in: "${ .items }"
each: item
do:
- processOne:
try:
- checkItem:
switch:
- isBad:
when: ${ $item == "bad" }
then: raiseBad
- isGood:
when: ${ $item != "bad" }
then: continue
- raiseBad:
raise:
error:
type: validation
title: Bad Item
status: 400
catch:
errors:
with:
type: validation
do:
- handleError:
set:
hasBadItem: true
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["a", "bad", "c"], "collected": []}))
.await
.unwrap();
assert_eq!(output["hasBadItem"], json!(true));
}
#[tokio::test]
async fn test_runner_for_with_input_from() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-input-from
version: '0.1.0'
do:
- processData:
for:
in: "${ .items }"
each: item
do:
- transform:
set:
result: "${ [.result, $item] }"
input:
from: "${ {result: [], item: $item} }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"items": ["a", "b", "c"]}))
.await
.unwrap();
assert!(output["result"].is_array());
}
#[tokio::test]
async fn test_runner_for_while_output_as_combined() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-while-output-as
version: '0.1.0'
do:
- limitedLoop:
for:
in: "${ .numbers }"
each: num
while: ${ .total < 10 }
output:
as: "${ {total: .total, stopped: (.total >= 10)} }"
do:
- addNum:
set:
total: "${ (.total // 0) + $num }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let result = runner.run(json!({"numbers": [3, 5, 7], "total": 0})).await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn test_runner_multiple_for_sequential() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: multiple-for-sequential
version: '0.1.0'
do:
- firstLoop:
for:
in: "${ .nums }"
each: n
do:
- doubleIt:
set:
doubled: "${ .doubled + [$n * 2] }"
nums: "${ .nums }"
- secondLoop:
for:
in: "${ .doubled }"
each: d
do:
- sumIt:
set:
total: "${ .total + $d }"
doubled: "${ .doubled }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"nums": [1, 2, 3], "doubled": [], "total": 0}))
.await
.unwrap();
assert_eq!(output["doubled"], json!([2, 4, 6]));
assert_eq!(output["total"], json!(12));
}
#[tokio::test]
async fn test_runner_for_object_iteration() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-object-iteration
version: '0.1.0'
do:
- iterateConfig:
for:
in: "${ .config | to_entries | map({key: .key, value: .value}) }"
each: entry
do:
- processEntry:
set:
processed: "${ .processed + [$entry] }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"config": {"host": "localhost", "port": 8080}, "processed": []}))
.await
.unwrap();
assert_eq!(output["processed"].as_array().unwrap().len(), 2);
}
#[tokio::test]
async fn test_runner_for_while_break_early() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-while-break
version: '0.1.0'
do:
- findFirst:
for:
in: "${ .items }"
each: item
while: ${ .found == false }
do:
- checkItem:
set:
found: "${ $item == .target }"
current: "${ $item }"
target: "${ .target }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["a", "b", "c", "d"], "target": "c", "found": false}))
.await
.unwrap();
assert_eq!(output["found"], json!(true));
assert_eq!(output["current"], json!("c"));
}
#[tokio::test]
async fn test_runner_for_while_custom_vars_v2() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-while-custom-v2
version: '0.1.0'
do:
- loop:
for:
in: "${ .numbers }"
each: num
at: idx
while: ${ .total < 20 }
do:
- add:
set:
total: "${ .total + $num }"
lastIdx: "${ $idx }"
numbers: "${ .numbers }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"numbers": [5, 8, 3, 12, 7], "total": 0}))
.await
.unwrap();
assert_eq!(output["total"], json!(28));
assert_eq!(output["lastIdx"], json!(3));
}
#[tokio::test]
async fn test_runner_for_object_keys_v2() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-object-keys-v2
version: '0.1.0'
do:
- loopKeys:
for:
in: "${ [.data | keys[]] }"
each: key
do:
- collect:
set:
result: "${ .result + [$key] }"
data: "${ .data }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"data": {"a": 1, "b": 2}, "result": []}))
.await
.unwrap();
assert!(output["result"].is_array());
let arr = output["result"].as_array().unwrap();
assert!(arr.contains(&json!("a")));
assert!(arr.contains(&json!("b")));
}
#[tokio::test]
async fn test_runner_for_then_end_only_ends_for() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-then-end-scope
version: '0.1.0'
do:
- loop:
for:
in: "${ .items }"
each: item
do:
- add:
set:
total: "${ .total + $item }"
items: "${ .items }"
then: end
- afterLoop:
set:
done: true
total: "${ .total }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"items": [5], "total": 0}))
.await
.unwrap();
assert_eq!(output["total"], json!(5));
assert_eq!(output["done"], json!(true));
}
#[tokio::test]
async fn test_runner_for_with_input_from_v2() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-sum-accumulate-v2
version: '0.1.0'
do:
- loop:
for:
in: "${ .numbers }"
each: num
do:
- add:
set:
total: "${ .total + $num }"
numbers: "${ .numbers }"
- check:
set:
totalResult: "${ .total }"
done: true
numbers: "${ .numbers }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"numbers": [10, 20, 30], "total": 0}))
.await
.unwrap();
assert_eq!(output["totalResult"], json!(60));
assert_eq!(output["done"], json!(true));
}
#[tokio::test]
async fn test_runner_for_nested_outer_var() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-nested-outer-var
version: '0.1.0'
do:
- outer:
for:
in: "${ .groups }"
each: group
do:
- innerLoop:
for:
in: "${ .items }"
each: item
do:
- collect:
set:
result: "${ .result + [{group: $group, item: $item}] }"
items: "${ .items }"
groups: "${ .groups }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"groups": ["A", "B"], "items": [1, 2], "result": []}))
.await
.unwrap();
assert_eq!(output["result"].as_array().unwrap().len(), 4);
}
#[tokio::test]
async fn test_runner_for_sum_with_export_as_context() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-sum-export
version: '0.1.0'
do:
- sumAll:
for:
each: number
in: .input
do:
- accumulate:
set:
counter: ${.counter+$number}
export:
as: "${ if .incr == null then {incr:[$number+1]} else {incr: (.incr + [$number+1])} end }"
output:
as: "${ .counter }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"input": [1, 2, 3], "counter": 0}))
.await
.unwrap();
assert_eq!(output, json!(6));
}
#[tokio::test]
async fn test_runner_for_collect_with_input_from_and_index() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-collect
version: '0.1.0'
do:
- sumAll:
for:
each: number
in: .input
at: index
input:
from: '${ {input: .input, output: []} }'
do:
- sumIndex:
set:
output: ${.output + [$number + $index + 1]}
"#;
let output = run_workflow_yaml(&yaml_str, json!({"input": [1, 2, 3]}))
.await
.unwrap();
assert_eq!(output["output"], json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_sum_with_output_as() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-sum-output-as
version: '0.1.0'
do:
- sumAll:
for:
each: number
in: .input
do:
- accumulate:
set:
counter: ${.counter + $number}
incr: ${ if .incr == null then [$number + 1] else (.incr + [$number + 1]) end }
output:
as: '${ { total: .counter, increments: .incr } }'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"input": [1, 2, 3], "counter": 0}))
.await
.unwrap();
assert_eq!(output["total"], json!(6));
assert_eq!(output["increments"], json!([2, 3, 4]));
}
#[tokio::test]
async fn test_runner_for_collect_output() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-collect
version: '0.1.0'
do:
- collectNumbers:
for:
each: number
in: '.input'
do:
- double:
set:
output: '${ [.output // [] | .[] , ($number * 2)] }'
"#;
let output = run_workflow_yaml(&yaml_str, json!({"input": [1, 2, 3]}))
.await
.unwrap();
assert_eq!(output["output"], json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_object_collection() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-objects
version: '0.1.0'
do:
- processItems:
for:
in: ${ .items }
each: item
do:
- accumulate:
set:
names: "${ [.names[]] + [$item.name] }"
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"items": [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25}
],
"names": []
}))
.await
.unwrap();
assert_eq!(output["names"], json!(["Alice", "Bob"]));
}
#[tokio::test]
async fn test_runner_for_with_at_and_output_as() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-at-output
version: '0.1.0'
do:
- indexedLoop:
for:
in: ${ .items }
each: item
at: idx
do:
- tag:
set:
tagged: "${ [.tagged[]] + [{value: $item, index: $idx}] }"
output:
as: .tagged
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"items": ["a", "b", "c"], "tagged": []}))
.await
.unwrap();
assert_eq!(
output,
json!([
{"value": "a", "index": 0},
{"value": "b", "index": 1},
{"value": "c", "index": 2}
])
);
}
#[tokio::test]
async fn test_runner_for_with_input_from_java_pattern() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-input-from
version: '0.1.0'
do:
- sumAll:
for:
each: number
in: .input
at: index
do:
- sumIndex:
set:
output: '${ [.output // [] | .[] , ($number + $index + 1)] }'
"#;
let output = run_workflow_yaml(&yaml_str, json!({"input": [1, 2, 3]}))
.await
.unwrap();
assert_eq!(output["output"], json!([2, 4, 6]));
}
#[tokio::test]
async fn test_runner_for_sum_export_as_context_accumulation() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-sum-export
version: '0.1.0'
do:
- sumAll:
for:
each: number
in: .input
do:
- accumulate:
set:
counter: '${ .counter + $number }'
export:
as: 'if .incr == null then {incr: [$number + 1]} else .incr += [$number + 1] end'
output:
as: .counter
"#;
let output = run_workflow_yaml(&yaml_str, json!({"input": [1, 2, 3]}))
.await
.unwrap();
assert_eq!(output, json!(6));
}
#[tokio::test]
async fn test_runner_for_with_while_condition() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-while
version: '0.1.0'
do:
- countLoop:
for:
each: item
in: .items
while: '${ .count == null or .count < 3 }'
do:
- increment:
set:
count: '${ (.count // 0) + 1 }'
"#;
let output = run_workflow_yaml(&yaml_str, json!({"items": [1, 2, 3, 4, 5]}))
.await
.unwrap();
assert_eq!(output["count"], json!(3));
}
#[tokio::test]
async fn test_runner_for_colors_with_index_go_pattern() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-colors
version: '1.0.0'
do:
- init:
set:
processed:
colors: []
indexes: []
- loopColors:
for:
each: color
in: '${ $input.colors }'
do:
- markProcessed:
set:
processed:
colors: '${ .processed.colors + [$color] }'
indexes: '${ .processed.indexes + [$index] }'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"colors": ["red", "green", "blue"]}))
.await
.unwrap();
let processed = &output["processed"];
let colors = processed["colors"].as_array().unwrap();
assert_eq!(colors.len(), 3);
assert_eq!(colors[0], json!("red"));
assert_eq!(colors[1], json!("green"));
assert_eq!(colors[2], json!("blue"));
let indexes = processed["indexes"].as_array().unwrap();
assert_eq!(indexes.len(), 3);
assert_eq!(indexes[0], json!(0));
assert_eq!(indexes[1], json!(1));
assert_eq!(indexes[2], json!(2));
}
#[tokio::test]
async fn test_runner_for_sum_numbers_go_pattern() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-sum
version: '1.0.0'
do:
- sumNumbers:
for:
each: number
in: '${ .numbers }'
do:
- accumulate:
set:
result: '${ (.result // 0) + $number }'
"#;
let output = run_workflow_yaml(&yaml_str, json!({"numbers": [2, 3, 4]}))
.await
.unwrap();
assert_eq!(output["result"], json!(9));
}
#[tokio::test]
async fn test_e2e_for_try_catch_loop_with_validation() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-try-catch-loop
version: '0.1.0'
do:
- processItems:
for:
each: item
in: '${ .items }'
input:
from: '${ {items: .items, results: []} }'
do:
- validateAndProcess:
try:
- checkAndProcess:
set:
results: '${ if $item.value >= 0 then (.results + [{name: $item.name, status: "ok"}]) else error("invalid") end }'
catch:
errors:
with:
type: expression
do:
- logError:
set:
results: '${ .results + [{name: $item.name, status: "error"}] }'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({
"items": [
{"name": "alpha", "value": 10},
{"name": "beta", "value": -5},
{"name": "gamma", "value": 30}
]
}))
.await
.unwrap();
let results = output["results"]
.as_array()
.expect("expected results array");
assert_eq!(results.len(), 3);
assert_eq!(results[0]["status"], json!("ok"));
assert_eq!(results[1]["status"], json!("error"));
assert_eq!(results[2]["status"], json!("ok"));
}
#[tokio::test]
async fn test_e2e_for_colors() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: default
name: for
version: '1.0.0'
do:
- loopColors:
for:
each: color
in: '${ .colors }'
do:
- markProcessed:
set:
processed: '${ { colors: (if .processed == null then [$color] else (.processed.colors + [$color]) end), indexes: (if .processed == null then [$index] else (.processed.indexes + [$index]) end) } }'
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"colors": ["red", "green", "blue"]}))
.await
.unwrap();
assert_eq!(
output["processed"]["colors"],
json!(["red", "green", "blue"])
);
assert_eq!(output["processed"]["indexes"], json!([0, 1, 2]));
}
#[tokio::test]
async fn test_e2e_for_sum_numbers() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: for-tests
name: sum-numbers
version: '1.0.0'
do:
- sumLoop:
for:
each: item
in: ${ .numbers }
do:
- addNumber:
set:
total: ${ if .total == null then $item else (.total + $item) end }
- finalize:
set:
result: ${ .total }
"#;
let output = run_workflow_yaml(&yaml_str, json!({"numbers": [2, 3, 4]}))
.await
.unwrap();
assert_eq!(output["result"], json!(9));
}
#[tokio::test]
async fn test_e2e_for_nested_loops() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: for-tests
name: nested-loops
version: '1.0.0'
do:
- outerLoop:
for:
in: ${ .fruits }
each: fruit
at: fruitIdx
do:
- innerLoop:
for:
in: ${ $input.colors }
each: color
at: colorIdx
do:
- combinePair:
set:
matrix: ${ .matrix + [[$fruit, $color]] }
"#;
let runner = WorkflowRunner::new(serde_yaml::from_str(&yaml_str).unwrap()).unwrap();
let output = runner
.run(json!({"fruits": ["apple", "banana"], "colors": ["red", "green"]}))
.await
.unwrap();
assert_eq!(
output["matrix"],
json!([
["apple", "red"],
["apple", "green"],
["banana", "red"],
["banana", "green"]
])
);
}
#[tokio::test]
async fn test_e2e_for_collect() {
let yaml_str = r#"
document:
dsl: '1.0.0'
namespace: test
name: for-collect-example
version: '0.1.0'
do:
- sumAll:
for:
each: number
in: .input
at: index
input:
from: "${ {input: .input, output: []} }"
do:
- sumIndex:
set:
output: "${ .output + [$number + $index + 1] }"
"#;
let output = run_workflow_yaml(&yaml_str, json!({"input": [5, 3, 1]}))
.await
.unwrap();
assert_eq!(output["output"], json!([6, 5, 4]));
}