Skip to main content

lmn_core/execution/
error.rs

1use tokio::task::JoinError;
2
3/// Errors that can occur during a load test run.
4#[derive(Debug)]
5pub enum RunError {
6    /// Failed to build the HTTP client for this run.
7    HttpClientBuild(reqwest::Error),
8    /// The internal drain task terminated unexpectedly.
9    DrainTaskFailed(JoinError),
10}
11
12impl std::fmt::Display for RunError {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        match self {
15            Self::HttpClientBuild(e) => write!(f, "failed to build HTTP client: {e}"),
16            Self::DrainTaskFailed(e) => write!(f, "drain task failed unexpectedly: {e}"),
17        }
18    }
19}
20
21impl std::error::Error for RunError {
22    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
23        match self {
24            Self::HttpClientBuild(e) => Some(e),
25            Self::DrainTaskFailed(e) => Some(e),
26        }
27    }
28}
29
30impl From<reqwest::Error> for RunError {
31    fn from(e: reqwest::Error) -> Self {
32        Self::HttpClientBuild(e)
33    }
34}
35
36impl From<JoinError> for RunError {
37    fn from(e: JoinError) -> Self {
38        Self::DrainTaskFailed(e)
39    }
40}
41
42// ── Tests ─────────────────────────────────────────────────────────────────────
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    fn make_reqwest_error() -> reqwest::Error {
49        // reqwest::get on an unparseable URL fails immediately without a network
50        // call, giving us a real reqwest::Error without async overhead.
51        tokio::runtime::Runtime::new()
52            .unwrap()
53            .block_on(reqwest::get("not-a-valid-url"))
54            .unwrap_err()
55    }
56
57    fn make_join_error() -> JoinError {
58        tokio::runtime::Runtime::new().unwrap().block_on(async {
59            tokio::spawn(async { panic!("test panic") })
60                .await
61                .unwrap_err()
62        })
63    }
64
65    #[test]
66    fn display_http_client_build() {
67        let run_err = RunError::HttpClientBuild(make_reqwest_error());
68        assert!(
69            run_err
70                .to_string()
71                .starts_with("failed to build HTTP client:"),
72            "unexpected: {run_err}"
73        );
74    }
75
76    #[test]
77    fn display_drain_task_failed() {
78        let run_err = RunError::DrainTaskFailed(make_join_error());
79        assert!(
80            run_err
81                .to_string()
82                .starts_with("drain task failed unexpectedly:"),
83            "unexpected: {run_err}"
84        );
85    }
86
87    #[test]
88    fn source_is_set() {
89        let run_err = RunError::HttpClientBuild(make_reqwest_error());
90        assert!(
91            std::error::Error::source(&run_err).is_some(),
92            "source must be set"
93        );
94    }
95
96    #[test]
97    fn from_reqwest_error_produces_http_client_build_variant() {
98        let run_err = RunError::from(make_reqwest_error());
99        assert!(matches!(run_err, RunError::HttpClientBuild(_)));
100    }
101
102    #[test]
103    fn from_join_error_produces_drain_task_failed_variant() {
104        let run_err = RunError::from(make_join_error());
105        assert!(matches!(run_err, RunError::DrainTaskFailed(_)));
106    }
107}