rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
use super::{TaskId, TaskStatus};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskCheckKind {
    Readiness,
    Liveness,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskCheckStatus {
    Pass,
    Warn,
    Fail,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TaskHealthReport {
    pub kind: TaskCheckKind,
    pub status: TaskCheckStatus,
    pub summary: String,
    pub observed_tasks: Vec<TaskId>,
}

impl TaskHealthReport {
    #[must_use]
    pub fn is_passing(&self) -> bool {
        matches!(self.status, TaskCheckStatus::Pass)
    }
}

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TaskHealthSnapshot {
    pub backend_configured: bool,
    pub scheduler_metadata_loaded: bool,
    pub registered_tasks: usize,
    pub tracked_tasks: Vec<(TaskId, TaskStatus)>,
}

impl TaskHealthSnapshot {
    #[must_use]
    pub fn readiness(&self) -> TaskHealthReport {
        let status = if self.backend_configured && self.scheduler_metadata_loaded {
            TaskCheckStatus::Pass
        } else {
            TaskCheckStatus::Fail
        };

        let summary = match status {
            TaskCheckStatus::Pass => format!(
                "task subsystem is ready from local metadata: {} registered task(s); no worker or broker probe was performed",
                self.registered_tasks
            ),
            TaskCheckStatus::Fail if !self.backend_configured => {
                "task subsystem is not ready: backend configuration is missing; this stub only checks local metadata"
                    .to_string()
            }
            _ => {
                "task subsystem is not ready: scheduler metadata is unavailable; this stub does not inspect external infrastructure"
                    .to_string()
            }
        };

        TaskHealthReport {
            kind: TaskCheckKind::Readiness,
            status,
            summary,
            observed_tasks: self
                .tracked_tasks
                .iter()
                .map(|(task_id, _)| task_id.clone())
                .collect(),
        }
    }

    #[must_use]
    pub fn liveness(&self) -> TaskHealthReport {
        let active_tasks = self
            .tracked_tasks
            .iter()
            .filter(|(_, status)| {
                matches!(status, TaskStatus::Running | TaskStatus::Retrying { .. })
            })
            .map(|(task_id, _)| task_id.clone())
            .collect::<Vec<_>>();

        let failed_tasks = self
            .tracked_tasks
            .iter()
            .filter(|(_, status)| matches!(status, TaskStatus::Failed(_)))
            .count();

        let status = if self.backend_configured {
            TaskCheckStatus::Pass
        } else {
            TaskCheckStatus::Warn
        };

        let summary = if self.backend_configured {
            format!(
                "task subsystem is live from local metadata: {} active task(s), {} failed task(s); no worker heartbeat was checked",
                active_tasks.len(),
                failed_tasks
            )
        } else {
            format!(
                "task subsystem can still report metadata, but backend configuration is missing: {} active task(s), {} failed task(s)",
                active_tasks.len(),
                failed_tasks
            )
        };

        TaskHealthReport {
            kind: TaskCheckKind::Liveness,
            status,
            summary,
            observed_tasks: active_tasks,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{TaskCheckKind, TaskCheckStatus, TaskHealthSnapshot};
    use crate::tasks::TaskStatus;

    #[test]
    fn readiness_passes_when_backend_and_scheduler_metadata_exist() {
        let snapshot = TaskHealthSnapshot {
            backend_configured: true,
            scheduler_metadata_loaded: true,
            registered_tasks: 2,
            tracked_tasks: vec![],
        };

        let report = snapshot.readiness();

        assert_eq!(report.kind, TaskCheckKind::Readiness);
        assert_eq!(report.status, TaskCheckStatus::Pass);
        assert!(report.is_passing());
        assert!(
            report
                .summary
                .contains("no worker or broker probe was performed")
        );
    }

    #[test]
    fn readiness_fails_when_backend_configuration_is_missing() {
        let snapshot = TaskHealthSnapshot {
            backend_configured: false,
            scheduler_metadata_loaded: true,
            registered_tasks: 1,
            tracked_tasks: vec![("task-1".to_string(), TaskStatus::Pending)],
        };

        let report = snapshot.readiness();

        assert_eq!(report.status, TaskCheckStatus::Fail);
        assert_eq!(report.observed_tasks, vec!["task-1".to_string()]);
        assert!(report.summary.contains("backend configuration is missing"));
    }

    #[test]
    fn liveness_reports_only_active_task_ids() {
        let snapshot = TaskHealthSnapshot {
            backend_configured: true,
            scheduler_metadata_loaded: false,
            registered_tasks: 3,
            tracked_tasks: vec![
                ("task-1".to_string(), TaskStatus::Running),
                (
                    "task-2".to_string(),
                    TaskStatus::Retrying {
                        attempt: 2,
                        max_retries: 3,
                    },
                ),
                ("task-3".to_string(), TaskStatus::Failed("boom".to_string())),
                ("task-4".to_string(), TaskStatus::Completed),
            ],
        };

        let report = snapshot.liveness();

        assert_eq!(report.kind, TaskCheckKind::Liveness);
        assert_eq!(report.status, TaskCheckStatus::Pass);
        assert_eq!(
            report.observed_tasks,
            vec!["task-1".to_string(), "task-2".to_string()]
        );
        assert!(report.summary.contains("1 failed task(s)"));
    }

    #[test]
    fn liveness_warns_when_backend_is_not_configured() {
        let snapshot = TaskHealthSnapshot {
            backend_configured: false,
            scheduler_metadata_loaded: false,
            registered_tasks: 0,
            tracked_tasks: vec![],
        };

        let report = snapshot.liveness();

        assert_eq!(report.status, TaskCheckStatus::Warn);
        assert!(!report.is_passing());
        assert!(report.summary.contains("backend configuration is missing"));
    }
}