Merge pull request #176 from Orange-OpenSource/feature/type_input_variables

Type input variables
This commit is contained in:
Fabrice Reix 2021-02-25 21:06:17 +01:00 committed by GitHub
commit 866074fb9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 177 additions and 40 deletions

View File

@ -1 +1 @@
<div class="hurl-file"><div class="hurl-entry"><div class="request"><span class="line"><span class="method">POST</span> <span class="url">http://localhost:8000/variables</span></span></div><span class="line">{<span class="line"> "name": "{{name}}",</span><span class="line"> "age": {{age}},</span><span class="line"> "female": {{female}}</span><span class="line">}</span></div><span class="line"> </span></div>
<div class="hurl-file"><div class="hurl-entry"><div class="request"><span class="line"><span class="method">POST</span> <span class="url">http://localhost:8000/variables</span></span></div><span class="line">{<span class="line"> "name": "{{name}}",</span><span class="line"> "age": {{age}},</span><span class="line"> "height": {{height}},</span><span class="line"> "female": {{female}},</span><span class="line"> "id": "{{id}}",</span><span class="line"> "a_null": {{a_null}}</span><span class="line">}</span><div class="response"><span class="line"></span><span class="line"><span class="version">HTTP/*</span> <span class="status">200</span></span><span class="line section-header">[Asserts]</span></span><span class="line"><span class="query-type">variable</span> <span class="string">"name"</span> <span class="predicate-type">equals</span> <span class="string">"Jennifer"</span></span><span class="line"><span class="query-type">variable</span> <span class="string">"female"</span> <span class="predicate-type">equals</span> <span class="boolean">true</span></span><span class="line"><span class="query-type">variable</span> <span class="string">"age"</span> <span class="predicate-type">equals</span> <span class="number">30</span></span><span class="line"><span class="query-type">variable</span> <span class="string">"height"</span> <span class="predicate-type">equals</span> <span class="number">1.70</span></span><span class="line"><span class="query-type">variable</span> <span class="string">"a_null"</span> <span class="predicate-type">equals</span> <span class="null">null</span></span><span class="line"><span class="query-type">variable</span> <span class="string">"id"</span> <span class="predicate-type">equals</span> <span class="string">"123"</span></span></div></div></div>

View File

@ -2,6 +2,17 @@ POST http://localhost:8000/variables
{
"name": "{{name}}",
"age": {{age}},
"female": {{female}}
"height": {{height}},
"female": {{female}},
"id": "{{id}}",
"a_null": {{a_null}}
}
HTTP/* 200
[Asserts]
variable "name" equals "Jennifer"
variable "female" equals true
variable "age" equals 30
variable "height" equals 1.70
variable "a_null" equals null
variable "id" equals "123"

View File

@ -1 +1 @@
{"entries":[{"request":{"method":"POST","url":"http://localhost:8000/variables","body":{"type":"json","value":{"name":"{{name}}","age":"{{age}}","female":"{{female}}"}}}}]}
{"entries":[{"request":{"method":"POST","url":"http://localhost:8000/variables","body":{"type":"json","value":{"name":"{{name}}","age":"{{age}}","height":"{{height}}","female":"{{female}}","id":"{{id}}","a_null":"{{a_null}}"}}},"response":{"status":200,"asserts":[{"query":{"type":"variable","name":"name"},"predicate":{"type":"equal","value":"Jennifer"}},{"query":{"type":"variable","name":"female"},"predicate":{"type":"equal","value":true}},{"query":{"type":"variable","name":"age"},"predicate":{"type":"equal","value":30}},{"query":{"type":"variable","name":"height"},"predicate":{"type":"equal","value":1.70}},{"query":{"type":"variable","name":"a_null"},"predicate":{"type":"equal","value":null}},{"query":{"type":"variable","name":"id"},"predicate":{"type":"equal","value":"123"}}]}}]}

View File

@ -1,4 +1,7 @@
# Variables for hurl
name=Jennifer
age=30
height=1.70
id="123"
a_null=null

View File

@ -11,6 +11,9 @@ def variables():
assert data['name'] == 'Jennifer'
assert data['age'] == 30
assert data['female'] == True
assert data['id'] == '123'
assert data['height'] == 1.7
assert data['a_null'] == None
return ''

View File

@ -21,10 +21,12 @@ pub use self::logger::{
log_info, make_logger_error_message, make_logger_parser_error, make_logger_runner_error,
make_logger_verbose,
};
pub use self::variables::parse as parse_variable;
mod fs;
pub mod interactive;
mod logger;
mod variables;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CLIError {

View File

@ -0,0 +1,137 @@
/*
* hurl (https://hurl.dev)
* Copyright (C) 2020 Orange
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
use crate::cli::CLIError;
use crate::runner::Value;
pub fn parse(s: &str) -> Result<(String, Value), CLIError> {
match s.find('=') {
None => Err(CLIError {
message: format!("Missing value for variable {}!", s),
}),
Some(index) => {
let (name, value) = s.split_at(index);
let value = parse_value(&value[1..])?;
Ok((name.to_string(), value))
}
}
}
fn parse_value(s: &str) -> Result<Value, CLIError> {
if s == "true" {
Ok(Value::Bool(true))
} else if s == "false" {
Ok(Value::Bool(false))
} else if s == "null" {
Ok(Value::Null {})
} else if let Ok(v) = s.parse::<i64>() {
Ok(Value::Integer(v))
} else if let Ok(v) = s.parse::<f64>() {
Ok(Value::Float(
v.trunc() as i64,
(v.fract() * 1_000_000_000_000_000_000.0).round() as u64,
))
} else if let Some(s) = s.strip_prefix('"') {
if let Some(s) = s.strip_suffix('"') {
Ok(Value::String(s.to_string()))
} else {
Err(CLIError {
message: "Value should end with a double quote".to_string(),
})
}
} else {
Ok(Value::String(s.to_string()))
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
fn test_parse() {
assert_eq!(
parse("name=Jennifer").unwrap(),
("name".to_string(), Value::String("Jennifer".to_string()))
);
assert_eq!(
parse("female=true").unwrap(),
("female".to_string(), Value::Bool(true))
);
assert_eq!(
parse("age=30").unwrap(),
("age".to_string(), Value::Integer(30))
);
assert_eq!(
parse("height=1.7").unwrap(),
(
"height".to_string(),
Value::Float(1, 700_000_000_000_000_000)
)
);
assert_eq!(
parse("id=\"123\"").unwrap(),
("id".to_string(), Value::String("123".to_string()))
);
assert_eq!(
parse("a_null=null").unwrap(),
("a_null".to_string(), Value::Null {})
);
}
#[test]
fn test_parse_error() {
assert_eq!(
parse("name").err().unwrap(),
CLIError {
message: "Missing value for variable name!".to_string()
}
);
}
#[test]
fn test_parse_value() {
assert_eq!(
parse_value("Jennifer").unwrap(),
Value::String("Jennifer".to_string())
);
assert_eq!(parse_value("true").unwrap(), Value::Bool(true));
assert_eq!(parse_value("30").unwrap(), Value::Integer(30));
assert_eq!(
parse_value("1.7").unwrap(),
Value::Float(1, 700_000_000_000_000_000)
);
assert_eq!(parse_value("1.0").unwrap(), Value::Float(1, 0));
assert_eq!(parse_value("-1.0").unwrap(), Value::Float(-1, 0));
assert_eq!(
parse_value("\"123\"").unwrap(),
Value::String("123".to_string())
);
assert_eq!(parse_value("null").unwrap(), Value::Null {});
}
#[test]
fn test_parse_value_error() {
assert_eq!(
parse_value("\"123").err().unwrap(),
CLIError {
message: "Value should end with a double quote".to_string()
}
)
}
}

View File

@ -33,7 +33,7 @@ use hurl::cli::CLIError;
use hurl::html;
use hurl::http;
use hurl::runner;
use hurl::runner::{HurlResult, RunnerOptions};
use hurl::runner::{HurlResult, RunnerOptions, Value};
use hurl_core::ast::{Pos, SourceInfo};
use hurl_core::error::Error;
use hurl_core::parser;
@ -46,7 +46,7 @@ pub struct CLIOptions {
pub fail_fast: bool,
pub insecure: bool,
pub interactive: bool,
pub variables: HashMap<String, String>,
pub variables: HashMap<String, Value>,
pub to_entry: Option<usize>,
pub follow_location: bool,
pub max_redirect: Option<usize>,
@ -287,7 +287,7 @@ fn html_report(matches: ArgMatches) -> Result<Option<std::path::PathBuf>, CLIErr
}
}
fn variables(matches: ArgMatches) -> Result<HashMap<String, String>, CLIError> {
fn variables(matches: ArgMatches) -> Result<HashMap<String, Value>, CLIError> {
let mut variables = HashMap::new();
if let Some(filename) = matches.value_of("variables_file") {
@ -313,45 +313,22 @@ fn variables(matches: ArgMatches) -> Result<HashMap<String, String>, CLIError> {
if line.starts_with('#') || line.is_empty() {
continue;
}
let (name, value) = parse_variable(line)?;
if variables.contains_key(name.as_str()) {
return Err(CLIError {
message: format!("Variable {} defined twice!", name),
});
}
variables.insert(name.to_string(), value[1..].to_string());
let (name, value) = cli::parse_variable(line)?;
variables.insert(name.to_string(), value);
}
}
if matches.is_present("variable") {
let input: Vec<_> = matches.values_of("variable").unwrap().collect();
for s in input {
let (name, value) = parse_variable(s)?;
if variables.contains_key(name.as_str()) {
return Err(CLIError {
message: format!("Variable {} defined twice!", name),
});
}
variables.insert(name.to_string(), value[1..].to_string());
let (name, value) = cli::parse_variable(s)?;
variables.insert(name.to_string(), value);
}
}
Ok(variables)
}
fn parse_variable(s: &str) -> Result<(String, String), CLIError> {
match s.find('=') {
None => Err(CLIError {
message: format!("Missing variable value for {}!", s),
}),
Some(index) => {
let (name, value) = s.split_at(index);
Ok((name.to_string(), value.to_string()))
}
}
}
fn app() -> clap::App<'static, 'static> {
clap::App::new("hurl")
//.author(clap::crate_authors!())

View File

@ -25,7 +25,7 @@ use super::value::Value;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RunnerOptions {
pub fail_fast: bool,
pub variables: HashMap<String, String>,
pub variables: HashMap<String, Value>,
pub to_entry: Option<usize>,
pub context_dir: String,
pub pre_entry: fn() -> bool,

View File

@ -23,7 +23,6 @@ use hurl_core::ast::*;
use super::core::*;
use super::entry;
use super::value::Value;
/// Run a Hurl file with the hurl http client
///
@ -101,7 +100,7 @@ pub fn run(
let mut variables = HashMap::default();
for (key, value) in options.variables {
variables.insert(key.to_string(), Value::String(value.to_string()));
variables.insert(key.to_string(), value);
}
let n = if let Some(to_entry) = options.to_entry {

View File

@ -26,6 +26,7 @@
pub use self::core::{Error, HurlResult, RunnerError, RunnerOptions};
pub use self::hurl_file::run as run_hurl_file;
pub use self::log_deserialize::parse_results as deserialize_results;
pub use self::value::Value;
mod assert;
mod body;

View File

@ -78,7 +78,11 @@ impl Value {
pub fn is_renderable(&self) -> bool {
matches!(
self,
Value::Integer(_) | Value::Bool(_) | Value::Float(_, _) | Value::String(_)
Value::Integer(_)
| Value::Bool(_)
| Value::Float(_, _)
| Value::String(_)
| Value::Null
)
}
}

View File

@ -30,7 +30,7 @@ pub enum Value {
// can not use simply Float(f64)
// the trait `std::cmp::Eq` is not implemented for `f64`
// integer part, decimal part (9 digits) TODO Clarify your custom type
// integer part, decimal part (18 digits) TODO Clarify your custom type
Float(i64, u64),
String(String),
@ -55,7 +55,7 @@ impl fmt::Display for Value {
Value::Object(_) => "Object()".to_string(),
Value::Nodeset(x) => format!("Nodeset{:?}", x),
Value::Bytes(x) => format!("Bytes({:x?})", x),
Value::Null => "Null".to_string(),
Value::Null => "null".to_string(),
Value::Unit => "Unit".to_string(),
};
write!(f, "{}", value)