api_testing_core/rest/
cleanup.rs1use 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.starts_with('/') {
24 anyhow::bail!("cleanup.pathTemplate must resolve to an absolute path (starts with /)");
25 }
26
27 Ok(path)
28}
29
30pub fn execute_cleanup(
31 cleanup: &RestCleanup,
32 base_url: &str,
33 bearer_token: Option<&str>,
34 main_response_body: &[u8],
35) -> Result<()> {
36 let cleanup_path = render_cleanup_path(cleanup, main_response_body)?;
37 let base = base_url.trim_end_matches('/');
38 let cleanup_url = format!("{base}{cleanup_path}");
39
40 let method = reqwest::Method::from_bytes(cleanup.method.as_bytes())
41 .with_context(|| format!("invalid cleanup HTTP method: {}", cleanup.method))?;
42
43 let client = reqwest::blocking::Client::new();
44 let mut builder = client.request(method, &cleanup_url);
45 if let Some(token) = bearer_token {
46 let value = format!("Bearer {token}");
47 builder = builder.header(reqwest::header::AUTHORIZATION, value);
48 }
49
50 let response = builder
51 .send()
52 .with_context(|| format!("cleanup request failed: {} {}", cleanup.method, cleanup_url))?;
53
54 let got = response.status().as_u16();
55 let expected = cleanup.expect_status;
56 if got != expected {
57 anyhow::bail!(
58 "cleanup failed: expected {expected} but got {got} ({} {cleanup_url})",
59 cleanup.method
60 );
61 }
62
63 Ok(())
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use pretty_assertions::assert_eq;
70
71 #[test]
72 fn rest_cleanup_renders_path_and_substitutes_vars() {
73 let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
74 "method": "GET",
75 "path": "/x",
76 "cleanup": {
77 "pathTemplate": "/files/{{key}}",
78 "vars": { "key": ".key" }
79 }
80 }))
81 .unwrap()
82 .cleanup
83 .unwrap();
84
85 let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
86 let path = render_cleanup_path(&cleanup, &body).unwrap();
87 assert_eq!(path, "/files/abc");
88 }
89
90 #[test]
91 fn rest_cleanup_var_null_is_error() {
92 let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
93 "method": "GET",
94 "path": "/x",
95 "cleanup": {
96 "pathTemplate": "/files/{{key}}",
97 "vars": { "key": ".missing" }
98 }
99 }))
100 .unwrap()
101 .cleanup
102 .unwrap();
103
104 let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
105 let err = render_cleanup_path(&cleanup, &body).unwrap_err();
106 assert!(err.to_string().contains("cleanup var 'key' is empty"));
107 }
108
109 #[test]
110 fn rest_cleanup_requires_absolute_path() {
111 let cleanup = crate::rest::schema::parse_rest_request_json(serde_json::json!({
112 "method": "GET",
113 "path": "/x",
114 "cleanup": {
115 "pathTemplate": "{{key}}",
116 "vars": { "key": ".key" }
117 }
118 }))
119 .unwrap()
120 .cleanup
121 .unwrap();
122
123 let body = serde_json::to_vec(&serde_json::json!({"key": "abc"})).unwrap();
124 let err = render_cleanup_path(&cleanup, &body).unwrap_err();
125 assert!(err.to_string().contains("absolute path"));
126 }
127}