240 lines
5.6 KiB
Rust
240 lines
5.6 KiB
Rust
// Stateless task pattern tests
|
|
use crate::common::task_fn;
|
|
use futures::FutureExt;
|
|
|
|
use dagx::*;
|
|
|
|
// Stateless task structs
|
|
struct StatelessAdd;
|
|
|
|
#[task]
|
|
impl StatelessAdd {
|
|
async fn run(a: &i32, b: &i32) -> i32 {
|
|
a + b
|
|
}
|
|
}
|
|
|
|
struct StatelessDouble;
|
|
|
|
#[task]
|
|
impl StatelessDouble {
|
|
async fn run(x: &i32) -> i32 {
|
|
x * 2
|
|
}
|
|
}
|
|
|
|
struct StatelessSource;
|
|
|
|
#[task]
|
|
impl StatelessSource {
|
|
async fn run() -> i32 {
|
|
42
|
|
}
|
|
}
|
|
|
|
struct StatelessFormat;
|
|
|
|
#[task]
|
|
impl StatelessFormat {
|
|
async fn run(n: &i32, s: &String, b: &bool) -> String {
|
|
format!("{}: {} ({})", s, n, b)
|
|
}
|
|
}
|
|
|
|
struct StatelessMultiply;
|
|
|
|
#[task]
|
|
impl StatelessMultiply {
|
|
async fn run(a: &i32, b: &i32) -> i32 {
|
|
a * b
|
|
}
|
|
}
|
|
|
|
struct StatelessInc;
|
|
|
|
#[task]
|
|
impl StatelessInc {
|
|
async fn run(x: &i32) -> i32 {
|
|
x + 1
|
|
}
|
|
}
|
|
|
|
struct StatelessSquare;
|
|
|
|
#[task]
|
|
impl StatelessSquare {
|
|
async fn run(x: &i32) -> i32 {
|
|
x * x
|
|
}
|
|
}
|
|
|
|
struct StatelessConcat;
|
|
|
|
#[task]
|
|
impl StatelessConcat {
|
|
async fn run(a: &String, b: &String) -> String {
|
|
format!("{}{}", a, b)
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_basic() {
|
|
// Test basic stateless task with two inputs
|
|
let dag = DagRunner::new();
|
|
|
|
let x = dag.add_task(task_fn(|_: ()| async { 10 }));
|
|
let y = dag.add_task(task_fn(|_: ()| async { 20 }));
|
|
let sum = dag.add_task(StatelessAdd).depends_on((&x, &y));
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(dag.get(sum).unwrap(), 30);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_single_input() {
|
|
// Test stateless task with single input
|
|
let dag = DagRunner::new();
|
|
|
|
let input = dag.add_task(task_fn(|_: ()| async { 21 }));
|
|
let doubled = dag.add_task(StatelessDouble).depends_on(&input);
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(dag.get(doubled).unwrap(), 42);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_no_input() {
|
|
// Test stateless source task with no inputs
|
|
let dag = DagRunner::new();
|
|
|
|
let source = dag.add_task(StatelessSource);
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(dag.get(source).unwrap(), 42);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_multiple_inputs() {
|
|
// Test stateless task with multiple inputs of different types
|
|
let dag = DagRunner::new();
|
|
|
|
let num = dag.add_task(task_fn(|_: ()| async { 42 }));
|
|
let text = dag.add_task(task_fn(|_: ()| async { "test".to_string() }));
|
|
let flag = dag.add_task(task_fn(|_: ()| async { true }));
|
|
|
|
let result = dag
|
|
.add_task(StatelessFormat)
|
|
.depends_on((&num, &text, &flag));
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(dag.get(result).unwrap(), "test: 42 (true)");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_mixed_with_stateful() {
|
|
// Test mixing stateless and stateful tasks
|
|
// Define Counter inline for this test
|
|
struct Counter {
|
|
count: i32,
|
|
}
|
|
|
|
impl Counter {
|
|
fn new(initial: i32) -> Self {
|
|
Self { count: initial }
|
|
}
|
|
}
|
|
|
|
#[task]
|
|
impl Counter {
|
|
async fn run(&mut self, input: &i32) -> i32 {
|
|
self.count += input;
|
|
self.count
|
|
}
|
|
}
|
|
|
|
let dag = DagRunner::new();
|
|
|
|
let x = dag.add_task(task_fn(|_: ()| async { 5 }));
|
|
let y = dag.add_task(task_fn(|_: ()| async { 3 }));
|
|
|
|
// Stateless multiplication
|
|
let product = dag.add_task(StatelessMultiply).depends_on((&x, &y));
|
|
|
|
// Stateful counter
|
|
let counted = dag.add_task(Counter::new(10)).depends_on(product);
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(dag.get(product).unwrap(), 15);
|
|
assert_eq!(dag.get(counted).unwrap(), 25); // 10 + 15
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_chain() {
|
|
// Test chaining stateless tasks
|
|
let dag = DagRunner::new();
|
|
|
|
let start = dag.add_task(task_fn(|_: ()| async { 0 }));
|
|
let step1 = dag.add_task(StatelessInc).depends_on(&start);
|
|
let step2 = dag.add_task(StatelessInc).depends_on(step1);
|
|
let step3 = dag.add_task(StatelessInc).depends_on(step2);
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(dag.get(step1).unwrap(), 1);
|
|
assert_eq!(dag.get(step2).unwrap(), 2);
|
|
assert_eq!(dag.get(step3).unwrap(), 3);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_parallel() {
|
|
// Test parallel execution of stateless tasks
|
|
let dag = DagRunner::new();
|
|
|
|
let tasks: Vec<_> = (1..=5)
|
|
.map(|i| {
|
|
let source = dag.add_task(task_fn(move |_: ()| async move { i }));
|
|
dag.add_task(StatelessSquare).depends_on(&source)
|
|
})
|
|
.collect();
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(dag.get(tasks[0]).unwrap(), 1);
|
|
assert_eq!(dag.get(tasks[1]).unwrap(), 4);
|
|
assert_eq!(dag.get(tasks[2]).unwrap(), 9);
|
|
assert_eq!(dag.get(tasks[3]).unwrap(), 16);
|
|
assert_eq!(dag.get(tasks[4]).unwrap(), 25);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stateless_with_string_types() {
|
|
// Test stateless tasks with complex types
|
|
let dag = DagRunner::new();
|
|
|
|
let hello = dag.add_task(task_fn(|_: ()| async { "Hello, ".to_string() }));
|
|
let world = dag.add_task(task_fn(|_: ()| async { "World!".to_string() }));
|
|
|
|
let greeting = dag.add_task(StatelessConcat).depends_on((&hello, &world));
|
|
|
|
dag.run(|fut| tokio::spawn(fut).map(Result::unwrap))
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(dag.get(greeting).unwrap(), "Hello, World!");
|
|
}
|