@@ -32,7 +32,7 @@ pub struct Validate<'a> {
32
32
/// Optionally validate arbitrary texts in the response html.
33
33
texts : Vec < ( bool , & ' a str ) > ,
34
34
/// Optionally validate the response headers.
35
- headers : Vec < ( & ' a str , & ' a str ) > ,
35
+ headers : Vec < ( bool , & ' a str , & ' a str ) > ,
36
36
/// Optionally validate whether or not the page redirects
37
37
redirect : Option < bool > ,
38
38
}
@@ -103,7 +103,7 @@ pub struct ValidateBuilder<'a> {
103
103
/// Optionally validate arbitrary texts in the response html.
104
104
texts : Vec < ( bool , & ' a str ) > ,
105
105
/// Optionally validate the response headers.
106
- headers : Vec < ( & ' a str , & ' a str ) > ,
106
+ headers : Vec < ( bool , & ' a str , & ' a str ) > ,
107
107
/// Optionally validate whether or not the page redirects
108
108
redirect : Option < bool > ,
109
109
}
@@ -344,8 +344,9 @@ impl<'a> ValidateBuilder<'a> {
344
344
/// .build();
345
345
/// ```
346
346
///
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`].
349
350
///
350
351
/// # Multiple Example
351
352
/// ```rust
@@ -357,7 +358,42 @@ impl<'a> ValidateBuilder<'a> {
357
358
/// .build();
358
359
/// ```
359
360
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 ( ) , "" ) ) ;
361
397
self
362
398
}
363
399
@@ -379,8 +415,8 @@ impl<'a> ValidateBuilder<'a> {
379
415
/// ```
380
416
///
381
417
/// 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`] .
384
420
///
385
421
/// # Multiple Example
386
422
/// ```rust
@@ -396,7 +432,50 @@ impl<'a> ValidateBuilder<'a> {
396
432
/// .build();
397
433
/// ```
398
434
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 ( ) ) ) ;
400
479
self
401
480
}
402
481
@@ -1039,38 +1118,73 @@ pub async fn validate_page<'a>(
1039
1118
1040
1119
// Validate headers if defined.
1041
1120
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
+ }
1074
1188
}
1075
1189
}
1076
1190
0 commit comments