Splits eval_query function in multiple functions.

This commit is contained in:
jcamiel 2023-03-01 18:48:13 +01:00
parent 689064e4c4
commit 5fccfe706d
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
2 changed files with 276 additions and 208 deletions

View File

@ -33,11 +33,12 @@ use crate::runner::response::eval_version_status_asserts;
use crate::runner::runner_options::RunnerOptions;
use crate::runner::template::eval_template;
/// Runs an `entry` with `http_client` and returns one or more
/// [`EntryResult`] (if following redirect).
/// Runs an `entry` with `http_client` and returns one [`EntryResult`].
///
/// `variables` are used to render values at runtime, and can be updated
/// by captures.
/// The `calls` field of the [`EntryResult`] contains a list of HTTP requests and responses that have
/// been executed. If `http_client` has been configured to follow redirection, the `calls` list contains
/// every step of the redirection for the first to the last.
/// `variables` are used to render values at runtime, and can be updated by captures.
pub fn run(
entry: &Entry,
entry_index: usize,
@ -116,6 +117,10 @@ pub fn run(
// We runs capture and asserts on the last HTTP request/response chains.
let (_, http_response) = calls.last().unwrap();
let time_in_ms = calls
.iter()
.map(|(_, resp)| resp.duration.as_millis())
.sum();
let calls: Vec<Call> = calls
.iter()
.map(|(req, resp)| Call {
@ -123,7 +128,6 @@ pub fn run(
response: resp.clone(),
})
.collect();
let time_in_ms = calls.iter().map(|c| c.response.duration.as_millis()).sum();
// We proceed asserts and captures in this order:
// 1. first, check implicit assert on status and version. If KO, test is failed

View File

@ -30,17 +30,53 @@ use sha2::Digest;
pub type QueryResult = Result<Option<Value>, Error>;
/// Evaluates this `query` and returns a [`QueryResult`], using the HTTP response `http_response` and `variables`.
pub fn eval_query(
query: &Query,
variables: &HashMap<String, Value>,
http_response: &http::Response,
) -> QueryResult {
match query.value.clone() {
QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))),
QueryValue::Url {} => Ok(Some(Value::String(http_response.url.clone()))),
QueryValue::Header { name, .. } => {
let header_name = eval_template(&name, variables)?;
let values = http_response.get_header_values(&header_name);
QueryValue::Status {} => eval_query_status(http_response),
QueryValue::Url {} => eval_query_url(http_response),
QueryValue::Header { name, .. } => eval_query_header(http_response, &name, variables),
QueryValue::Cookie {
expr: CookiePath { name, attribute },
..
} => eval_query_cookie(http_response, &name, &attribute, variables),
QueryValue::Body {} => eval_query_body(http_response, &query.source_info),
QueryValue::Xpath { expr, .. } => {
eval_query_xpath(http_response, &expr, variables, &query.source_info)
}
QueryValue::Jsonpath { expr, .. } => {
eval_query_jsonpath(http_response, &expr, variables, &query.source_info)
}
QueryValue::Regex { value, .. } => {
eval_query_regex(http_response, &value, variables, &query.source_info)
}
QueryValue::Variable { name, .. } => eval_query_variable(&name, variables),
QueryValue::Duration {} => eval_query_duration(http_response),
QueryValue::Bytes {} => eval_query_bytes(http_response, &query.source_info),
QueryValue::Sha256 {} => eval_query_sha256(http_response, &query.source_info),
QueryValue::Md5 {} => eval_query_md5(http_response, &query.source_info),
}
}
fn eval_query_status(response: &http::Response) -> QueryResult {
Ok(Some(Value::Integer(i64::from(response.status))))
}
fn eval_query_url(response: &http::Response) -> QueryResult {
Ok(Some(Value::String(response.url.clone())))
}
fn eval_query_header(
response: &http::Response,
header: &Template,
variables: &HashMap<String, Value>,
) -> QueryResult {
let header = eval_template(header, variables)?;
let values = response.get_header_values(&header);
if values.is_empty() {
Ok(None)
} else if values.len() == 1 {
@ -53,46 +89,56 @@ pub fn eval_query(
.collect();
Ok(Some(Value::List(values)))
}
}
QueryValue::Cookie {
expr: CookiePath { name, attribute },
..
} => {
let cookie_name = eval_template(&name, variables)?;
match http_response.get_cookie(cookie_name) {
}
fn eval_query_cookie(
response: &http::Response,
name: &Template,
attribute: &Option<CookieAttribute>,
variables: &HashMap<String, Value>,
) -> QueryResult {
let name = eval_template(name, variables)?;
match response.get_cookie(name) {
None => Ok(None),
Some(cookie) => {
let attribute_name = if let Some(attribute) = attribute {
attribute.name
attribute.name.clone()
} else {
CookieAttributeName::Value("Value".to_string())
};
Ok(eval_cookie_attribute_name(attribute_name, cookie))
}
}
}
QueryValue::Body {} => {
// can return a string if encoding is known and utf8
match http_response.text() {
}
fn eval_query_body(response: &http::Response, query_source_info: &SourceInfo) -> QueryResult {
// Can return a string if encoding is known and utf8.
match response.text() {
Ok(s) => Ok(Some(Value::String(s))),
Err(inner) => Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
}),
}
}
QueryValue::Xpath { expr, .. } => {
let source_info = &expr.source_info;
let value = eval_template(&expr, variables)?;
match http_response.text() {
}
fn eval_query_xpath(
response: &http::Response,
expr: &Template,
variables: &HashMap<String, Value>,
query_source_info: &SourceInfo,
) -> QueryResult {
let expr_source_info = &expr.source_info;
let value = eval_template(expr, variables)?;
match response.text() {
Err(inner) => Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
}),
Ok(xml) => {
let result = if http_response.is_html() {
let result = if response.is_html() {
xpath::eval_html(&xml, &value)
} else {
xpath::eval_xml(&xml, &value)
@ -100,17 +146,17 @@ pub fn eval_query(
match result {
Ok(value) => Ok(Some(value)),
Err(xpath::XpathError::InvalidXml {}) => Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::QueryInvalidXml,
assert: false,
}),
Err(xpath::XpathError::InvalidHtml {}) => Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::QueryInvalidXml,
assert: false,
}),
Err(xpath::XpathError::Eval {}) => Err(Error {
source_info: source_info.clone(),
source_info: expr_source_info.clone(),
inner: RunnerError::QueryInvalidXpathEval,
assert: false,
}),
@ -120,24 +166,30 @@ pub fn eval_query(
}
}
}
}
QueryValue::Jsonpath { expr, .. } => {
let value = eval_template(&expr, variables)?;
let source_info = expr.source_info;
}
fn eval_query_jsonpath(
response: &http::Response,
expr: &Template,
variables: &HashMap<String, Value>,
query_source_info: &SourceInfo,
) -> QueryResult {
let value = eval_template(expr, variables)?;
let expr_source_info = &expr.source_info;
let jsonpath_query = match jsonpath::parse(value.as_str()) {
Ok(q) => q,
Err(_) => {
return Err(Error {
source_info,
source_info: expr_source_info.clone(),
inner: RunnerError::QueryInvalidJsonpathExpression { value },
assert: false,
});
}
};
let json = match http_response.text() {
let json = match response.text() {
Err(inner) => {
return Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
});
@ -147,7 +199,7 @@ pub fn eval_query(
let value = match serde_json::from_str(json.as_str()) {
Err(_) => {
return Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::QueryInvalidJson,
assert: false,
});
@ -158,30 +210,37 @@ pub fn eval_query(
if results.is_empty() {
Ok(None)
} else if results.len() == 1 {
// list coercions
// All results from JSONPath queries return an array <https://goessner.net/articles/JsonPath/>.
// For usability, we coerce lists that have only one element to this element.
Ok(Some(Value::from_json(results.get(0).unwrap())))
} else {
Ok(Some(Value::from_json(&serde_json::Value::Array(results))))
}
}
QueryValue::Regex { value, .. } => {
let s = match http_response.text() {
}
fn eval_query_regex(
response: &http::Response,
regex: &RegexValue,
variables: &HashMap<String, Value>,
query_source_info: &SourceInfo,
) -> QueryResult {
let s = match response.text() {
Err(inner) => {
return Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
});
}
Ok(v) => v,
};
let re = match value {
let re = match regex {
RegexValue::Template(t) => {
let value = eval_template(&t, variables)?;
let value = eval_template(t, variables)?;
match Regex::new(value.as_str()) {
Ok(re) => re,
Err(_) => {
let source_info = t.source_info;
let source_info = t.source_info.clone();
return Err(Error {
source_info,
inner: RunnerError::InvalidRegex(),
@ -190,7 +249,7 @@ pub fn eval_query(
}
}
}
RegexValue::Regex(re) => re.inner,
RegexValue::Regex(re) => re.inner.clone(),
};
match re.captures(s.as_str()) {
Some(captures) => match captures.get(1) {
@ -199,32 +258,38 @@ pub fn eval_query(
},
None => Ok(None),
}
}
QueryValue::Variable { name, .. } => {
let name = eval_template(&name, variables)?;
}
fn eval_query_variable(name: &Template, variables: &HashMap<String, Value>) -> QueryResult {
let name = eval_template(name, variables)?;
if let Some(value) = variables.get(name.as_str()) {
Ok(Some(value.clone()))
} else {
Ok(None)
}
}
QueryValue::Duration {} => Ok(Some(Value::Integer(
http_response.duration.as_millis() as i64
))),
QueryValue::Bytes {} => match http_response.uncompress_body() {
}
fn eval_query_duration(response: &http::Response) -> QueryResult {
Ok(Some(Value::Integer(response.duration.as_millis() as i64)))
}
fn eval_query_bytes(response: &http::Response, query_source_info: &SourceInfo) -> QueryResult {
match response.uncompress_body() {
Ok(s) => Ok(Some(Value::Bytes(s))),
Err(inner) => Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
}),
},
QueryValue::Sha256 {} => {
let bytes = match http_response.uncompress_body() {
}
}
fn eval_query_sha256(response: &http::Response, query_source_info: &SourceInfo) -> QueryResult {
let bytes = match response.uncompress_body() {
Ok(s) => s,
Err(inner) => {
return Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
})
@ -235,13 +300,14 @@ pub fn eval_query(
let result = hasher.finalize();
let bytes = Value::Bytes(result[..].to_vec());
Ok(Some(bytes))
}
QueryValue::Md5 {} => {
let bytes = match http_response.uncompress_body() {
}
fn eval_query_md5(response: &http::Response, query_source_info: &SourceInfo) -> QueryResult {
let bytes = match response.uncompress_body() {
Ok(s) => s,
Err(inner) => {
return Err(Error {
source_info: query.source_info.clone(),
source_info: query_source_info.clone(),
inner: RunnerError::from(inner),
assert: false,
})
@ -249,11 +315,9 @@ pub fn eval_query(
};
let bytes = md5::compute(bytes).to_vec();
Ok(Some(Value::Bytes(bytes)))
}
}
}
pub fn eval_cookie_attribute_name(
fn eval_cookie_attribute_name(
cookie_attribute_name: CookieAttributeName,
cookie: http::ResponseCookie,
) -> Option<Value> {