use crate::prelude::*;
use crate::{abort, check_abort, continue_loop};
use async_trait::async_trait;
struct AbortTestOp {
should_abort: bool,
abort_reason: Option<String>,
}
impl AbortTestOp {
fn new(should_abort: bool, abort_reason: Option<String>) -> Self {
Self {
should_abort,
abort_reason,
}
}
}
#[async_trait]
impl Op<i32> for AbortTestOp {
async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<i32> {
if self.should_abort {
if let Some(ref reason) = self.abort_reason {
abort!(dry, reason.clone());
} else {
abort!(dry);
}
}
Ok(42)
}
fn metadata(&self) -> OpMetadata {
OpMetadata::builder("AbortTestOp").build()
}
}
struct ContinueTestOp {
should_continue: bool,
value: i32,
}
impl ContinueTestOp {
fn new(should_continue: bool, value: i32) -> Self {
Self {
should_continue,
value,
}
}
}
#[async_trait]
impl Op<i32> for ContinueTestOp {
async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<i32> {
if self.should_continue {
continue_loop!(dry);
}
Ok(self.value)
}
fn metadata(&self) -> OpMetadata {
OpMetadata::builder("ContinueTestOp").build()
}
}
struct CheckAbortOp;
#[async_trait]
impl Op<i32> for CheckAbortOp {
async fn perform(&self, dry: &mut DryContext, _wet: &mut WetContext) -> OpResult<i32> {
check_abort!(dry);
Ok(100)
}
fn metadata(&self) -> OpMetadata {
OpMetadata::builder("CheckAbortOp").build()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{loop_op::LoopOp, BatchOp};
use std::sync::Arc;
#[tokio::test]
async fn test0057_abort_macro_without_reason() {
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let op = AbortTestOp::new(true, None);
let result = op.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
assert!(dry.is_aborted());
assert_eq!(dry.abort_reason(), None);
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Operation aborted");
} else {
panic!("Expected Aborted error");
}
}
#[tokio::test]
async fn test0058_abort_macro_with_reason() {
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let op = AbortTestOp::new(true, Some("Test reason".to_string()));
let result = op.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
assert!(dry.is_aborted());
assert_eq!(dry.abort_reason(), Some(&"Test reason".to_string()));
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Test reason");
} else {
panic!("Expected Aborted error");
}
}
#[tokio::test]
async fn test0059_continue_loop_macro() {
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let loop_id = "test_loop_123";
dry.insert("__current_loop_id", loop_id.to_string());
let op = ContinueTestOp::new(true, 99);
let result = op.perform(&mut dry, &mut wet).await;
assert!(result.is_ok());
let continue_var = format!("__continue_loop_{}", loop_id);
assert!(dry.get::<bool>(&continue_var).unwrap_or(false));
assert_eq!(result.unwrap(), 0); }
#[tokio::test]
async fn test0060_check_abort_macro() {
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let op = CheckAbortOp;
let result = op.perform(&mut dry, &mut wet).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 100);
dry.set_abort(Some("Pre-existing abort".to_string()));
let result = op.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Pre-existing abort");
} else {
panic!("Expected Aborted error");
}
}
#[tokio::test]
async fn test0061_batch_op_with_abort() {
let ops = vec![
Arc::new(AbortTestOp::new(false, None)) as Arc<dyn Op<i32>>,
Arc::new(AbortTestOp::new(true, Some("Batch abort".to_string()))) as Arc<dyn Op<i32>>,
Arc::new(AbortTestOp::new(false, None)) as Arc<dyn Op<i32>>, ];
let batch = BatchOp::new(ops);
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let result = batch.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Batch abort");
} else {
panic!("Expected Aborted error, got: {:?}", result);
}
}
#[tokio::test]
async fn test0062_batch_op_with_pre_existing_abort() {
let ops = vec![
Arc::new(AbortTestOp::new(false, None)) as Arc<dyn Op<i32>>,
Arc::new(AbortTestOp::new(false, None)) as Arc<dyn Op<i32>>,
];
let batch = BatchOp::new(ops);
let mut dry = DryContext::new();
let mut wet = WetContext::new();
dry.set_abort(Some("Pre-existing abort".to_string()));
let result = batch.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Pre-existing abort");
} else {
panic!("Expected Aborted error");
}
}
#[tokio::test]
async fn test0063_loop_op_with_continue() {
let ops: Vec<Arc<dyn Op<i32>>> = vec![
Arc::new(ContinueTestOp::new(false, 10)), Arc::new(ContinueTestOp::new(true, 20)), Arc::new(AbortTestOp::new(false, None)), ];
let loop_op = LoopOp::new("test_counter".to_string(), 2, ops);
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let result = loop_op.perform(&mut dry, &mut wet).await;
assert!(result.is_ok());
let results = result.unwrap();
assert_eq!(results.len(), 4);
assert_eq!(results, vec![10, 0, 10, 0]);
}
#[tokio::test]
async fn test0064_loop_op_with_abort() {
let ops: Vec<Arc<dyn Op<i32>>> = vec![
Arc::new(AbortTestOp::new(false, None)),
Arc::new(AbortTestOp::new(true, Some("Loop abort".to_string()))),
Arc::new(AbortTestOp::new(false, None)), ];
let loop_op = LoopOp::new("test_counter".to_string(), 3, ops);
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let result = loop_op.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Loop abort");
} else {
panic!("Expected Aborted error");
}
}
#[tokio::test]
async fn test0065_loop_op_with_pre_existing_abort() {
let ops: Vec<Arc<dyn Op<i32>>> = vec![Arc::new(AbortTestOp::new(false, None))];
let loop_op = LoopOp::new("test_counter".to_string(), 2, ops);
let mut dry = DryContext::new();
let mut wet = WetContext::new();
dry.set_abort(Some("Pre-existing loop abort".to_string()));
let result = loop_op.perform(&mut dry, &mut wet).await;
assert!(result.is_err());
if let Err(OpError::Aborted(msg)) = result {
assert_eq!(msg, "Pre-existing loop abort");
} else {
panic!("Expected Aborted error");
}
}
#[tokio::test]
async fn test0066_complex_control_flow_scenario() {
let batch_ops = vec![
Arc::new(ContinueTestOp::new(false, 100)) as Arc<dyn Op<i32>>,
Arc::new(ContinueTestOp::new(true, 200)) as Arc<dyn Op<i32>>, ];
let loop_ops: Vec<Arc<dyn Op<Vec<i32>>>> = vec![Arc::new(BatchOp::new(batch_ops))];
let loop_op = LoopOp::new("complex_counter".to_string(), 2, loop_ops);
let mut dry = DryContext::new();
let mut wet = WetContext::new();
let result = loop_op.perform(&mut dry, &mut wet).await;
assert!(result.is_ok());
let results = result.unwrap();
assert_eq!(results.len(), 2);
for batch_result in results {
assert_eq!(batch_result.len(), 2);
assert_eq!(batch_result[0], 100);
assert_eq!(batch_result[1], 0); }
}
}