test: update tests/examples/benches
This commit is contained in:
1 parent
e4fc0b0d44
commit
72a83e29dc
47 files changed
+491
-1239
No files matched your search
+48
-72
@@ -1,17 +1,17 @@
|
||||
//! Tests for error propagation through the DAG
|
||||
|
||||
use dagx::{task, DagRunner, TaskHandle};
|
||||
use dagx::{task, DagError, DagRunner};
|
||||
use dagx_test::task_fn;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_linear_error_propagation() {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
|
||||
let t1: TaskHandle<_> = dag.add_task(task_fn::<(), _, _>(|_: ()| 1)).into();
|
||||
let t1 = dag.add_task(task_fn::<(), _, _>(|_: ()| 1));
|
||||
let t2 = dag
|
||||
.add_task(task_fn::<i32, _, _>(|&x: &i32| -> i32 {
|
||||
panic!("Error at t2 with input {}", x);
|
||||
@@ -20,28 +20,19 @@ async fn test_linear_error_propagation() {
|
||||
let t3 = dag
|
||||
.add_task(task_fn::<i32, _, _>(|&x: &i32| x + 1))
|
||||
.depends_on(t2);
|
||||
let t4 = dag
|
||||
.add_task(task_fn::<i32, _, _>(|&x: &i32| x * 2))
|
||||
dag.add_task(task_fn::<i32, _, _>(|&x: &i32| x * 2))
|
||||
.depends_on(t3);
|
||||
|
||||
let _ = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await;
|
||||
|
||||
// t1 succeeds
|
||||
assert_eq!(dag.get(t1).unwrap(), 1);
|
||||
|
||||
// t2 fails
|
||||
assert!(dag.get(t2).is_err());
|
||||
|
||||
// t3 and t4 fail due to dependency
|
||||
assert!(dag.get(t3).is_err());
|
||||
assert!(dag.get(t4).is_err());
|
||||
assert!(matches!(
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await,
|
||||
Err(DagError::TaskPanicked { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_diamond_error_propagation() {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
|
||||
// source
|
||||
// / \
|
||||
@@ -49,7 +40,7 @@ async fn test_diamond_error_propagation() {
|
||||
// \ /
|
||||
// sink
|
||||
|
||||
let source: TaskHandle<_> = dag.add_task(task_fn::<(), _, _>(|_: ()| 100)).into();
|
||||
let source = dag.add_task(task_fn::<(), _, _>(|_: ()| 100));
|
||||
|
||||
let left = dag
|
||||
.add_task(task_fn::<i32, _, _>(|&x: &i32| x / 2))
|
||||
@@ -61,61 +52,45 @@ async fn test_diamond_error_propagation() {
|
||||
}))
|
||||
.depends_on(source);
|
||||
|
||||
let sink = dag
|
||||
.add_task(task_fn::<(i32, i32), _, _>(|(l, r): (&i32, &i32)| l + r))
|
||||
dag.add_task(task_fn::<(i32, i32), _, _>(|(l, r): (&i32, &i32)| l + r))
|
||||
.depends_on((&left, &right));
|
||||
|
||||
let _ = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await;
|
||||
|
||||
assert_eq!(dag.get(source).unwrap(), 100);
|
||||
assert_eq!(dag.get(left).unwrap(), 50);
|
||||
assert!(dag.get(right).is_err());
|
||||
assert!(dag.get(sink).is_err()); // Fails due to right dependency
|
||||
assert!(matches!(
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await,
|
||||
Err(DagError::TaskPanicked { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_in_wide_fanout() {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
|
||||
let source: TaskHandle<_> = dag
|
||||
.add_task(task_fn::<(), _, _>(|_: ()| -> i32 {
|
||||
panic!("Source fails");
|
||||
}))
|
||||
.into();
|
||||
let source = dag.add_task(task_fn::<(), _, _>(|_: ()| -> i32 {
|
||||
panic!("Source fails");
|
||||
}));
|
||||
|
||||
// Create wide fanout from failing source
|
||||
let dependents: Vec<_> = (0..20)
|
||||
.map(|i| {
|
||||
dag.add_task(task_fn::<i32, _, _>(move |&x: &i32| x + i))
|
||||
.depends_on(source)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let _ = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await;
|
||||
|
||||
// Source fails
|
||||
assert!(dag.get(source).is_err());
|
||||
|
||||
// All dependents fail
|
||||
for dependent in &dependents {
|
||||
assert!(dag.get(dependent).is_err());
|
||||
for i in 0..20 {
|
||||
dag.add_task(task_fn::<i32, _, _>(move |&x: &i32| x + i))
|
||||
.depends_on(source);
|
||||
}
|
||||
|
||||
assert!(matches!(
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await,
|
||||
Err(DagError::TaskPanicked { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_selective_error_propagation() {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
|
||||
// Source that produces a Result
|
||||
let source: TaskHandle<_> = dag
|
||||
.add_task(task_fn::<(), _, _>(|_: ()| {
|
||||
Result::<i32, String>::Err("Source error".to_string())
|
||||
}))
|
||||
.into();
|
||||
let source = dag.add_task(task_fn::<(), _, _>(|_: ()| {
|
||||
Result::<i32, String>::Err("Source error".to_string())
|
||||
}));
|
||||
|
||||
// Handler that processes the Result
|
||||
let handler = dag
|
||||
@@ -140,34 +115,35 @@ async fn test_selective_error_propagation() {
|
||||
}))
|
||||
.depends_on(handler);
|
||||
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
let mut output = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Source returns an Err variant (not a panic)
|
||||
assert_eq!(dag.get(source).unwrap(), Err("Source error".to_string()));
|
||||
assert_eq!(output.get(source).unwrap(), Err("Source error".to_string()));
|
||||
|
||||
// Handler processes it
|
||||
assert_eq!(dag.get(handler).unwrap(), -1);
|
||||
assert_eq!(output.get(handler).unwrap(), -1);
|
||||
|
||||
// Final task handles the error case
|
||||
assert_eq!(dag.get(final_task).unwrap(), "Handled error case");
|
||||
assert_eq!(output.get(final_task).unwrap(), "Handled error case");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_propagation_timing() {
|
||||
let dag = DagRunner::new();
|
||||
let propagation_order = Arc::new(parking_lot::Mutex::new(Vec::new()));
|
||||
let mut dag = DagRunner::new();
|
||||
let propagation_order = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
struct T1Task {
|
||||
order: Arc<parking_lot::Mutex<Vec<&'static str>>>,
|
||||
order: Arc<Mutex<Vec<&'static str>>>,
|
||||
}
|
||||
|
||||
#[task]
|
||||
impl T1Task {
|
||||
async fn run(&self) -> i32 {
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
self.order.lock().push("t1_complete");
|
||||
self.order.lock().unwrap().push("t1_complete");
|
||||
1
|
||||
}
|
||||
}
|
||||
@@ -177,13 +153,13 @@ async fn test_error_propagation_timing() {
|
||||
});
|
||||
|
||||
struct T2Task {
|
||||
order: Arc<parking_lot::Mutex<Vec<&'static str>>>,
|
||||
order: Arc<Mutex<Vec<&'static str>>>,
|
||||
}
|
||||
|
||||
#[task]
|
||||
impl T2Task {
|
||||
async fn run(&self, x: &i32) -> i32 {
|
||||
self.order.lock().push("t2_start");
|
||||
self.order.lock().unwrap().push("t2_start");
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
panic!("t2 fails with {x}");
|
||||
}
|
||||
@@ -200,7 +176,7 @@ async fn test_error_propagation_timing() {
|
||||
let order = propagation_order.clone();
|
||||
move |x: &i32| {
|
||||
let order = order.clone();
|
||||
order.lock().push("t3_should_not_run");
|
||||
order.lock().unwrap().push("t3_should_not_run");
|
||||
x + 1
|
||||
}
|
||||
}))
|
||||
@@ -210,7 +186,7 @@ async fn test_error_propagation_timing() {
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await;
|
||||
|
||||
let order = propagation_order.lock().clone();
|
||||
let order = propagation_order.lock().unwrap().clone();
|
||||
|
||||
// t1 completes, t2 starts and fails, t3 never runs
|
||||
assert!(order.contains(&"t1_complete"));
|
||||
|
||||
+26
-19
@@ -4,11 +4,11 @@ use dagx::{DagResult, DagRunner};
|
||||
use dagx_test::task_fn;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_retry_pattern_simulation() -> DagResult<()> {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
let attempt_counter = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
// Task that would fail on first attempts (simulated)
|
||||
@@ -29,18 +29,19 @@ async fn test_retry_pattern_simulation() -> DagResult<()> {
|
||||
}
|
||||
}));
|
||||
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
let mut output = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await?;
|
||||
|
||||
// Task completes with error on first "attempt"
|
||||
assert_eq!(dag.get(task)?, Err("Simulated failure"));
|
||||
assert_eq!(output.get(task)?, Err("Simulated failure"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_circuit_breaker_pattern() -> DagResult<()> {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
let failure_count = Arc::new(AtomicUsize::new(0));
|
||||
let circuit_open = Arc::new(AtomicBool::new(false));
|
||||
|
||||
@@ -71,12 +72,13 @@ async fn test_circuit_breaker_pattern() -> DagResult<()> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
let mut output = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await?;
|
||||
|
||||
// First few tasks fail and open circuit
|
||||
for task in tasks.into_iter().take(4) {
|
||||
assert!(dag.get(task)?.is_err());
|
||||
assert!(output.get(task)?.is_err());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -84,8 +86,8 @@ async fn test_circuit_breaker_pattern() -> DagResult<()> {
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_accumulation_pattern() -> DagResult<()> {
|
||||
let dag = DagRunner::new();
|
||||
let errors = Arc::new(parking_lot::Mutex::new(Vec::new()));
|
||||
let mut dag = DagRunner::new();
|
||||
let errors = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
// Tasks that may produce errors
|
||||
let mut tasks: Vec<_> = (0..10)
|
||||
@@ -94,7 +96,10 @@ async fn test_error_accumulation_pattern() -> DagResult<()> {
|
||||
dag.add_task(task_fn::<(), _, _>(move |_: ()| {
|
||||
let errors = errors.clone();
|
||||
if i % 2 == 0 {
|
||||
errors.lock().push(format!("Error from task {}", i));
|
||||
errors
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(format!("Error from task {}", i));
|
||||
return Err(format!("Task {} error", i));
|
||||
}
|
||||
Ok(i)
|
||||
@@ -102,11 +107,12 @@ async fn test_error_accumulation_pattern() -> DagResult<()> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
let mut output = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await?;
|
||||
|
||||
// Check accumulated errors
|
||||
let error_list = errors.lock().clone();
|
||||
let error_list = errors.lock().unwrap().clone();
|
||||
assert_eq!(error_list.len(), 5); // Even numbered tasks
|
||||
|
||||
tasks.truncate(2);
|
||||
@@ -114,15 +120,15 @@ async fn test_error_accumulation_pattern() -> DagResult<()> {
|
||||
let [first, second] = <[_; 2]>::try_from(tasks).map_err(|_e| ()).unwrap();
|
||||
|
||||
// Verify specific results
|
||||
assert!(dag.get(first)?.is_err());
|
||||
assert_eq!(dag.get(second)?, Ok(1));
|
||||
assert!(output.get(first)?.is_err());
|
||||
assert_eq!(output.get(second)?, Ok(1));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_error_boundary_isolation() -> DagResult<()> {
|
||||
let dag = DagRunner::new();
|
||||
let mut dag = DagRunner::new();
|
||||
|
||||
// Group A: contains an error - use Result
|
||||
let a1 = dag.add_task(task_fn::<(), _, _>(|_: ()| 10));
|
||||
@@ -143,15 +149,16 @@ async fn test_error_boundary_isolation() -> DagResult<()> {
|
||||
.add_task(task_fn::<i32, _, _>(|&x: &i32| x + 5))
|
||||
.depends_on(b2);
|
||||
|
||||
dag.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
let mut output = dag
|
||||
.run(|fut| async move { tokio::spawn(fut).await.unwrap() })
|
||||
.await?;
|
||||
|
||||
// Group A fails (returns Err)
|
||||
assert_eq!(dag.get(a2)?, Err("Group A error"));
|
||||
assert_eq!(output.get(a2)?, Err("Group A error"));
|
||||
|
||||
// Groups B and C succeed
|
||||
assert_eq!(dag.get(b2)?, 40);
|
||||
assert_eq!(dag.get(c1)?, 45);
|
||||
assert_eq!(output.get(b2)?, 40);
|
||||
assert_eq!(output.get(c1)?, 45);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in new issue
Block a user