Skip to content

Commit 1d625cd

Browse files
committed
introduce not_header and not_header_value
1 parent f342639 commit 1d625cd

File tree

2 files changed

+155
-41
lines changed

2 files changed

+155
-41
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 0.5.2-dev
44
- match "http://example.com/example.css", "/path/to/example.css", and "path/to/example.css" formatted paths for all types of static assets
5-
- introduce `not_status()`, `not_title()`, `not_text()`, `not_texts()`
5+
- introduce `not_status()`, `not_title()`, `not_text()`, `not_texts()`, `not_header()`, and `not_header_value()`
66

77
## 0.5.1 January 28, 2023
88
- in `drupal::log_in`

src/lib.rs

+154-40
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub struct Validate<'a> {
3232
/// Optionally validate arbitrary texts in the response html.
3333
texts: Vec<(bool, &'a str)>,
3434
/// Optionally validate the response headers.
35-
headers: Vec<(&'a str, &'a str)>,
35+
headers: Vec<(bool, &'a str, &'a str)>,
3636
/// Optionally validate whether or not the page redirects
3737
redirect: Option<bool>,
3838
}
@@ -103,7 +103,7 @@ pub struct ValidateBuilder<'a> {
103103
/// Optionally validate arbitrary texts in the response html.
104104
texts: Vec<(bool, &'a str)>,
105105
/// Optionally validate the response headers.
106-
headers: Vec<(&'a str, &'a str)>,
106+
headers: Vec<(bool, &'a str, &'a str)>,
107107
/// Optionally validate whether or not the page redirects
108108
redirect: Option<bool>,
109109
}
@@ -344,8 +344,9 @@ impl<'a> ValidateBuilder<'a> {
344344
/// .build();
345345
/// ```
346346
///
347-
/// It's possible to call this function multiple times to validate that multiple
348-
/// headers are set.
347+
/// It's possible to call this function multiple times, and/or together with
348+
/// [`ValidateBuilder::not_header`], [`ValidateBuilder::header_value`] and
349+
/// [`ValidateBuilder::not_header_value`].
349350
///
350351
/// # Multiple Example
351352
/// ```rust
@@ -357,7 +358,42 @@ impl<'a> ValidateBuilder<'a> {
357358
/// .build();
358359
/// ```
359360
pub fn header(mut self, header: impl Into<&'a str>) -> Self {
360-
self.headers.push((header.into(), ""));
361+
self.headers.push((false, header.into(), ""));
362+
self
363+
}
364+
365+
/// Create a [`Validate`] object to validate that the response does not include the
366+
/// specified header.
367+
///
368+
/// To validate that a header does not contain a specific value (instead of just validating
369+
/// that it does not exist), use [`ValidateBuilder::not_header_value`].
370+
///
371+
/// This structure is passed to [`validate_page`] or [`validate_and_load_static_assets`].
372+
///
373+
/// # Example
374+
/// ```rust
375+
/// use goose_eggs::Validate;
376+
///
377+
/// let _validate = Validate::builder()
378+
/// .not_header("x-cache")
379+
/// .build();
380+
/// ```
381+
///
382+
/// It's possible to call this function multiple times, and/or together with
383+
/// [`ValidateBuilder::header`], [`ValidateBuilder::header_value`] and
384+
/// [`ValidateBuilder::not_header_value`].
385+
///
386+
/// # Multiple Example
387+
/// ```rust
388+
/// use goose_eggs::Validate;
389+
///
390+
/// let _validate = Validate::builder()
391+
/// .not_header("x-cache")
392+
/// .header("x-generator")
393+
/// .build();
394+
/// ```
395+
pub fn not_header(mut self, header: impl Into<&'a str>) -> Self {
396+
self.headers.push((true, header.into(), ""));
361397
self
362398
}
363399

@@ -379,8 +415,8 @@ impl<'a> ValidateBuilder<'a> {
379415
/// ```
380416
///
381417
/// It's possible to call this function multiple times, and/or together with
382-
/// [`ValidateBuilder::header`] to validate that multiple headers are set and their
383-
/// values.
418+
/// [`ValidateBuilder::header`], [`ValidateBuilder::not_header`] and
419+
/// [`ValidateBuilder::not_header_value`].
384420
///
385421
/// # Multiple Example
386422
/// ```rust
@@ -396,7 +432,50 @@ impl<'a> ValidateBuilder<'a> {
396432
/// .build();
397433
/// ```
398434
pub fn header_value(mut self, header: impl Into<&'a str>, value: impl Into<&'a str>) -> Self {
399-
self.headers.push((header.into(), value.into()));
435+
self.headers.push((false, header.into(), value.into()));
436+
self
437+
}
438+
439+
/// Create a [`Validate`] object to validate that given header does not contain the specified
440+
/// value.
441+
///
442+
/// To validate that a header simply doesn't exist without confirming that it doesn't contain
443+
/// a specific value, use [`ValidateBuilder::not_header`].
444+
///
445+
/// This structure is passed to [`validate_page`] or [`validate_and_load_static_assets`].
446+
///
447+
/// # Example
448+
/// ```rust
449+
/// use goose_eggs::Validate;
450+
///
451+
/// let _validate = Validate::builder()
452+
/// .not_header_value("x-generator", "Drupal 7")
453+
/// .build();
454+
/// ```
455+
///
456+
/// It's possible to call this function multiple times, and/or together with
457+
/// [`ValidateBuilder::header_value`], [`ValidateBuilder::not_header`] and
458+
/// [`ValidateBuilder::header`].
459+
///
460+
/// # Multiple Example
461+
/// ```rust
462+
/// use goose_eggs::Validate;
463+
///
464+
/// let _validate = Validate::builder()
465+
/// // Validate that the "x-cache" header is set.
466+
/// .header("x-cache")
467+
/// // Validate that the "x-generator" header if set does not contain "Drupal 7".
468+
/// .not_header_value("x-generator", "Drupal-7")
469+
/// // Validate that the "x-drupal-cache" header is set to "HIT".
470+
/// .header_value("x-drupal-cache", "HIT")
471+
/// .build();
472+
/// ```
473+
pub fn not_header_value(
474+
mut self,
475+
header: impl Into<&'a str>,
476+
value: impl Into<&'a str>,
477+
) -> Self {
478+
self.headers.push((true, header.into(), value.into()));
400479
self
401480
}
402481

@@ -1039,38 +1118,73 @@ pub async fn validate_page<'a>(
10391118

10401119
// Validate headers if defined.
10411120
let headers = &response.headers().clone();
1042-
for header in &validate.headers {
1043-
if !header_is_set(headers, header.0) {
1044-
// Get as much as we can from the response for useful debug logging.
1045-
let html = response.text().await.unwrap_or_else(|_| "".to_string());
1046-
user.set_failure(
1047-
&format!(
1048-
"{}: header not included in response: {:?}",
1049-
goose.request.raw.url, header
1050-
),
1051-
&mut goose.request,
1052-
Some(headers),
1053-
Some(&html),
1054-
)?;
1055-
// Exit as soon as validation fails, to avoid cascades of
1056-
// errors when a page fails to load.
1057-
return Ok(html);
1058-
}
1059-
if !header.1.is_empty() && !valid_header_value(headers, *header) {
1060-
// Get as much as we can from the response for useful debug logging.
1061-
let html = response.text().await.unwrap_or_else(|_| "".to_string());
1062-
user.set_failure(
1063-
&format!(
1064-
"{}: header does not contain expected value: {:?}",
1065-
goose.request.raw.url, header.1
1066-
),
1067-
&mut goose.request,
1068-
Some(headers),
1069-
Some(&html),
1070-
)?;
1071-
// Exit as soon as validation fails, to avoid cascades of
1072-
// errors when a page fails to load.
1073-
return Ok(html);
1121+
for (inverse, header, value) in &validate.headers {
1122+
if *inverse {
1123+
if header_is_set(headers, header) {
1124+
// Get as much as we can from the response for useful debug logging.
1125+
let html = response.text().await.unwrap_or_else(|_| "".to_string());
1126+
user.set_failure(
1127+
&format!(
1128+
"{}: header included in response: {:?}",
1129+
goose.request.raw.url, header
1130+
),
1131+
&mut goose.request,
1132+
Some(headers),
1133+
Some(&html),
1134+
)?;
1135+
// Exit as soon as validation fails, to avoid cascades of
1136+
// errors when a page fails to load.
1137+
return Ok(html);
1138+
}
1139+
if !value.is_empty() && valid_header_value(headers, (*header, *value)) {
1140+
// Get as much as we can from the response for useful debug logging.
1141+
let html = response.text().await.unwrap_or_else(|_| "".to_string());
1142+
user.set_failure(
1143+
&format!(
1144+
"{}: header contains unexpected value: {:?}",
1145+
goose.request.raw.url, value
1146+
),
1147+
&mut goose.request,
1148+
Some(headers),
1149+
Some(&html),
1150+
)?;
1151+
// Exit as soon as validation fails, to avoid cascades of
1152+
// errors when a page fails to load.
1153+
return Ok(html);
1154+
}
1155+
} else {
1156+
if !header_is_set(headers, header) {
1157+
// Get as much as we can from the response for useful debug logging.
1158+
let html = response.text().await.unwrap_or_else(|_| "".to_string());
1159+
user.set_failure(
1160+
&format!(
1161+
"{}: header not included in response: {:?}",
1162+
goose.request.raw.url, header
1163+
),
1164+
&mut goose.request,
1165+
Some(headers),
1166+
Some(&html),
1167+
)?;
1168+
// Exit as soon as validation fails, to avoid cascades of
1169+
// errors when a page fails to load.
1170+
return Ok(html);
1171+
}
1172+
if !value.is_empty() && !valid_header_value(headers, (*header, *value)) {
1173+
// Get as much as we can from the response for useful debug logging.
1174+
let html = response.text().await.unwrap_or_else(|_| "".to_string());
1175+
user.set_failure(
1176+
&format!(
1177+
"{}: header does not contain expected value: {:?}",
1178+
goose.request.raw.url, value
1179+
),
1180+
&mut goose.request,
1181+
Some(headers),
1182+
Some(&html),
1183+
)?;
1184+
// Exit as soon as validation fails, to avoid cascades of
1185+
// errors when a page fails to load.
1186+
return Ok(html);
1187+
}
10741188
}
10751189
}
10761190

0 commit comments

Comments
 (0)