use agentic_workflow::types::*;
use agentic_workflow::types::workflow::*;
use agentic_workflow::types::execution::*;
use agentic_workflow::types::schedule::*;
use agentic_workflow::types::trigger::*;
use agentic_workflow::types::retry::*;
use agentic_workflow::types::batch::*;
use agentic_workflow::types::stream::*;
use agentic_workflow::types::fanout::*;
use agentic_workflow::types::fsm::*;
use agentic_workflow::types::variable::*;
use agentic_workflow::types::approval::*;
use agentic_workflow::types::audit::*;
use agentic_workflow::types::circuit::*;
use agentic_workflow::types::rollback::*;
use agentic_workflow::types::idempotency::*;
use agentic_workflow::types::template::*;
use std::collections::HashMap;
#[test]
fn test_workflow_creation() {
let wf = Workflow::new("deploy-pipeline", "Deploys to production");
assert_eq!(wf.name, "deploy-pipeline");
assert_eq!(wf.description, "Deploys to production");
assert_eq!(wf.version, 1);
assert!(wf.steps.is_empty());
assert!(wf.edges.is_empty());
assert!(!wf.id.is_empty());
}
#[test]
fn test_workflow_add_step() {
let mut wf = Workflow::new("wf", "test");
let step = StepNode::new("Build", StepType::Noop);
let sid = step.id.clone();
wf.add_step(step);
assert_eq!(wf.steps.len(), 1);
assert!(wf.step_by_id(&sid).is_some());
}
#[test]
fn test_workflow_add_edge() {
let mut wf = Workflow::new("wf", "test");
let s1 = StepNode::new("A", StepType::Noop);
let s2 = StepNode::new("B", StepType::Noop);
let edge = Edge {
from: s1.id.clone(),
to: s2.id.clone(),
edge_type: EdgeType::Sequence,
};
wf.add_step(s1);
wf.add_step(s2);
wf.add_edge(edge);
assert_eq!(wf.edges.len(), 1);
}
#[test]
fn test_workflow_remove_step_cascades_edges() {
let mut wf = Workflow::new("wf", "test");
let s1 = StepNode::new("A", StepType::Noop);
let s2 = StepNode::new("B", StepType::Noop);
let s1_id = s1.id.clone();
let s2_id = s2.id.clone();
wf.add_step(s1);
wf.add_step(s2);
wf.add_edge(Edge { from: s1_id.clone(), to: s2_id, edge_type: EdgeType::Sequence });
wf.remove_step(&s1_id);
assert_eq!(wf.steps.len(), 1);
assert!(wf.edges.is_empty(), "Edges referencing removed step must be removed");
}
#[test]
fn test_workflow_step_ids() {
let mut wf = Workflow::new("wf", "test");
let s1 = StepNode::new("A", StepType::Noop);
let s2 = StepNode::new("B", StepType::Noop);
let id1 = s1.id.clone();
let id2 = s2.id.clone();
wf.add_step(s1);
wf.add_step(s2);
let ids = wf.step_ids();
assert!(ids.contains(&id1.as_str()));
assert!(ids.contains(&id2.as_str()));
}
#[test]
fn test_step_type_command() {
let step = StepNode::new("Run", StepType::Command {
command: "cargo".into(), args: vec!["build".into()],
});
matches!(step.step_type, StepType::Command { .. });
}
#[test]
fn test_step_type_mcp_tool() {
let step = StepNode::new("MCP", StepType::McpTool {
sister: "codebase".into(), tool: "search".into(),
params: serde_json::json!({"query": "main"}),
});
matches!(step.step_type, StepType::McpTool { .. });
}
#[test]
fn test_step_type_http_request() {
let step = StepNode::new("HTTP", StepType::HttpRequest {
method: "POST".into(), url: "https://api.example.com".into(),
headers: HashMap::new(), body: Some("{}".into()),
});
matches!(step.step_type, StepType::HttpRequest { .. });
}
#[test]
fn test_step_type_sub_workflow() {
let step = StepNode::new("Sub", StepType::SubWorkflow { workflow_id: "wf-2".into() });
matches!(step.step_type, StepType::SubWorkflow { .. });
}
#[test]
fn test_step_type_fan_out() {
let step = StepNode::new("Fan", StepType::FanOut {
destinations: vec!["d1".into()], completion_policy: CompletionPolicy::WaitAll,
});
matches!(step.step_type, StepType::FanOut { .. });
}
#[test]
fn test_step_type_approval_gate() {
let step = StepNode::new("Approve", StepType::ApprovalGate {
approvers: vec!["admin@co.com".into()], timeout_ms: Some(3600_000),
});
matches!(step.step_type, StepType::ApprovalGate { .. });
}
#[test]
fn test_step_type_expression() {
let step = StepNode::new("Expr", StepType::Expression {
expression: "result = input.x + 1".into(),
});
matches!(step.step_type, StepType::Expression { .. });
}
#[test]
fn test_step_type_noop() {
let step = StepNode::new("Noop", StepType::Noop);
matches!(step.step_type, StepType::Noop);
}
#[test]
fn test_edge_type_sequence() {
let e = EdgeType::Sequence;
assert!(matches!(e, EdgeType::Sequence));
}
#[test]
fn test_edge_type_parallel() {
let e = EdgeType::Parallel;
assert!(matches!(e, EdgeType::Parallel));
}
#[test]
fn test_edge_type_conditional() {
let e = EdgeType::Conditional { expression: "status == 200".into() };
assert!(matches!(e, EdgeType::Conditional { .. }));
}
#[test]
fn test_edge_type_loop() {
let e = EdgeType::Loop { max_iterations: Some(10), condition: Some("retry".into()) };
assert!(matches!(e, EdgeType::Loop { .. }));
}
#[test]
fn test_execution_status_eq() {
assert_eq!(ExecutionStatus::Pending, ExecutionStatus::Pending);
assert_eq!(ExecutionStatus::Running, ExecutionStatus::Running);
assert_ne!(ExecutionStatus::Running, ExecutionStatus::Paused);
let f = ExecutionStatus::Failed { error: "boom".into() };
assert!(matches!(f, ExecutionStatus::Failed { .. }));
}
#[test]
fn test_step_lifecycle_all_variants() {
let variants = vec![
StepLifecycle::Pending, StepLifecycle::Queued, StepLifecycle::Running,
StepLifecycle::Success, StepLifecycle::Failed, StepLifecycle::Skipped,
StepLifecycle::Cancelled, StepLifecycle::WaitingApproval, StepLifecycle::RolledBack,
];
assert_eq!(variants.len(), 9);
assert_ne!(StepLifecycle::Pending, StepLifecycle::Running);
}
#[test]
fn test_trigger_type_filesystem() {
let t = TriggerType::FileSystem {
path: "/data".into(), events: vec![FileEvent::Created, FileEvent::Modified],
};
assert!(matches!(t, TriggerType::FileSystem { .. }));
}
#[test]
fn test_trigger_type_webhook() {
let t = TriggerType::Webhook { endpoint: "/hook".into(), method: "POST".into() };
assert!(matches!(t, TriggerType::Webhook { .. }));
}
#[test]
fn test_trigger_type_schedule() {
let t = TriggerType::Schedule { schedule_id: "s1".into() };
assert!(matches!(t, TriggerType::Schedule { .. }));
}
#[test]
fn test_trigger_type_manual() {
assert!(matches!(TriggerType::Manual, TriggerType::Manual));
}
#[test]
fn test_trigger_type_workflow_output() {
let t = TriggerType::WorkflowOutput { source_workflow_id: "wf-1".into() };
assert!(matches!(t, TriggerType::WorkflowOutput { .. }));
}
#[test]
fn test_trigger_type_api_callback() {
let t = TriggerType::ApiCallback { callback_id: "cb-1".into() };
assert!(matches!(t, TriggerType::ApiCallback { .. }));
}
#[test]
fn test_trigger_type_custom() {
let t = TriggerType::Custom {
event_type: "git-push".into(), config: serde_json::json!({}),
};
assert!(matches!(t, TriggerType::Custom { .. }));
}
#[test]
fn test_schedule_expression_all_variants() {
let _cron = ScheduleExpression::Cron("0 8 * * 1-5".into());
let _nat = ScheduleExpression::Natural("every weekday at 8am".into());
let _int = ScheduleExpression::Interval { every_ms: 60_000 };
let _once = ScheduleExpression::Once { at: chrono::Utc::now() };
}
#[test]
fn test_conflict_policy_all_variants() {
let _skip = ConflictPolicy::Skip;
let _queue = ConflictPolicy::Queue;
let _cancel = ConflictPolicy::CancelPrevious;
let _wait = ConflictPolicy::Wait;
}
#[test]
fn test_failure_class_all_variants() {
let classes = vec![
FailureClass::Transient, FailureClass::Permanent, FailureClass::RateLimit,
FailureClass::ResourceContention, FailureClass::Authentication,
FailureClass::Network, FailureClass::Timeout, FailureClass::Unknown,
];
assert_eq!(classes.len(), 8);
assert_eq!(FailureClass::Transient, FailureClass::Transient);
assert_ne!(FailureClass::Transient, FailureClass::Permanent);
}
#[test]
fn test_circuit_state_transitions() {
assert_eq!(CircuitState::Closed, CircuitState::Closed);
assert_ne!(CircuitState::Closed, CircuitState::Open);
assert_ne!(CircuitState::Open, CircuitState::HalfOpen);
}
#[test]
fn test_batch_status_variants() {
let statuses = vec![
BatchStatus::Pending, BatchStatus::Running, BatchStatus::Paused,
BatchStatus::Completed, BatchStatus::PartiallyCompleted,
BatchStatus::Failed, BatchStatus::Cancelled,
];
assert_eq!(statuses.len(), 7);
}
#[test]
fn test_batch_item_status_variants() {
let statuses = vec![
BatchItemStatus::Pending, BatchItemStatus::Running,
BatchItemStatus::Success, BatchItemStatus::Failed, BatchItemStatus::Skipped,
];
assert_eq!(statuses.len(), 5);
}
#[test]
fn test_stream_status_transitions() {
let chain = vec![
StreamStatus::Created, StreamStatus::Running,
StreamStatus::Paused, StreamStatus::Stopped, StreamStatus::Error,
];
assert_eq!(chain.len(), 5);
assert_ne!(StreamStatus::Created, StreamStatus::Running);
}
#[test]
fn test_fanout_branch_status_variants() {
let statuses = vec![
FanOutBranchStatus::Pending, FanOutBranchStatus::Running,
FanOutBranchStatus::Success, FanOutBranchStatus::Failed,
FanOutBranchStatus::TimedOut, FanOutBranchStatus::Cancelled,
];
assert_eq!(statuses.len(), 6);
}
#[test]
fn test_fsm_state_creation() {
let state = State {
name: "Idle".into(), description: Some("Waiting".into()),
entry_action: None, exit_action: None, is_terminal: false,
};
assert_eq!(state.name, "Idle");
assert!(!state.is_terminal);
}
#[test]
fn test_fsm_transition_creation() {
let t = Transition {
from: "A".into(), to: "B".into(), event: "go".into(),
guard: None, action: None,
};
assert_eq!(t.from, "A");
assert_eq!(t.event, "go");
}