Use only the RunnerOptions struct when running a Hurl File/Entry

This commit is contained in:
Fabrice Reix 2022-09-02 15:59:28 +02:00
parent 91f7396a6d
commit fefab14152
No known key found for this signature in database
GPG Key ID: BF5213154B2E7155
6 changed files with 141 additions and 108 deletions

View File

@ -27,11 +27,11 @@ use colored::*;
use hurl::cli; use hurl::cli;
use hurl::cli::{CliError, CliOptions, Logger, OutputType}; use hurl::cli::{CliError, CliOptions, Logger, OutputType};
use hurl::http; use hurl::http;
use hurl::http::{ContextDir, Verbosity}; use hurl::http::ContextDir;
use hurl::report; use hurl::report;
use hurl::report::canonicalize_filename; use hurl::report::canonicalize_filename;
use hurl::runner; use hurl::runner;
use hurl::runner::{HurlResult, RunnerError, RunnerOptions}; use hurl::runner::{HurlResult, RunnerError, RunnerOptions, Verbosity};
use hurl::util::logger::BaseLogger; use hurl::util::logger::BaseLogger;
use hurl_core::ast::{Entry, Pos, SourceInfo}; use hurl_core::ast::{Entry, Pos, SourceInfo};
use hurl_core::error::Error; use hurl_core::error::Error;
@ -163,24 +163,6 @@ fn execute(
}; };
let context_dir = ContextDir::new(current_dir, file_root); let context_dir = ContextDir::new(current_dir, file_root);
let client_options = http::ClientOptions {
cacert_file,
follow_location,
max_redirect,
cookie_input_file,
proxy,
no_proxy,
verbosity,
insecure,
timeout,
connect_timeout,
user,
user_agent,
compressed,
};
let mut client = http::Client::new(&client_options);
let pre_entry = if cli_options.interactive { let pre_entry = if cli_options.interactive {
Some(cli::interactive::pre_entry as fn(Entry) -> bool) Some(cli::interactive::pre_entry as fn(Entry) -> bool)
} else { } else {
@ -197,23 +179,49 @@ fn execute(
let ignore_asserts = cli_options.ignore_asserts; let ignore_asserts = cli_options.ignore_asserts;
let very_verbose = cli_options.very_verbose; let very_verbose = cli_options.very_verbose;
let runner_options = RunnerOptions { let runner_options = RunnerOptions {
cacert_file,
compressed,
fail_fast, fail_fast,
variables, variables,
to_entry, to_entry,
user,
context_dir, context_dir,
ignore_asserts, ignore_asserts,
insecure,
max_redirect,
very_verbose, very_verbose,
pre_entry, pre_entry,
proxy,
post_entry, post_entry,
connect_timeout,
cookie_input_file,
follow_location,
no_proxy,
timeout,
user_agent,
verbosity,
}; };
let result = runner::run( let client_options = http::ClientOptions {
&hurl_file, cacert_file: runner_options.cacert_file.clone(),
filename, follow_location: runner_options.follow_location,
&mut client, max_redirect: runner_options.max_redirect,
&runner_options, cookie_input_file: runner_options.cookie_input_file.clone(),
&client_options, proxy: runner_options.proxy.clone(),
logger, no_proxy: runner_options.no_proxy.clone(),
); verbosity: runner_options.verbosity.as_ref().map(|v| match v {
Verbosity::Verbose => http::Verbosity::Verbose,
Verbosity::VeryVerbose => http::Verbosity::VeryVerbose,
}),
insecure: runner_options.insecure,
timeout: runner_options.timeout,
connect_timeout: runner_options.connect_timeout,
user: runner_options.user.clone(),
user_agent: runner_options.user_agent.clone(),
compressed: runner_options.compressed,
};
let mut client = http::Client::new(&client_options);
let result = runner::run(&hurl_file, filename, &mut client, &runner_options, logger);
if cli_options.progress { if cli_options.progress {
logger.test_completed(&result); logger.test_completed(&result);
} }

View File

@ -17,6 +17,7 @@
*/ */
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration;
use crate::http; use crate::http;
use crate::http::ContextDir; use crate::http::ContextDir;
@ -26,14 +27,61 @@ use super::value::Value;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct RunnerOptions { pub struct RunnerOptions {
pub fail_fast: bool, pub cacert_file: Option<String>,
pub variables: HashMap<String, Value>, pub compressed: bool,
pub to_entry: Option<usize>, pub connect_timeout: Duration,
pub context_dir: ContextDir, pub context_dir: ContextDir,
pub cookie_input_file: Option<String>,
pub fail_fast: bool,
pub follow_location: bool,
pub ignore_asserts: bool, pub ignore_asserts: bool,
pub very_verbose: bool, // If true, log body response in verbose mode. pub insecure: bool,
pub pre_entry: Option<fn(Entry) -> bool>, pub max_redirect: Option<usize>,
pub no_proxy: Option<String>,
pub post_entry: Option<fn() -> bool>, pub post_entry: Option<fn() -> bool>,
pub pre_entry: Option<fn(Entry) -> bool>,
pub proxy: Option<String>,
pub timeout: Duration,
pub to_entry: Option<usize>,
pub user: Option<String>,
pub user_agent: Option<String>,
pub variables: HashMap<String, Value>,
pub verbosity: Option<Verbosity>,
pub very_verbose: bool, // If true, log body response in verbose mode.
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Verbosity {
Verbose,
VeryVerbose,
}
impl Default for RunnerOptions {
fn default() -> Self {
RunnerOptions {
cacert_file: None,
compressed: false,
connect_timeout: Duration::from_secs(300),
context_dir: Default::default(),
cookie_input_file: None,
fail_fast: false,
follow_location: false,
ignore_asserts: false,
insecure: false,
max_redirect: Some(50),
no_proxy: None,
post_entry: None,
pre_entry: None,
proxy: None,
timeout: Duration::from_secs(300),
to_entry: None,
user: None,
user_agent: None,
variables: Default::default(),
verbosity: None,
very_verbose: false,
}
}
} }
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]

View File

@ -19,7 +19,6 @@ use std::collections::HashMap;
use crate::cli::Logger; use crate::cli::Logger;
use crate::http; use crate::http;
use crate::http::{ClientOptions, Verbosity};
use hurl_core::ast::*; use hurl_core::ast::*;
use super::core::*; use super::core::*;
@ -39,7 +38,6 @@ pub fn run(
http_client: &mut http::Client, http_client: &mut http::Client,
variables: &mut HashMap<String, Value>, variables: &mut HashMap<String, Value>,
runner_options: &RunnerOptions, runner_options: &RunnerOptions,
client_options: &ClientOptions,
logger: &Logger, logger: &Logger,
) -> Vec<EntryResult> { ) -> Vec<EntryResult> {
let http_request = match eval_request(&entry.request, variables, &runner_options.context_dir) { let http_request = match eval_request(&entry.request, variables, &runner_options.context_dir) {
@ -52,23 +50,42 @@ pub fn run(
asserts: vec![], asserts: vec![],
errors: vec![error], errors: vec![error],
time_in_ms: 0, time_in_ms: 0,
compressed: client_options.compressed, compressed: runner_options.compressed,
}]; }];
} }
}; };
let client_options = http::ClientOptions {
cacert_file: runner_options.cacert_file.clone(),
follow_location: runner_options.follow_location,
max_redirect: runner_options.max_redirect,
cookie_input_file: runner_options.cookie_input_file.clone(),
proxy: runner_options.proxy.clone(),
no_proxy: runner_options.no_proxy.clone(),
verbosity: runner_options.verbosity.as_ref().map(|v| match v {
Verbosity::Verbose => http::Verbosity::Verbose,
Verbosity::VeryVerbose => http::Verbosity::VeryVerbose,
}),
insecure: runner_options.insecure,
timeout: runner_options.timeout,
connect_timeout: runner_options.connect_timeout,
user: runner_options.user.clone(),
user_agent: runner_options.user_agent.clone(),
compressed: runner_options.compressed,
};
// Experimental features // Experimental features
// with cookie storage // with cookie storage
use std::str::FromStr; use std::str::FromStr;
if let Some(s) = cookie_storage_set(&entry.request) { if let Some(s) = cookie_storage_set(&entry.request) {
if let Ok(cookie) = http::Cookie::from_str(s.as_str()) { if let Ok(cookie) = http::Cookie::from_str(s.as_str()) {
http_client.add_cookie(&cookie, client_options); http_client.add_cookie(&cookie, &client_options);
} else { } else {
logger.warning(format!("Cookie string can not be parsed: '{}'", s).as_str()); logger.warning(format!("Cookie string can not be parsed: '{}'", s).as_str());
} }
} }
if cookie_storage_clear(&entry.request) { if cookie_storage_clear(&entry.request) {
http_client.clear_cookie_storage(client_options); http_client.clear_cookie_storage(&client_options);
} }
logger.debug(""); logger.debug("");
@ -81,12 +98,12 @@ pub fn run(
logger.debug("Request can be run with the following curl command:"); logger.debug("Request can be run with the following curl command:");
logger.debug( logger.debug(
http_client http_client
.curl_command_line(&http_request, &runner_options.context_dir, client_options) .curl_command_line(&http_request, &runner_options.context_dir, &client_options)
.as_str(), .as_str(),
); );
logger.debug(""); logger.debug("");
let calls = match http_client.execute_with_redirect(&http_request, client_options, logger) { let calls = match http_client.execute_with_redirect(&http_request, &client_options, logger) {
Ok(calls) => calls, Ok(calls) => calls,
Err(http_error) => { Err(http_error) => {
let runner_error = RunnerError::from(http_error); let runner_error = RunnerError::from(http_error);
@ -228,16 +245,16 @@ fn log_request_spec(request: &http::RequestSpec, logger: &Logger) {
logger.debug(""); logger.debug("");
} }
/// Returns a new [`ClientOptions`] based on the `entry` optional Options section /// Returns a new [`RunnerOptions`] based on the `entry` optional Options section
/// and a default `client_options`. /// and a default `runner_options`.
pub fn get_entry_options( pub fn get_entry_options(
entry: &Entry, entry: &Entry,
client_options: &ClientOptions, runner_options: &RunnerOptions,
logger: &Logger, logger: &Logger,
) -> ClientOptions { ) -> RunnerOptions {
let mut client_options = client_options.clone(); let mut runner_options = runner_options.clone();
if !has_options(entry) { if !has_options(entry) {
return client_options; return runner_options;
} }
logger.debug(""); logger.debug("");
@ -248,27 +265,27 @@ pub fn get_entry_options(
for option in options { for option in options {
match option { match option {
EntryOption::CaCertificate(option) => { EntryOption::CaCertificate(option) => {
client_options.cacert_file = Some(option.filename.value.clone()); runner_options.cacert_file = Some(option.filename.value.clone());
logger.debug(format!("cacert: {}", option.filename.value).as_str()); logger.debug(format!("cacert: {}", option.filename.value).as_str());
} }
EntryOption::Compressed(option) => { EntryOption::Compressed(option) => {
client_options.compressed = option.value; runner_options.compressed = option.value;
logger.debug(format!("compressed: {}", option.value).as_str()); logger.debug(format!("compressed: {}", option.value).as_str());
} }
EntryOption::Insecure(option) => { EntryOption::Insecure(option) => {
client_options.insecure = option.value; runner_options.insecure = option.value;
logger.debug(format!("insecure: {}", option.value).as_str()); logger.debug(format!("insecure: {}", option.value).as_str());
} }
EntryOption::FollowLocation(option) => { EntryOption::FollowLocation(option) => {
client_options.follow_location = option.value; runner_options.follow_location = option.value;
logger.debug(format!("location: {}", option.value).as_str()); logger.debug(format!("location: {}", option.value).as_str());
} }
EntryOption::MaxRedirect(option) => { EntryOption::MaxRedirect(option) => {
client_options.max_redirect = Some(option.value); runner_options.max_redirect = Some(option.value);
logger.debug(format!("max-redirs: {}", option.value).as_str()); logger.debug(format!("max-redirs: {}", option.value).as_str());
} }
EntryOption::Verbose(option) => { EntryOption::Verbose(option) => {
client_options.verbosity = if option.value { runner_options.verbosity = if option.value {
Some(Verbosity::Verbose) Some(Verbosity::Verbose)
} else { } else {
None None
@ -277,7 +294,7 @@ pub fn get_entry_options(
} }
EntryOption::VeryVerbose(option) => { EntryOption::VeryVerbose(option) => {
client_options.verbosity = if option.value { runner_options.verbosity = if option.value {
Some(Verbosity::VeryVerbose) Some(Verbosity::VeryVerbose)
} else { } else {
None None
@ -288,7 +305,7 @@ pub fn get_entry_options(
} }
} }
} }
client_options runner_options
} }
/// Returns [`true`] if this `entry` has an Option section, [`false`] otherwise. /// Returns [`true`] if this `entry` has an Option section, [`false`] otherwise.

View File

@ -20,7 +20,6 @@ use std::time::Instant;
use crate::cli::Logger; use crate::cli::Logger;
use crate::http; use crate::http;
use crate::http::ClientOptions;
use crate::runner::entry::get_entry_verbosity; use crate::runner::entry::get_entry_verbosity;
use hurl_core::ast::*; use hurl_core::ast::*;
@ -57,17 +56,10 @@ use super::entry;
/// let logger = Logger::new(false, false, filename, s); /// let logger = Logger::new(false, false, filename, s);
/// ///
/// // Define runner options /// // Define runner options
/// let variables = std::collections::HashMap::new();
/// let runner_options = runner::RunnerOptions { /// let runner_options = runner::RunnerOptions {
/// fail_fast: false, /// very_verbose: true,
/// variables, /// ..runner::RunnerOptions::default()
/// to_entry: None, /// };
/// context_dir: ContextDir::default(),
/// ignore_asserts: false,
/// very_verbose: false,
/// pre_entry: None,
/// post_entry: None,
/// };
/// ///
/// // Run the hurl file /// // Run the hurl file
/// let hurl_results = runner::run( /// let hurl_results = runner::run(
@ -75,8 +67,7 @@ use super::entry;
/// filename, /// filename,
/// &mut client, /// &mut client,
/// &runner_options, /// &runner_options,
/// &client_options, /// &logger
/// &logger,
/// ); /// );
/// assert!(hurl_results.success); /// assert!(hurl_results.success);
/// ``` /// ```
@ -85,7 +76,6 @@ pub fn run(
filename: &str, filename: &str,
http_client: &mut http::Client, http_client: &mut http::Client,
runner_options: &RunnerOptions, runner_options: &RunnerOptions,
client_options: &ClientOptions,
logger: &Logger, logger: &Logger,
) -> HurlResult { ) -> HurlResult {
let mut entries = vec![]; let mut entries = vec![];
@ -119,7 +109,7 @@ pub fn run(
// We compute these new overridden options for this entry, before entering into the `run` // We compute these new overridden options for this entry, before entering into the `run`
// function because entry options can modify the logger and we want the preamble // function because entry options can modify the logger and we want the preamble
// "Executing entry..." to be displayed based on the entry level verbosity. // "Executing entry..." to be displayed based on the entry level verbosity.
let entry_verbosity = get_entry_verbosity(entry, &client_options.verbosity); let entry_verbosity = get_entry_verbosity(entry, &runner_options.verbosity);
let logger = &Logger::new( let logger = &Logger::new(
logger.color, logger.color,
entry_verbosity.is_some(), entry_verbosity.is_some(),
@ -132,16 +122,9 @@ pub fn run(
); );
logger.debug_important(format!("Executing entry {}", entry_index + 1).as_str()); logger.debug_important(format!("Executing entry {}", entry_index + 1).as_str());
let client_options = entry::get_entry_options(entry, client_options, logger); let runner_options = entry::get_entry_options(entry, runner_options, logger);
let entry_results = entry::run( let entry_results = entry::run(entry, http_client, &mut variables, &runner_options, logger);
entry,
http_client,
&mut variables,
runner_options,
&client_options,
logger,
);
for entry_result in &entry_results { for entry_result in &entry_results {
for e in &entry_result.errors { for e in &entry_result.errors {

View File

@ -25,6 +25,7 @@
pub use self::core::{ pub use self::core::{
AssertResult, CaptureResult, EntryResult, Error, HurlResult, RunnerError, RunnerOptions, AssertResult, CaptureResult, EntryResult, Error, HurlResult, RunnerError, RunnerOptions,
Verbosity,
}; };
pub use self::hurl_file::run; pub use self::hurl_file::run;
pub use self::value::Value; pub use self::value::Value;

View File

@ -16,15 +16,15 @@
* *
*/ */
use std::collections::HashMap;
use hurl::cli; use hurl::cli;
use hurl::cli::Logger; use hurl::cli::Logger;
use hurl::http; use hurl::http;
use hurl::http::ContextDir;
use hurl::runner; use hurl::runner;
use hurl::runner::RunnerOptions; use hurl::runner::RunnerOptions;
use hurl_core::ast::*; use hurl_core::ast::*;
use hurl_core::parser; use hurl_core::parser;
use std::collections::HashMap;
// Can be used for debugging // Can be used for debugging
#[test] #[test]
@ -38,24 +38,12 @@ fn test_hurl_file() {
let logger = Logger::new(false, false, filename, &content); let logger = Logger::new(false, false, filename, &content);
let runner_options = RunnerOptions { let runner_options = RunnerOptions {
fail_fast: false,
variables, variables,
to_entry: None,
context_dir: ContextDir::default(), ..RunnerOptions::default()
ignore_asserts: false,
very_verbose: false,
pre_entry: None,
post_entry: None,
}; };
let _hurl_log = runner::run( let _hurl_log = runner::run(&hurl_file, filename, &mut client, &runner_options, &logger);
&hurl_file,
filename,
&mut client,
&runner_options,
&client_options,
&logger,
);
} }
#[cfg(test)] #[cfg(test)]
@ -168,24 +156,12 @@ fn test_hello() {
}], }],
line_terminators: vec![], line_terminators: vec![],
}; };
let variables = HashMap::new(); let runner_options = RunnerOptions::default();
let runner_options = RunnerOptions {
fail_fast: true,
variables,
to_entry: None,
context_dir: ContextDir::default(),
ignore_asserts: false,
very_verbose: false,
pre_entry: None,
post_entry: None,
};
runner::run( runner::run(
&hurl_file, &hurl_file,
"filename", "filename",
&mut client, &mut client,
&runner_options, &runner_options,
&client_options,
&logger, &logger,
); );
} }