use crate::{helpers::spawn_http_server, support::TestWorkspace};
#[test]
fn run_outputs_verbose_text_with_timing_breakdown() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"ok":true,"service":"hen"}"#,
)
.replacen("127.0.0.1", "localhost", 1);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Verbose Timing Fixture
Exercises verbose timing output.
---
Fetch fixture
GET {server_url}
^ & body.ok == true
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--verbose"]);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
assert!(output.stdout.contains("timing: dns "));
assert!(output.stdout.contains("responseStart "));
assert!(output.stdout.contains("bodyRead "));
assert!(!output.stdout.contains("Body:"));
assert!(!output.stdout.contains("Body preview:"));
}
#[test]
fn run_outputs_verbose_text_body_when_requested() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"ok":true,"service":"hen"}"#,
)
.replacen("127.0.0.1", "localhost", 1);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Verbose Body Fixture
Exercises explicit body output.
---
Fetch fixture
GET {server_url}
^ & body.ok == true
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "--verbose", "--body", "selected"]);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
assert!(output.stdout.contains("timing: dns "));
assert!(output.stdout.contains("Body:"));
assert!(output.stdout.contains("\"service\":\"hen\""));
}
#[test]
fn run_outputs_assertion_labels_in_text_output() {
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#"Assertion Label Fixture
Exercises assertion label output.
---
Fetch fixture
GET {server_url}
# The page loads
^ & status == 200
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "0"]);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
assert!(output.stdout.contains("[ok] #0 Fetch fixture"));
assert!(output.stdout.contains("✅ [Fetch fixture] [The page loads]"));
}
#[test]
fn run_outputs_text_body_for_failed_requests_when_requested() {
let success_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"ok":true}"#,
);
let failure_url = spawn_http_server(
500,
"Internal Server Error",
"application/json",
r#"{"error":"login failed"}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Failed Body Fixture
Exercises failed-only body output.
---
Warmup
GET {success_url}
^ & body.ok == true
---
Login
GET {failure_url}
^ & status.code == 200
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "all", "--body", "failed"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(
output.stderr.contains("One or more requests failed"),
"stderr: {}",
output.stderr
);
assert!(!output.stdout.contains("\"ok\":true"));
assert!(output.stdout.contains("[x] #1 Login"));
assert!(output.stdout.contains("Body preview:"));
assert!(output.stdout.contains("\"error\":\"login failed\""));
}
#[test]
fn run_outputs_text_body_for_all_requests_when_requested() {
let login_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"token":"abc123"}"#,
);
let profile_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"name":"Alice"}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"All Body Fixture
Exercises all-request body output.
---
Login
GET {login_url}
& body.token -> $TOKEN
^ & status.code == 200
---
Load Profile
GET {profile_url}
> requires: Login
* Authorization = Bearer {{{{ TOKEN }}}}
^ &[Login].body.token == 'abc123'
^ & body.name == 'Alice'
"#
),
);
let output = workspace.run_hen(["run", "collection.hen", "1", "--body", "all"]);
assert_eq!(output.status_code, 0, "stderr: {}", output.stderr);
assert!(output.stderr.is_empty(), "stderr: {}", output.stderr);
assert!(output.stdout.contains("[ok] #0 Login"));
assert!(output.stdout.contains("[ok] #1 Load Profile"));
assert!(output.stdout.contains("\"token\":\"[redacted]\""));
assert!(!output.stdout.contains("\"token\":\"abc123\""));
assert!(output.stdout.contains("\"name\":\"Alice\""));
}
#[test]
fn run_outputs_verbose_text_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 verbose dependency trace text 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",
"--parallel",
"--continue-on-error",
"--verbose",
]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(
output.stderr.contains("One or more requests failed"),
"stderr: {}",
output.stderr
);
assert!(output.stdout.contains("Trace"));
assert!(output.stdout.contains("waiting #1 Load Profile"));
assert!(output.stdout.contains("waiting on Login"));
assert!(output.stdout.contains("skipped #1 Load Profile"));
assert!(output.stdout.contains("dependency Login failed"));
}
#[test]
fn run_outputs_text_diff_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 text diff rendering for nested structural mismatch reporting.
---
Fetch fixture
GET {server_url}
^ & body.user ~= {{"profile":{{"name":"Alice"}}}}
"#
),
);
let output = workspace.run_hen(["run", "collection.hen"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(
output.stderr.contains("One or more requests failed"),
"stderr: {}",
output.stderr
);
assert!(output.stdout.contains("[x] #0 Fetch fixture — Assertion failed"));
assert!(output.stdout.contains("assertion: ^ & body.user ~= {\"profile\":{\"name\":\"Alice\"}}"));
assert!(output.stdout.contains("actual path: body.user"));
assert!(output.stdout.contains("mismatch path: body.user.profile.name"));
assert!(output.stdout.contains("reason: value_mismatch"));
assert!(output.stdout.contains("diff:"));
assert!(output.stdout.contains("@@ body.user @@"));
assert!(output.stdout.contains("- \"name\": \"Alice\""));
assert!(output.stdout.contains("+ \"name\": \"Bob\""));
}
#[test]
fn run_outputs_text_diff_with_omitted_context_for_large_structural_failures() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"user":{"id":"123","email":"alice@example.com","profile":{"name":"Bob","active":true},"role":"admin","status":"active"}}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Large Structural Failure Fixture
Exercises context-window diff rendering for larger structural mismatches.
---
Fetch fixture
GET {server_url}
^ & body.user ~= {{"id":"123","email":"alice@example.com","profile":{{"name":"Alice"}},"role":"admin","status":"active"}}
"#
),
);
let output = workspace.run_hen(["run", "collection.hen"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(
output.stderr.contains("One or more requests failed"),
"stderr: {}",
output.stderr
);
assert!(output.stdout.contains("@@ body.user @@"));
assert!(output.stdout.contains("... 2 unchanged lines ..."));
assert!(output.stdout.contains(" \"profile\": {"));
assert!(output.stdout.contains("- \"name\": \"Alice\""));
assert!(output.stdout.contains("+ \"name\": \"Bob\""));
assert!(output.stdout.contains("+ \"active\": true"));
assert!(output.stdout.contains(" \"role\": \"admin\","));
}
#[test]
fn run_outputs_text_diff_with_omitted_changed_lines_for_large_replacements() {
let server_url = spawn_http_server(
200,
"OK",
"application/json",
r#"{"items":["actual-01","actual-02","actual-03","actual-04","actual-05","actual-06","actual-07","actual-08"]}"#,
);
let workspace = TestWorkspace::new();
workspace.write_file(
"collection.hen",
&format!(
r#"Large Replacement Diff Fixture
Exercises changed-block truncation for large diff replacements.
---
Fetch fixture
GET {server_url}
^ & body.items == ["expected-01","expected-02","expected-03","expected-04","expected-05","expected-06","expected-07","expected-08"]
"#
),
);
let output = workspace.run_hen(["run", "collection.hen"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(
output.stderr.contains("One or more requests failed"),
"stderr: {}",
output.stderr
);
assert!(output.stdout.contains("@@ body.items @@"));
assert!(output.stdout.contains(" ["));
assert!(output.stdout.contains("- \"expected-01\","));
assert!(output.stdout.contains("- \"expected-03\","));
assert!(output.stdout.contains("... 2 removed lines ..."));
assert!(output.stdout.contains("- \"expected-06\","));
assert!(output.stdout.contains("- \"expected-08\""));
assert!(output.stdout.contains("+ \"actual-01\","));
assert!(output.stdout.contains("+ \"actual-03\","));
assert!(output.stdout.contains("... 2 added lines ..."));
assert!(output.stdout.contains("+ \"actual-06\","));
assert!(output.stdout.contains("+ \"actual-08\""));
assert!(output.stdout.contains(" ]"));
}
#[test]
fn run_outputs_text_diff_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"]);
assert_eq!(output.status_code, 1, "stderr: {}", output.stderr);
assert!(
output.stderr.contains("One or more requests failed"),
"stderr: {}",
output.stderr
);
assert!(output.stdout.contains("[x] #0 Schema Failure Fixture — Assertion failed"));
assert!(output.stdout.contains("assertion: ^ & body === User"));
assert!(output.stdout.contains("actual path: body"));
assert!(output.stdout.contains("mismatch path: body.address.postalCode"));
assert!(output.stdout.contains("reason: missing_required_field"));
assert!(output.stdout.contains("diff:"));
assert!(output.stdout.contains("@@ body.address.postalCode @@"));
assert!(output.stdout.contains("-<required field postalCode>"));
assert!(output.stdout.contains("+<missing>"));
}