api_testing_core/rest/
expect.rs1use 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 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}