Merge pull request #61 from Orange-OpenSource/refacto/remove_inherent_impl

Refacto - Remove inherent impl
This commit is contained in:
Fabrice Reix 2020-11-01 20:13:31 +01:00 committed by GitHub
commit 198caf5603
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1458 additions and 1414 deletions

View File

@ -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,
}
}

View File

@ -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),

View File

@ -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!(

View File

@ -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 {});
}

View File

@ -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()

View File

@ -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,
})
}
}

View File

@ -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"
}"#

View File

@ -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

View File

@ -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));

View File

@ -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()
})
);
}
}

View File

@ -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),

View File

@ -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));