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.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}