1use crate::executor::{DoctorCheck, DoctorReport, Executor, ExecutorCapabilities};
16use anyhow::Result;
17use chrono::Utc;
18use runner_protocol::{FailureInfo, FailureKind, JobResult, JobSpec, SandboxResult};
19use tokio_util::sync::CancellationToken;
20
21#[derive(Debug, Default)]
22pub struct MockExecutor;
23
24#[async_trait::async_trait]
25impl Executor for MockExecutor {
26 fn capabilities(&self) -> ExecutorCapabilities {
27 ExecutorCapabilities::new(["artifacts", "cancellation", "streaming_logs"])
28 }
29
30 async fn doctor(&self) -> Result<DoctorReport> {
31 Ok(DoctorReport {
32 executor: "mock".into(),
33 healthy: true,
34 capabilities: self.capabilities(),
35 checks: vec![DoctorCheck {
36 name: "mock".into(),
37 healthy: true,
38 message: "mock executor is ready".into(),
39 }],
40 })
41 }
42
43 async fn run(&self, spec: JobSpec, cancel: Option<CancellationToken>) -> Result<JobResult> {
44 let now = Utc::now().to_rfc3339();
45 let cancelled = cancel.is_some_and(|token| token.is_cancelled());
46 Ok(JobResult {
47 job_id: spec.id,
48 attempt: spec.attempt,
49 status: if cancelled { "cancelled" } else { "completed" }.into(),
50 exit_code: (!cancelled).then_some(0),
51 started_at: now.clone(),
52 finished_at: now,
53 duration_ms: 0,
54 log_truncated: false,
55 stdout: String::new(),
56 stderr: String::new(),
57 error_summary: None,
58 failure: cancelled.then(|| FailureInfo {
59 kind: FailureKind::Cancellation,
60 code: "cancelled".into(),
61 message: "execution cancelled".into(),
62 }),
63 failed_phase: None,
64 artifacts: Vec::new(),
65 artifact_dir: None,
66 sandbox: SandboxResult {
67 executor: "mock".into(),
68 container_id: String::new(),
69 image_id: None,
70 },
71 })
72 }
73
74 async fn cleanup(&self) -> Result<()> {
75 Ok(())
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::MockExecutor;
82 use crate::executor::Executor;
83
84 #[tokio::test]
85 async fn doctor_is_healthy() {
86 let report = MockExecutor.doctor().await.unwrap();
87
88 assert!(report.healthy);
89 assert!(report.capabilities.supports("cancellation"));
90 assert_eq!(report.checks.len(), 1);
91 }
92}