use crate::error::WorkflowError;
use molten_core::document::Document;
use molten_core::workflow::{WorkflowDefinition, WorkflowGraph};
pub fn transition(
doc: &mut Document,
workflow: &WorkflowDefinition,
target_phase_id: &str,
) -> Result<(), WorkflowError> {
if doc.workflow_id != workflow.id() {
return Err(WorkflowError::WorkflowMismatch {
doc_wf: doc.workflow_id.clone(),
provided_wf: workflow.id().to_string(),
});
}
if workflow.get_phase(target_phase_id).is_none() {
return Err(WorkflowError::UnknownPhase(target_phase_id.to_string()));
}
if doc.current_phase.is_empty() {
if let Some(start_phase) = workflow.get_start_phase() {
if start_phase.id == target_phase_id {
doc.current_phase = target_phase_id.to_string();
return Ok(());
} else {
return Err(WorkflowError::InvalidTransition {
current: "WAITING_TO_START".to_string(),
target: target_phase_id.to_string(),
});
}
} else {
return Err(WorkflowError::UnknownPhase(
"No start phase defined".to_string(),
));
}
}
if !workflow.can_transition(&doc.current_phase, target_phase_id) {
return Err(WorkflowError::InvalidTransition {
current: doc.current_phase.clone(),
target: target_phase_id.to_string(),
});
}
doc.current_phase = target_phase_id.to_string();
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use molten_core::workflow::{Phase, PhaseType, Transition, WorkflowBuilder};
fn create_simple_workflow() -> WorkflowDefinition {
WorkflowBuilder::new("wf_ticket", "Ticket Workflow")
.add_phase(Phase::new("draft", "Draft", PhaseType::Start))
.add_phase(Phase::new("review", "Review", PhaseType::Normal))
.add_phase(Phase::new("closed", "Closed", PhaseType::End))
.add_transition(Transition::new("submit", "draft", "review"))
.add_transition(Transition::new("approve", "review", "closed"))
.add_transition(Transition::new("reject", "review", "draft"))
.build()
.unwrap()
}
#[test]
fn test_valid_transitions() {
let wf = create_simple_workflow();
let mut doc = Document::new("doc1", "form_ticket", "wf_ticket");
assert!(transition(&mut doc, &wf, "draft").is_ok());
assert_eq!(doc.current_phase, "draft");
assert!(transition(&mut doc, &wf, "review").is_ok());
assert_eq!(doc.current_phase, "review");
assert!(transition(&mut doc, &wf, "closed").is_ok());
assert_eq!(doc.current_phase, "closed");
}
#[test]
fn test_invalid_jump() {
let wf = create_simple_workflow();
let mut doc = Document::new("doc1", "doc_ticket", "wf_ticket");
let _ = transition(&mut doc, &wf, "draft");
let res = transition(&mut doc, &wf, "closed");
assert!(res.is_err());
assert!(matches!(
res.unwrap_err(),
WorkflowError::InvalidTransition { .. }
));
assert_eq!(doc.current_phase, "draft");
}
#[test]
fn test_workflow_mismatch() {
let wf = create_simple_workflow(); let mut doc = Document::new("doc1", "doc_ticket", "other_workflow_id");
let res = transition(&mut doc, &wf, "draft");
assert!(matches!(
res.unwrap_err(),
WorkflowError::WorkflowMismatch { .. }
));
}
}