mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-28 12:53:01 +03:00
Merge pull request #61 from Orange-OpenSource/refacto/remove_inherent_impl
Refacto - Remove inherent impl
This commit is contained in:
commit
198caf5603
@ -89,7 +89,7 @@ impl Lintable<Request> for Request {
|
||||
Some(body) => Some(body.lint()),
|
||||
};
|
||||
let mut sections: Vec<Section> = self.sections.iter().map(|e| e.lint()).collect();
|
||||
sections.sort_by_key(|k| k.value.clone().index());
|
||||
sections.sort_by_key(|k| section_value_index(k.value.clone()));
|
||||
|
||||
let source_info = SourceInfo::init(0, 0, 0, 0);
|
||||
Request {
|
||||
@ -128,7 +128,7 @@ impl Lintable<Response> for Response {
|
||||
let line_terminator0 = self.clone().line_terminator0;
|
||||
let headers = self.headers.iter().map(|e| e.lint()).collect();
|
||||
let mut sections: Vec<Section> = self.sections.iter().map(|e| e.lint()).collect();
|
||||
sections.sort_by_key(|k| k.value.clone().index());
|
||||
sections.sort_by_key(|k| section_value_index(k.value.clone()));
|
||||
|
||||
let b = self.body.clone();
|
||||
Response {
|
||||
@ -194,16 +194,14 @@ impl Lintable<SectionValue> for SectionValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl SectionValue {
|
||||
fn index(self) -> u32 {
|
||||
match self {
|
||||
SectionValue::QueryParams(_) => 0,
|
||||
SectionValue::FormParams(_) => 1,
|
||||
SectionValue::MultipartFormData(_) => 2,
|
||||
SectionValue::Cookies(_) => 3,
|
||||
SectionValue::Captures(_) => 0,
|
||||
SectionValue::Asserts(_) => 1,
|
||||
}
|
||||
fn section_value_index(section_value: SectionValue) -> u32 {
|
||||
match section_value {
|
||||
SectionValue::QueryParams(_) => 0,
|
||||
SectionValue::FormParams(_) => 1,
|
||||
SectionValue::MultipartFormData(_) => 2,
|
||||
SectionValue::Cookies(_) => 3,
|
||||
SectionValue::Captures(_) => 0,
|
||||
SectionValue::Asserts(_) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ use crate::http;
|
||||
|
||||
use super::core::*;
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::predicate::eval_predicate;
|
||||
use super::query::eval_query;
|
||||
use super::value::Value;
|
||||
|
||||
impl AssertResult {
|
||||
@ -126,24 +128,22 @@ impl AssertResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl Assert {
|
||||
pub fn eval(
|
||||
self,
|
||||
http_response: http::Response,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> AssertResult {
|
||||
let actual = self.query.eval(variables, http_response);
|
||||
let source_info = self.predicate.clone().predicate_func.source_info;
|
||||
let predicate_result = match actual.clone() {
|
||||
Err(_) => None,
|
||||
Ok(actual) => Some(self.predicate.eval(variables, actual)),
|
||||
};
|
||||
pub fn eval_assert(
|
||||
assert: Assert,
|
||||
variables: &HashMap<String, Value>,
|
||||
http_response: http::Response,
|
||||
) -> AssertResult {
|
||||
let actual = eval_query(assert.query.clone(), variables, http_response);
|
||||
let source_info = assert.predicate.clone().predicate_func.source_info;
|
||||
let predicate_result = match actual.clone() {
|
||||
Err(_) => None,
|
||||
Ok(actual) => Some(eval_predicate(assert.predicate, variables, actual)),
|
||||
};
|
||||
|
||||
AssertResult::Explicit {
|
||||
actual,
|
||||
source_info,
|
||||
predicate_result,
|
||||
}
|
||||
AssertResult::Explicit {
|
||||
actual,
|
||||
source_info,
|
||||
predicate_result,
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,7 +192,11 @@ pub mod tests {
|
||||
fn test_eval() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
assert_count_user().eval(http::xml_three_users_http_response(), &variables),
|
||||
eval_assert(
|
||||
assert_count_user(),
|
||||
&variables,
|
||||
http::xml_three_users_http_response()
|
||||
),
|
||||
AssertResult::Explicit {
|
||||
actual: Ok(Some(Value::Nodeset(3))),
|
||||
source_info: SourceInfo::init(1, 14, 1, 27),
|
||||
|
@ -24,62 +24,60 @@ use std::path::Path;
|
||||
use crate::ast::*;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::json::eval_json_value;
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
impl Body {
|
||||
pub fn eval(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
self.value.eval(variables, context_dir)
|
||||
}
|
||||
pub fn eval_body(
|
||||
body: Body,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
eval_bytes(body.value, variables, context_dir)
|
||||
}
|
||||
|
||||
impl Bytes {
|
||||
pub fn eval(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
match self {
|
||||
Bytes::RawString { value, .. } => {
|
||||
let value = value.eval(variables)?;
|
||||
Ok(value.into_bytes())
|
||||
}
|
||||
Bytes::Base64 { value, .. } => Ok(value),
|
||||
Bytes::Xml { value, .. } => Ok(value.into_bytes()),
|
||||
Bytes::Json { value, .. } => {
|
||||
let value = value.eval(variables)?;
|
||||
Ok(value.into_bytes())
|
||||
}
|
||||
Bytes::File { filename, .. } => {
|
||||
let path = Path::new(filename.value.as_str());
|
||||
let absolute_filename = if path.is_absolute() {
|
||||
filename.clone().value
|
||||
} else {
|
||||
Path::new(context_dir.as_str())
|
||||
.join(filename.value)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
};
|
||||
match File::open(absolute_filename.clone()) {
|
||||
Ok(f) => {
|
||||
let mut bytes = vec![];
|
||||
for byte in f.bytes() {
|
||||
bytes.push(byte.unwrap());
|
||||
}
|
||||
Ok(bytes)
|
||||
pub fn eval_bytes(
|
||||
bytes: Bytes,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
match bytes {
|
||||
Bytes::RawString { value, .. } => {
|
||||
let value = eval_template(value, variables)?;
|
||||
Ok(value.into_bytes())
|
||||
}
|
||||
Bytes::Base64 { value, .. } => Ok(value),
|
||||
Bytes::Xml { value, .. } => Ok(value.into_bytes()),
|
||||
Bytes::Json { value, .. } => {
|
||||
let value = eval_json_value(value, variables)?;
|
||||
Ok(value.into_bytes())
|
||||
}
|
||||
Bytes::File { filename, .. } => {
|
||||
let path = Path::new(filename.value.as_str());
|
||||
let absolute_filename = if path.is_absolute() {
|
||||
filename.clone().value
|
||||
} else {
|
||||
Path::new(context_dir.as_str())
|
||||
.join(filename.value)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
};
|
||||
match File::open(absolute_filename.clone()) {
|
||||
Ok(f) => {
|
||||
let mut bytes = vec![];
|
||||
for byte in f.bytes() {
|
||||
bytes.push(byte.unwrap());
|
||||
}
|
||||
Err(_) => Err(Error {
|
||||
source_info: filename.source_info,
|
||||
inner: RunnerError::FileReadAccess {
|
||||
value: absolute_filename,
|
||||
},
|
||||
assert: false,
|
||||
}),
|
||||
Ok(bytes)
|
||||
}
|
||||
Err(_) => Err(Error {
|
||||
source_info: filename.source_info,
|
||||
inner: RunnerError::FileReadAccess {
|
||||
value: absolute_filename,
|
||||
},
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,7 +125,7 @@ mod tests {
|
||||
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
bytes.eval(&variables, "current_dir".to_string()).unwrap(),
|
||||
eval_bytes(bytes, &variables, "current_dir".to_string()).unwrap(),
|
||||
b"Hello World!"
|
||||
);
|
||||
}
|
||||
@ -151,8 +149,7 @@ mod tests {
|
||||
|
||||
let variables = HashMap::new();
|
||||
|
||||
let error = bytes
|
||||
.eval(&variables, "current_dir".to_string())
|
||||
let error = eval_bytes(bytes, &variables, "current_dir".to_string())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
|
@ -23,76 +23,74 @@ use crate::http;
|
||||
|
||||
use super::core::RunnerError;
|
||||
use super::core::{CaptureResult, Error};
|
||||
use super::query::eval_query;
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
impl Capture {
|
||||
pub fn eval(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
http_response: http::Response,
|
||||
) -> Result<CaptureResult, Error> {
|
||||
let name = self.name.value;
|
||||
let value = self.query.clone().eval(variables, http_response)?;
|
||||
let value = match value {
|
||||
None => {
|
||||
return Err(Error {
|
||||
source_info: self.query.source_info,
|
||||
inner: RunnerError::NoQueryResult {},
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
Some(value) => match self.subquery {
|
||||
None => value,
|
||||
Some(subquery) => {
|
||||
let value = subquery.clone().eval(variables, value)?;
|
||||
match value {
|
||||
None => {
|
||||
return Err(Error {
|
||||
source_info: subquery.source_info,
|
||||
inner: RunnerError::NoQueryResult {},
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
Some(value) => value,
|
||||
pub fn eval_capture(
|
||||
capture: Capture,
|
||||
variables: &HashMap<String, Value>,
|
||||
http_response: http::Response,
|
||||
) -> Result<CaptureResult, Error> {
|
||||
let name = capture.name.value;
|
||||
let value = eval_query(capture.query.clone(), variables, http_response)?;
|
||||
let value = match value {
|
||||
None => {
|
||||
return Err(Error {
|
||||
source_info: capture.query.source_info,
|
||||
inner: RunnerError::NoQueryResult {},
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
Some(value) => match capture.subquery {
|
||||
None => value,
|
||||
Some(subquery) => {
|
||||
let value = eval_subquery(subquery.clone(), variables, value)?;
|
||||
match value {
|
||||
None => {
|
||||
return Err(Error {
|
||||
source_info: subquery.source_info,
|
||||
inner: RunnerError::NoQueryResult {},
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
Some(value) => value,
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(CaptureResult { name, value })
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(CaptureResult { name, value })
|
||||
}
|
||||
|
||||
impl Subquery {
|
||||
pub fn eval(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
value: Value,
|
||||
) -> Result<Option<Value>, Error> {
|
||||
match self.value {
|
||||
SubqueryValue::Regex { expr, .. } => {
|
||||
let source_info = expr.source_info.clone();
|
||||
let expr = expr.eval(variables)?;
|
||||
match value {
|
||||
Value::String(s) => match Regex::new(expr.as_str()) {
|
||||
Ok(re) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
fn eval_subquery(
|
||||
subquery: Subquery,
|
||||
variables: &HashMap<String, Value>,
|
||||
value: Value,
|
||||
) -> Result<Option<Value>, Error> {
|
||||
match subquery.value {
|
||||
SubqueryValue::Regex { expr, .. } => {
|
||||
let source_info = expr.source_info.clone();
|
||||
let expr = eval_template(expr, variables)?;
|
||||
match value {
|
||||
Value::String(s) => match Regex::new(expr.as_str()) {
|
||||
Ok(re) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
Err(_) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
}),
|
||||
None => Ok(None),
|
||||
},
|
||||
_ => Err(Error {
|
||||
source_info: self.source_info,
|
||||
inner: RunnerError::SubqueryInvalidInput,
|
||||
Err(_) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
},
|
||||
_ => Err(Error {
|
||||
source_info: subquery.source_info,
|
||||
inner: RunnerError::SubqueryInvalidInput,
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,8 +193,7 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
|
||||
let error = capture
|
||||
.eval(&variables, http::xml_three_users_http_response())
|
||||
let error = eval_capture(capture, &variables, http::xml_three_users_http_response())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info.start, Pos { line: 1, column: 7 });
|
||||
@ -251,9 +248,12 @@ pub mod tests {
|
||||
fn test_capture() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
user_count_capture()
|
||||
.eval(&variables, http::xml_three_users_http_response())
|
||||
.unwrap(),
|
||||
eval_capture(
|
||||
user_count_capture(),
|
||||
&variables,
|
||||
http::xml_three_users_http_response(),
|
||||
)
|
||||
.unwrap(),
|
||||
CaptureResult {
|
||||
name: "UserCount".to_string(),
|
||||
value: Value::from_f64(3.0),
|
||||
@ -261,9 +261,7 @@ pub mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
duration_capture()
|
||||
.eval(&variables, http::json_http_response())
|
||||
.unwrap(),
|
||||
eval_capture(duration_capture(), &variables, http::json_http_response()).unwrap(),
|
||||
CaptureResult {
|
||||
name: "duration".to_string(),
|
||||
value: Value::from_f64(1.5),
|
||||
@ -294,14 +292,18 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
subquery
|
||||
.clone()
|
||||
.eval(&variables, Value::String("Hello Bob!".to_string()))
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
eval_subquery(
|
||||
subquery.clone(),
|
||||
&variables,
|
||||
Value::String("Hello Bob!".to_string())
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String("Bob".to_string())
|
||||
);
|
||||
let error = subquery.eval(&variables, Value::Bool(true)).err().unwrap();
|
||||
let error = eval_subquery(subquery, &variables, Value::Bool(true))
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 1, 1, 20));
|
||||
assert_eq!(error.inner, RunnerError::SubqueryInvalidInput);
|
||||
}
|
||||
@ -327,10 +329,13 @@ pub mod tests {
|
||||
},
|
||||
},
|
||||
};
|
||||
let error = subquery
|
||||
.eval(&variables, Value::String("Hello Bob!".to_string()))
|
||||
.err()
|
||||
.unwrap();
|
||||
let error = eval_subquery(
|
||||
subquery,
|
||||
&variables,
|
||||
Value::String("Hello Bob!".to_string()),
|
||||
)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 7, 1, 20));
|
||||
assert_eq!(error.inner, RunnerError::InvalidRegex {});
|
||||
}
|
||||
|
@ -24,7 +24,10 @@ use crate::http::HttpError;
|
||||
|
||||
use super::core::*;
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::request::eval_request;
|
||||
use super::response::{eval_asserts, eval_captures};
|
||||
use super::value::Value;
|
||||
use crate::runner::request::{cookie_storage_clear, cookie_storage_set};
|
||||
|
||||
/// Run an entry with the hurl http client
|
||||
///
|
||||
@ -53,7 +56,7 @@ pub fn run(
|
||||
log_verbose: &impl Fn(&str),
|
||||
log_error_message: &impl Fn(bool, &str),
|
||||
) -> EntryResult {
|
||||
let http_request = match entry.clone().request.eval(variables, context_dir.clone()) {
|
||||
let http_request = match eval_request(entry.request.clone(), variables, context_dir.clone()) {
|
||||
Ok(r) => r,
|
||||
Err(error) => {
|
||||
return EntryResult {
|
||||
@ -75,7 +78,7 @@ pub fn run(
|
||||
// with cookie storage
|
||||
//
|
||||
use std::str::FromStr;
|
||||
if let Some(s) = entry.request.add_cookie_in_storage() {
|
||||
if let Some(s) = cookie_storage_set(entry.request.clone()) {
|
||||
if let Ok(cookie) = http::Cookie::from_str(s.as_str()) {
|
||||
http_client.add_cookie(cookie);
|
||||
} else {
|
||||
@ -85,7 +88,7 @@ pub fn run(
|
||||
);
|
||||
}
|
||||
}
|
||||
if entry.request.clear_cookie_storage() {
|
||||
if cookie_storage_clear(entry.request.clone()) {
|
||||
http_client.clear_cookie_storage();
|
||||
}
|
||||
|
||||
@ -142,7 +145,7 @@ pub fn run(
|
||||
|
||||
let captures = match entry.response.clone() {
|
||||
None => vec![],
|
||||
Some(response) => match response.eval_captures(http_response.clone(), variables) {
|
||||
Some(response) => match eval_captures(response, http_response.clone(), variables) {
|
||||
Ok(captures) => captures,
|
||||
Err(e) => {
|
||||
return EntryResult {
|
||||
@ -164,7 +167,7 @@ pub fn run(
|
||||
|
||||
let asserts = match entry.response {
|
||||
None => vec![],
|
||||
Some(response) => response.eval_asserts(variables, http_response.clone(), context_dir),
|
||||
Some(response) => eval_asserts(response, variables, http_response.clone(), context_dir),
|
||||
};
|
||||
let errors = asserts
|
||||
.iter()
|
||||
|
@ -22,18 +22,16 @@ use crate::ast::Expr;
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::value::Value;
|
||||
|
||||
impl Expr {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>) -> Result<Value, Error> {
|
||||
if let Some(value) = variables.get(self.variable.name.as_str()) {
|
||||
Ok(value.clone())
|
||||
} else {
|
||||
Err(Error {
|
||||
source_info: self.variable.source_info,
|
||||
inner: RunnerError::TemplateVariableNotDefined {
|
||||
name: self.variable.name,
|
||||
},
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
pub fn eval_expr(expr: Expr, variables: &HashMap<String, Value>) -> Result<Value, Error> {
|
||||
if let Some(value) = variables.get(expr.variable.name.as_str()) {
|
||||
Ok(value.clone())
|
||||
} else {
|
||||
Err(Error {
|
||||
source_info: expr.variable.source_info,
|
||||
inner: RunnerError::TemplateVariableNotDefined {
|
||||
name: expr.variable.name,
|
||||
},
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -20,53 +20,57 @@ use std::collections::HashMap;
|
||||
use crate::ast::{JsonListElement, JsonObjectElement, JsonValue};
|
||||
|
||||
use super::core::Error;
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
impl JsonValue {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>) -> Result<String, Error> {
|
||||
match self {
|
||||
JsonValue::Null {} => Ok("null".to_string()),
|
||||
JsonValue::Number(s) => Ok(s),
|
||||
JsonValue::String(template) => {
|
||||
let s = template.eval(variables)?;
|
||||
Ok(format!("\"{}\"", s))
|
||||
pub fn eval_json_value(
|
||||
json_value: JsonValue,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<String, Error> {
|
||||
match json_value {
|
||||
JsonValue::Null {} => Ok("null".to_string()),
|
||||
JsonValue::Number(s) => Ok(s),
|
||||
JsonValue::String(template) => {
|
||||
let s = eval_template(template, variables)?;
|
||||
Ok(format!("\"{}\"", s))
|
||||
}
|
||||
JsonValue::Boolean(v) => Ok(v.to_string()),
|
||||
JsonValue::List { space0, elements } => {
|
||||
let mut elems_string = vec![];
|
||||
for element in elements {
|
||||
let s = eval_json_list_element(element, variables)?;
|
||||
elems_string.push(s);
|
||||
}
|
||||
JsonValue::Boolean(v) => Ok(v.to_string()),
|
||||
JsonValue::List { space0, elements } => {
|
||||
let mut elems_string = vec![];
|
||||
for element in elements {
|
||||
let s = element.eval(variables)?;
|
||||
elems_string.push(s);
|
||||
}
|
||||
Ok(format!("[{}{}]", space0, elems_string.join(",")))
|
||||
}
|
||||
JsonValue::Object { space0, elements } => {
|
||||
let mut elems_string = vec![];
|
||||
for element in elements {
|
||||
let s = element.eval(variables)?;
|
||||
elems_string.push(s);
|
||||
}
|
||||
Ok(format!("{{{}{}}}", space0, elems_string.join(",")))
|
||||
Ok(format!("[{}{}]", space0, elems_string.join(",")))
|
||||
}
|
||||
JsonValue::Object { space0, elements } => {
|
||||
let mut elems_string = vec![];
|
||||
for element in elements {
|
||||
let s = eval_json_object_element(element, variables)?;
|
||||
elems_string.push(s);
|
||||
}
|
||||
Ok(format!("{{{}{}}}", space0, elems_string.join(",")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonListElement {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>) -> Result<String, Error> {
|
||||
let s = self.value.eval(variables)?;
|
||||
Ok(format!("{}{}{}", self.space0, s, self.space1))
|
||||
}
|
||||
pub fn eval_json_list_element(
|
||||
element: JsonListElement,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<String, Error> {
|
||||
let s = eval_json_value(element.value, variables)?;
|
||||
Ok(format!("{}{}{}", element.space0, s, element.space1))
|
||||
}
|
||||
|
||||
impl JsonObjectElement {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>) -> Result<String, Error> {
|
||||
let value = self.value.eval(variables)?;
|
||||
Ok(format!(
|
||||
"{}\"{}\"{}:{}{}{}",
|
||||
self.space0, self.name, self.space1, self.space2, value, self.space3
|
||||
))
|
||||
}
|
||||
pub fn eval_json_object_element(
|
||||
element: JsonObjectElement,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<String, Error> {
|
||||
let value = eval_json_value(element.value, variables)?;
|
||||
Ok(format!(
|
||||
"{}\"{}\"{}:{}{}{}",
|
||||
element.space0, element.name, element.space1, element.space2, value, element.space3
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -80,21 +84,19 @@ mod tests {
|
||||
let mut variables = HashMap::new();
|
||||
variables.insert("name".to_string(), Value::String("Bob".to_string()));
|
||||
assert_eq!(
|
||||
JsonValue::Null {}.eval(&variables).unwrap(),
|
||||
eval_json_value(JsonValue::Null {}, &variables).unwrap(),
|
||||
"null".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
JsonValue::Number("3.14".to_string())
|
||||
.eval(&variables)
|
||||
.unwrap(),
|
||||
eval_json_value(JsonValue::Number("3.14".to_string()), &variables).unwrap(),
|
||||
"3.14".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
JsonValue::Boolean(false).eval(&variables).unwrap(),
|
||||
eval_json_value(JsonValue::Boolean(false), &variables).unwrap(),
|
||||
"false".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
json_hello_world_value().eval(&variables).unwrap(),
|
||||
eval_json_value(json_hello_world_value(), &variables).unwrap(),
|
||||
"\"Hello Bob!\"".to_string()
|
||||
);
|
||||
}
|
||||
@ -102,7 +104,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_error() {
|
||||
let variables = HashMap::new();
|
||||
let error = json_hello_world_value().eval(&variables).err().unwrap();
|
||||
let error = eval_json_value(json_hello_world_value(), &variables)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 15, 1, 19));
|
||||
assert_eq!(
|
||||
error.inner,
|
||||
@ -117,37 +121,41 @@ mod tests {
|
||||
let mut variables = HashMap::new();
|
||||
variables.insert("name".to_string(), Value::String("Bob".to_string()));
|
||||
assert_eq!(
|
||||
JsonValue::List {
|
||||
space0: "".to_string(),
|
||||
elements: vec![],
|
||||
}
|
||||
.eval(&variables)
|
||||
eval_json_value(
|
||||
JsonValue::List {
|
||||
space0: "".to_string(),
|
||||
elements: vec![],
|
||||
},
|
||||
&variables
|
||||
)
|
||||
.unwrap(),
|
||||
"[]".to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
JsonValue::List {
|
||||
space0: "".to_string(),
|
||||
elements: vec![
|
||||
JsonListElement {
|
||||
space0: "".to_string(),
|
||||
value: JsonValue::Number("1".to_string()),
|
||||
space1: "".to_string()
|
||||
},
|
||||
JsonListElement {
|
||||
space0: " ".to_string(),
|
||||
value: JsonValue::Number("-2".to_string()),
|
||||
space1: "".to_string()
|
||||
},
|
||||
JsonListElement {
|
||||
space0: " ".to_string(),
|
||||
value: JsonValue::Number("3.0".to_string()),
|
||||
space1: "".to_string()
|
||||
},
|
||||
],
|
||||
}
|
||||
.eval(&variables)
|
||||
eval_json_value(
|
||||
JsonValue::List {
|
||||
space0: "".to_string(),
|
||||
elements: vec![
|
||||
JsonListElement {
|
||||
space0: "".to_string(),
|
||||
value: JsonValue::Number("1".to_string()),
|
||||
space1: "".to_string()
|
||||
},
|
||||
JsonListElement {
|
||||
space0: " ".to_string(),
|
||||
value: JsonValue::Number("-2".to_string()),
|
||||
space1: "".to_string()
|
||||
},
|
||||
JsonListElement {
|
||||
space0: " ".to_string(),
|
||||
value: JsonValue::Number("3.0".to_string()),
|
||||
space1: "".to_string()
|
||||
},
|
||||
],
|
||||
},
|
||||
&variables
|
||||
)
|
||||
.unwrap(),
|
||||
"[1, -2, 3.0]".to_string()
|
||||
);
|
||||
@ -161,22 +169,24 @@ mod tests {
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
};
|
||||
assert_eq!(
|
||||
JsonValue::List {
|
||||
space0: "".to_string(),
|
||||
elements: vec![
|
||||
JsonListElement {
|
||||
space0: "".to_string(),
|
||||
value: JsonValue::String(template),
|
||||
space1: "".to_string()
|
||||
},
|
||||
JsonListElement {
|
||||
space0: " ".to_string(),
|
||||
value: json_hello_world_value(),
|
||||
space1: "".to_string()
|
||||
},
|
||||
],
|
||||
}
|
||||
.eval(&variables)
|
||||
eval_json_value(
|
||||
JsonValue::List {
|
||||
space0: "".to_string(),
|
||||
elements: vec![
|
||||
JsonListElement {
|
||||
space0: "".to_string(),
|
||||
value: JsonValue::String(template),
|
||||
space1: "".to_string()
|
||||
},
|
||||
JsonListElement {
|
||||
space0: " ".to_string(),
|
||||
value: json_hello_world_value(),
|
||||
space1: "".to_string()
|
||||
},
|
||||
],
|
||||
},
|
||||
&variables
|
||||
)
|
||||
.unwrap(),
|
||||
"[\"Hi\", \"Hello Bob!\"]".to_string()
|
||||
);
|
||||
@ -186,16 +196,18 @@ mod tests {
|
||||
fn test_object_value() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
JsonValue::Object {
|
||||
space0: "".to_string(),
|
||||
elements: vec![]
|
||||
}
|
||||
.eval(&variables)
|
||||
eval_json_value(
|
||||
JsonValue::Object {
|
||||
space0: "".to_string(),
|
||||
elements: vec![]
|
||||
},
|
||||
&variables
|
||||
)
|
||||
.unwrap(),
|
||||
"{}".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
json_person_value().eval(&variables).unwrap(),
|
||||
eval_json_value(json_person_value(), &variables).unwrap(),
|
||||
r#"{
|
||||
"firstName": "John"
|
||||
}"#
|
||||
|
@ -29,6 +29,7 @@ use crate::ast::*;
|
||||
use crate::http;
|
||||
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
impl MultipartParam {
|
||||
@ -40,7 +41,7 @@ impl MultipartParam {
|
||||
match self {
|
||||
MultipartParam::Param(KeyValue { key, value, .. }) => {
|
||||
let name = key.value;
|
||||
let value = value.eval(variables)?;
|
||||
let value = eval_template(value, variables)?;
|
||||
Ok(http::MultipartParam::Param(http::Param { name, value }))
|
||||
}
|
||||
MultipartParam::FileParam(param) => {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,226 +25,209 @@ use crate::jsonpath;
|
||||
|
||||
use super::cookie;
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
use super::xpath;
|
||||
|
||||
pub type QueryResult = Result<Option<Value>, Error>;
|
||||
|
||||
impl Query {
|
||||
pub fn eval(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
http_response: http::Response,
|
||||
) -> QueryResult {
|
||||
match self.value {
|
||||
QueryValue::Status {} => Ok(Some(Value::Integer(i64::from(http_response.status)))),
|
||||
QueryValue::Header { name, .. } => {
|
||||
let header_name = name.eval(variables)?;
|
||||
let values = http_response.get_header(header_name);
|
||||
if values.is_empty() {
|
||||
Ok(None)
|
||||
} else if values.len() == 1 {
|
||||
let value = values.first().unwrap().to_string();
|
||||
Ok(Some(Value::String(value)))
|
||||
} else {
|
||||
let values = values
|
||||
.iter()
|
||||
.map(|v| Value::String(v.to_string()))
|
||||
.collect();
|
||||
Ok(Some(Value::List(values)))
|
||||
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::Header { name, .. } => {
|
||||
let header_name = eval_template(name, variables)?;
|
||||
let values = http_response.get_header(header_name);
|
||||
if values.is_empty() {
|
||||
Ok(None)
|
||||
} else if values.len() == 1 {
|
||||
let value = values.first().unwrap().to_string();
|
||||
Ok(Some(Value::String(value)))
|
||||
} else {
|
||||
let values = values
|
||||
.iter()
|
||||
.map(|v| Value::String(v.to_string()))
|
||||
.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) {
|
||||
None => Ok(None),
|
||||
Some(cookie) => {
|
||||
let attribute_name = if let Some(attribute) = attribute {
|
||||
attribute.name
|
||||
} else {
|
||||
CookieAttributeName::Value("Value".to_string())
|
||||
};
|
||||
Ok(eval_cookie_attribute_name(attribute_name, cookie))
|
||||
}
|
||||
}
|
||||
QueryValue::Cookie {
|
||||
expr: CookiePath { name, attribute },
|
||||
..
|
||||
} => {
|
||||
let cookie_name = name.eval(variables)?;
|
||||
match http_response.get_cookie(cookie_name) {
|
||||
None => Ok(None),
|
||||
Some(cookie) => {
|
||||
let attribute_name = if let Some(attribute) = attribute {
|
||||
attribute.name
|
||||
} else {
|
||||
CookieAttributeName::Value("Value".to_string())
|
||||
};
|
||||
Ok(attribute_name.eval(cookie))
|
||||
}
|
||||
}
|
||||
}
|
||||
QueryValue::Body {} => {
|
||||
// can return a string if encoding is known and utf8
|
||||
match http_response.text() {
|
||||
Ok(s) => Ok(Some(Value::String(s))),
|
||||
Err(inner) => Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner,
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
QueryValue::Body {} => {
|
||||
// can return a string if encoding is known and utf8
|
||||
match http_response.text() {
|
||||
Ok(s) => Ok(Some(Value::String(s))),
|
||||
Err(inner) => Err(Error {
|
||||
source_info: self.source_info.clone(),
|
||||
inner,
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
QueryValue::Xpath { expr, .. } => {
|
||||
let source_info = expr.source_info.clone();
|
||||
let value = expr.eval(variables)?;
|
||||
match http_response.text() {
|
||||
Err(inner) => Err(Error {
|
||||
source_info: self.source_info.clone(),
|
||||
inner,
|
||||
assert: false,
|
||||
}),
|
||||
Ok(xml) => {
|
||||
let result = if http_response.is_html() {
|
||||
xpath::eval_html(xml, value.clone())
|
||||
} else {
|
||||
xpath::eval_xml(xml, value.clone())
|
||||
};
|
||||
match result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(xpath::XpathError::InvalidXML {}) => Err(Error {
|
||||
source_info: self.source_info,
|
||||
inner: RunnerError::QueryInvalidXml,
|
||||
assert: false,
|
||||
}),
|
||||
Err(xpath::XpathError::InvalidHtml {}) => Err(Error {
|
||||
source_info: self.source_info,
|
||||
inner: RunnerError::QueryInvalidXml,
|
||||
assert: false,
|
||||
}),
|
||||
Err(xpath::XpathError::Eval {}) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::QueryInvalidXpathEval,
|
||||
assert: false,
|
||||
}),
|
||||
Err(xpath::XpathError::Unsupported {}) => {
|
||||
panic!("Unsupported xpath {}", value); // good usecase for panic - I could nmot reporduce this usecase myself
|
||||
}
|
||||
}
|
||||
QueryValue::Xpath { expr, .. } => {
|
||||
let source_info = expr.source_info.clone();
|
||||
let value = eval_template(expr, variables)?;
|
||||
match http_response.text() {
|
||||
Err(inner) => Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner,
|
||||
assert: false,
|
||||
}),
|
||||
Ok(xml) => {
|
||||
let result = if http_response.is_html() {
|
||||
xpath::eval_html(xml, value.clone())
|
||||
} else {
|
||||
xpath::eval_xml(xml, value.clone())
|
||||
};
|
||||
match result {
|
||||
Ok(value) => Ok(Some(value)),
|
||||
Err(xpath::XpathError::InvalidXML {}) => Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner: RunnerError::QueryInvalidXml,
|
||||
assert: false,
|
||||
}),
|
||||
Err(xpath::XpathError::InvalidHtml {}) => Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner: RunnerError::QueryInvalidXml,
|
||||
assert: false,
|
||||
}),
|
||||
Err(xpath::XpathError::Eval {}) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::QueryInvalidXpathEval,
|
||||
assert: false,
|
||||
}),
|
||||
Err(xpath::XpathError::Unsupported {}) => {
|
||||
panic!("Unsupported xpath {}", value); // good usecase for panic - I could nmot reporduce this usecase myself
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
QueryValue::Jsonpath { expr, .. } => {
|
||||
let value = expr.clone().eval(variables)?;
|
||||
let source_info = expr.source_info;
|
||||
// let expr = match jsonpath::Expr::init(value.as_str()) {
|
||||
// None => return Err(Error { source_info: source_info.clone(), inner: RunnerError::QueryInvalidJsonpathExpression {}, assert: false }),
|
||||
// Some(expr) => expr
|
||||
// };
|
||||
// let json = match String::from_utf8(http_response.body) {
|
||||
// Err(_) => return Err(Error { source_info: self.source_info, inner: RunnerError::InvalidUtf8, assert: false }),
|
||||
// Ok(v) => v
|
||||
// };
|
||||
// let value = match expr.eval(json.as_str()) {
|
||||
// Err(_) => {
|
||||
// return Err(Error { source_info: self.source_info, inner: RunnerError::QueryInvalidJson, assert: false });
|
||||
// }
|
||||
// Ok(value) => {
|
||||
// if value == Value::List(vec![]) { Value::None } else { value }
|
||||
// }
|
||||
// };
|
||||
// Using your own json implem
|
||||
let query = match jsonpath::parse(value.as_str()) {
|
||||
Ok(q) => q,
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::QueryInvalidJsonpathExpression { value },
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
};
|
||||
let json = match http_response.text() {
|
||||
Err(inner) => {
|
||||
return Err(Error {
|
||||
source_info: self.source_info.clone(),
|
||||
inner,
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
let value = match serde_json::from_str(json.as_str()) {
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info: self.source_info,
|
||||
inner: RunnerError::QueryInvalidJson,
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
let results = query.eval(value);
|
||||
if results.is_empty() {
|
||||
Ok(None)
|
||||
} else if results.len() == 1 {
|
||||
// list coercions
|
||||
Ok(Some(Value::from_json(results.get(0).unwrap())))
|
||||
} else {
|
||||
Ok(Some(Value::from_json(&serde_json::Value::Array(results))))
|
||||
}
|
||||
QueryValue::Jsonpath { expr, .. } => {
|
||||
let value = eval_template(expr.clone(), variables)?;
|
||||
let source_info = expr.source_info;
|
||||
let jsonpath_query = match jsonpath::parse(value.as_str()) {
|
||||
Ok(q) => q,
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::QueryInvalidJsonpathExpression { value },
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
let json = match http_response.text() {
|
||||
Err(inner) => {
|
||||
return Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner,
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
let value = match serde_json::from_str(json.as_str()) {
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner: RunnerError::QueryInvalidJson,
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
let results = jsonpath_query.eval(value);
|
||||
if results.is_empty() {
|
||||
Ok(None)
|
||||
} else if results.len() == 1 {
|
||||
// list coercions
|
||||
Ok(Some(Value::from_json(results.get(0).unwrap())))
|
||||
} else {
|
||||
Ok(Some(Value::from_json(&serde_json::Value::Array(results))))
|
||||
}
|
||||
QueryValue::Regex { expr, .. } => {
|
||||
let value = expr.clone().eval(variables)?;
|
||||
let source_info = expr.source_info;
|
||||
let s = match http_response.text() {
|
||||
Err(inner) => {
|
||||
return Err(Error {
|
||||
source_info: self.source_info.clone(),
|
||||
inner,
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
match Regex::new(value.as_str()) {
|
||||
Ok(re) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
}
|
||||
QueryValue::Regex { expr, .. } => {
|
||||
let value = eval_template(expr.clone(), variables)?;
|
||||
let source_info = expr.source_info;
|
||||
let s = match http_response.text() {
|
||||
Err(inner) => {
|
||||
return Err(Error {
|
||||
source_info: query.source_info,
|
||||
inner,
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
match Regex::new(value.as_str()) {
|
||||
Ok(re) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
Err(_) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
Err(_) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
QueryValue::Variable { name, .. } => {
|
||||
let name = name.eval(variables)?;
|
||||
if let Some(value) = variables.get(name.as_str()) {
|
||||
Ok(Some(value.clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
QueryValue::Variable { name, .. } => {
|
||||
let name = eval_template(name, variables)?;
|
||||
if let Some(value) = variables.get(name.as_str()) {
|
||||
Ok(Some(value.clone()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CookieAttributeName {
|
||||
pub fn eval(self, cookie: cookie::ResponseCookie) -> Option<Value> {
|
||||
match self {
|
||||
CookieAttributeName::Value(_) => Some(Value::String(cookie.value)),
|
||||
CookieAttributeName::Expires(_) => cookie.expires().map(Value::String),
|
||||
CookieAttributeName::MaxAge(_) => cookie.max_age().map(Value::Integer),
|
||||
CookieAttributeName::Domain(_) => cookie.domain().map(Value::String),
|
||||
CookieAttributeName::Path(_) => cookie.path().map(Value::String),
|
||||
CookieAttributeName::Secure(_) => {
|
||||
if cookie.has_secure() {
|
||||
Some(Value::Unit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn eval_cookie_attribute_name(
|
||||
cookie_attribute_name: CookieAttributeName,
|
||||
cookie: cookie::ResponseCookie,
|
||||
) -> Option<Value> {
|
||||
match cookie_attribute_name {
|
||||
CookieAttributeName::Value(_) => Some(Value::String(cookie.value)),
|
||||
CookieAttributeName::Expires(_) => cookie.expires().map(Value::String),
|
||||
CookieAttributeName::MaxAge(_) => cookie.max_age().map(Value::Integer),
|
||||
CookieAttributeName::Domain(_) => cookie.domain().map(Value::String),
|
||||
CookieAttributeName::Path(_) => cookie.path().map(Value::String),
|
||||
CookieAttributeName::Secure(_) => {
|
||||
if cookie.has_secure() {
|
||||
Some(Value::Unit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
CookieAttributeName::HttpOnly(_) => {
|
||||
if cookie.has_httponly() {
|
||||
Some(Value::Unit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
CookieAttributeName::SameSite(_) => cookie.samesite().map(Value::String),
|
||||
}
|
||||
CookieAttributeName::HttpOnly(_) => {
|
||||
if cookie.has_httponly() {
|
||||
Some(Value::Unit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
CookieAttributeName::SameSite(_) => cookie.samesite().map(Value::String),
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,11 +460,14 @@ pub mod tests {
|
||||
fn test_query_status() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
Query {
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
value: QueryValue::Status {}
|
||||
}
|
||||
.eval(&variables, http::hello_http_response())
|
||||
eval_query(
|
||||
Query {
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
value: QueryValue::Status {},
|
||||
},
|
||||
&variables,
|
||||
http::hello_http_response(),
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::Integer(200)
|
||||
@ -513,9 +499,7 @@ pub mod tests {
|
||||
// assert_eq!(error.source_info.start, Pos { line: 1, column: 8 });
|
||||
// assert_eq!(error.inner, RunnerError::QueryHeaderNotFound);
|
||||
assert_eq!(
|
||||
query_header
|
||||
.eval(&variables, http::hello_http_response())
|
||||
.unwrap(),
|
||||
eval_query(query_header, &variables, http::hello_http_response()).unwrap(),
|
||||
None
|
||||
);
|
||||
}
|
||||
@ -542,8 +526,7 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
query_header
|
||||
.eval(&variables, http::hello_http_response())
|
||||
eval_query(query_header, &variables, http::hello_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String(String::from("text/html; charset=utf-8"))
|
||||
@ -588,7 +571,9 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
query.eval(&variables, response.clone()).unwrap().unwrap(),
|
||||
eval_query(query, &variables, response.clone())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String("DQAAAKEaem_vYg".to_string())
|
||||
);
|
||||
|
||||
@ -615,7 +600,9 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
query.eval(&variables, response.clone()).unwrap().unwrap(),
|
||||
eval_query(query, &variables, response.clone())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String("/accounts".to_string())
|
||||
);
|
||||
|
||||
@ -642,7 +629,9 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
query.eval(&variables, response.clone()).unwrap().unwrap(),
|
||||
eval_query(query, &variables, response.clone())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::Unit
|
||||
);
|
||||
|
||||
@ -668,7 +657,7 @@ pub mod tests {
|
||||
},
|
||||
},
|
||||
};
|
||||
assert_eq!(query.eval(&variables, response).unwrap(), None);
|
||||
assert_eq!(eval_query(query, &variables, response).unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -696,45 +685,55 @@ pub mod tests {
|
||||
],
|
||||
};
|
||||
assert_eq!(
|
||||
CookieAttributeName::Value("_".to_string())
|
||||
.eval(cookie.clone())
|
||||
eval_cookie_attribute_name(CookieAttributeName::Value("_".to_string()), cookie.clone())
|
||||
.unwrap(),
|
||||
Value::String("DQAAAKEaem_vYg".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::Domain("_".to_string()).eval(cookie.clone()),
|
||||
eval_cookie_attribute_name(
|
||||
CookieAttributeName::Domain("_".to_string()),
|
||||
cookie.clone()
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::Path("_".to_string())
|
||||
.eval(cookie.clone())
|
||||
eval_cookie_attribute_name(CookieAttributeName::Path("_".to_string()), cookie.clone())
|
||||
.unwrap(),
|
||||
Value::String("/accounts".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::MaxAge("_".to_string()).eval(cookie.clone()),
|
||||
eval_cookie_attribute_name(
|
||||
CookieAttributeName::MaxAge("_".to_string()),
|
||||
cookie.clone()
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::Expires("_".to_string())
|
||||
.eval(cookie.clone())
|
||||
.unwrap(),
|
||||
eval_cookie_attribute_name(
|
||||
CookieAttributeName::Expires("_".to_string()),
|
||||
cookie.clone()
|
||||
)
|
||||
.unwrap(),
|
||||
Value::String("Wed, 13 Jan 2021 22:23:01 GMT".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::Secure("_".to_string())
|
||||
.eval(cookie.clone())
|
||||
.unwrap(),
|
||||
eval_cookie_attribute_name(
|
||||
CookieAttributeName::Secure("_".to_string()),
|
||||
cookie.clone()
|
||||
)
|
||||
.unwrap(),
|
||||
Value::Unit
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::HttpOnly("_".to_string())
|
||||
.eval(cookie.clone())
|
||||
.unwrap(),
|
||||
eval_cookie_attribute_name(
|
||||
CookieAttributeName::HttpOnly("_".to_string()),
|
||||
cookie.clone()
|
||||
)
|
||||
.unwrap(),
|
||||
Value::Unit
|
||||
);
|
||||
assert_eq!(
|
||||
CookieAttributeName::SameSite("_".to_string()).eval(cookie),
|
||||
eval_cookie_attribute_name(CookieAttributeName::SameSite("_".to_string()), cookie),
|
||||
None
|
||||
);
|
||||
}
|
||||
@ -743,20 +742,26 @@ pub mod tests {
|
||||
fn test_body() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
Query {
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
value: QueryValue::Body {},
|
||||
}
|
||||
.eval(&variables, http::hello_http_response())
|
||||
eval_query(
|
||||
Query {
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
value: QueryValue::Body {},
|
||||
},
|
||||
&variables,
|
||||
http::hello_http_response(),
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String(String::from("Hello World!"))
|
||||
);
|
||||
let error = Query {
|
||||
source_info: SourceInfo::init(1, 1, 1, 2),
|
||||
value: QueryValue::Body {},
|
||||
}
|
||||
.eval(&variables, http::bytes_http_response())
|
||||
let error = eval_query(
|
||||
Query {
|
||||
source_info: SourceInfo::init(1, 1, 1, 2),
|
||||
value: QueryValue::Body {},
|
||||
},
|
||||
&variables,
|
||||
http::bytes_http_response(),
|
||||
)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 1, 1, 2));
|
||||
@ -777,7 +782,9 @@ pub mod tests {
|
||||
headers: vec![],
|
||||
body: vec![200],
|
||||
};
|
||||
let error = xpath_users().eval(&variables, http_response).err().unwrap();
|
||||
let error = eval_query(xpath_users(), &variables, http_response)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info.start, Pos { line: 1, column: 1 });
|
||||
assert_eq!(
|
||||
error.inner,
|
||||
@ -808,8 +815,7 @@ pub mod tests {
|
||||
},
|
||||
},
|
||||
};
|
||||
let error = query
|
||||
.eval(&variables, http::xml_two_users_http_response())
|
||||
let error = eval_query(query, &variables, http::xml_two_users_http_response())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.inner, RunnerError::QueryInvalidXpathEval);
|
||||
@ -821,17 +827,23 @@ pub mod tests {
|
||||
let variables = HashMap::new();
|
||||
|
||||
assert_eq!(
|
||||
xpath_users()
|
||||
.eval(&variables, http::xml_two_users_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
eval_query(
|
||||
xpath_users(),
|
||||
&variables,
|
||||
http::xml_two_users_http_response(),
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::Nodeset(2)
|
||||
);
|
||||
assert_eq!(
|
||||
xpath_count_user_query()
|
||||
.eval(&variables, http::xml_two_users_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
eval_query(
|
||||
xpath_count_user_query(),
|
||||
&variables,
|
||||
http::xml_two_users_http_response(),
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::Float(2, 0)
|
||||
);
|
||||
}
|
||||
@ -863,8 +875,7 @@ pub mod tests {
|
||||
fn test_query_xpath_with_html() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
xpath_html_charset()
|
||||
.eval(&variables, http::html_http_response())
|
||||
eval_query(xpath_html_charset(), &variables, http::html_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String(String::from("UTF-8"))
|
||||
@ -895,15 +906,14 @@ pub mod tests {
|
||||
},
|
||||
};
|
||||
|
||||
let error = jsonpath_query
|
||||
.eval(&variables, json_http_response())
|
||||
let error = eval_query(jsonpath_query, &variables, json_http_response())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
error.source_info.start,
|
||||
Pos {
|
||||
line: 1,
|
||||
column: 10
|
||||
column: 10,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
@ -923,8 +933,7 @@ pub mod tests {
|
||||
headers: vec![],
|
||||
body: String::into_bytes(String::from("xxx")),
|
||||
};
|
||||
let error = jsonpath_success()
|
||||
.eval(&variables, http_response)
|
||||
let error = eval_query(jsonpath_success(), &variables, http_response)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info.start, Pos { line: 1, column: 1 });
|
||||
@ -942,7 +951,7 @@ pub mod tests {
|
||||
};
|
||||
//assert_eq!(jsonpath_success().eval(http_response).unwrap(), Value::List(vec![]));
|
||||
assert_eq!(
|
||||
jsonpath_success().eval(&variables, http_response).unwrap(),
|
||||
eval_query(jsonpath_success(), &variables, http_response).unwrap(),
|
||||
None
|
||||
);
|
||||
}
|
||||
@ -951,15 +960,13 @@ pub mod tests {
|
||||
fn test_query_json() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
jsonpath_success()
|
||||
.eval(&variables, json_http_response())
|
||||
eval_query(jsonpath_success(), &variables, json_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::Bool(false)
|
||||
);
|
||||
assert_eq!(
|
||||
jsonpath_errors()
|
||||
.eval(&variables, json_http_response())
|
||||
eval_query(jsonpath_errors(), &variables, json_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::List(vec![
|
||||
@ -979,15 +986,13 @@ pub mod tests {
|
||||
fn test_query_regex() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
regex_name()
|
||||
.eval(&variables, http::hello_http_response())
|
||||
eval_query(regex_name(), &variables, http::hello_http_response())
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
Value::String("World".to_string())
|
||||
);
|
||||
|
||||
let error = regex_invalid()
|
||||
.eval(&variables, http::hello_http_response())
|
||||
let error = eval_query(regex_invalid(), &variables, http::hello_http_response())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 7, 1, 10));
|
||||
|
@ -26,185 +26,161 @@ use std::io::prelude::*;
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
|
||||
use super::body::eval_body;
|
||||
use super::core::Error;
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
impl Request {
|
||||
pub fn eval(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<http::Request, Error> {
|
||||
let method = self.method.clone().eval();
|
||||
pub fn eval_request(
|
||||
request: Request,
|
||||
variables: &HashMap<String, Value>,
|
||||
context_dir: String,
|
||||
) -> Result<http::Request, Error> {
|
||||
let method = eval_method(request.method.clone());
|
||||
|
||||
let url = self.clone().url.eval(&variables)?;
|
||||
let url = eval_template(request.clone().url, &variables)?;
|
||||
|
||||
// headers
|
||||
let mut headers: Vec<http::Header> = vec![];
|
||||
for header in self.clone().headers {
|
||||
let name = header.key.value;
|
||||
let value = header.value.eval(variables)?;
|
||||
headers.push(http::Header { name, value });
|
||||
}
|
||||
// headers
|
||||
let mut headers: Vec<http::Header> = vec![];
|
||||
for header in request.clone().headers {
|
||||
let name = header.key.value;
|
||||
let value = eval_template(header.value, variables)?;
|
||||
headers.push(http::Header { name, value });
|
||||
}
|
||||
|
||||
let mut querystring: Vec<http::Param> = vec![];
|
||||
for param in self.clone().querystring_params() {
|
||||
let name = param.key.value;
|
||||
let value = param.value.eval(variables)?;
|
||||
querystring.push(http::Param { name, value });
|
||||
}
|
||||
let mut querystring: Vec<http::Param> = vec![];
|
||||
for param in request.clone().querystring_params() {
|
||||
let name = param.key.value;
|
||||
let value = eval_template(param.value, variables)?;
|
||||
querystring.push(http::Param { name, value });
|
||||
}
|
||||
|
||||
let mut form: Vec<http::Param> = vec![];
|
||||
for param in self.clone().form_params() {
|
||||
let name = param.key.value;
|
||||
let value = param.value.eval(variables)?;
|
||||
form.push(http::Param { name, value });
|
||||
}
|
||||
// if !self.clone().form_params().is_empty() {
|
||||
// headers.push(http::ast::Header {
|
||||
// name: String::from("Content-Type"),
|
||||
// value: String::from("application/x-www-form-urlencoded"),
|
||||
// });
|
||||
// }
|
||||
let mut form: Vec<http::Param> = vec![];
|
||||
for param in request.clone().form_params() {
|
||||
let name = param.key.value;
|
||||
let value = eval_template(param.value, variables)?;
|
||||
form.push(http::Param { name, value });
|
||||
}
|
||||
// if !self.clone().form_params().is_empty() {
|
||||
// headers.push(http::ast::Header {
|
||||
// name: String::from("Content-Type"),
|
||||
// value: String::from("application/x-www-form-urlencoded"),
|
||||
// });
|
||||
// }
|
||||
|
||||
let mut cookies = vec![];
|
||||
for cookie in self.clone().cookies() {
|
||||
let cookie = http::RequestCookie {
|
||||
name: cookie.clone().name.value,
|
||||
value: cookie.clone().value.value,
|
||||
};
|
||||
cookies.push(cookie);
|
||||
}
|
||||
|
||||
let bytes = match self.clone().body {
|
||||
Some(body) => body.eval(variables, context_dir.clone())?,
|
||||
None => vec![],
|
||||
let mut cookies = vec![];
|
||||
for cookie in request.clone().cookies() {
|
||||
let cookie = http::RequestCookie {
|
||||
name: cookie.clone().name.value,
|
||||
value: cookie.clone().value.value,
|
||||
};
|
||||
|
||||
let mut multipart = vec![];
|
||||
for multipart_param in self.clone().multipart_form_data() {
|
||||
let param = multipart_param.eval(variables, context_dir.clone())?;
|
||||
multipart.push(param);
|
||||
}
|
||||
|
||||
let content_type = if !form.is_empty() {
|
||||
Some("application/x-www-form-urlencoded".to_string())
|
||||
} else if !multipart.is_empty() {
|
||||
Some("multipart/form-data".to_string())
|
||||
} else if let Some(Body {
|
||||
value: Bytes::Json { .. },
|
||||
..
|
||||
}) = self.body
|
||||
{
|
||||
Some("application/json".to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// add implicit content type
|
||||
// if self.content_type().is_none() {
|
||||
// if let Some(body) = self.body {
|
||||
// if let Bytes::Json { .. } = body.value {
|
||||
// headers.push(http::ast::Header {
|
||||
// name: String::from("Content-Type"),
|
||||
// value: String::from("application/json"),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Ok(http::Request {
|
||||
method,
|
||||
url,
|
||||
querystring,
|
||||
headers,
|
||||
cookies,
|
||||
body: bytes,
|
||||
multipart,
|
||||
form,
|
||||
content_type,
|
||||
})
|
||||
cookies.push(cookie);
|
||||
}
|
||||
|
||||
pub fn content_type(&self) -> Option<Template> {
|
||||
for header in self.headers.clone() {
|
||||
if header.key.value.to_lowercase().as_str() == "content-type" {
|
||||
return Some(header.value);
|
||||
}
|
||||
}
|
||||
let bytes = match request.clone().body {
|
||||
Some(body) => eval_body(body, variables, context_dir.clone())?,
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
let mut multipart = vec![];
|
||||
for multipart_param in request.clone().multipart_form_data() {
|
||||
let param = multipart_param.eval(variables, context_dir.clone())?;
|
||||
multipart.push(param);
|
||||
}
|
||||
|
||||
let content_type = if !form.is_empty() {
|
||||
Some("application/x-www-form-urlencoded".to_string())
|
||||
} else if !multipart.is_empty() {
|
||||
Some("multipart/form-data".to_string())
|
||||
} else if let Some(Body {
|
||||
value: Bytes::Json { .. },
|
||||
..
|
||||
}) = request.body
|
||||
{
|
||||
Some("application/json".to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// experimental feature
|
||||
/// @cookie_storage_add
|
||||
///
|
||||
pub fn add_cookie_in_storage(&self) -> Option<String> {
|
||||
for line_terminator in self.line_terminators.iter() {
|
||||
if let Some(s) = line_terminator.comment.clone() {
|
||||
if s.value.contains("@cookie_storage_set:") {
|
||||
let index = "#@cookie_storage_set:".to_string().len();
|
||||
let value = &s.value[index..s.value.len()].to_string().trim().to_string();
|
||||
return Some(value.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
// add implicit content type
|
||||
// if self.content_type().is_none() {
|
||||
// if let Some(body) = self.body {
|
||||
// if let Bytes::Json { .. } = body.value {
|
||||
// headers.push(http::ast::Header {
|
||||
// name: String::from("Content-Type"),
|
||||
// value: String::from("application/json"),
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
///
|
||||
/// experimental feature
|
||||
/// @cookie_storage_clear
|
||||
///
|
||||
pub fn clear_cookie_storage(&self) -> bool {
|
||||
for line_terminator in self.line_terminators.iter() {
|
||||
if let Some(s) = line_terminator.comment.clone() {
|
||||
if s.value.contains("@cookie_storage_clear") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
Ok(http::Request {
|
||||
method,
|
||||
url,
|
||||
querystring,
|
||||
headers,
|
||||
cookies,
|
||||
body: bytes,
|
||||
multipart,
|
||||
form,
|
||||
content_type,
|
||||
})
|
||||
}
|
||||
|
||||
impl Method {
|
||||
fn eval(self) -> http::Method {
|
||||
match self {
|
||||
Method::Get => http::Method::Get,
|
||||
Method::Head => http::Method::Head,
|
||||
Method::Post => http::Method::Post,
|
||||
Method::Put => http::Method::Put,
|
||||
Method::Delete => http::Method::Delete,
|
||||
Method::Connect => http::Method::Connect,
|
||||
Method::Options => http::Method::Options,
|
||||
Method::Trace => http::Method::Trace,
|
||||
Method::Patch => http::Method::Patch,
|
||||
// pub fn get_content_type(request: Request) -> Option<Template> {
|
||||
// for header in request.headers.clone() {
|
||||
// if header.key.value.to_lowercase().as_str() == "content-type" {
|
||||
// return Some(header.value);
|
||||
// }
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
|
||||
///
|
||||
/// experimental feature
|
||||
/// @cookie_storage_add
|
||||
///
|
||||
pub fn cookie_storage_set(request: Request) -> Option<String> {
|
||||
for line_terminator in request.line_terminators.iter() {
|
||||
if let Some(s) = line_terminator.comment.clone() {
|
||||
if s.value.contains("@cookie_storage_set:") {
|
||||
let index = "#@cookie_storage_set:".to_string().len();
|
||||
let value = &s.value[index..s.value.len()].to_string().trim().to_string();
|
||||
return Some(value.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
//pub fn split_url(url: String) -> (String, Vec<http::Param>) {
|
||||
// match url.find('?') {
|
||||
// None => (url, vec![]),
|
||||
// Some(index) => {
|
||||
// let (url, params) = url.split_at(index);
|
||||
// let params: Vec<http::Param> = params[1..].split('&')
|
||||
// .map(|s| {
|
||||
// match s.find('=') {
|
||||
// None => http::Param { name: s.to_string(), value: String::from("") },
|
||||
// Some(index) => {
|
||||
// let (name, value) = s.split_at(index);
|
||||
// http::Param { name: name.to_string(), value: value[1..].to_string() }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .collect();
|
||||
//
|
||||
// (url.to_string(), params)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
///
|
||||
/// experimental feature
|
||||
/// @cookie_storage_clear
|
||||
///
|
||||
pub fn cookie_storage_clear(request: Request) -> bool {
|
||||
for line_terminator in request.line_terminators.iter() {
|
||||
if let Some(s) = line_terminator.comment.clone() {
|
||||
if s.value.contains("@cookie_storage_clear") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn eval_method(method: Method) -> http::Method {
|
||||
match method {
|
||||
Method::Get => http::Method::Get,
|
||||
Method::Head => http::Method::Head,
|
||||
Method::Post => http::Method::Post,
|
||||
Method::Put => http::Method::Put,
|
||||
Method::Delete => http::Method::Delete,
|
||||
Method::Connect => http::Method::Connect,
|
||||
Method::Options => http::Method::Options,
|
||||
Method::Trace => http::Method::Trace,
|
||||
Method::Patch => http::Method::Patch,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -347,8 +323,7 @@ mod tests {
|
||||
#[test]
|
||||
pub fn test_error_variable() {
|
||||
let variables = HashMap::new();
|
||||
let error = hello_request()
|
||||
.eval(&variables, "current_dir".to_string())
|
||||
let error = eval_request(hello_request(), &variables, "current_dir".to_string())
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 7, 1, 15));
|
||||
@ -367,9 +342,8 @@ mod tests {
|
||||
String::from("base_url"),
|
||||
Value::String(String::from("http://localhost:8000")),
|
||||
);
|
||||
let http_request = hello_request()
|
||||
.eval(&variables, "current_dir".to_string())
|
||||
.unwrap();
|
||||
let http_request =
|
||||
eval_request(hello_request(), &variables, "current_dir".to_string()).unwrap();
|
||||
assert_eq!(http_request, http::hello_http_request());
|
||||
}
|
||||
|
||||
@ -380,15 +354,14 @@ mod tests {
|
||||
String::from("param1"),
|
||||
Value::String(String::from("value1")),
|
||||
);
|
||||
let http_request = query_request()
|
||||
.eval(&variables, "current_dir".to_string())
|
||||
.unwrap();
|
||||
let http_request =
|
||||
eval_request(query_request(), &variables, "current_dir".to_string()).unwrap();
|
||||
assert_eq!(http_request, http::query_http_request());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clear_cookie_store() {
|
||||
assert_eq!(false, hello_request().clear_cookie_storage());
|
||||
assert_eq!(false, cookie_storage_clear(hello_request()));
|
||||
|
||||
let line_terminator = LineTerminator {
|
||||
space0: whitespace(),
|
||||
@ -397,7 +370,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(
|
||||
true,
|
||||
Request {
|
||||
cookie_storage_clear(Request {
|
||||
line_terminators: vec![LineTerminator {
|
||||
space0: whitespace(),
|
||||
comment: Some(Comment {
|
||||
@ -421,14 +394,13 @@ mod tests {
|
||||
sections: vec![],
|
||||
body: None,
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
}
|
||||
.clear_cookie_storage()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_cookie_in_storage() {
|
||||
assert_eq!(None, hello_request().add_cookie_in_storage());
|
||||
assert_eq!(None, cookie_storage_set(hello_request()));
|
||||
|
||||
let line_terminator = LineTerminator {
|
||||
space0: whitespace(),
|
||||
@ -437,7 +409,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(
|
||||
Some("localhost\tFALSE\t/\tFALSE\t0\tcookie1\tvalueA".to_string()),
|
||||
Request {
|
||||
cookie_storage_set(Request {
|
||||
line_terminators: vec![LineTerminator {
|
||||
space0: whitespace(),
|
||||
comment: Some(Comment {
|
||||
@ -463,8 +435,7 @@ mod tests {
|
||||
sections: vec![],
|
||||
body: None,
|
||||
source_info: SourceInfo::init(0, 0, 0, 0),
|
||||
}
|
||||
.add_cookie_in_storage()
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -20,202 +20,205 @@ use std::collections::HashMap;
|
||||
use crate::ast::*;
|
||||
use crate::http;
|
||||
|
||||
use super::assert::eval_assert;
|
||||
use super::body::eval_body;
|
||||
use super::capture::eval_capture;
|
||||
use super::core::*;
|
||||
use super::json::eval_json_value;
|
||||
use super::template::eval_template;
|
||||
use super::value::Value;
|
||||
|
||||
impl Response {
|
||||
pub fn eval_asserts(
|
||||
self,
|
||||
variables: &HashMap<String, Value>,
|
||||
http_response: http::Response,
|
||||
context_dir: String,
|
||||
) -> Vec<AssertResult> {
|
||||
let mut asserts = vec![];
|
||||
pub fn eval_asserts(
|
||||
response: Response,
|
||||
variables: &HashMap<String, Value>,
|
||||
http_response: http::Response,
|
||||
context_dir: String,
|
||||
) -> Vec<AssertResult> {
|
||||
let mut asserts = vec![];
|
||||
|
||||
let version = self.clone().version;
|
||||
asserts.push(AssertResult::Version {
|
||||
actual: http_response.version.to_string(),
|
||||
expected: version.value.as_str().to_string(),
|
||||
source_info: version.source_info,
|
||||
});
|
||||
let version = response.clone().version;
|
||||
asserts.push(AssertResult::Version {
|
||||
actual: http_response.version.to_string(),
|
||||
expected: version.value.as_str().to_string(),
|
||||
source_info: version.source_info,
|
||||
});
|
||||
|
||||
let status = self.clone().status;
|
||||
asserts.push(AssertResult::Status {
|
||||
actual: u64::from(http_response.status),
|
||||
expected: status.value as u64,
|
||||
source_info: status.source_info,
|
||||
});
|
||||
let status = response.clone().status;
|
||||
asserts.push(AssertResult::Status {
|
||||
actual: u64::from(http_response.status),
|
||||
expected: status.value as u64,
|
||||
source_info: status.source_info,
|
||||
});
|
||||
|
||||
for header in self.clone().headers {
|
||||
match header.value.clone().eval(variables) {
|
||||
Err(e) => {
|
||||
for header in response.clone().headers {
|
||||
match eval_template(header.value.clone(), variables) {
|
||||
Err(e) => {
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Err(e),
|
||||
expected: String::from(""),
|
||||
source_info: header.key.clone().source_info,
|
||||
});
|
||||
}
|
||||
Ok(expected) => {
|
||||
let header_name = header.key.value.clone();
|
||||
let actuals = http_response.get_header(header_name);
|
||||
if actuals.is_empty() {
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Err(e),
|
||||
expected: String::from(""),
|
||||
actual: Err(Error {
|
||||
source_info: header.key.clone().source_info,
|
||||
inner: RunnerError::QueryHeaderNotFound {},
|
||||
assert: false,
|
||||
}),
|
||||
expected,
|
||||
source_info: header.key.clone().source_info,
|
||||
});
|
||||
}
|
||||
Ok(expected) => {
|
||||
let header_name = header.key.value.clone();
|
||||
let actuals = http_response.get_header(header_name);
|
||||
if actuals.is_empty() {
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Err(Error {
|
||||
source_info: header.key.clone().source_info,
|
||||
inner: RunnerError::QueryHeaderNotFound {},
|
||||
assert: false,
|
||||
}),
|
||||
expected,
|
||||
source_info: header.key.clone().source_info,
|
||||
});
|
||||
} else if actuals.len() == 1 {
|
||||
let actual = actuals.first().unwrap().to_string();
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Ok(actual),
|
||||
expected,
|
||||
source_info: header.value.clone().source_info,
|
||||
});
|
||||
} else {
|
||||
// failure by default
|
||||
// expected value not found in the list
|
||||
// actual is therefore the full list
|
||||
let mut actual = format!(
|
||||
"[{}]",
|
||||
actuals
|
||||
.iter()
|
||||
.map(|v| format!("\"{}\"", v))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
);
|
||||
for value in actuals {
|
||||
if value == expected {
|
||||
actual = value;
|
||||
break;
|
||||
}
|
||||
} else if actuals.len() == 1 {
|
||||
let actual = actuals.first().unwrap().to_string();
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Ok(actual),
|
||||
expected,
|
||||
source_info: header.value.clone().source_info,
|
||||
});
|
||||
} else {
|
||||
// failure by default
|
||||
// expected value not found in the list
|
||||
// actual is therefore the full list
|
||||
let mut actual = format!(
|
||||
"[{}]",
|
||||
actuals
|
||||
.iter()
|
||||
.map(|v| format!("\"{}\"", v))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
);
|
||||
for value in actuals {
|
||||
if value == expected {
|
||||
actual = value;
|
||||
break;
|
||||
}
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Ok(actual),
|
||||
expected,
|
||||
source_info: header.value.clone().source_info,
|
||||
});
|
||||
}
|
||||
asserts.push(AssertResult::Header {
|
||||
actual: Ok(actual),
|
||||
expected,
|
||||
source_info: header.value.clone().source_info,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implicit assert on body
|
||||
if let Some(body) = self.clone().body {
|
||||
match body.value {
|
||||
Bytes::Json { value } => {
|
||||
let expected = match value.eval(variables) {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let actual = match http_response.text() {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(Error {
|
||||
source_info: SourceInfo {
|
||||
start: body.space0.source_info.end.clone(),
|
||||
end: body.space0.source_info.end.clone(),
|
||||
},
|
||||
inner: e,
|
||||
assert: true,
|
||||
}),
|
||||
};
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: body.space0.source_info.clone(),
|
||||
})
|
||||
}
|
||||
Bytes::Xml { value } => {
|
||||
let expected = Ok(Value::String(value));
|
||||
let actual = match http_response.text() {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(Error {
|
||||
source_info: SourceInfo {
|
||||
start: body.space0.source_info.end.clone(),
|
||||
end: body.space0.source_info.end.clone(),
|
||||
},
|
||||
inner: e,
|
||||
assert: true,
|
||||
}),
|
||||
};
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: body.space0.source_info.clone(),
|
||||
})
|
||||
}
|
||||
Bytes::RawString { value, .. } => {
|
||||
let expected = match value.clone().eval(variables) {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let actual = match http_response.text() {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(Error {
|
||||
source_info: SourceInfo {
|
||||
start: body.space0.source_info.end.clone(),
|
||||
end: body.space0.source_info.end.clone(),
|
||||
},
|
||||
inner: e,
|
||||
assert: true,
|
||||
}),
|
||||
};
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: value.source_info,
|
||||
})
|
||||
}
|
||||
Bytes::Base64 {
|
||||
value,
|
||||
space0,
|
||||
space1,
|
||||
..
|
||||
} => asserts.push(AssertResult::Body {
|
||||
actual: Ok(Value::Bytes(http_response.body.clone())),
|
||||
expected: Ok(Value::Bytes(value)),
|
||||
source_info: SourceInfo {
|
||||
start: space0.source_info.end,
|
||||
end: space1.source_info.start,
|
||||
},
|
||||
}),
|
||||
Bytes::File { .. } => {
|
||||
let expected = match body.clone().eval(variables, context_dir) {
|
||||
Ok(bytes) => Ok(Value::Bytes(bytes)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let actual = Ok(Value::Bytes(http_response.body.clone()));
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: body.space0.source_info.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for assert in self.asserts() {
|
||||
let assert_result = assert.eval(http_response.clone(), variables);
|
||||
asserts.push(assert_result);
|
||||
}
|
||||
asserts
|
||||
}
|
||||
|
||||
pub fn eval_captures(
|
||||
self,
|
||||
http_response: http::Response,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<Vec<CaptureResult>, Error> {
|
||||
let mut captures = vec![];
|
||||
for capture in self.captures() {
|
||||
let capture_result = capture.eval(variables, http_response.clone())?;
|
||||
captures.push(capture_result);
|
||||
}
|
||||
Ok(captures)
|
||||
// implicit assert on body
|
||||
if let Some(body) = response.clone().body {
|
||||
match body.value {
|
||||
Bytes::Json { value } => {
|
||||
let expected = match eval_json_value(value, variables) {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let actual = match http_response.text() {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(Error {
|
||||
source_info: SourceInfo {
|
||||
start: body.space0.source_info.end.clone(),
|
||||
end: body.space0.source_info.end.clone(),
|
||||
},
|
||||
inner: e,
|
||||
assert: true,
|
||||
}),
|
||||
};
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: body.space0.source_info.clone(),
|
||||
})
|
||||
}
|
||||
Bytes::Xml { value } => {
|
||||
let expected = Ok(Value::String(value));
|
||||
let actual = match http_response.text() {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(Error {
|
||||
source_info: SourceInfo {
|
||||
start: body.space0.source_info.end.clone(),
|
||||
end: body.space0.source_info.end.clone(),
|
||||
},
|
||||
inner: e,
|
||||
assert: true,
|
||||
}),
|
||||
};
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: body.space0.source_info.clone(),
|
||||
})
|
||||
}
|
||||
Bytes::RawString { value, .. } => {
|
||||
let expected = match eval_template(value.clone(), variables) {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let actual = match http_response.text() {
|
||||
Ok(s) => Ok(Value::String(s)),
|
||||
Err(e) => Err(Error {
|
||||
source_info: SourceInfo {
|
||||
start: body.space0.source_info.end.clone(),
|
||||
end: body.space0.source_info.end.clone(),
|
||||
},
|
||||
inner: e,
|
||||
assert: true,
|
||||
}),
|
||||
};
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: value.source_info,
|
||||
})
|
||||
}
|
||||
Bytes::Base64 {
|
||||
value,
|
||||
space0,
|
||||
space1,
|
||||
..
|
||||
} => asserts.push(AssertResult::Body {
|
||||
actual: Ok(Value::Bytes(http_response.body.clone())),
|
||||
expected: Ok(Value::Bytes(value)),
|
||||
source_info: SourceInfo {
|
||||
start: space0.source_info.end,
|
||||
end: space1.source_info.start,
|
||||
},
|
||||
}),
|
||||
Bytes::File { .. } => {
|
||||
let expected = match eval_body(body.clone(), variables, context_dir) {
|
||||
Ok(bytes) => Ok(Value::Bytes(bytes)),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let actual = Ok(Value::Bytes(http_response.body.clone()));
|
||||
asserts.push(AssertResult::Body {
|
||||
actual,
|
||||
expected,
|
||||
source_info: body.space0.source_info.clone(),
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for assert in response.asserts() {
|
||||
let assert_result = eval_assert(assert, variables, http_response.clone());
|
||||
asserts.push(assert_result);
|
||||
}
|
||||
asserts
|
||||
}
|
||||
|
||||
pub fn eval_captures(
|
||||
response: Response,
|
||||
http_response: http::Response,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<Vec<CaptureResult>, Error> {
|
||||
let mut captures = vec![];
|
||||
for capture in response.captures() {
|
||||
let capture_result = eval_capture(capture, variables, http_response.clone())?;
|
||||
captures.push(capture_result);
|
||||
}
|
||||
Ok(captures)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -276,10 +279,11 @@ mod tests {
|
||||
let variables = HashMap::new();
|
||||
let context_dir = "undefined".to_string();
|
||||
assert_eq!(
|
||||
user_response().eval_asserts(
|
||||
eval_asserts(
|
||||
user_response(),
|
||||
&variables,
|
||||
http::xml_two_users_http_response(),
|
||||
context_dir
|
||||
context_dir,
|
||||
),
|
||||
vec![
|
||||
AssertResult::Version {
|
||||
@ -313,9 +317,12 @@ mod tests {
|
||||
pub fn test_eval_captures() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
user_response()
|
||||
.eval_captures(http::xml_two_users_http_response(), &variables)
|
||||
.unwrap(),
|
||||
eval_captures(
|
||||
user_response(),
|
||||
http::xml_two_users_http_response(),
|
||||
&variables,
|
||||
)
|
||||
.unwrap(),
|
||||
vec![CaptureResult {
|
||||
name: "UserCount".to_string(),
|
||||
value: Value::Float(2, 0),
|
||||
|
@ -22,50 +22,52 @@ use crate::ast::*;
|
||||
use super::core::{Error, RunnerError};
|
||||
use super::value::Value;
|
||||
|
||||
impl Template {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>) -> Result<String, Error> {
|
||||
let Template { elements, .. } = self;
|
||||
{
|
||||
let mut value = String::from("");
|
||||
for elem in elements {
|
||||
match elem.eval(variables) {
|
||||
Ok(v) => value.push_str(v.as_str()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
pub fn eval_template(
|
||||
template: Template,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<String, Error> {
|
||||
let Template { elements, .. } = template;
|
||||
{
|
||||
let mut value = String::from("");
|
||||
for elem in elements {
|
||||
match eval_template_element(elem, variables) {
|
||||
Ok(v) => value.push_str(v.as_str()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TemplateElement {
|
||||
pub fn eval(self, variables: &HashMap<String, Value>) -> Result<String, Error> {
|
||||
match self {
|
||||
TemplateElement::String { value, .. } => Ok(value),
|
||||
TemplateElement::Expression(Expr {
|
||||
variable: Variable { name, source_info },
|
||||
..
|
||||
}) => match variables.get(&name as &str) {
|
||||
Some(value) => {
|
||||
if value.is_renderable() {
|
||||
Ok(value.clone().to_string())
|
||||
} else {
|
||||
Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::UnrenderableVariable {
|
||||
value: value.to_string(),
|
||||
},
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
fn eval_template_element(
|
||||
template_element: TemplateElement,
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<String, Error> {
|
||||
match template_element {
|
||||
TemplateElement::String { value, .. } => Ok(value),
|
||||
TemplateElement::Expression(Expr {
|
||||
variable: Variable { name, source_info },
|
||||
..
|
||||
}) => match variables.get(&name as &str) {
|
||||
Some(value) => {
|
||||
if value.is_renderable() {
|
||||
Ok(value.clone().to_string())
|
||||
} else {
|
||||
Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::UnrenderableVariable {
|
||||
value: value.to_string(),
|
||||
},
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
_ => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::TemplateVariableNotDefined { name },
|
||||
assert: false,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::TemplateVariableNotDefined { name },
|
||||
assert: false,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,11 +105,13 @@ mod tests {
|
||||
fn test_template_element() {
|
||||
let variables = HashMap::new();
|
||||
assert_eq!(
|
||||
TemplateElement::String {
|
||||
value: "World".to_string(),
|
||||
encoded: "World".to_string()
|
||||
}
|
||||
.eval(&variables)
|
||||
eval_template_element(
|
||||
TemplateElement::String {
|
||||
value: "World".to_string(),
|
||||
encoded: "World".to_string(),
|
||||
},
|
||||
&variables
|
||||
)
|
||||
.unwrap(),
|
||||
"World".to_string()
|
||||
);
|
||||
@ -115,7 +119,7 @@ mod tests {
|
||||
let mut variables = HashMap::new();
|
||||
variables.insert("name".to_string(), Value::String("World".to_string()));
|
||||
assert_eq!(
|
||||
template_element_expression().eval(&variables).unwrap(),
|
||||
eval_template_element(template_element_expression(), &variables).unwrap(),
|
||||
"World".to_string()
|
||||
);
|
||||
}
|
||||
@ -127,8 +131,7 @@ mod tests {
|
||||
"name".to_string(),
|
||||
Value::List(vec![Value::Integer(1), Value::Integer(2)]),
|
||||
);
|
||||
let error = template_element_expression()
|
||||
.eval(&variables)
|
||||
let error = eval_template_element(template_element_expression(), &variables)
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 3, 1, 7));
|
||||
|
Loading…
Reference in New Issue
Block a user