use cloacina::cloacina_workflow::Task;
use cloacina::{task, Context, TaskError};
use std::sync::atomic::{AtomicU32, Ordering};
static TEST1_SUCCESS_COUNT: AtomicU32 = AtomicU32::new(0);
static TEST2_FAILURE_COUNT: AtomicU32 = AtomicU32::new(0);
static TEST3_SUCCESS_COUNT: AtomicU32 = AtomicU32::new(0);
static TEST3_FAILURE_COUNT: AtomicU32 = AtomicU32::new(0);
static TEST4_SUCCESS_COUNT: AtomicU32 = AtomicU32::new(0);
static TEST4_FAILURE_COUNT: AtomicU32 = AtomicU32::new(0);
async fn test1_success_callback(
_task_id: &str,
_context: &Context<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
TEST1_SUCCESS_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(())
}
async fn test2_failure_callback(
_task_id: &str,
_error: &cloacina::cloacina_workflow::TaskError,
_context: &Context<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
TEST2_FAILURE_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(())
}
async fn test3_success_callback(
_task_id: &str,
_context: &Context<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
TEST3_SUCCESS_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(())
}
async fn test3_failure_callback(
_task_id: &str,
_error: &cloacina::cloacina_workflow::TaskError,
_context: &Context<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
TEST3_FAILURE_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(())
}
async fn test4_success_callback(
_task_id: &str,
_context: &Context<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
TEST4_SUCCESS_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(())
}
async fn test4_failure_callback(
_task_id: &str,
_error: &cloacina::cloacina_workflow::TaskError,
_context: &Context<serde_json::Value>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
TEST4_FAILURE_COUNT.fetch_add(1, Ordering::SeqCst);
Ok(())
}
#[task(id = "test1-success-only", dependencies = [], on_success = test1_success_callback)]
async fn test1_task(_context: &mut Context<serde_json::Value>) -> Result<(), TaskError> {
Ok(())
}
#[task(id = "test2-failure-only", dependencies = [], on_failure = test2_failure_callback)]
async fn test2_task(_context: &mut Context<serde_json::Value>) -> Result<(), TaskError> {
Err(TaskError::ExecutionFailed {
message: "Intentional failure for testing".to_string(),
task_id: "test2-failure-only".to_string(),
timestamp: chrono::Utc::now(),
})
}
#[task(
id = "test3-both-success",
dependencies = [],
on_success = test3_success_callback,
on_failure = test3_failure_callback
)]
async fn test3_task(_context: &mut Context<serde_json::Value>) -> Result<(), TaskError> {
Ok(())
}
#[task(
id = "test4-both-failure",
dependencies = [],
on_success = test4_success_callback,
on_failure = test4_failure_callback
)]
async fn test4_task(_context: &mut Context<serde_json::Value>) -> Result<(), TaskError> {
Err(TaskError::ExecutionFailed {
message: "Intentional failure".to_string(),
task_id: "test4-both-failure".to_string(),
timestamp: chrono::Utc::now(),
})
}
#[tokio::test]
async fn test_on_success_callback_invoked() {
let initial = TEST1_SUCCESS_COUNT.load(Ordering::SeqCst);
let task = test1_task_task();
let context = Context::new();
let result = task.execute(context).await;
assert!(result.is_ok(), "Task should succeed");
assert_eq!(
TEST1_SUCCESS_COUNT.load(Ordering::SeqCst),
initial + 1,
"on_success callback should have been called once"
);
}
#[tokio::test]
async fn test_on_failure_callback_invoked() {
let initial = TEST2_FAILURE_COUNT.load(Ordering::SeqCst);
let task = test2_task_task();
let context = Context::new();
let result = task.execute(context).await;
assert!(result.is_err(), "Task should fail");
assert_eq!(
TEST2_FAILURE_COUNT.load(Ordering::SeqCst),
initial + 1,
"on_failure callback should have been called once"
);
}
#[tokio::test]
async fn test_both_callbacks_success_path() {
let initial_success = TEST3_SUCCESS_COUNT.load(Ordering::SeqCst);
let initial_failure = TEST3_FAILURE_COUNT.load(Ordering::SeqCst);
let task = test3_task_task();
let context = Context::new();
let result = task.execute(context).await;
assert!(result.is_ok(), "Task should succeed");
assert_eq!(
TEST3_SUCCESS_COUNT.load(Ordering::SeqCst),
initial_success + 1,
"on_success callback should have been called"
);
assert_eq!(
TEST3_FAILURE_COUNT.load(Ordering::SeqCst),
initial_failure,
"on_failure callback should NOT have been called"
);
}
#[tokio::test]
async fn test_both_callbacks_failure_path() {
let initial_success = TEST4_SUCCESS_COUNT.load(Ordering::SeqCst);
let initial_failure = TEST4_FAILURE_COUNT.load(Ordering::SeqCst);
let task = test4_task_task();
let context = Context::new();
let result = task.execute(context).await;
assert!(result.is_err(), "Task should fail");
assert_eq!(
TEST4_SUCCESS_COUNT.load(Ordering::SeqCst),
initial_success,
"on_success callback should NOT have been called"
);
assert_eq!(
TEST4_FAILURE_COUNT.load(Ordering::SeqCst),
initial_failure + 1,
"on_failure callback should have been called"
);
}