use serde_json::Value;
use crate::{
helpers::{
spawn_header_echo_server, spawn_http_server, spawn_oauth_client_credentials_server,
spawn_oauth_refresh_server,
},
support::TestWorkspace,
};
#[test]
fn run_outputs_json_report() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"ok":true,"service":"hen"}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"JSON Fixture
Exercises machine-readable output.
---
Fetch fixture
GET {server_url}
^ & body.ok == true
[ true == false ] ^ & body.service == 'hen'
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(
output.status_code,
0,
"stdout: {}\nstderr: {}",
output.stdout,
output.stderr
);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(parsed["plan"], serde_json::json!([0]));
assert_eq!(parsed["selectedRequests"], serde_json::json!([0]));
assert_eq!(parsed["records"][0]["status"], 200);
assert_eq!(
parsed["records"][0]["assertions"],
serde_json::json!([
{
"assertion": "^ & body.ok == true",
"status": "passed",
"message": null,
"mismatch": null,
"diff": null,
},
{
"assertion": "[ true == false ] ^ & body.service == 'hen'",
"status": "skipped",
"message": "guard evaluated to false",
"mismatch": null,
"diff": null,
}
])
);
assert!(parsed["records"][0]["body"]
.as_str()
.expect("body should be serialized as a string")
.contains("\"service\":\"hen\""));
assert_eq!(parsed["records"][0]["bodyChars"], 27);
assert_eq!(parsed["records"][0]["bodyTruncated"], false);
assert_eq!(parsed["interrupted"], false);
assert_eq!(parsed["interruptSignal"], Value::Null);
assert_eq!(parsed["failures"], serde_json::json!([]));
assert_eq!(parsed["trace"][0]["kind"], "started");
assert_eq!(parsed["trace"][0]["request"], "Fetch fixture");
assert_eq!(parsed["trace"][1]["kind"], "completed");
}
#[test]
fn run_outputs_json_report_with_dependency_trace() {
let login_url = spawn_http_server(
500,
"Internal Server Error",
"application/json",
r#"{"error":"login failed"}"#,
);
let health_url = spawn_http_server(200, "OK", "application/json", r#"{"ok":true}"#);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Dependency Trace Fixture
Exercises dependency-aware execution trace output.
---
Login
GET {login_url}
^ & status.code == 200
---
Load Profile
GET {health_url}
> requires: Login
^ & body.ok == true
---
Health
GET {health_url}
^ & body.ok == true
"#
),
);
let output = workspace.run_hen([
"run",
"collection.hen",
"all",
"--output",
"json",
"--parallel",
"--continue-on-error",
]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let trace = parsed["trace"].as_array().expect("trace should be an array");
assert!(trace.iter().any(|entry| {
entry["kind"] == "waiting"
&& entry["request"] == "Load Profile"
&& entry["reason"] == "dependencies_pending"
&& entry["waitingOn"] == serde_json::json!(["Login"])
}));
assert!(trace.iter().any(|entry| {
entry["kind"] == "skipped"
&& entry["request"] == "Load Profile"
&& entry["reason"] == "dependency_failed"
&& entry["relatedRequest"] == "Login"
}));
}
#[test]
fn run_outputs_json_report_with_selected_environment() {
let default_url = spawn_http_server(200, "OK", "application/json", r#"{"env":"prod"}"#);
let local_url = spawn_http_server(200, "OK", "application/json", r#"{"env":"local"}"#);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"name = Environment Fixture
$ API_ORIGIN = {default_url}
env local
$ API_ORIGIN = {local_url}
---
Fetch fixture
GET {{{{ API_ORIGIN }}}}
^ & body.env == 'local'
"#
),
);
let output = workspace.run_hen([
"run",
"collection.hen",
"--env",
"local",
"--output",
"json",
]);
assert_eq!(
output.status_code,
0,
"stdout: {}\nstderr: {}",
output.stdout,
output.stderr
);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(parsed["collection"]["selectedEnvironment"], "local");
assert_eq!(parsed["collection"]["availableEnvironments"][0], "local");
assert_eq!(parsed["records"][0]["url"], local_url);
assert_eq!(parsed["records"][0]["assertions"][0]["status"], "passed");
}
#[test]
fn run_outputs_json_report_with_local_secret_providers() {
let server_url = spawn_http_server(200, "OK", "application/json", r#"{"ok":true}"#);
let workspace = TestWorkspace::new();
workspace.write_file("secrets/client_segment.txt", "client-file\n");
workspace.write_file(
"collection.hen",
&format!(
r#"name = Local Secret Fixture
$ API_SEGMENT = secret.env("HEN_TEST_API_SEGMENT")
$ CLIENT_SEGMENT = secret.file("./secrets/client_segment.txt")
---
Fetch fixture
GET {server_url}/{{{{ API_SEGMENT }}}}/{{{{ CLIENT_SEGMENT }}}}
^ & body.ok == true
"#
),
);
let output = workspace.run_hen_with_env(
["run", "collection.hen", "--output", "json"],
[("HEN_TEST_API_SEGMENT", "env-segment")],
);
assert_eq!(
output.status_code,
0,
"stdout: {}\nstderr: {}",
output.stdout,
output.stderr
);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(
parsed["records"][0]["url"],
format!("{server_url}/[redacted]/[redacted]")
);
assert_eq!(parsed["records"][0]["assertions"][0]["status"], "passed");
}
#[test]
fn run_outputs_json_report_for_oauth_client_credentials_requests() {
let issuer_url = spawn_oauth_client_credentials_server();
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"name = OAuth Client Credentials Fixture
oauth api
grant = client_credentials
issuer = {issuer_url}
client_id = hen-client
client_secret = secret.env("HEN_TEST_CLIENT_SECRET")
scope = read:fixtures
param audience = fixtures
access_token -> $API_ACCESS_TOKEN
token_type -> $API_TOKEN_TYPE
---
Fetch fixture
auth = api
GET {issuer_url}/resource/one
* X-Token-Type = {{{{ API_TOKEN_TYPE }}}}
^ & body.path == 'one'
^ & body.authorization == 'Bearer oauth-access-token'
^ $API_ACCESS_TOKEN == 'oauth-access-token'
---
Reuse mapped token
GET {issuer_url}/resource/two
> requires: Fetch fixture
* Authorization = Bearer {{{{ API_ACCESS_TOKEN }}}}
^ & body.path == 'two'
^ & body.authorization == 'Bearer oauth-access-token'
---
Cached auth profile
auth = api
GET {issuer_url}/resource/three
> requires: Reuse mapped token
^ & body.path == 'three'
^ & body.authorization == 'Bearer oauth-access-token'
"#
),
);
let output = workspace.run_hen_with_env(
["run", "collection.hen", "all", "--output", "json"],
[("HEN_TEST_CLIENT_SECRET", "hen-secret")],
);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(parsed["records"][0]["assertions"][0]["status"], "passed");
assert_eq!(parsed["records"][0]["assertions"][1]["status"], "passed");
assert_eq!(parsed["records"][0]["assertions"][2]["status"], "passed");
assert_eq!(parsed["records"][1]["assertions"][0]["status"], "passed");
assert_eq!(parsed["records"][1]["assertions"][1]["status"], "passed");
assert_eq!(parsed["records"][2]["assertions"][0]["status"], "passed");
assert_eq!(parsed["records"][2]["assertions"][1]["status"], "passed");
assert!(parsed["records"][0]["body"]
.as_str()
.expect("body should be a string")
.contains("Bearer [redacted]"));
assert!(parsed["records"][1]["body"]
.as_str()
.expect("body should be a string")
.contains("Bearer [redacted]"));
assert!(parsed["records"][2]["body"]
.as_str()
.expect("body should be a string")
.contains("Bearer [redacted]"));
}
#[test]
fn run_outputs_json_report_for_oauth_refresh_token_requests() {
let token_origin = spawn_oauth_refresh_server();
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"name = OAuth Refresh Token Fixture
oauth api
grant = refresh_token
token_url = {token_origin}/token
client_id = hen-client
refresh_token = seed-refresh
access_token -> $API_ACCESS_TOKEN
refresh_token -> $API_REFRESH_TOKEN
---
Refresh once
auth = api
GET {token_origin}/resource/one
* X-Refresh-Token = {{{{ API_REFRESH_TOKEN }}}}
^ & body.path == 'one'
^ & body.authorization == 'Bearer first-access-token'
---
Refresh again
auth = api
GET {token_origin}/resource/two
> requires: Refresh once
^ & body.path == 'two'
^ & body.authorization == 'Bearer second-access-token'
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "all", "--output", "json"]);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(parsed["records"][0]["assertions"][0]["status"], "passed");
assert_eq!(parsed["records"][0]["assertions"][1]["status"], "passed");
assert_eq!(parsed["records"][1]["assertions"][0]["status"], "passed");
assert_eq!(parsed["records"][1]["assertions"][1]["status"], "passed");
assert!(parsed["records"][0]["body"]
.as_str()
.expect("body should be a string")
.contains("Bearer [redacted]"));
assert!(parsed["records"][1]["body"]
.as_str()
.expect("body should be a string")
.contains("Bearer [redacted]"));
}
#[test]
fn run_outputs_json_report_redacts_inherited_sensitive_exports_from_configured_captures() {
let login_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"session":"super-secret-token"}"#,
);
let profile_url = spawn_header_echo_server("Authorization");
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"name = Configured Capture Redaction Fixture
redact_capture = SESSION
---
Login
GET {login_url}
& body.session -> $SESSION
^ & status.code == 200
---
Load Profile
GET {profile_url}
> requires: Login
* Authorization = Bearer {{{{ SESSION }}}}
^ & status.code == 200
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "all", "--output", "json"]);
assert_eq!(
output.status_code,
0,
"stdout: {}\nstderr: {}",
output.stdout,
output.stderr
);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let records = parsed["records"].as_array().expect("records should be an array");
let login_body = records[0]["body"].as_str().expect("body should be a string");
let profile_body = records[1]["body"].as_str().expect("body should be a string");
assert!(login_body.contains("[redacted]"));
assert!(!login_body.contains("super-secret-token"));
assert!(
profile_body.contains("Bearer [redacted]"),
"profile body was: {profile_body}"
);
assert!(!profile_body.contains("super-secret-token"));
}
#[test]
fn run_outputs_json_report_redacts_configured_body_paths() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"ok":true,"session":{"token":"nested-secret-token"}}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"name = Configured Body Redaction Fixture
redact_body = body.session.token
---
Fetch fixture
GET {server_url}
^ & body.ok == true
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(
output.status_code,
0,
"stdout: {}\nstderr: {}",
output.stdout,
output.stderr
);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let body = parsed["records"][0]["body"]
.as_str()
.expect("body should be serialized as a string");
assert!(body.contains("[redacted]"));
assert!(!body.contains("nested-secret-token"));
}
#[test]
fn run_outputs_json_report_redacts_configured_header_names() {
let server_url = spawn_http_server(
200,
"OK",
"text/plain",
"X-Custom-Secret: header-secret\nX-Trace: keep",
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"name = Configured Header Redaction Fixture
redact_header = X-Custom-Secret
---
Fetch fixture
GET {server_url}
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(
output.status_code,
0,
"stdout: {}\nstderr: {}",
output.stdout,
output.stderr
);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let body = parsed["records"][0]["body"]
.as_str()
.expect("body should be serialized as a string");
assert!(body.contains("X-Custom-Secret: [redacted]"));
assert!(body.contains("X-Trace: keep"));
assert!(!body.contains("header-secret"));
}
#[test]
fn run_outputs_json_report_includes_dns_timing_for_hostname_requests() {
let server_url = spawn_http_server(200, "OK", "application/json", r#"{"ok":true}"#)
.replacen("127.0.0.1", "localhost", 1);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"DNS Timing Fixture
Exercises hostname-based timing output.
---
Fetch fixture
GET {server_url}
^ & body.ok == true
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(parsed["records"][0]["timing"]["phases"][0]["name"], "dns");
assert!(parsed["records"][0]["timing"]["phases"][0]["durationMs"].as_u64().is_some());
assert_eq!(parsed["records"][0]["timing"]["phases"][1]["name"], "responseStart");
assert_eq!(parsed["records"][0]["timing"]["phases"][2]["name"], "bodyRead");
}
#[test]
fn run_outputs_json_report_for_failed_assertions() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"ok":false,"service":"hen"}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Failed Assertions Fixture
Exercises assertion-level failure reporting.
---
Fetch fixture
GET {server_url}
^ & body.service == 'hen'
[ true == false ] ^ & body.service == 'hen'
^ & body.ok == true
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
assert_eq!(parsed["executionFailed"], true);
assert_eq!(parsed["interrupted"], false);
assert_eq!(parsed["interruptSignal"], Value::Null);
assert_eq!(parsed["records"], serde_json::json!([]));
assert_eq!(parsed["failures"][0]["protocol"], "http");
assert!(parsed["failures"][0]["startedAtUnixMs"].as_u64().is_some());
assert!(parsed["failures"][0]["durationMs"].as_u64().is_some());
assert!(parsed["failures"][0]["timing"]["totalMs"].as_u64().is_some());
assert_eq!(parsed["failures"][0]["timing"]["phases"][0]["name"], "responseStart");
assert!(parsed["failures"][0]["timing"]["phases"][0]["durationMs"].as_u64().is_some());
assert_eq!(parsed["failures"][0]["timing"]["phases"][1]["name"], "bodyRead");
assert!(parsed["failures"][0]["timing"]["phases"][1]["durationMs"].as_u64().is_some());
assert_eq!(parsed["failures"][0]["transcripts"][0]["direction"], "outgoing");
assert_eq!(parsed["failures"][0]["transcripts"][0]["label"], "http.request");
assert_eq!(parsed["failures"][0]["transcripts"][1]["direction"], "incoming");
assert_eq!(parsed["failures"][0]["transcripts"][1]["label"], "http.response");
assert!(parsed["failures"][0]["transcripts"][1]["body"]
.as_str()
.expect("failure transcript body should be serialized as a string")
.contains("\"ok\":false"));
assert_eq!(parsed["failures"][0]["retainedArtifacts"], serde_json::json!([]));
assert_eq!(
parsed["failures"][0]["assertions"],
serde_json::json!([
{
"assertion": "^ & body.service == 'hen'",
"status": "passed",
"message": null,
"mismatch": null,
"diff": null,
},
{
"assertion": "[ true == false ] ^ & body.service == 'hen'",
"status": "skipped",
"message": "guard evaluated to false",
"mismatch": null,
"diff": null,
},
{
"assertion": "^ & body.ok == true",
"status": "failed",
"message": "Assertion failed: ^ & body.ok == true (actual path: body.ok, actual type: boolean, actual value: false, operator: ==, compared type: boolean, compared value: true)",
"mismatch": {
"kind": "comparison",
"reason": "value_mismatch",
"target": null,
"path": "body.ok",
"actualPath": "body.ok",
"comparedPath": null,
"operator": "==",
"actual": {
"type": "boolean",
"value": false
},
"expected": {
"type": "boolean",
"value": true
}
},
"diff": {
"format": "unified",
"path": "body.ok",
"text": "@@ body.ok @@\n-true\n+false"
}
}
])
);
}
#[test]
fn run_outputs_json_report_for_structural_match_failures() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"user":{"profile":{"name":"Bob","active":true}}}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Structural Failure Fixture
Exercises nested structural mismatch reporting.
---
Fetch fixture
GET {server_url}
^ & body.user ~= {{"profile":{{"name":"Alice"}}}}
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let message = parsed["failures"][0]["assertions"][0]["message"]
.as_str()
.expect("message should be serialized as a string");
let mismatch = &parsed["failures"][0]["assertions"][0]["mismatch"];
let diff = &parsed["failures"][0]["assertions"][0]["diff"];
assert!(message.contains("mismatch path: body.user.profile.name"));
assert!(message.contains("mismatch actual value: \"Bob\""));
assert!(message.contains("mismatch expected value: \"Alice\""));
assert_eq!(mismatch["kind"], "match");
assert_eq!(mismatch["reason"], "value_mismatch");
assert_eq!(mismatch["target"], Value::Null);
assert_eq!(mismatch["path"], "body.user.profile.name");
assert_eq!(mismatch["actualPath"], "body.user");
assert_eq!(mismatch["operator"], "~=");
assert_eq!(mismatch["actual"]["type"], "string");
assert_eq!(mismatch["actual"]["value"], "Bob");
assert_eq!(mismatch["expected"]["type"], "string");
assert_eq!(mismatch["expected"]["value"], "Alice");
assert_eq!(diff["format"], "unified");
assert_eq!(diff["path"], "body.user");
assert!(diff["text"]
.as_str()
.expect("diff text should be serialized as a string")
.contains("@@ body.user @@"));
assert!(diff["text"]
.as_str()
.expect("diff text should be serialized as a string")
.contains("- \"name\": \"Alice\""));
assert!(diff["text"]
.as_str()
.expect("diff text should be serialized as a string")
.contains("+ \"name\": \"Bob\""));
}
#[test]
fn run_outputs_json_report_for_filtered_selector_failures() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"jobs":[{"recipient":"bob@example.com","status":"queued"},{"recipient":"alice@example.com","status":"succeeded"}]}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Filtered Selector Failure Fixture
Exercises concrete selector-path reporting in JSON output.
---
Fetch fixture
GET {server_url}
^ & body.jobs[? recipient == "alice@example.com"].status == 'queued'
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let message = parsed["failures"][0]["assertions"][0]["message"]
.as_str()
.expect("message should be serialized as a string");
let mismatch = &parsed["failures"][0]["assertions"][0]["mismatch"];
assert!(message.contains("actual path: body.jobs[1].status"));
assert_eq!(mismatch["path"], "body.jobs[1].status");
assert_eq!(mismatch["actualPath"], "body.jobs[1].status");
assert_eq!(mismatch["target"], Value::Null);
assert_eq!(mismatch["actual"]["value"], "succeeded");
assert_eq!(mismatch["expected"]["value"], "queued");
}
#[test]
fn run_outputs_json_report_for_schema_failures() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"id":"550e8400-e29b-41d4-a716-446655440000","address":{}}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"schema Address {{
postalCode: string
}}
schema User {{
id: UUID
address: Address
}}
---
Schema Failure Fixture
GET {server_url}
^ & body === User
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--output", "json"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
let parsed: Value = serde_json::from_str(&output.stdout).expect("stdout should be valid json");
let message = parsed["failures"][0]["assertions"][0]["message"]
.as_str()
.expect("message should be serialized as a string");
let mismatch = &parsed["failures"][0]["assertions"][0]["mismatch"];
let diff = &parsed["failures"][0]["assertions"][0]["diff"];
assert!(message.contains("schema target: User"));
assert!(message.contains("mismatch path: body.address.postalCode"));
assert_eq!(mismatch["kind"], "schema");
assert_eq!(mismatch["reason"], "missing_required_field");
assert_eq!(mismatch["target"], "User");
assert_eq!(mismatch["path"], "body.address.postalCode");
assert_eq!(mismatch["actualPath"], "body");
assert_eq!(mismatch["operator"], "===");
assert_eq!(mismatch["actual"]["type"], "missing");
assert_eq!(mismatch["actual"]["value"], Value::Null);
assert_eq!(mismatch["expected"]["type"], "required_field");
assert_eq!(mismatch["expected"]["value"], "postalCode");
assert_eq!(diff["format"], "unified");
assert_eq!(diff["path"], "body.address.postalCode");
assert_eq!(
diff["text"],
"@@ body.address.postalCode @@\n-<required field postalCode>\n+<missing>"
);
}