use crate::assert::AssertionEngine;
#[cfg(test)]
use crate::parser::ast::{Section, SectionContent, SectionType};
use crate::plugins::AssertionTiming;
use serde_json::Value;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct AssertionResult {
pub passed: bool,
pub failure_messages: Vec<String>,
}
pub struct AssertionHandler {
engine: AssertionEngine,
}
fn append_failures_with_context(
results: &[crate::assert::AssertionResult],
context: &str,
failure_messages: &mut Vec<String>,
) {
for fail in results {
match fail {
crate::assert::AssertionResult::Fail {
message,
expected,
actual,
} => {
failure_messages.push(format!("Assertion failed {}: {}", context, message));
if let (Some(exp), Some(act)) = (expected, actual) {
failure_messages.push(format!(" Expected: {}\n Actual: {}", exp, act));
}
}
crate::assert::AssertionResult::Error(msg) => {
failure_messages.push(format!("Assertion error {}: {}", context, msg));
}
_ => {}
}
}
}
impl AssertionHandler {
pub fn new(_verbose: bool) -> Self {
Self {
engine: AssertionEngine::new(),
}
}
#[cfg(test)]
pub fn evaluate_assertions(
&self,
sections: &[Section],
target_value: &Value,
headers: &HashMap<String, String>,
trailers: &HashMap<String, String>,
) -> AssertionResult {
let mut failure_messages = Vec::new();
for section in sections {
if section.section_type == SectionType::Asserts
&& let SectionContent::Assertions(lines) = §ion.content
{
let results =
self.engine
.evaluate_all(lines, target_value, Some(headers), Some(trailers));
let context = format!("at line {}", section.start_line);
append_failures_with_context(&results, &context, &mut failure_messages);
}
}
AssertionResult {
passed: failure_messages.is_empty(),
failure_messages,
}
}
#[cfg(test)]
pub fn evaluate_section_assertions(
&self,
section: &Section,
target_value: &Value,
headers: &HashMap<String, String>,
trailers: &HashMap<String, String>,
) -> AssertionResult {
let mut failure_messages = Vec::new();
if section.section_type == SectionType::Asserts
&& let SectionContent::Assertions(lines) = §ion.content
{
let results =
self.engine
.evaluate_all(lines, target_value, Some(headers), Some(trailers));
let context = format!("at line {}", section.start_line);
append_failures_with_context(&results, &context, &mut failure_messages);
}
AssertionResult {
passed: failure_messages.is_empty(),
failure_messages,
}
}
#[cfg(test)]
pub fn has_assertions(&self, section: &Section) -> bool {
section.section_type == SectionType::Asserts
}
#[cfg(test)]
pub fn get_assertion_lines<'a>(&self, section: &'a Section) -> Vec<&'a String> {
if let SectionContent::Assertions(lines) = §ion.content {
lines.iter().collect()
} else {
Vec::new()
}
}
#[cfg(test)]
pub fn evaluate_single_assertion(
&self,
assertion: &str,
target_value: &Value,
headers: Option<&HashMap<String, String>>,
trailers: Option<&HashMap<String, String>>,
) -> Result<crate::assert::AssertionResult, String> {
self.engine
.evaluate(assertion, target_value, headers, trailers)
.map_err(|e| e.to_string())
}
pub fn evaluate_assertions_for_section(
&self,
lines: &[String],
target_value: &Value,
headers: &HashMap<String, String>,
trailers: &HashMap<String, String>,
context: &str,
timing: Option<&AssertionTiming>,
) -> AssertionResult {
let mut failure_messages = Vec::new();
let results = self.engine.evaluate_all_with_timing(
lines,
target_value,
Some(headers),
Some(trailers),
timing,
);
append_failures_with_context(&results, context, &mut failure_messages);
AssertionResult {
passed: failure_messages.is_empty(),
failure_messages,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_evaluate_assertions_pass() {
let handler = AssertionHandler::new(false);
let sections = vec![Section {
section_type: SectionType::Asserts,
content: SectionContent::Assertions(vec![".id == 123".to_string()]),
inline_options: Default::default(),
raw_content: "".to_string(),
start_line: 0,
end_line: 0,
}];
let target = json!({"id": 123, "name": "test"});
let headers = HashMap::new();
let trailers = HashMap::new();
let result = handler.evaluate_assertions(§ions, &target, &headers, &trailers);
assert!(result.passed);
assert!(result.failure_messages.is_empty());
}
#[test]
fn test_evaluate_assertions_fail() {
let handler = AssertionHandler::new(false);
let sections = vec![Section {
section_type: SectionType::Asserts,
content: SectionContent::Assertions(vec![".id == 456".to_string()]),
inline_options: Default::default(),
raw_content: "".to_string(),
start_line: 0,
end_line: 0,
}];
let target = json!({"id": 123, "name": "test"});
let headers = HashMap::new();
let trailers = HashMap::new();
let result = handler.evaluate_assertions(§ions, &target, &headers, &trailers);
assert!(!result.passed);
assert!(!result.failure_messages.is_empty());
}
#[test]
fn test_has_assertions() {
let handler = AssertionHandler::new(false);
let asserts_section = Section {
section_type: SectionType::Asserts,
content: SectionContent::Assertions(vec![]),
inline_options: Default::default(),
raw_content: "".to_string(),
start_line: 0,
end_line: 0,
};
let other_section = Section {
section_type: SectionType::Response,
content: SectionContent::Json(json!({})),
inline_options: Default::default(),
raw_content: "".to_string(),
start_line: 0,
end_line: 0,
};
assert!(handler.has_assertions(&asserts_section));
assert!(!handler.has_assertions(&other_section));
}
#[test]
fn test_get_assertion_lines() {
let handler = AssertionHandler::new(false);
let section = Section {
section_type: SectionType::Asserts,
content: SectionContent::Assertions(vec![
".id == 123".to_string(),
".name == \"test\"".to_string(),
]),
inline_options: Default::default(),
raw_content: "".to_string(),
start_line: 0,
end_line: 0,
};
let lines = handler.get_assertion_lines(§ion);
assert_eq!(lines.len(), 2);
}
#[test]
fn test_evaluate_single_assertion() {
let handler = AssertionHandler::new(false);
let target = json!({"id": 123, "name": "test"});
let headers = HashMap::new();
let trailers = HashMap::new();
let result = handler.evaluate_single_assertion(
".id == 123",
&target,
Some(&headers),
Some(&trailers),
);
assert!(result.is_ok());
}
#[test]
fn test_evaluate_section_assertions_empty() {
let handler = AssertionHandler::new(false);
let section = Section {
section_type: SectionType::Asserts,
content: SectionContent::Assertions(vec![]),
inline_options: Default::default(),
raw_content: "".to_string(),
start_line: 0,
end_line: 0,
};
let target = json!({"id": 123});
let headers = HashMap::new();
let trailers = HashMap::new();
let result = handler.evaluate_section_assertions(§ion, &target, &headers, &trailers);
assert!(result.passed);
}
}