use std::cmp::Ordering;
use hurl_core::ast::{Predicate, PredicateFunc, PredicateFuncValue, PredicateValue, SourceInfo};
use hurl_core::reader::Pos;
use crate::util::path::ContextDir;
use super::error::{RunnerError, RunnerErrorKind};
use super::number::Number;
use super::predicate_value::{eval_predicate_value, eval_predicate_value_template};
use super::value::{EvalError, Value};
use super::variable::VariableSet;
pub fn eval_predicate(
predicate: &Predicate,
variables: &VariableSet,
value: &Option<Value>,
context_dir: &ContextDir,
) -> Result<(), RunnerError> {
let result = eval_predicate_func(
&predicate.predicate_func,
variables,
value.as_ref(),
context_dir,
)?;
let source_info = SourceInfo::new(
Pos::new(predicate.space0.source_info.start.line, 0),
Pos::new(predicate.space0.source_info.start.line, 0),
);
if result.type_mismatch {
let not = if predicate.not { "not " } else { "" };
let expected = format!("{}{}", not, result.expected);
let kind = RunnerErrorKind::AssertFailure {
actual: result.actual,
expected,
type_mismatch: true,
};
Err(RunnerError::new(source_info, kind, true))
} else if predicate.not && result.success {
let kind = RunnerErrorKind::AssertFailure {
actual: result.actual,
expected: format!("not {}", result.expected),
type_mismatch: false,
};
Err(RunnerError::new(source_info, kind, true))
} else if !predicate.not && !result.success {
let kind = RunnerErrorKind::AssertFailure {
actual: result.actual,
expected: result.expected,
type_mismatch: false,
};
Err(RunnerError::new(source_info, kind, true))
} else {
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct PredicateResult {
pub success: bool,
pub type_mismatch: bool,
pub actual: String,
pub expected: String,
}
impl Value {
fn format(&self) -> String {
match self {
Value::Bool(value) => format!("boolean <{value}>"),
Value::Bytes(values) => format!(
"{} byte{}",
values.len(),
if values.len() > 1 { "s" } else { "" }
),
Value::Date(value) => format!("date <{value}>"),
Value::HttpResponse(value) => format!("HTTP response <{value}>"),
Value::List(value) => format!("list of size {}", value.len()),
Value::Nodeset(size) => format!("list of size {size}"),
Value::Null => "null".to_string(),
Value::Number(number) => number.expected(),
Value::Object(values) => format!("list of size {}", values.len()),
Value::Regex(value) => format!("regex <{value}>"),
Value::String(value) => format!("string <{value}>"),
Value::Unit => "something".to_string(),
}
}
}
impl Number {
fn expected(&self) -> String {
match self {
Number::Float(f) => format!("float <{}>", format_float(*f)),
Number::Integer(value) => format!("integer <{value}>"),
Number::BigInteger(s) => format!("number <{s}>"),
}
}
}
fn format_float(value: f64) -> String {
if value.fract() < f64::EPSILON {
format!("{value}.0")
} else {
value.to_string()
}
}
fn expected_no_value(
predicate_func_value: &PredicateFuncValue,
variables: &VariableSet,
context_dir: &ContextDir,
) -> Result<String, RunnerError> {
match &predicate_func_value {
PredicateFuncValue::Equal { value, .. } | PredicateFuncValue::NotEqual { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(value.format())
}
PredicateFuncValue::GreaterThan { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("greater than <{}>", value.format()))
}
PredicateFuncValue::GreaterThanOrEqual { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("greater than or equals to <{}>", value.format()))
}
PredicateFuncValue::LessThan { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("less than <{}>", value.format()))
}
PredicateFuncValue::LessThanOrEqual { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("less than or equals to <{}>", value.format()))
}
PredicateFuncValue::StartWith { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("starts with {}", value.format()))
}
PredicateFuncValue::EndWith { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("ends with {}", value.format()))
}
PredicateFuncValue::Contain { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("contains {}", value.format()))
}
PredicateFuncValue::Include { value, .. } => {
let value = eval_predicate_value(value, variables, context_dir)?;
Ok(format!("include {}", value.format()))
}
PredicateFuncValue::Match {
value: expected, ..
} => {
let expected = eval_predicate_value_template(expected, variables)?;
Ok(format!("matches regex <{expected}>"))
}
PredicateFuncValue::Exist => Ok("something".to_string()),
PredicateFuncValue::IsBoolean => Ok("boolean".to_string()),
PredicateFuncValue::IsCollection => Ok("collection".to_string()),
PredicateFuncValue::IsDate => Ok("date".to_string()),
PredicateFuncValue::IsEmpty => Ok("empty".to_string()),
PredicateFuncValue::IsFloat => Ok("float".to_string()),
PredicateFuncValue::IsInteger => Ok("integer".to_string()),
PredicateFuncValue::IsIpv4 => Ok("ipv4".to_string()),
PredicateFuncValue::IsIpv6 => Ok("ipv6".to_string()),
PredicateFuncValue::IsIsoDate => Ok("date".to_string()),
PredicateFuncValue::IsList => Ok("list".to_string()),
PredicateFuncValue::IsNumber => Ok("number".to_string()),
PredicateFuncValue::IsObject => Ok("object".to_string()),
PredicateFuncValue::IsString => Ok("string".to_string()),
PredicateFuncValue::IsUuid => Ok("uuid".to_string()),
}
}
fn eval_predicate_func(
predicate_func: &PredicateFunc,
variables: &VariableSet,
value: Option<&Value>,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let value = match value {
Some(value) => value,
None => {
let expected = expected_no_value(&predicate_func.value, variables, context_dir)?;
return Ok(PredicateResult {
success: false,
actual: "none".to_string(),
expected,
type_mismatch: false,
});
}
};
match &predicate_func.value {
PredicateFuncValue::Equal {
value: expected, ..
} => eval_equal(expected, variables, value, context_dir),
PredicateFuncValue::NotEqual {
value: expected, ..
} => eval_not_equal(expected, variables, value, context_dir),
PredicateFuncValue::GreaterThan {
value: expected, ..
} => eval_greater_than(expected, variables, value, context_dir),
PredicateFuncValue::GreaterThanOrEqual {
value: expected, ..
} => eval_greater_than_or_equal(expected, variables, value, context_dir),
PredicateFuncValue::LessThan {
value: expected, ..
} => eval_less_than(expected, variables, value, context_dir),
PredicateFuncValue::LessThanOrEqual {
value: expected, ..
} => eval_less_than_or_equal(expected, variables, value, context_dir),
PredicateFuncValue::StartWith {
value: expected, ..
} => eval_start_with(expected, variables, value, context_dir),
PredicateFuncValue::EndWith {
value: expected, ..
} => eval_end_with(expected, variables, value, context_dir),
PredicateFuncValue::Contain {
value: expected, ..
} => eval_contain(expected, variables, value, context_dir),
PredicateFuncValue::Include {
value: expected, ..
} => eval_include(expected, variables, value, context_dir),
PredicateFuncValue::Match {
value: expected, ..
} => eval_match(
expected,
predicate_func.source_info,
variables,
value,
context_dir,
),
PredicateFuncValue::Exist => eval_exist(value),
PredicateFuncValue::IsBoolean => eval_is_boolean(value),
PredicateFuncValue::IsCollection => eval_is_collection(value),
PredicateFuncValue::IsDate => eval_is_date(value),
PredicateFuncValue::IsEmpty => eval_is_empty(value),
PredicateFuncValue::IsFloat => eval_is_float(value),
PredicateFuncValue::IsInteger => eval_is_integer(value),
PredicateFuncValue::IsIpv4 => eval_is_ipv4(value),
PredicateFuncValue::IsIpv6 => eval_is_ipv6(value),
PredicateFuncValue::IsIsoDate => eval_is_iso_date(value),
PredicateFuncValue::IsList => eval_is_list(value),
PredicateFuncValue::IsNumber => eval_is_number(value),
PredicateFuncValue::IsObject => eval_is_object(value),
PredicateFuncValue::IsString => eval_is_string(value),
PredicateFuncValue::IsUuid => eval_is_uuid(value),
}
}
fn eval_equal(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_values_equal(actual, &expected))
}
fn eval_not_equal(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_values_not_equal(actual, &expected))
}
fn eval_greater_than(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_values_greater(actual, &expected))
}
fn eval_greater_than_or_equal(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_values_greater_or_equal(actual, &expected))
}
fn eval_less_than(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_values_less(actual, &expected))
}
fn eval_less_than_or_equal(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_values_less_or_equal(actual, &expected))
}
fn eval_start_with(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
let expected_display = format!("starts with {}", expected.repr());
let actual_display = actual.repr();
match actual.starts_with(&expected) {
Ok(success) => Ok(PredicateResult {
success,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
}),
Err(_) => Ok(PredicateResult {
success: false,
actual: actual_display,
expected: expected_display,
type_mismatch: true,
}),
}
}
fn eval_end_with(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
let expected_display = format!("ends with {}", expected.repr());
let actual_display = actual.repr();
match actual.ends_with(&expected) {
Ok(success) => Ok(PredicateResult {
success,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
}),
Err(_) => Ok(PredicateResult {
success: false,
actual: actual_display,
expected: expected_display,
type_mismatch: true,
}),
}
}
fn eval_contain(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
let expected_display = format!("contains {}", expected.repr());
let actual_display = actual.repr();
match actual.contains(&expected) {
Ok(success) => Ok(PredicateResult {
success,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: false,
actual: actual_display,
expected: expected_display,
type_mismatch: true,
}),
}
}
fn eval_include(
expected: &PredicateValue,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
Ok(assert_include(actual, &expected))
}
fn eval_match(
expected: &PredicateValue,
source_info: SourceInfo,
variables: &VariableSet,
actual: &Value,
context_dir: &ContextDir,
) -> Result<PredicateResult, RunnerError> {
let expected = eval_predicate_value(expected, variables, context_dir)?;
let actual_display = actual.repr();
let expected_display = format!("matches regex <{expected}>");
match actual.is_match(&expected) {
Ok(success) => Ok(PredicateResult {
success,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
}),
Err(EvalError::Type) => Ok(PredicateResult {
success: false,
actual: actual_display,
expected: expected_display,
type_mismatch: true,
}),
Err(EvalError::InvalidRegex) => Err(RunnerError::new(
source_info,
RunnerErrorKind::InvalidRegex,
false,
)),
}
}
fn eval_is_integer(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_integer(),
actual: actual.repr(),
expected: "integer".to_string(),
type_mismatch: false,
})
}
fn eval_is_float(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_float(),
actual: actual.repr(),
expected: "float".to_string(),
type_mismatch: false,
})
}
fn eval_is_boolean(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_boolean(),
actual: actual.repr(),
expected: "boolean".to_string(),
type_mismatch: false,
})
}
fn eval_is_string(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_string(),
actual: actual.repr(),
expected: "string".to_string(),
type_mismatch: false,
})
}
fn eval_is_collection(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_collection(),
actual: actual.repr(),
expected: "collection".to_string(),
type_mismatch: false,
})
}
fn eval_is_list(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_list(),
actual: actual.repr(),
expected: "list".to_string(),
type_mismatch: false,
})
}
fn eval_is_object(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_object(),
actual: actual.repr(),
expected: "object".to_string(),
type_mismatch: false,
})
}
fn eval_is_date(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_date(),
actual: actual.repr(),
expected: "date".to_string(),
type_mismatch: false,
})
}
fn eval_is_iso_date(actual: &Value) -> Result<PredicateResult, RunnerError> {
match actual.is_iso_date() {
Ok(success) => Ok(PredicateResult {
success,
actual: actual.to_string(),
expected: "string with format YYYY-MM-DDTHH:mm:ss.sssZ".to_string(),
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: false,
actual: actual.repr(),
expected: "string".to_string(),
type_mismatch: true,
}),
}
}
fn eval_exist(actual: &Value) -> Result<PredicateResult, RunnerError> {
let actual_display = actual.repr();
let expected_display = "something".to_string();
match actual {
Value::Nodeset(0) => Ok(PredicateResult {
success: false,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: true,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
}),
}
}
fn eval_is_empty(actual: &Value) -> Result<PredicateResult, RunnerError> {
let expected_display = "count equals to 0".to_string();
match actual.count() {
Ok(count) => {
let actual_display = format!("count equals to {count}");
Ok(PredicateResult {
success: count == 0,
actual: actual_display,
expected: expected_display,
type_mismatch: false,
})
}
_ => Ok(PredicateResult {
success: false,
actual: actual.repr(),
expected: expected_display,
type_mismatch: true,
}),
}
}
fn eval_is_number(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_number(),
actual: actual.repr(),
expected: "number".to_string(),
type_mismatch: false,
})
}
fn eval_is_ipv4(actual: &Value) -> Result<PredicateResult, RunnerError> {
match actual.is_ipv4() {
Ok(success) => Ok(PredicateResult {
success,
actual: actual.to_string(),
expected: "string in IPv4 format".to_string(),
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: false,
actual: actual.repr(),
expected: "string".to_string(),
type_mismatch: true,
}),
}
}
fn eval_is_ipv6(actual: &Value) -> Result<PredicateResult, RunnerError> {
match actual.is_ipv6() {
Ok(success) => Ok(PredicateResult {
success,
actual: actual.to_string(),
expected: "string in IPv6 format".to_string(),
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: false,
actual: actual.repr(),
expected: "string".to_string(),
type_mismatch: true,
}),
}
}
fn eval_is_uuid(actual: &Value) -> Result<PredicateResult, RunnerError> {
match actual.is_uuid() {
Ok(success) => Ok(PredicateResult {
success,
actual: actual.to_string(),
expected: "string in UUID format".to_string(),
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: false,
actual: actual.repr(),
expected: "string".to_string(),
type_mismatch: true,
}),
}
}
fn assert_values_equal(actual: &Value, expected: &Value) -> PredicateResult {
let success = actual == expected;
let actual = actual.repr();
let expected = expected.repr();
let type_mismatch = false;
PredicateResult {
success,
actual,
expected,
type_mismatch,
}
}
fn assert_values_not_equal(actual: &Value, expected: &Value) -> PredicateResult {
let success = actual != expected;
let actual = actual.repr();
let expected = expected.repr();
let type_mismatch = false;
PredicateResult {
success,
actual,
expected,
type_mismatch,
}
}
fn assert_values_greater(actual_value: &Value, expected_value: &Value) -> PredicateResult {
let actual = actual_value.repr();
let expected = format!("greater than {}", expected_value.repr());
match actual_value.compare(expected_value) {
Ok(ordering) => PredicateResult {
success: ordering == Ordering::Greater,
actual,
expected,
type_mismatch: false,
},
_ => PredicateResult {
success: false,
actual,
expected,
type_mismatch: true,
},
}
}
fn assert_values_greater_or_equal(actual_value: &Value, expected_value: &Value) -> PredicateResult {
let actual = actual_value.repr();
let expected = format!("greater or equal than {}", expected_value.repr());
match actual_value.compare(expected_value) {
Ok(ordering) => PredicateResult {
success: ordering == Ordering::Greater || ordering == Ordering::Equal,
actual,
expected,
type_mismatch: false,
},
_ => PredicateResult {
success: false,
actual,
expected,
type_mismatch: true,
},
}
}
fn assert_values_less(actual_value: &Value, expected_value: &Value) -> PredicateResult {
let actual = actual_value.repr();
let expected = format!("less than {}", expected_value.repr());
match actual_value.compare(expected_value) {
Ok(ordering) => PredicateResult {
success: ordering == Ordering::Less,
actual,
expected,
type_mismatch: false,
},
_ => PredicateResult {
success: false,
actual,
expected,
type_mismatch: true,
},
}
}
fn assert_values_less_or_equal(actual_value: &Value, expected_value: &Value) -> PredicateResult {
let actual = actual_value.repr();
let expected = format!("less or equal than {}", expected_value.repr());
match actual_value.compare(expected_value) {
Ok(ordering) => PredicateResult {
success: ordering == Ordering::Less || ordering == Ordering::Equal,
actual,
expected,
type_mismatch: false,
},
_ => PredicateResult {
success: false,
actual,
expected,
type_mismatch: true,
},
}
}
fn assert_include(value: &Value, element: &Value) -> PredicateResult {
let actual = value.repr();
let expected = format!("includes {}", element.repr());
match value.includes(element) {
Ok(success) => PredicateResult {
success,
actual,
expected,
type_mismatch: false,
},
Err(_) => PredicateResult {
success: false,
actual,
expected,
type_mismatch: true,
},
}
}
#[cfg(test)]
mod tests {
use std::path::Path;
use hurl_core::ast::{
Expr, ExprKind, Float, I64, Placeholder, Regex, Template, TemplateElement, Variable,
Whitespace,
};
use hurl_core::types::ToSource;
use super::{PredicateResult, *};
fn whitespace() -> Whitespace {
Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
}
}
#[test]
fn test_predicate() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let whitespace = Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(0, 0)),
};
let predicate = Predicate {
not: true,
space0: whitespace.clone(),
predicate_func: PredicateFunc {
value: PredicateFuncValue::Equal {
space0: whitespace,
value: PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
10,
"10".to_source(),
))),
},
source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 12)),
},
};
assert!(
eval_predicate(
&predicate,
&variables,
&Some(Value::Bool(true)),
&context_dir
)
.is_ok()
);
let error = eval_predicate(
&predicate,
&variables,
&Some(Value::Number(Number::Integer(10))),
&context_dir,
)
.unwrap_err();
assert_eq!(
error.kind,
RunnerErrorKind::AssertFailure {
actual: "integer <10>".to_string(),
expected: "not integer <10>".to_string(),
type_mismatch: false,
}
);
assert_eq!(
error.source_info,
SourceInfo::new(Pos::new(1, 0), Pos::new(1, 0))
);
assert!(
eval_predicate(
&predicate,
&variables,
&Some(Value::Number(Number::Integer(1))),
&context_dir
)
.is_ok()
);
}
#[test]
fn test_predicate_type_mismatch() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
10,
"10".to_source(),
)));
let value = Value::Bool(true);
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "boolean <true>");
assert_eq!(result.expected, "integer <10>");
}
#[test]
fn test_predicate_type_mismatch_with_unit() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
10,
"10".to_source(),
)));
let value = Value::Unit;
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "unit");
assert_eq!(result.expected, "integer <10>");
}
#[test]
fn test_predicate_value_error() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
10,
"10".to_source(),
)));
let value = Value::Number(Number::Integer(1));
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "integer <1>");
assert_eq!(result.expected, "integer <10>");
let expected = PredicateValue::Bool(true);
let value = Value::Bool(false);
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "boolean <false>");
assert_eq!(result.expected, "boolean <true>");
let expected = PredicateValue::Number(hurl_core::ast::Number::Float(Float::new(
1.2,
"1.2".to_source(),
)));
let value = Value::Number(Number::Float(1.1));
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "float <1.1>");
assert_eq!(result.expected, "float <1.2>");
}
#[test]
fn test_predicate_exist() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let pred_func = PredicateFunc {
value: PredicateFuncValue::Exist,
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
};
let value = Some(&Value::Unit);
let result = eval_predicate_func(&pred_func, &variables, value, &context_dir).unwrap();
assert!(result.success);
assert_eq!(result.actual.as_str(), "unit");
assert_eq!(result.expected.as_str(), "something");
let value = None;
let result = eval_predicate_func(&pred_func, &variables, value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "none");
assert_eq!(result.expected, "something");
}
#[test]
fn test_predicate_value_equals_integers() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
1,
"1".to_source(),
)));
let value = Value::Number(Number::Integer(1));
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "integer <1>");
assert_eq!(result.expected, "integer <1>");
}
#[test]
fn test_predicate_value_equals_booleans() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Bool(false);
let value = Value::Bool(false);
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "boolean <false>");
assert_eq!(result.expected, "boolean <false>");
let expected = PredicateValue::Bool(true);
let value = Value::Bool(false);
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "boolean <false>");
assert_eq!(result.expected, "boolean <true>");
let expected = PredicateValue::Bool(true);
let value = Value::Bool(true);
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "boolean <true>");
assert_eq!(result.expected, "boolean <true>");
}
#[test]
fn test_predicate_value_equals_floats() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Float(Float::new(
1.1,
"1.1".to_source(),
)));
let value = Value::Number(Number::Float(1.1));
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "float <1.1>");
assert_eq!(result.expected, "float <1.1>");
}
#[test]
fn test_predicate_value_equals_float_integer() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
1,
"1".to_source(),
)));
let value = Value::Number(Number::Float(1.0));
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "float <1.0>");
assert_eq!(result.expected, "integer <1>");
}
#[test]
fn test_predicate_value_not_equals() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let expected = PredicateValue::Number(hurl_core::ast::Number::Integer(I64::new(
1,
"1".to_source(),
)));
let value = Value::Number(Number::Integer(2));
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "integer <2>");
assert_eq!(result.expected, "integer <1>");
}
#[test]
fn test_predicate_value_equals_string() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let template = Template::new(
Some('"'),
vec![TemplateElement::Placeholder(Placeholder {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 11)),
},
expr: Expr {
kind: ExprKind::Variable(Variable {
name: "base_url".to_string(),
source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 19)),
}),
source_info: SourceInfo::new(Pos::new(1, 11), Pos::new(1, 19)),
},
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 19), Pos::new(1, 19)),
},
})],
SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
);
let expected = PredicateValue::String(template.clone());
let value = Value::String(String::from("http://localhost:8000"));
let error = eval_equal(&expected, &variables, &value, &context_dir).unwrap_err();
assert_eq!(
error.kind,
RunnerErrorKind::TemplateVariableNotDefined {
name: String::from("base_url")
}
);
assert_eq!(
error.source_info,
SourceInfo::new(Pos::new(1, 11), Pos::new(1, 19))
);
let mut variables = VariableSet::new();
variables.insert(
String::from("base_url"),
Value::String(String::from("http://localhost:8000")),
);
let result = eval_equal(&expected, &variables, &value, &context_dir).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "string <http://localhost:8000>");
assert_eq!(result.expected, "string <http://localhost:8000>");
}
#[test]
fn test_assert_value_greater() {
assert_eq!(
assert_values_greater(
&Value::Number(Number::Integer(2)),
&Value::Number(Number::Integer(1))
),
PredicateResult {
success: true,
type_mismatch: false,
actual: "integer <2>".to_string(),
expected: "greater than integer <1>".to_string(),
}
);
assert_eq!(
assert_values_greater(
&Value::Number(Number::Integer(1)),
&Value::Number(Number::Integer(1))
),
PredicateResult {
success: false,
type_mismatch: false,
actual: "integer <1>".to_string(),
expected: "greater than integer <1>".to_string(),
}
);
assert_eq!(
assert_values_greater(
&Value::Number(Number::Float(1.1)),
&Value::Number(Number::Integer(1))
),
PredicateResult {
success: true,
type_mismatch: false,
actual: "float <1.1>".to_string(),
expected: "greater than integer <1>".to_string(),
}
);
assert_eq!(
assert_values_greater(
&Value::Number(Number::Float(1.1)),
&Value::Number(Number::Integer(2))
),
PredicateResult {
success: false,
type_mismatch: false,
actual: "float <1.1>".to_string(),
expected: "greater than integer <2>".to_string(),
}
);
}
#[test]
fn test_predicate_is_empty_are_false() {
let value = Value::List(vec![Value::Number(Number::Integer(1))]);
let result = eval_is_empty(&value).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "count equals to 1");
assert_eq!(result.expected, "count equals to 0");
let value = Value::Nodeset(12);
let result = eval_is_empty(&value).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "count equals to 12");
assert_eq!(result.expected, "count equals to 0");
}
#[test]
fn test_predicate_is_empty_are_true() {
let value = Value::List(vec![]);
let result = eval_is_empty(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "count equals to 0");
assert_eq!(result.expected, "count equals to 0");
let value = Value::Nodeset(0);
let result = eval_is_empty(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "count equals to 0");
assert_eq!(result.expected, "count equals to 0");
}
#[test]
fn test_predicate_type() {
let value = Value::Number(Number::Integer(1));
let result = eval_is_integer(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "integer <1>");
assert_eq!(result.expected, "integer");
let value = Value::Number(Number::Float(1.0));
let result = eval_is_integer(&value).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "float <1.0>");
assert_eq!(result.expected, "integer");
}
#[test]
fn test_predicate_not_with_different_types() {
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let predicate = Predicate {
not: true,
space0: whitespace(),
predicate_func: PredicateFunc {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
value: PredicateFuncValue::Equal {
space0: whitespace(),
value: PredicateValue::Null,
},
},
};
let variables = VariableSet::new();
assert!(
eval_predicate(
&predicate,
&variables,
&Some(Value::Number(Number::Integer(1))),
&context_dir
)
.is_ok()
);
let predicate = Predicate {
not: true,
space0: whitespace(),
predicate_func: PredicateFunc {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
value: PredicateFuncValue::StartWith {
space0: whitespace(),
value: PredicateValue::String(Template::new(
None,
vec![TemplateElement::String {
value: "toto".to_string(),
source: "toto".to_source(),
}],
SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
)),
},
},
};
let error = eval_predicate(
&predicate,
&variables,
&Some(Value::Number(Number::Integer(1))),
&context_dir,
)
.unwrap_err();
assert_eq!(
error.kind,
RunnerErrorKind::AssertFailure {
actual: "integer <1>".to_string(),
expected: "not starts with string <toto>".to_string(),
type_mismatch: true,
}
);
}
#[test]
fn test_date_predicate() {
let value = Value::Date(
chrono::TimeZone::with_ymd_and_hms(&chrono::Utc, 2002, 6, 16, 10, 10, 10).unwrap(),
);
let result = eval_is_date(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "date <2002-06-16 10:10:10 UTC>");
assert_eq!(result.expected, "date");
let value = Value::String("toto".to_string());
let result = eval_is_date(&value).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "string <toto>");
assert_eq!(result.expected, "date");
}
#[test]
fn test_no_type_mismatch_with_none_value() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let predicate = Predicate {
not: false,
space0: whitespace(),
predicate_func: PredicateFunc {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
value: PredicateFuncValue::Equal {
space0: whitespace(),
value: PredicateValue::Null,
},
},
};
let error = eval_predicate(&predicate, &variables, &None, &context_dir)
.err()
.unwrap();
assert_eq!(
error.kind,
RunnerErrorKind::AssertFailure {
actual: "none".to_string(),
expected: "null".to_string(),
type_mismatch: false,
}
);
let predicate = Predicate {
not: true,
space0: whitespace(),
predicate_func: PredicateFunc {
source_info: SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0)),
value: PredicateFuncValue::Equal {
space0: whitespace(),
value: PredicateValue::Null,
},
},
};
let variables = VariableSet::new();
assert!(eval_predicate(&predicate, &variables, &None, &context_dir).is_ok());
}
#[test]
fn test_predicate_match() {
let variables = VariableSet::new();
let current_dir = Path::new("/home");
let file_root = Path::new("file_root");
let context_dir = ContextDir::new(current_dir, file_root);
let regex = Regex {
inner: regex::Regex::new(r#"a{3}"#).unwrap(),
source: r#"/a{3}/"#.to_source(),
};
let expected = PredicateValue::Regex(regex);
let value = Value::String("aa".to_string());
let source_info = SourceInfo::new(Pos::new(0, 0), Pos::new(0, 0));
let result = eval_match(&expected, source_info, &variables, &value, &context_dir).unwrap();
assert!(!result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "string <aa>");
assert_eq!(result.expected, "matches regex </a{3}/>");
}
#[test]
fn test_predicate_is_iso_date() {
let value = Value::String("2020-03-09T22:18:26.625Z".to_string());
let result = eval_is_iso_date(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "2020-03-09T22:18:26.625Z");
assert_eq!(
result.expected,
"string with format YYYY-MM-DDTHH:mm:ss.sssZ"
);
}
#[test]
fn test_predicate_is_number() {
let value = Value::Number(Number::Integer(1));
let result = eval_is_number(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "integer <1>");
assert_eq!(result.expected, "number");
let value = Value::Number(Number::Float(1.0));
let result = eval_is_number(&value).unwrap();
assert!(result.success);
assert!(!result.type_mismatch);
assert_eq!(result.actual, "float <1.0>");
assert_eq!(result.expected, "number");
}
}