Skip to main content

runner_core/
mock.rs

1// Copyright 2026 Kotelnikovekb
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://apache.org
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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}