clawspec_core/client/response/
status.rs1use std::ops::{Range, RangeInclusive};
2
3#[derive(Debug, Clone)]
7pub struct ExpectedStatusCodes {
8 ranges: Vec<StatusCodeRange>,
9}
10
11#[derive(Debug, Clone)]
13enum StatusCodeRange {
14 Single(u16),
15 Inclusive(RangeInclusive<u16>),
16 Exclusive(Range<u16>),
17}
18
19impl ExpectedStatusCodes {
20 pub fn new() -> Self {
22 Self {
23 ranges: vec![StatusCodeRange::Exclusive(200..500)],
24 }
25 }
26
27 pub fn add_single(mut self, status: u16) -> Self {
29 self.ranges.push(StatusCodeRange::Single(status));
30 self
31 }
32
33 pub fn add_inclusive_range(mut self, range: RangeInclusive<u16>) -> Self {
35 self.ranges.push(StatusCodeRange::Inclusive(range));
36 self
37 }
38
39 pub fn add_exclusive_range(mut self, range: Range<u16>) -> Self {
41 self.ranges.push(StatusCodeRange::Exclusive(range));
42 self
43 }
44
45 pub fn from_inclusive_range(range: RangeInclusive<u16>) -> Self {
51 assert!(
52 *range.start() >= 100 && *range.start() <= 599,
53 "HTTP status code range start must be between 100 and 599, got {}",
54 range.start()
55 );
56 assert!(
57 *range.end() >= 100 && *range.end() <= 599,
58 "HTTP status code range end must be between 100 and 599, got {}",
59 range.end()
60 );
61 assert!(
62 range.start() <= range.end(),
63 "HTTP status code range start ({}) must be less than or equal to end ({})",
64 range.start(),
65 range.end()
66 );
67
68 Self {
69 ranges: vec![StatusCodeRange::Inclusive(range)],
70 }
71 }
72
73 pub fn from_exclusive_range(range: Range<u16>) -> Self {
79 assert!(
80 range.start >= 100 && range.start <= 599,
81 "HTTP status code range start must be between 100 and 599, got {}",
82 range.start
83 );
84 assert!(
85 range.end >= 100 && range.end <= 600, "HTTP status code range end must be between 100 and 600 (exclusive), got {}",
87 range.end
88 );
89 assert!(
90 range.start < range.end,
91 "HTTP status code range start ({}) must be less than end ({})",
92 range.start,
93 range.end
94 );
95
96 Self {
97 ranges: vec![StatusCodeRange::Exclusive(range)],
98 }
99 }
100
101 pub fn from_single(status: u16) -> Self {
107 assert!(
108 (100..=599).contains(&status),
109 "HTTP status code must be between 100 and 599, got {status}"
110 );
111
112 Self {
113 ranges: vec![StatusCodeRange::Single(status)],
114 }
115 }
116
117 pub fn from_status_code(status: http::StatusCode) -> Self {
133 Self {
135 ranges: vec![StatusCodeRange::Single(status.as_u16())],
136 }
137 }
138
139 pub fn from_status_code_range_inclusive(range: RangeInclusive<http::StatusCode>) -> Self {
159 let start = range.start().as_u16();
161 let end = range.end().as_u16();
162 Self {
163 ranges: vec![StatusCodeRange::Inclusive(start..=end)],
164 }
165 }
166
167 pub fn from_status_code_range_exclusive(range: Range<http::StatusCode>) -> Self {
187 let start = range.start.as_u16();
189 let end = range.end.as_u16();
190 Self {
191 ranges: vec![StatusCodeRange::Exclusive(start..end)],
192 }
193 }
194
195 pub fn contains(&self, status: u16) -> bool {
197 self.ranges.iter().any(|range| match range {
198 StatusCodeRange::Single(s) => *s == status,
199 StatusCodeRange::Inclusive(r) => r.contains(&status),
200 StatusCodeRange::Exclusive(r) => r.contains(&status),
201 })
202 }
203
204 pub fn contains_status_code(&self, status: http::StatusCode) -> bool {
219 self.contains(status.as_u16())
220 }
221
222 pub fn add_expected_status(mut self, status: u16) -> Self {
224 self.ranges.push(StatusCodeRange::Single(status));
225 self
226 }
227
228 pub fn add_expected_range(mut self, range: RangeInclusive<u16>) -> Self {
230 self.ranges.push(StatusCodeRange::Inclusive(range));
231 self
232 }
233}
234
235impl Default for ExpectedStatusCodes {
236 fn default() -> Self {
237 Self::new()
238 }
239}
240
241#[cfg(test)]
242mod status_code_tests {
243 use super::*;
244 use http::StatusCode;
245
246 #[test]
247 fn test_default_status_codes() {
248 let codes = ExpectedStatusCodes::default();
249 assert!(codes.contains(200));
250 assert!(codes.contains(299));
251 assert!(codes.contains(404));
252 assert!(codes.contains(499));
253 assert!(!codes.contains(500));
254 assert!(!codes.contains(199));
255 }
256
257 #[test]
258 fn test_single_status_code() {
259 let codes = ExpectedStatusCodes::from_single(200);
260 assert!(codes.contains(200));
261 assert!(!codes.contains(201));
262 assert!(!codes.contains(404));
263 }
264
265 #[test]
266 fn test_inclusive_range() {
267 let codes = ExpectedStatusCodes::from_inclusive_range(200..=204);
268 assert!(codes.contains(200));
269 assert!(codes.contains(202));
270 assert!(codes.contains(204));
271 assert!(!codes.contains(199));
272 assert!(!codes.contains(205));
273 }
274
275 #[test]
276 fn test_exclusive_range() {
277 let codes = ExpectedStatusCodes::from_exclusive_range(200..205);
278 assert!(codes.contains(200));
279 assert!(codes.contains(202));
280 assert!(codes.contains(204));
281 assert!(!codes.contains(199));
282 assert!(!codes.contains(205));
283 }
284
285 #[test]
286 fn test_multiple_ranges() {
287 let codes = ExpectedStatusCodes::default()
288 .add_single(201)
289 .add_inclusive_range(300..=304)
290 .add_exclusive_range(400..405);
291
292 assert!(codes.contains(200));
294 assert!(codes.contains(299));
295 assert!(codes.contains(404));
296 assert!(codes.contains(499));
297 assert!(!codes.contains(500));
298
299 assert!(codes.contains(201));
301
302 assert!(codes.contains(300));
304 assert!(codes.contains(304));
305
306 assert!(codes.contains(400));
308 assert!(codes.contains(404));
309 assert!(codes.contains(405)); }
311
312 #[test]
313 fn test_status_code_variants() {
314 let codes = ExpectedStatusCodes::from_status_code(StatusCode::OK);
315 assert!(codes.contains_status_code(StatusCode::OK));
316 assert!(!codes.contains_status_code(StatusCode::NOT_FOUND));
317
318 let range_codes = ExpectedStatusCodes::from_status_code_range_inclusive(
319 StatusCode::OK..=StatusCode::NO_CONTENT,
320 );
321 assert!(range_codes.contains_status_code(StatusCode::OK));
322 assert!(range_codes.contains_status_code(StatusCode::CREATED));
323 assert!(range_codes.contains_status_code(StatusCode::NO_CONTENT));
324 assert!(!range_codes.contains_status_code(StatusCode::PARTIAL_CONTENT));
325
326 let exclusive_codes = ExpectedStatusCodes::from_status_code_range_exclusive(
327 StatusCode::OK..StatusCode::PARTIAL_CONTENT,
328 );
329 assert!(exclusive_codes.contains_status_code(StatusCode::OK));
330 assert!(exclusive_codes.contains_status_code(StatusCode::NO_CONTENT));
331 assert!(!exclusive_codes.contains_status_code(StatusCode::PARTIAL_CONTENT));
332 }
333
334 #[test]
335 #[should_panic(expected = "HTTP status code must be between 100 and 599, got 99")]
336 fn test_invalid_single_status_code_low() {
337 ExpectedStatusCodes::from_single(99);
338 }
339
340 #[test]
341 #[should_panic(expected = "HTTP status code must be between 100 and 599, got 600")]
342 fn test_invalid_single_status_code_high() {
343 ExpectedStatusCodes::from_single(600);
344 }
345
346 #[test]
347 #[should_panic(expected = "HTTP status code range start must be between 100 and 599, got 99")]
348 fn test_invalid_range_start_low() {
349 ExpectedStatusCodes::from_inclusive_range(99..=200);
350 }
351
352 #[test]
353 #[should_panic(expected = "HTTP status code range end must be between 100 and 599, got 600")]
354 fn test_invalid_range_end_high() {
355 ExpectedStatusCodes::from_inclusive_range(200..=600);
356 }
357
358 #[test]
359 #[should_panic(
360 expected = "HTTP status code range start (300) must be less than or equal to end (200)"
361 )]
362 #[allow(clippy::reversed_empty_ranges)]
363 fn test_invalid_range_order() {
364 ExpectedStatusCodes::from_inclusive_range(300..=200);
365 }
366
367 #[test]
368 #[should_panic(expected = "HTTP status code range start must be between 100 and 599, got 99")]
369 fn test_invalid_exclusive_range_start() {
370 ExpectedStatusCodes::from_exclusive_range(99..200);
371 }
372
373 #[test]
374 #[should_panic(
375 expected = "HTTP status code range end must be between 100 and 600 (exclusive), got 601"
376 )]
377 fn test_invalid_exclusive_range_end() {
378 ExpectedStatusCodes::from_exclusive_range(200..601);
379 }
380
381 #[test]
382 fn test_add_invalid_status() {
383 let _codes = ExpectedStatusCodes::default().add_single(99);
385 }
386
387 #[test]
388 fn test_add_invalid_range() {
389 let _codes = ExpectedStatusCodes::default().add_inclusive_range(99..=600);
391 }
392}