Skip to main content

api_testing_core/rest/
expect.rs

1use anyhow::Context;
2
3use crate::Result;
4use crate::rest::runner::RestExecutedRequest;
5use crate::rest::schema::RestRequest;
6
7pub fn evaluate_main_response(request: &RestRequest, executed: &RestExecutedRequest) -> Result<()> {
8    let status = executed.response.status;
9
10    if let Some(expect) = &request.expect {
11        if status != expect.status {
12            anyhow::bail!("Expected HTTP status {} but got {}.", expect.status, status);
13        }
14
15        if let Some(expr) = &expect.jq {
16            let body_json: serde_json::Value = serde_json::from_slice(&executed.response.body)
17                .context("expect.jq requires a JSON response body")?;
18            if !crate::jq::eval_exit_status(&body_json, expr).unwrap_or(false) {
19                anyhow::bail!("expect.jq failed: {expr}");
20            }
21        }
22
23        return Ok(());
24    }
25
26    if !(200..300).contains(&status) {
27        anyhow::bail!(
28            "HTTP request failed with status {status}: {} {}",
29            executed.method,
30            executed.url
31        );
32    }
33
34    Ok(())
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    fn executed_with(status: u16, body: serde_json::Value) -> RestExecutedRequest {
42        RestExecutedRequest {
43            method: "GET".to_string(),
44            url: "http://localhost:6700/health".to_string(),
45            response: crate::http::HttpResponse {
46                status,
47                body: serde_json::to_vec(&body).unwrap(),
48                content_type: Some("application/json".to_string()),
49            },
50        }
51    }
52
53    fn executed_with_raw_body(status: u16, body: &[u8], content_type: &str) -> RestExecutedRequest {
54        RestExecutedRequest {
55            method: "GET".to_string(),
56            url: "http://localhost:6700/health".to_string(),
57            response: crate::http::HttpResponse {
58                status,
59                body: body.to_vec(),
60                content_type: Some(content_type.to_string()),
61            },
62        }
63    }
64
65    #[test]
66    fn rest_expect_status_mismatch_fails() {
67        let request = crate::rest::schema::parse_rest_request_json(serde_json::json!({
68            "method": "GET",
69            "path": "/health",
70            "expect": { "status": 200 }
71        }))
72        .unwrap();
73        let executed = executed_with(500, serde_json::json!({"ok": false}));
74        let err = evaluate_main_response(&request, &executed).unwrap_err();
75        assert!(
76            err.to_string()
77                .contains("Expected HTTP status 200 but got 500")
78        );
79    }
80
81    #[test]
82    fn rest_expect_jq_false_fails() {
83        let request = crate::rest::schema::parse_rest_request_json(serde_json::json!({
84            "method": "GET",
85            "path": "/health",
86            "expect": { "status": 200, "jq": ".ok == true" }
87        }))
88        .unwrap();
89        let executed = executed_with(200, serde_json::json!({"ok": false}));
90        let err = evaluate_main_response(&request, &executed).unwrap_err();
91        assert!(err.to_string().contains("expect.jq failed"));
92    }
93
94    #[test]
95    fn rest_expect_jq_non_json_body_reports_parse_error() {
96        let request = crate::rest::schema::parse_rest_request_json(serde_json::json!({
97            "method": "GET",
98            "path": "/health",
99            "expect": { "status": 200, "jq": ".ok == true" }
100        }))
101        .unwrap();
102        // A non-JSON response body (e.g. an HTML error page) is a system error,
103        // not an assertion failure. It must not be reported as `expect.jq failed`.
104        let executed = executed_with_raw_body(200, b"<html>not json</html>", "text/html");
105        let err = evaluate_main_response(&request, &executed).unwrap_err();
106        let message = format!("{err:#}");
107        assert!(
108            message.contains("expect.jq requires a JSON response body"),
109            "expected a JSON-body parse error, got: {message}"
110        );
111        assert!(
112            !message.contains("expect.jq failed"),
113            "a non-JSON body must not be conflated with a jq assertion failure: {message}"
114        );
115    }
116
117    #[test]
118    fn rest_expect_default_non_2xx_fails() {
119        let request = crate::rest::schema::parse_rest_request_json(serde_json::json!({
120            "method": "GET",
121            "path": "/health"
122        }))
123        .unwrap();
124        let executed = executed_with(404, serde_json::json!({"error": "no"}));
125        let err = evaluate_main_response(&request, &executed).unwrap_err();
126        assert!(
127            err.to_string()
128                .contains("HTTP request failed with status 404")
129        );
130    }
131}