Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/core/src/processor/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ where
result.assertion_results = assertion_results;
}
Ok((RequestProcessResult::Completed(result), processed_request))
}
},
Err(e) => {
output::log_execution_error(&processed_request, &e, log, config.include_secrets);
Ok((RequestProcessResult::ExecutionError, processed_request))
Expand Down
136 changes: 136 additions & 0 deletions src/core/src/processor/executor_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,142 @@ GET https://api.example.com/final
assert_eq!(res.files[0].failed_count, 1);
}

#[test]
fn test_processor_evaluates_assertions_on_raw_executor_result() {
use crate::types::AssertionType;

// The file has EXPECTED_RESPONSE_STATUS 200 which the parser converts
// into Assertion objects on the request.
let file_content = r#"
GET https://api.example.com/test
> EXPECTED_RESPONSE_STATUS 200
"#;
let temp_file = create_temp_http_file(file_content);
let file_path = temp_file.path().to_str().unwrap().to_string();

// The mock executor returns a raw result with EMPTY assertion_results.
// The processor must evaluate the request's assertions and merge them.
let mock = MockHttpExecutor::new(vec![HttpResult {
request_name: None,
status_code: 200,
success: true,
error_message: None,
duration_ms: 1,
response_headers: None,
response_body: Some(r#"{"status":"ok"}"#.to_string()),
assertion_results: Vec::new(), // raw — no pre-evaluated assertions
}]);

let result = process_http_files_with_executor(
&[file_path],
false,
None,
None,
false,
false,
&|req, v, i| mock.execute(req, v, i),
);

assert!(result.is_ok());
let res = result.unwrap();
assert!(res.success);
assert_eq!(res.files[0].success_count, 1);

// The processed result contexts should have assertion_results populated
// by the processor (not by the mock).
let ctx = &res.files[0].result_contexts[0];
let http_result = ctx.result.as_ref().expect("expected result");
assert!(
!http_result.assertion_results.is_empty(),
"processor should have evaluated assertions, but assertion_results is empty"
);
assert!(
http_result.assertion_results[0].passed,
"status assertion for 200 should pass"
);
assert_eq!(http_result.assertion_results.len(), 1);
assert_eq!(
http_result.assertion_results[0].assertion.assertion_type,
AssertionType::Status
);
assert_eq!(
http_result.assertion_results[0].assertion.expected_value,
"200"
);
}

#[test]
fn test_processor_evaluates_assertions_and_marks_failure() {
// Request expects status 200 but executor returns 404
let file_content = r#"
GET https://api.example.com/not-found
> EXPECTED_RESPONSE_STATUS 200
"#;
let temp_file = create_temp_http_file(file_content);
let file_path = temp_file.path().to_str().unwrap().to_string();

let mock = MockHttpExecutor::new(vec![HttpResult {
request_name: None,
status_code: 404,
success: false,
error_message: None,
duration_ms: 1,
response_headers: None,
response_body: None,
assertion_results: Vec::new(), // raw — no assertions pre-evaluated
}]);

let result = process_http_files_with_executor(
&[file_path],
false,
None,
None,
false,
false,
&|req, v, i| mock.execute(req, v, i),
);

assert!(result.is_ok());
let res = result.unwrap();
// The request failed assertions, so overall result is failure
assert!(!res.success);
assert_eq!(res.files[0].failed_count, 1);

let ctx = &res.files[0].result_contexts[0];
let http_result = ctx.result.as_ref().expect("expected result");
assert_eq!(http_result.assertion_results.len(), 1);
assert!(!http_result.assertion_results[0].passed);
assert!(!http_result.success);
}

#[test]
fn test_processor_without_assertions_does_not_evaluate() {
let file_content = "GET https://api.example.com/test\n";
let temp_file = create_temp_http_file(file_content);
let file_path = temp_file.path().to_str().unwrap().to_string();

let mock = MockHttpExecutor::new(vec![create_success_response(None)]);

let result = process_http_files_with_executor(
&[file_path],
false,
None,
None,
false,
false,
&|req, v, i| mock.execute(req, v, i),
);

assert!(result.is_ok());
let ctx = &result.unwrap().files[0].result_contexts[0];
let http_result = ctx.result.as_ref().expect("expected result");
assert!(
http_result.assertion_results.is_empty(),
"no assertions on request should leave assertion_results empty"
);
assert!(http_result.success);
}

#[test]
fn test_fail_fast_disabled_runs_all_requests() {
let file_content = r#"
Expand Down
112 changes: 112 additions & 0 deletions src/core/src/processor/incremental_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,118 @@ GET https://api.example.com/json
}
}

#[test]
fn test_incremental_evaluates_assertions_on_raw_executor_result() {
use crate::types::AssertionType;

// Use EXPECTED_RESPONSE_STATUS which IS parsed by the parser.
let file_content = r#"
GET https://api.example.com/test
> EXPECTED_RESPONSE_STATUS 200
"#;
let temp_file = create_temp_http_file(file_content);
let file_path = temp_file.path().to_str().unwrap();

let captured = Arc::new(Mutex::new(None::<HttpResult>));
let captured_clone = Arc::clone(&captured);

// Mock returns raw result (empty assertion_results). The processor
// must evaluate the request's assertions and merge them.
let raw_result = HttpResult {
request_name: None,
status_code: 200,
success: true,
error_message: None,
duration_ms: 1,
response_headers: None,
response_body: Some(r#"{"status":"ok"}"#.to_string()),
assertion_results: Vec::new(),
};
let mock = MockHttpExecutor::new(vec![raw_result]);

let _ = process_http_file_incremental_with_executor(
file_path,
None,
false,
0,
move |_idx, _total, result| {
if let RequestProcessingResult::Executed { result, .. } = result {
*captured_clone.lock().unwrap() = Some(result);
}
false
},
&|req, v, i| mock.execute(req, v, i),
);

let http_result = captured.lock().unwrap().take()
.expect("expected Executed result");

assert!(
!http_result.assertion_results.is_empty(),
"processor should have evaluated assertions in incremental path"
);
assert_eq!(http_result.assertion_results.len(), 1);
assert!(
http_result.assertion_results[0].passed,
"status assertion for 200 should pass"
);
assert_eq!(
http_result.assertion_results[0].assertion.assertion_type,
AssertionType::Status
);
assert_eq!(
http_result.assertion_results[0].assertion.expected_value,
"200"
);
}

#[test]
fn test_incremental_evaluates_assertions_and_marks_failure() {
// Request expects status 200 but executor returns 404
let file_content = r#"
GET https://api.example.com/not-found
> EXPECTED_RESPONSE_STATUS 200
"#;
let temp_file = create_temp_http_file(file_content);
let file_path = temp_file.path().to_str().unwrap();

let captured = Arc::new(Mutex::new(None::<HttpResult>));
let captured_clone = Arc::clone(&captured);

let raw_result = HttpResult {
request_name: None,
status_code: 404,
success: false,
error_message: None,
duration_ms: 1,
response_headers: None,
response_body: None,
assertion_results: Vec::new(),
};
let mock = MockHttpExecutor::new(vec![raw_result]);

let _ = process_http_file_incremental_with_executor(
file_path,
None,
false,
0,
move |_idx, _total, result| {
if let RequestProcessingResult::Executed { result, .. } = result {
*captured_clone.lock().unwrap() = Some(result);
}
false
},
&|req, v, i| mock.execute(req, v, i),
);

let http_result = captured.lock().unwrap().take()
.expect("expected Executed result");

assert_eq!(http_result.assertion_results.len(), 1);
assert!(!http_result.assertion_results[0].passed);
assert!(!http_result.success);
}

#[test]
fn test_multiple_requests_with_mixed_results() {
let file_content = r#"
Expand Down
Loading