dag-executor 0.1.0

A production-ready DAG executor with state management and advanced patterns
Documentation
//! Unit tests for task state, records, and the validator.

use std::collections::HashMap;

use dag_executor::state::{StateValidator, TaskRecord, TaskState};

#[test]
fn legal_transitions_are_allowed() {
    assert!(TaskState::Pending.can_transition_to(TaskState::Ready));
    assert!(TaskState::Ready.can_transition_to(TaskState::Running));
    assert!(TaskState::Running.can_transition_to(TaskState::Completed));
    assert!(TaskState::Running.can_transition_to(TaskState::Failed));
    assert!(TaskState::Failed.can_transition_to(TaskState::DeadLettered));
}

#[test]
fn illegal_transitions_are_rejected() {
    assert!(!TaskState::Pending.can_transition_to(TaskState::Running));
    assert!(!TaskState::Completed.can_transition_to(TaskState::Running));
    assert!(!TaskState::Completed.can_transition_to(TaskState::Pending));
}

#[test]
fn terminal_and_success_classification() {
    assert!(TaskState::Completed.is_terminal());
    assert!(TaskState::Completed.is_success());
    assert!(TaskState::DeadLettered.is_failure());
    assert!(!TaskState::Running.is_terminal());
}

#[test]
fn record_transition_tracks_timestamps() {
    let mut r = TaskRecord::new("t");
    assert!(r.transition(TaskState::Ready));
    assert!(r.transition(TaskState::Running));
    assert!(r.started_at.is_some());
    assert!(r.transition(TaskState::Completed));
    assert!(r.finished_at.is_some());
    assert!(r.duration_millis().is_some());
}

#[test]
fn record_rejects_illegal_transition() {
    let mut r = TaskRecord::new("t");
    assert!(!r.transition(TaskState::Completed));
    assert_eq!(r.state, TaskState::Pending);
}

#[test]
fn validator_repairs_orphaned_running_tasks() {
    let mut records = HashMap::new();
    let mut running = TaskRecord::new("crashed");
    running.transition(TaskState::Ready);
    running.transition(TaskState::Running);
    running.attempts = 1;
    records.insert("crashed".to_string(), running);

    let report = StateValidator::new().repair(&mut records, 3);
    assert!(!report.is_clean());
    assert_eq!(report.reset_running, vec!["crashed".to_string()]);
    // With retry budget remaining it returns to Pending for rescheduling.
    assert_eq!(records["crashed"].state, TaskState::Pending);
}

#[test]
fn validator_fails_exhausted_running_tasks() {
    let mut records = HashMap::new();
    let mut running = TaskRecord::new("crashed");
    running.transition(TaskState::Ready);
    running.transition(TaskState::Running);
    running.attempts = 3;
    records.insert("crashed".to_string(), running);

    StateValidator::new().repair(&mut records, 3);
    assert_eq!(records["crashed"].state, TaskState::Failed);
}