Response Header Assertions
The [assert.headers] block allows you to validate metadata returned in response headers, ensuring correct caching, content typing, security configurations, and custom application metadata.
1. Syntax & Configuration
Rumour supports two equivalent TOML syntaxes for declaring response header assertions:
Option A: Dotted Subtable Syntax (Recommended for multi-constraint assertions)
Define a separate subtable for each target header:
[request]
method = "GET"
url = "{{base_url}}/response-headers?X-Custom-Header=HelloHeader"
[assert.headers."X-Custom-Header"]
equal = "HelloHeader"
contains = "loHea"
[assert.headers."content-type"]
contains = "application/json"
Option B: Inline Table Syntax (Sleeker for single-constraint assertions)
Group all header assertions under a single [assert.headers] block using inline TOML tables:
[request]
method = "GET"
url = "{{base_url}}/response-headers?X-Custom-Header=HelloHeader"
[assert.headers]
"X-Custom-Header" = { equal = "HelloHeader", contains = "loHea" }
"content-type" = { contains = "application/json" }
2. Operators Reference
Header assertions support the following evaluation operators:
| Operator | Type | Description |
|---|---|---|
equal | String | Asserts that the header value is exactly equal to the specified string. |
not_equal | String | Asserts that the header value does not match the specified string. |
contains | String | Asserts that the header value contains the specified substring. |
exists | Boolean | Checks if the header is present (true) or absent (false) in the response. |
regex | String | Asserts that the header value matches the specified regular expression. |
⚠️ Numeric Headers Gotcha (greater_than / less_than)
All header values are processed internally as string values. Therefore, numeric comparison operators like greater_than and less_than are not supported for header assertions (they will always fail to parse as float values and evaluate to false).
To validate numerical constraints (e.g., checking if Content-Length is within limits), use the regex operator:
# Assert that Content-Length is between 100 and 999 bytes
[assert.headers."content-length"]
regex = "^[1-9][0-9]{2}$"
3. Case Sensitivity
In accordance with the HTTP/1.1 and HTTP/2 specifications, header name lookups in Rumour are case-insensitive. Declaring [assert.headers."Content-Type"] or [assert.headers."content-type"] will target the exact same header node.
4. Header Assertion Example (httpbin.org)
A. Dotted Subtable Syntax Example
# test_operators.toml
[request]
method = "GET"
url = "{{base_url}}/response-headers?X-Custom-Header=HelloHeader"
[headers]
User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
[assert.headers."X-Custom-Header"]
equal = "HelloHeader"
not_equal = "WrongValue"
contains = "loHea"
regex = "^Hello.*$"
exists = true
[assert.headers."content-type"]
contains = "application/json"
[assert.headers."x-non-existent"]
exists = false
Successful Execution Output Trace
rumour run header_assert_example/users/requests/test_operators.toml -tv
~/workspace/rumour suiteImplementation 8s ❯ rumour run /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators.toml -tv
GET https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
Header: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
URL: https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
✓ SUCCESS: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators.toml (1391ms)
✓ /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators.toml → PASS (1 Pass, 0 Fail, 0 Skip)
╭──────────────────────────────────────────────────────────────────────────╮
│ RUMOUR EXECUTION REPORT │
├──────────────────────────────────────────────────────────────────────────┤
│ Total Requests: 1 │
│ Successful: 1 │
│ Failed: 0 │
│ Skipped: 0 │
│ Success Rate: 100.0% │
│ Total Time: 1394ms │
╰──────────────────────────────────────────────────────────────────────────╯
✓ Successful Requests:
- /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators.toml [200] [1394ms]
Actionable Recommendations:
→ Run with --json to export this report for your CI/CD pipeline.
Let's break down what just happened:
- The Live Action: Rumour checked all the response headers against our rules. It verified
X-Custom-Headerexisted and contained the right text, confirmedcontent-typewas correct, and made surex-non-existentwasn't sneaking in there. Since everything matched perfectly, we get a green✓ SUCCESS! - The Final Report: The summary logs the entire request, giving you the confidence that your server is returning the exact metadata your application expects.
B. Inline Table Syntax Example
# test_operators_inline.toml
[request]
method = "GET"
url = "{{base_url}}/response-headers?X-Custom-Header=HelloHeader"
[headers]
User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
[assert.headers]
"X-Custom-Header" = { equal = "HelloHeader", not_equal = "WrongValue", contains = "loHea", regex = "^Hello.*$", exists = true }
"content-type" = { contains = "application/json" }
"x-non-existent" = { exists = false }
Successful Execution Output Trace
rumour run header_assert_example/users/requests/test_operators_inline.toml -tv
~/workspace/rumour suiteImplementation ❯ rumour run /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators_inline.toml -tv
GET https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
Header: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
URL: https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
✓ SUCCESS: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators_inline.toml (5005ms)
✓ /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators_inline.toml → PASS (1 Pass, 0 Fail, 0 Skip)
╭──────────────────────────────────────────────────────────────────────────╮
│ RUMOUR EXECUTION REPORT │
├──────────────────────────────────────────────────────────────────────────┤
│ Total Requests: 1 │
│ Successful: 1 │
│ Failed: 0 │
│ Skipped: 0 │
│ Success Rate: 100.0% │
│ Total Time: 5008ms │
╰──────────────────────────────────────────────────────────────────────────╯
✓ Successful Requests:
- /home/bugsfounder/workspace/testing/header_assert_example/users/requests/test_operators_inline.toml [200] [5007ms]
Actionable Recommendations:
→ Run with --json to export this report for your CI/CD pipeline.
Let's break down what just happened:
Just like the dotted syntax example above, Rumour completely evaluated the inline assertions and generated an identical success report. The inline syntax is simply a cleaner way to write your .toml files—under the hood, Rumour handles them exactly the same way!
5. Failing Header Assertions & Diagnostics
If one or more header assertions fail, Rumour reports the specific failure details:
Failing Example TOML
[request]
method = "GET"
url = "{{base_url}}/response-headers?X-Custom-Header=HelloHeader"
[headers]
User-Agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
[assert.headers."X-Custom-Header"]
equal = "WrongValue"
[assert.headers."X-Required-Header"]
exists = true
Diagnostics output:
~/workspace/testing main* ❯ rumour run /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml -tv
GET https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
Header: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
URL: https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
✗ FAILED: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml (4366ms) - Header: X-Custom-Header: Expected String("WrongValue"), got String("HelloHeader") | Header: X-Required-Header: Existence check: expected true, got false
↻ RETRYING: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml (Attempt 1/2) [Wait 500ms]
GET https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
Header: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
URL: https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
✗ FAILED: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml (3436ms) - Header: X-Custom-Header: Expected String("WrongValue"), got String("HelloHeader") | Header: X-Required-Header: Existence check: expected true, got false
↻ RETRYING: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml (Attempt 2/2) [Wait 1000ms]
GET https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
Header: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
URL: https://httpbin.org/response-headers?X-Custom-Header=HelloHeader
✗ FAILED: /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml (1373ms) - Header: X-Custom-Header: Expected String("WrongValue"), got String("HelloHeader") | Header: X-Required-Header: Existence check: expected true, got false
✗ /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml → FAIL (0 Pass, 1 Fail, 0 Skip)
╭──────────────────────────────────────────────────────────────────────────╮
│ RUMOUR EXECUTION REPORT │
├──────────────────────────────────────────────────────────────────────────┤
│ Total Requests: 1 │
│ Successful: 0 │
│ Failed: 1 │
│ Skipped: 0 │
│ Success Rate: 0.0% │
│ Total Time: 10688ms │
╰──────────────────────────────────────────────────────────────────────────╯
✗ Failed Requests:
- /home/bugsfounder/workspace/testing/header_assert_example/users/requests/failing_example.toml: [200]
Reason: Header: X-Custom-Header: Expected String("WrongValue"), got String("HelloHeader") | Header: X-Required-Header: Existence check: expected true, got false
Message: {
"Content-Length": "106",
"Content-Type": "application/json",
"X-Custom-Header": "HelloHeader"
}
Actionable Recommendations:
→ Run with -H (Self-Healing) to attempt automatic recovery of failing nodes.
Let's break down what just happened:
When an assertion fails, Rumour's execution engine provides an immense amount of context to help you debug the problem immediately!
- The Big Picture Mismatch: Notice how the
Reason:field reports both failures at once, separated by a pipe (|). Rumour checks all your rules, rather than quitting on the very first mistake, so you get a complete picture of everything that's broken. - No Guessing Games: For
X-Custom-Header, it explicitly states:Expected String("WrongValue"), got String("HelloHeader"). It doesn't just say "failed"; it tells you exactly what the server actually sent back. - The Built-in Retries: Because the request failed, Rumour automatically triggered its built-in retry mechanism (
↻ RETRYING), waiting500ms, then1000ms, to make sure it wasn't just a random network hiccup before officially failing the test. - The Helpful Payload Dump: Under the
Message:field in the final report, Rumour thoughtfully dumps the actual JSON of the headers it received from the server. This lets you visually inspect the payload and confirm for yourself thatX-Required-Headerwas indeed missing!