mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-26 11:43:08 +03:00
Splits eval_query function in multiple functions.
This commit is contained in:
parent
689064e4c4
commit
5fccfe706d
@ -33,11 +33,12 @@ use crate::runner::response::eval_version_status_asserts;
|
|||||||
use crate::runner::runner_options::RunnerOptions;
|
use crate::runner::runner_options::RunnerOptions;
|
||||||
use crate::runner::template::eval_template;
|
use crate::runner::template::eval_template;
|
||||||
|
|
||||||
/// Runs an `entry` with `http_client` and returns one or more
|
/// Runs an `entry` with `http_client` and returns one [`EntryResult`].
|
||||||
/// [`EntryResult`] (if following redirect).
|
|
||||||
///
|
///
|
||||||
/// `variables` are used to render values at runtime, and can be updated
|
/// The `calls` field of the [`EntryResult`] contains a list of HTTP requests and responses that have
|
||||||
/// by captures.
|
/// 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(
|
pub fn run(
|
||||||
entry: &Entry,
|
entry: &Entry,
|
||||||
entry_index: usize,
|
entry_index: usize,
|
||||||
@ -116,6 +117,10 @@ pub fn run(
|
|||||||
|
|
||||||
// We runs capture and asserts on the last HTTP request/response chains.
|
// We runs capture and asserts on the last HTTP request/response chains.
|
||||||
let (_, http_response) = calls.last().unwrap();
|
let (_, http_response) = calls.last().unwrap();
|
||||||
|
let time_in_ms = calls
|
||||||
|
.iter()
|
||||||
|
.map(|(_, resp)| resp.duration.as_millis())
|
||||||
|
.sum();
|
||||||
let calls: Vec<Call> = calls
|
let calls: Vec<Call> = calls
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(req, resp)| Call {
|
.map(|(req, resp)| Call {
|
||||||
@ -123,7 +128,6 @@ pub fn run(
|
|||||||
response: resp.clone(),
|
response: resp.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let time_in_ms = calls.iter().map(|c| c.response.duration.as_millis()).sum();
|
|
||||||
|
|
||||||
// We proceed asserts and captures in this order:
|
// We proceed asserts and captures in this order:
|
||||||
// 1. first, check implicit assert on status and version. If KO, test is failed
|
// 1. first, check implicit assert on status and version. If KO, test is failed
|
||||||
|
@ -30,17 +30,53 @@ use sha2::Digest;
|
|||||||
|
|
||||||
pub type QueryResult = Result<Option<Value>, Error>;
|
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(
|
pub fn eval_query(
|
||||||
query: &Query,
|
query: &Query,
|
||||||
variables: &HashMap<String, Value>,
|
variables: &HashMap<String, Value>,
|
||||||
http_response: &http::Response,
|
http_response: &http::Response,
|
||||||
) -> QueryResult {
|
) -> QueryResult {
|
||||||
match query.value.clone() {
|
match query.value.clone() {
|
||||||
QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))),
|
QueryValue::Status {} => eval_query_status(http_response),
|
||||||
QueryValue::Url {} => Ok(Some(Value::String(http_response.url.clone()))),
|
QueryValue::Url {} => eval_query_url(http_response),
|
||||||
QueryValue::Header { name, .. } => {
|
QueryValue::Header { name, .. } => eval_query_header(http_response, &name, variables),
|
||||||
let header_name = eval_template(&name, variables)?;
|
QueryValue::Cookie {
|
||||||
let values = http_response.get_header_values(&header_name);
|
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() {
|
if values.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if values.len() == 1 {
|
} else if values.len() == 1 {
|
||||||
@ -54,16 +90,19 @@ pub fn eval_query(
|
|||||||
Ok(Some(Value::List(values)))
|
Ok(Some(Value::List(values)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueryValue::Cookie {
|
|
||||||
expr: CookiePath { name, attribute },
|
fn eval_query_cookie(
|
||||||
..
|
response: &http::Response,
|
||||||
} => {
|
name: &Template,
|
||||||
let cookie_name = eval_template(&name, variables)?;
|
attribute: &Option<CookieAttribute>,
|
||||||
match http_response.get_cookie(cookie_name) {
|
variables: &HashMap<String, Value>,
|
||||||
|
) -> QueryResult {
|
||||||
|
let name = eval_template(name, variables)?;
|
||||||
|
match response.get_cookie(name) {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(cookie) => {
|
Some(cookie) => {
|
||||||
let attribute_name = if let Some(attribute) = attribute {
|
let attribute_name = if let Some(attribute) = attribute {
|
||||||
attribute.name
|
attribute.name.clone()
|
||||||
} else {
|
} else {
|
||||||
CookieAttributeName::Value("Value".to_string())
|
CookieAttributeName::Value("Value".to_string())
|
||||||
};
|
};
|
||||||
@ -71,28 +110,35 @@ pub fn eval_query(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueryValue::Body {} => {
|
|
||||||
// can return a string if encoding is known and utf8
|
fn eval_query_body(response: &http::Response, query_source_info: &SourceInfo) -> QueryResult {
|
||||||
match http_response.text() {
|
// Can return a string if encoding is known and utf8.
|
||||||
|
match response.text() {
|
||||||
Ok(s) => Ok(Some(Value::String(s))),
|
Ok(s) => Ok(Some(Value::String(s))),
|
||||||
Err(inner) => Err(Error {
|
Err(inner) => Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
assert: false,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueryValue::Xpath { expr, .. } => {
|
|
||||||
let source_info = &expr.source_info;
|
fn eval_query_xpath(
|
||||||
let value = eval_template(&expr, variables)?;
|
response: &http::Response,
|
||||||
match http_response.text() {
|
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 {
|
Err(inner) => Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
assert: false,
|
||||||
}),
|
}),
|
||||||
Ok(xml) => {
|
Ok(xml) => {
|
||||||
let result = if http_response.is_html() {
|
let result = if response.is_html() {
|
||||||
xpath::eval_html(&xml, &value)
|
xpath::eval_html(&xml, &value)
|
||||||
} else {
|
} else {
|
||||||
xpath::eval_xml(&xml, &value)
|
xpath::eval_xml(&xml, &value)
|
||||||
@ -100,17 +146,17 @@ pub fn eval_query(
|
|||||||
match result {
|
match result {
|
||||||
Ok(value) => Ok(Some(value)),
|
Ok(value) => Ok(Some(value)),
|
||||||
Err(xpath::XpathError::InvalidXml {}) => Err(Error {
|
Err(xpath::XpathError::InvalidXml {}) => Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::QueryInvalidXml,
|
inner: RunnerError::QueryInvalidXml,
|
||||||
assert: false,
|
assert: false,
|
||||||
}),
|
}),
|
||||||
Err(xpath::XpathError::InvalidHtml {}) => Err(Error {
|
Err(xpath::XpathError::InvalidHtml {}) => Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::QueryInvalidXml,
|
inner: RunnerError::QueryInvalidXml,
|
||||||
assert: false,
|
assert: false,
|
||||||
}),
|
}),
|
||||||
Err(xpath::XpathError::Eval {}) => Err(Error {
|
Err(xpath::XpathError::Eval {}) => Err(Error {
|
||||||
source_info: source_info.clone(),
|
source_info: expr_source_info.clone(),
|
||||||
inner: RunnerError::QueryInvalidXpathEval,
|
inner: RunnerError::QueryInvalidXpathEval,
|
||||||
assert: false,
|
assert: false,
|
||||||
}),
|
}),
|
||||||
@ -121,23 +167,29 @@ pub fn eval_query(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueryValue::Jsonpath { expr, .. } => {
|
|
||||||
let value = eval_template(&expr, variables)?;
|
fn eval_query_jsonpath(
|
||||||
let source_info = expr.source_info;
|
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()) {
|
let jsonpath_query = match jsonpath::parse(value.as_str()) {
|
||||||
Ok(q) => q,
|
Ok(q) => q,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info,
|
source_info: expr_source_info.clone(),
|
||||||
inner: RunnerError::QueryInvalidJsonpathExpression { value },
|
inner: RunnerError::QueryInvalidJsonpathExpression { value },
|
||||||
assert: false,
|
assert: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let json = match http_response.text() {
|
let json = match response.text() {
|
||||||
Err(inner) => {
|
Err(inner) => {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
assert: false,
|
||||||
});
|
});
|
||||||
@ -147,7 +199,7 @@ pub fn eval_query(
|
|||||||
let value = match serde_json::from_str(json.as_str()) {
|
let value = match serde_json::from_str(json.as_str()) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::QueryInvalidJson,
|
inner: RunnerError::QueryInvalidJson,
|
||||||
assert: false,
|
assert: false,
|
||||||
});
|
});
|
||||||
@ -158,30 +210,37 @@ pub fn eval_query(
|
|||||||
if results.is_empty() {
|
if results.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else if results.len() == 1 {
|
} 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())))
|
Ok(Some(Value::from_json(results.get(0).unwrap())))
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(Value::from_json(&serde_json::Value::Array(results))))
|
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) => {
|
Err(inner) => {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
assert: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
};
|
};
|
||||||
let re = match value {
|
let re = match regex {
|
||||||
RegexValue::Template(t) => {
|
RegexValue::Template(t) => {
|
||||||
let value = eval_template(&t, variables)?;
|
let value = eval_template(t, variables)?;
|
||||||
match Regex::new(value.as_str()) {
|
match Regex::new(value.as_str()) {
|
||||||
Ok(re) => re,
|
Ok(re) => re,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let source_info = t.source_info;
|
let source_info = t.source_info.clone();
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info,
|
source_info,
|
||||||
inner: RunnerError::InvalidRegex(),
|
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()) {
|
match re.captures(s.as_str()) {
|
||||||
Some(captures) => match captures.get(1) {
|
Some(captures) => match captures.get(1) {
|
||||||
@ -200,31 +259,37 @@ pub fn eval_query(
|
|||||||
None => Ok(None),
|
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()) {
|
if let Some(value) = variables.get(name.as_str()) {
|
||||||
Ok(Some(value.clone()))
|
Ok(Some(value.clone()))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QueryValue::Duration {} => Ok(Some(Value::Integer(
|
|
||||||
http_response.duration.as_millis() as i64
|
fn eval_query_duration(response: &http::Response) -> QueryResult {
|
||||||
))),
|
Ok(Some(Value::Integer(response.duration.as_millis() as i64)))
|
||||||
QueryValue::Bytes {} => match http_response.uncompress_body() {
|
}
|
||||||
|
|
||||||
|
fn eval_query_bytes(response: &http::Response, query_source_info: &SourceInfo) -> QueryResult {
|
||||||
|
match response.uncompress_body() {
|
||||||
Ok(s) => Ok(Some(Value::Bytes(s))),
|
Ok(s) => Ok(Some(Value::Bytes(s))),
|
||||||
Err(inner) => Err(Error {
|
Err(inner) => Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
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,
|
Ok(s) => s,
|
||||||
Err(inner) => {
|
Err(inner) => {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
assert: false,
|
||||||
})
|
})
|
||||||
@ -236,12 +301,13 @@ pub fn eval_query(
|
|||||||
let bytes = Value::Bytes(result[..].to_vec());
|
let bytes = Value::Bytes(result[..].to_vec());
|
||||||
Ok(Some(bytes))
|
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,
|
Ok(s) => s,
|
||||||
Err(inner) => {
|
Err(inner) => {
|
||||||
return Err(Error {
|
return Err(Error {
|
||||||
source_info: query.source_info.clone(),
|
source_info: query_source_info.clone(),
|
||||||
inner: RunnerError::from(inner),
|
inner: RunnerError::from(inner),
|
||||||
assert: false,
|
assert: false,
|
||||||
})
|
})
|
||||||
@ -250,10 +316,8 @@ pub fn eval_query(
|
|||||||
let bytes = md5::compute(bytes).to_vec();
|
let bytes = md5::compute(bytes).to_vec();
|
||||||
Ok(Some(Value::Bytes(bytes)))
|
Ok(Some(Value::Bytes(bytes)))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eval_cookie_attribute_name(
|
fn eval_cookie_attribute_name(
|
||||||
cookie_attribute_name: CookieAttributeName,
|
cookie_attribute_name: CookieAttributeName,
|
||||||
cookie: http::ResponseCookie,
|
cookie: http::ResponseCookie,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
|
Loading…
Reference in New Issue
Block a user