Skip to main content

api_testing_core/rest/
cleanup.rs

1use anyhow::Context;
2
3use crate::Result;
4use crate::rest::schema::RestCleanup;
5
6pub fn render_cleanup_path(cleanup: &RestCleanup, main_response_body: &[u8]) -> Result<String> {
7    let response_json: Option<serde_json::Value> = serde_json::from_slice(main_response_body).ok();
8
9    let mut path = cleanup.path_template.clone();
10    for (key, expr) in &cleanup.vars {
11        let lines = response_json
12            .as_ref()
13            .and_then(|json| crate::jq::query_raw(json, expr).ok())
14            .unwrap_or_default();
15        let value = lines.first().cloned().unwrap_or_default();
16        let value = value.trim().to_string();
17        if value.is_empty() || value == "null" {
18            anyhow::bail!("cleanup var '{key}' is empty");
19        }
20        path = path.replace(&format!("{{{{{key}}}}}"), &value);
21    }
22
23    if path.contains("{{") || path.contains("}}") {
24        anyhow::bail!("cleanup.pathTemplate has unresolved placeholders: {path}");
25    }
26
27    if !path.starts_with('/') {
28        anyhow::bail!("cleanup.pathTemplate must resolve to an absolute path (starts with /)");
29    }
30
31    Ok(path)
32}
33
34pub fn execute_cleanup(
35    cleanup: &RestCleanup,
36    base_url: &str,
37    bearer_token: Option<&str>,
38    main_response_body: &[u8],
39) -> Result<()> {
40    let cleanup_path = render_cleanup_path(cleanup, main_response_body)?;
41    let base = base_url.trim_end_matches('/');
42    let cleanup_url = format!("{base}{cleanup_path}");
43
44    let method = reqwest::Method::from_bytes(cleanup.method.as_bytes())
45        .with_context(|| format!("invalid cleanup HTTP method: {}", cleanup.method))?;
46
47    let client = reqwest::blocking::Client::new();
48    let mut builder = client.request(method, &cleanup_url);
49    if let Some(token) = bearer_token {
50        let value = format!("Bearer {token}");
51        builder = builder.header(reqwest::header::AUTHORIZATION, value);
52    }
53
54    let response = builder
55        .send()
56        .with_context(|| format!("cleanup request failed: {} {}", cleanup.method, cleanup_url))?;
57
58    let got = response.status().as_u16();
59    let expected = cleanup.expect_status;
60    if got != expected {
61        anyhow::bail!(
62            "cleanup failed: expected {expected} but got {got} ({} {cleanup_url})",
63            cleanup.method
64        );
65    }
66
67    Ok(())
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use pretty_assertions::assert_eq;
74
75    #[test]
76    fn rest_cleanup_renders_path_and_substitutes_vars() {
77        let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
78            "method": "GET",
79            "path": "/x",
80            "cleanup": {
81                "pathTemplate": "/files/{{key}}",
82                "vars": { "key": ".key" }
83            }
84        }))
85        .unwrap()
86        .cleanup
87        .unwrap();
88
89        let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
90        let path = render_cleanup_path(&cleanup, &body).unwrap();
91        assert_eq!(path, "/files/abc");
92    }
93
94    #[test]
95    fn rest_cleanup_var_null_is_error() {
96        let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
97            "method": "GET",
98            "path": "/x",
99            "cleanup": {
100                "pathTemplate": "/files/{{key}}",
101                "vars": { "key": ".missing" }
102            }
103        }))
104        .unwrap()
105        .cleanup
106        .unwrap();
107
108        let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
109        let err = render_cleanup_path(&cleanup, &body).unwrap_err();
110        assert!(err.to_string().contains("cleanup var 'key' is empty"));
111    }
112
113    #[test]
114    fn rest_cleanup_requires_absolute_path() {
115        let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
116            "method": "GET",
117            "path": "/x",
118            "cleanup": {
119                "pathTemplate": "{{key}}",
120                "vars": { "key": ".key" }
121            }
122        }))
123        .unwrap()
124        .cleanup
125        .unwrap();
126
127        let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
128        let err = render_cleanup_path(&cleanup, &body).unwrap_err();
129        assert!(err.to_string().contains("absolute path"));
130    }
131
132    #[test]
133    fn rest_cleanup_unresolved_placeholder_is_error() {
134        let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
135            "method": "GET",
136            "path": "/x",
137            "cleanup": {
138                "pathTemplate": "/files/{{key}}/{{missing}}",
139                "vars": { "key": ".key" }
140            }
141        }))
142        .unwrap()
143        .cleanup
144        .unwrap();
145
146        let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
147        let err = render_cleanup_path(&cleanup, &body).unwrap_err();
148        assert!(err.to_string().contains("unresolved placeholders"));
149    }
150}