wiremock_multipart/matchers/
contains_part.rs1use std::borrow::Cow;
2
3use wiremock::{Match, Request};
4
5use crate::request_utils::RequestUtils;
6
7#[derive(Default, Debug, PartialEq, Eq)]
32pub struct ContainsPart<'a, 'b, 'c, 'd> {
33 pub name: Option<Cow<'a, str>>,
34 pub filename: Option<Cow<'b, str>>,
35 pub content_type: Option<Cow<'c, str>>,
36 pub body: Option<Cow<'d, [u8]>>,
37}
38
39impl<'a, 'b, 'c, 'd> ContainsPart<'a, 'b, 'c, 'd> {
40 pub fn new() -> Self { Self::default() }
41
42 pub fn with_name<T: Into<Cow<'a, str>>>(self, name: T) -> Self {
43 ContainsPart {
44 name: Some(name.into()),
45 ..self
46 }
47 }
48
49 pub fn with_filename<T: Into<Cow<'b, str>>>(self, filename: T) -> Self {
50 ContainsPart {
51 filename: Some(filename.into()),
52 ..self
53 }
54 }
55
56 pub fn with_content_type<T: Into<Cow<'c, str>>>(self, content_type: T) -> Self {
57 ContainsPart {
58 content_type: Some(content_type.into()),
59 ..self
60 }
61 }
62
63 pub fn with_body<T: Into<Cow<'d, [u8]>>>(self, body: T) -> Self {
64 ContainsPart {
65 body: Some(body.into()),
66 ..self
67 }
68 }
69}
70
71impl<'a, 'b, 'c, 'd> Match for ContainsPart<'a, 'b, 'c, 'd> {
72 fn matches(&self, request: &Request) -> bool {
73 request.parts().iter()
74 .any(|part| {
75 let name = self.name.as_ref()
76 .map(|required_name| {
77 part.name()
78 .map(|part_name| required_name == part_name)
79 .unwrap_or(false)
80 })
81 .unwrap_or(true);
82
83 let filename = self.filename.as_ref()
84 .map(|required_filename| {
85 part.filename()
86 .map(|part_filename| required_filename == part_filename)
87 .unwrap_or(false)
88 })
89 .unwrap_or(true);
90
91 let content_type = self.content_type.as_ref()
92 .map(|required_content_type| {
93 part.content_type()
94 .map(|part_content_type| required_content_type == part_content_type)
95 .unwrap_or(false)
96 })
97 .unwrap_or(true);
98
99 let body = self.body.as_ref()
100 .map(|required_body| {
101 part.body()
102 .map(|part_body| required_body.as_ref() == part_body)
103 .unwrap_or(false)
104 })
105 .unwrap_or(true);
106
107 name && filename && content_type && body
108 })
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use indoc::indoc;
115 use maplit::hashmap;
116
117 use crate::test_utils::{multipart_header, name, requestb, values};
118
119 use super::*;
120
121 #[test]
122 fn default_should_be_all_none() {
123 assert_eq!(
124 ContainsPart::default(),
125 ContainsPart { name: None, filename: None, content_type: None, body: None }
126 );
127 }
128
129 #[test]
130 fn new_should_be_default() {
131 assert_eq!(
132 ContainsPart::default(),
133 ContainsPart::new(),
134 );
135 }
136
137 #[test]
138 fn should_add_name() {
139 assert_eq!(
140 ContainsPart::new().with_name("name"),
141 ContainsPart {
142 name: Some(Cow::Borrowed("name")),
143 ..Default::default()
144 }
145 );
146 }
147
148 #[test]
149 fn should_add_filename() {
150 assert_eq!(
151 ContainsPart::new().with_filename("filename"),
152 ContainsPart {
153 filename: Some(Cow::Borrowed("filename")),
154 ..Default::default()
155 }
156 );
157 }
158
159 #[test]
160 fn should_add_content_type() {
161 assert_eq!(
162 ContainsPart::new().with_content_type("application/json"),
163 ContainsPart {
164 content_type: Some(Cow::Borrowed("application/json")),
165 ..Default::default()
166 }
167 );
168 }
169
170 #[test]
171 fn should_add_body() {
172 assert_eq!(
173 ContainsPart::new().with_body("the body".as_bytes()),
174 ContainsPart {
175 body: Some(Cow::Borrowed("the body".as_bytes())),
176 ..Default::default()
177 }
178 );
179 }
180
181 #[test]
182 fn empty_should_match_any() {
183 assert_eq!(
184 ContainsPart::new().matches(
185 &requestb(
186 multipart_header(),
187 indoc!{r#"
188 --xyz
189 Content-Disposition: form-data; name="part"
190
191 content
192 --xyz--
193 "#}.as_bytes().into()
194 ),
195 ),
196 true
197 );
198 }
199
200 #[test]
201 fn empty_should_not_match_request_without_parts() {
202 assert_eq!(
203 ContainsPart::new().matches(
204 &requestb(
205 hashmap!{
206 name("content-type") => values("text/plain"),
207 },
208 "not a multipart request".as_bytes().into(),
209 ),
210 ),
211 false
212 );
213 }
214
215 #[test]
216 fn should_match_on_name() {
217 assert_eq!(
218 ContainsPart::new().with_name("part-a").matches(
219 &requestb(
220 multipart_header(),
221 indoc!{r#"
222 --xyz
223 Content-Disposition: form-data; name="not-the-part"
224
225 content
226 --xyz--
227 "#}.as_bytes().into()
228 ),
229 ),
230 false
231 );
232
233 assert_eq!(
234 ContainsPart::new().with_name("part-a").matches(
235 &requestb(
236 multipart_header(),
237 indoc!{r#"
238 --xyz
239 Content-Disposition: form-data; name="part-a"
240
241 content
242 --xyz--
243 "#}.as_bytes().into()
244 ),
245 ),
246 true
247 );
248 }
249
250 #[test]
251 fn should_match_on_filename() {
252 assert_eq!(
253 ContainsPart::new().with_filename("file-a").matches(
254 &requestb(
255 multipart_header(),
256 indoc!{r#"
257 --xyz
258 Content-Disposition: form-data; name="not-the-part"; filename="not-the-file"
259
260 content
261 --xyz--
262 "#}.as_bytes().into()
263 ),
264 ),
265 false
266 );
267
268 assert_eq!(
269 ContainsPart::new().with_filename("file-a").matches(
270 &requestb(
271 multipart_header(),
272 indoc!{r#"
273 --xyz
274 Content-Disposition: form-data; name="part-a"; filename="file-a"
275
276 content
277 --xyz--
278 "#}.as_bytes().into()
279 ),
280 ),
281 true
282 );
283 }
284
285 #[test]
286 fn should_match_on_content_type() {
287 assert_eq!(
288 ContainsPart::new().with_content_type("application/json").matches(
289 &requestb(
290 multipart_header(),
291 indoc!{r#"
292 --xyz
293 Content-Disposition: form-data; name="not-the-part" filename="not-the-file"
294 Content-Type: application/xml
295
296 content
297 --xyz--
298 "#}.as_bytes().into()
299 ),
300 ),
301 false
302 );
303
304 assert_eq!(
305 ContainsPart::new().with_content_type("application/json").matches(
306 &requestb(
307 multipart_header(),
308 indoc!{r#"
309 --xyz
310 Content-Disposition: form-data; name="part-a" filename="file-a"
311 Content-Type: application/json
312
313 content
314 --xyz--
315 "#}.as_bytes().into()
316 ),
317 ),
318 true
319 );
320 }
321
322 #[test]
323 fn should_match_on_body() {
324 assert_eq!(
325 ContainsPart::new().with_body("content".as_bytes()).matches(
326 &requestb(
327 multipart_header(),
328 indoc!{r#"
329 --xyz
330 Content-Disposition: form-data; name="not-the-part" filename="not-the-file"
331 Content-Type: application/xml
332
333 not the right content
334 --xyz--
335 "#}.as_bytes().into()
336 ),
337 ),
338 false
339 );
340
341 assert_eq!(
342 ContainsPart::new().with_body("content".as_bytes()).matches(
343 &requestb(
344 multipart_header(),
345 indoc!{r#"
346 --xyz
347 Content-Disposition: form-data; name="part-a" filename="file-a"
348 Content-Type: application/json
349
350 content
351 --xyz--
352 "#}.as_bytes().into()
353 ),
354 ),
355 true
356 );
357 }
358
359}