mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-11-26 11:43:08 +03:00
Add verbose entry option.
This commit is contained in:
parent
02a1e49ddb
commit
633500ebbd
@ -119,12 +119,15 @@ assert:
|
||||
|
||||
option:
|
||||
lt*
|
||||
(insecure-option | ca-certificate-option)
|
||||
(insecure-option | ca-certificate-option | verbose-option)
|
||||
|
||||
insecure-option: "insecure" ":" (boolean | template) lt
|
||||
insecure-option: "insecure" ":" boolean lt
|
||||
|
||||
ca-certificate-option: "cacert" ":" filename lt
|
||||
|
||||
verbose-option: "verbose" ":" boolean lt
|
||||
|
||||
|
||||
# Query
|
||||
|
||||
query: main-query (sp subquery)?
|
||||
|
28
integration/tests_ok/option_verbose.err.pattern
Normal file
28
integration/tests_ok/option_verbose.err.pattern
Normal file
@ -0,0 +1,28 @@
|
||||
* ------------------------------------------------------------------------------
|
||||
* Executing entry 2
|
||||
*
|
||||
* Entry options:
|
||||
* verbose: true
|
||||
*
|
||||
* Cookie store:
|
||||
*
|
||||
* Request:
|
||||
* GET http://localhost:8000/hello
|
||||
*
|
||||
* Request can be run with the following curl command:
|
||||
* curl 'http://localhost:8000/hello'
|
||||
*
|
||||
> GET /hello HTTP/1.1
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> User-Agent: hurl/~~~
|
||||
>
|
||||
* Response:
|
||||
*
|
||||
< HTTP/1.0 200 OK
|
||||
< Content-Type: text/html; charset=utf-8
|
||||
< Content-Length: 12
|
||||
< Server: Flask Server
|
||||
< Date: ~~~, ~~ ~~~ ~~~~ ~~:~~:~~ GMT
|
||||
<
|
||||
*
|
10
integration/tests_ok/option_verbose.html
Normal file
10
integration/tests_ok/option_verbose.html
Normal file
@ -0,0 +1,10 @@
|
||||
<pre><code class="language-hurl"><span class="hurl-entry"><span class="request"><span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/hello</span></span>
|
||||
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/hello</span></span>
|
||||
<span class="line section-header">[Options]</span>
|
||||
<span class="line"><span class="string">verbose</span><span>:</span> <span class="boolean">true</span></span>
|
||||
</span><span class="response"><span class="line"></span>
|
||||
<span class="line"><span class="version">HTTP/*</span> <span class="number">200</span></span>
|
||||
<span class="raw"><span class="line">```Hello World!```</span></span>
|
||||
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/hello</span></span></span></span></code></pre>
|
10
integration/tests_ok/option_verbose.hurl
Normal file
10
integration/tests_ok/option_verbose.hurl
Normal file
@ -0,0 +1,10 @@
|
||||
GET http://localhost:8000/hello
|
||||
|
||||
GET http://localhost:8000/hello
|
||||
[Options]
|
||||
verbose: true
|
||||
|
||||
HTTP/* 200
|
||||
```Hello World!```
|
||||
|
||||
GET http://localhost:8000/hello
|
1
integration/tests_ok/option_verbose.json
Normal file
1
integration/tests_ok/option_verbose.json
Normal file
@ -0,0 +1 @@
|
||||
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/hello"}},{"request":{"method":"GET","url":"http://localhost:8000/hello"},"response":{"status":200,"body":{"type":"raw-string","value":"Hello World!"}}},{"request":{"method":"GET","url":"http://localhost:8000/hello"}}]}
|
1
integration/tests_ok/option_verbose.out
Normal file
1
integration/tests_ok/option_verbose.out
Normal file
@ -0,0 +1 @@
|
||||
Hello World!
|
@ -17,7 +17,7 @@
|
||||
> GET /very-verbose/redirect HTTP/1.1
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
>
|
||||
* Request body:
|
||||
*
|
||||
@ -45,7 +45,7 @@
|
||||
> GET /very-verbose/redirected HTTP/1.1
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
>
|
||||
* Request body:
|
||||
*
|
||||
@ -74,7 +74,7 @@
|
||||
> GET /very-verbose/encoding/latin1 HTTP/1.1
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
>
|
||||
* Request body:
|
||||
*
|
||||
@ -108,7 +108,7 @@
|
||||
> Accept: */*
|
||||
> Accept-Encoding: brotli
|
||||
> Content-Type: application/json
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
> Content-Length: 37
|
||||
>
|
||||
* Request body:
|
||||
@ -142,7 +142,7 @@
|
||||
> GET /very-verbose/cat HTTP/1.1
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
>
|
||||
* Request body:
|
||||
*
|
||||
@ -175,7 +175,7 @@
|
||||
> POST /very-verbose/update-cat HTTP/1.1
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
> Content-Length: 26572
|
||||
> Content-Type: multipart/form-data; boundary=~~~~~
|
||||
>
|
||||
@ -208,7 +208,7 @@
|
||||
> Host: localhost:8000
|
||||
> Accept: */*
|
||||
> x-foo: bar
|
||||
> User-Agent: hurl/~~~-snapshot
|
||||
> User-Agent: hurl/~~~
|
||||
>
|
||||
* Request body:
|
||||
*
|
||||
|
@ -19,7 +19,7 @@ use std::collections::HashMap;
|
||||
|
||||
use crate::cli::Logger;
|
||||
use crate::http;
|
||||
use crate::http::ClientOptions;
|
||||
use crate::http::{ClientOptions, Verbosity};
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::*;
|
||||
@ -56,21 +56,18 @@ pub fn run(
|
||||
}
|
||||
};
|
||||
|
||||
// We computes overridden options for this entry.
|
||||
let client_options = get_entry_options(entry, client_options, logger);
|
||||
|
||||
// Experimental features
|
||||
// with cookie storage
|
||||
use std::str::FromStr;
|
||||
if let Some(s) = cookie_storage_set(&entry.request) {
|
||||
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 {
|
||||
logger.warning(format!("Cookie string can not be parsed: '{}'", s).as_str());
|
||||
}
|
||||
}
|
||||
if cookie_storage_clear(&entry.request) {
|
||||
http_client.clear_cookie_storage(&client_options);
|
||||
http_client.clear_cookie_storage(client_options);
|
||||
}
|
||||
|
||||
logger.debug("");
|
||||
@ -83,12 +80,12 @@ pub fn run(
|
||||
logger.debug("Request can be run with the following curl command:");
|
||||
logger.debug(
|
||||
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(),
|
||||
);
|
||||
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,
|
||||
Err(http_error) => {
|
||||
let runner_error = RunnerError::from(http_error);
|
||||
@ -229,23 +226,19 @@ fn log_request_spec(request: &http::RequestSpec, logger: &Logger) {
|
||||
|
||||
/// Returns a new [`ClientOptions`] based on the `entry` optional Options section
|
||||
/// and a default `client_options`.
|
||||
fn get_entry_options(
|
||||
pub fn get_entry_options(
|
||||
entry: &Entry,
|
||||
client_options: &ClientOptions,
|
||||
logger: &Logger,
|
||||
) -> ClientOptions {
|
||||
let mut client_options = client_options.clone();
|
||||
|
||||
let has_options = entry
|
||||
.request
|
||||
.sections
|
||||
.iter()
|
||||
.any(|s| matches!(s.value, SectionValue::Options(_)));
|
||||
if has_options {
|
||||
logger.debug("");
|
||||
logger.debug_important("Entry options:");
|
||||
if !has_options(entry) {
|
||||
return client_options;
|
||||
}
|
||||
|
||||
logger.debug("");
|
||||
logger.debug_important("Entry options:");
|
||||
|
||||
for section in &entry.request.sections {
|
||||
if let SectionValue::Options(options) = §ion.value {
|
||||
for option in options {
|
||||
@ -258,9 +251,46 @@ fn get_entry_options(
|
||||
client_options.cacert_file = Some(option.filename.value.clone());
|
||||
logger.debug(format!("cacert: {}", option.filename.value).as_str());
|
||||
}
|
||||
EntryOption::Verbose(option) => {
|
||||
client_options.verbosity = if option.value {
|
||||
Some(Verbosity::Verbose)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
logger.debug(format!("verbose: {}", option.value).as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
client_options
|
||||
}
|
||||
|
||||
/// Returns [`true`] if this `entry` has an Option section, [`false`] otherwise.
|
||||
fn has_options(entry: &Entry) -> bool {
|
||||
entry
|
||||
.request
|
||||
.sections
|
||||
.iter()
|
||||
.any(|s| matches!(s.value, SectionValue::Options(_)))
|
||||
}
|
||||
|
||||
/// Returns the overridden `entry` verbosity, or the default `verbosity` file.
|
||||
pub fn get_entry_verbosity(entry: &Entry, verbosity: &Option<Verbosity>) -> Option<Verbosity> {
|
||||
let mut verbosity = verbosity.clone();
|
||||
|
||||
for section in &entry.request.sections {
|
||||
if let SectionValue::Options(options) = §ion.value {
|
||||
for option in options {
|
||||
if let EntryOption::Verbose(option) = option {
|
||||
verbosity = if option.value {
|
||||
Some(Verbosity::Verbose)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
verbosity
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ use std::time::Instant;
|
||||
use crate::cli::Logger;
|
||||
use crate::http;
|
||||
use crate::http::ClientOptions;
|
||||
use crate::runner::entry::get_entry_verbosity;
|
||||
use hurl_core::ast::*;
|
||||
|
||||
use super::core::*;
|
||||
@ -90,8 +91,8 @@ pub fn run(
|
||||
let mut entries = vec![];
|
||||
let mut variables = HashMap::default();
|
||||
|
||||
for (key, value) in runner_options.variables.clone() {
|
||||
variables.insert(key.to_string(), value);
|
||||
for (key, value) in &runner_options.variables {
|
||||
variables.insert(key.to_string(), value.clone());
|
||||
}
|
||||
|
||||
let n = if let Some(to_entry) = runner_options.to_entry {
|
||||
@ -105,9 +106,8 @@ pub fn run(
|
||||
.entries
|
||||
.iter()
|
||||
.take(n)
|
||||
.cloned()
|
||||
.enumerate()
|
||||
.collect::<Vec<(usize, Entry)>>()
|
||||
.collect::<Vec<(usize, &Entry)>>()
|
||||
{
|
||||
if let Some(pre_entry) = runner_options.pre_entry {
|
||||
let exit = pre_entry(entry.clone());
|
||||
@ -116,17 +116,30 @@ pub fn 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
|
||||
// "Executing entry..." to be displayed based on the entry level verbosity.
|
||||
let entry_verbosity = get_entry_verbosity(entry, &client_options.verbosity);
|
||||
let logger = &Logger::new(
|
||||
logger.color,
|
||||
entry_verbosity.is_some(),
|
||||
logger.filename,
|
||||
logger.content,
|
||||
);
|
||||
|
||||
logger.debug_important(
|
||||
"------------------------------------------------------------------------------",
|
||||
);
|
||||
logger.debug_important(format!("Executing entry {}", entry_index + 1).as_str());
|
||||
|
||||
let client_options = entry::get_entry_options(entry, client_options, logger);
|
||||
|
||||
let entry_results = entry::run(
|
||||
&entry,
|
||||
entry,
|
||||
http_client,
|
||||
&mut variables,
|
||||
runner_options,
|
||||
client_options,
|
||||
&client_options,
|
||||
logger,
|
||||
);
|
||||
|
||||
|
@ -91,8 +91,10 @@ pub struct Logger<'a> {
|
||||
pub header_out: fn(&str, &str),
|
||||
pub test_running: fn(&str, usize, usize),
|
||||
pub test_completed: fn(&str, bool),
|
||||
pub content: &'a str,
|
||||
pub color: bool,
|
||||
pub verbose: bool,
|
||||
pub filename: &'a str,
|
||||
pub content: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Logger<'a> {
|
||||
@ -112,8 +114,10 @@ impl<'a> Logger<'a> {
|
||||
header_out: log_header_out,
|
||||
test_running: log_test_running,
|
||||
test_completed: log_test_completed,
|
||||
content,
|
||||
color,
|
||||
verbose,
|
||||
filename,
|
||||
content,
|
||||
},
|
||||
(false, true) => Logger {
|
||||
info: log_info,
|
||||
@ -128,8 +132,10 @@ impl<'a> Logger<'a> {
|
||||
header_out: log_header_out_no_color,
|
||||
test_running: log_test_running_no_color,
|
||||
test_completed: log_test_completed_no_color,
|
||||
content,
|
||||
color,
|
||||
verbose,
|
||||
filename,
|
||||
content,
|
||||
},
|
||||
(true, false) => Logger {
|
||||
info: log_info,
|
||||
@ -144,8 +150,10 @@ impl<'a> Logger<'a> {
|
||||
header_out: nop2,
|
||||
test_running: log_test_running,
|
||||
test_completed: log_test_completed,
|
||||
content,
|
||||
color,
|
||||
verbose,
|
||||
filename,
|
||||
content,
|
||||
},
|
||||
(false, false) => Logger {
|
||||
info: log_info,
|
||||
@ -160,8 +168,10 @@ impl<'a> Logger<'a> {
|
||||
header_out: nop2,
|
||||
test_running: log_test_running_no_color,
|
||||
test_completed: log_test_completed_no_color,
|
||||
content,
|
||||
color,
|
||||
verbose,
|
||||
filename,
|
||||
content,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -669,6 +669,7 @@ pub struct Variable {
|
||||
pub enum EntryOption {
|
||||
Insecure(InsecureOption),
|
||||
CaCertificate(CaCertificateOption),
|
||||
Verbose(VerboseOption),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@ -690,3 +691,13 @@ pub struct CaCertificateOption {
|
||||
pub filename: Filename,
|
||||
pub line_terminator0: LineTerminator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct VerboseOption {
|
||||
pub line_terminators: Vec<LineTerminator>,
|
||||
pub space0: Whitespace,
|
||||
pub space1: Whitespace,
|
||||
pub space2: Whitespace,
|
||||
pub value: bool,
|
||||
pub line_terminator0: LineTerminator,
|
||||
}
|
||||
|
@ -234,6 +234,7 @@ impl Htmlable for EntryOption {
|
||||
match self {
|
||||
EntryOption::Insecure(option) => option.to_html(),
|
||||
EntryOption::CaCertificate(option) => option.to_html(),
|
||||
EntryOption::Verbose(option) => option.to_html(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,6 +273,23 @@ impl Htmlable for CaCertificateOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for VerboseOption {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str("<span class=\"string\">verbose</span>");
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>:</span>");
|
||||
buffer.push_str(self.space2.to_html().as_str());
|
||||
buffer.push_str(format!("<span class=\"boolean\">{}</span>", self.value).as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for MultipartParam {
|
||||
fn to_html(&self) -> String {
|
||||
match self {
|
||||
|
@ -342,7 +342,7 @@ fn assert(reader: &mut Reader) -> ParseResult<'static, Assert> {
|
||||
}
|
||||
|
||||
fn option(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
choice(vec![option_insecure, option_cacert], reader)
|
||||
choice(vec![option_insecure, option_cacert, option_verbose], reader)
|
||||
}
|
||||
|
||||
fn option_insecure(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
@ -389,6 +389,28 @@ fn option_cacert(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
Ok(EntryOption::CaCertificate(option))
|
||||
}
|
||||
|
||||
fn option_verbose(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
let line_terminators = optional_line_terminators(reader)?;
|
||||
let space0 = zero_or_more_spaces(reader)?;
|
||||
try_literal("verbose", reader)?;
|
||||
let space1 = zero_or_more_spaces(reader)?;
|
||||
try_literal(":", reader)?;
|
||||
let space2 = zero_or_more_spaces(reader)?;
|
||||
let value = nonrecover(boolean, reader)?;
|
||||
let line_terminator0 = line_terminator(reader)?;
|
||||
|
||||
let option = VerboseOption {
|
||||
line_terminators,
|
||||
space0,
|
||||
space1,
|
||||
space2,
|
||||
value,
|
||||
line_terminator0,
|
||||
};
|
||||
|
||||
Ok(EntryOption::Verbose(option))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -820,6 +820,7 @@ impl Tokenizable for EntryOption {
|
||||
match self {
|
||||
EntryOption::Insecure(option) => option.tokenize(),
|
||||
EntryOption::CaCertificate(option) => option.tokenize(),
|
||||
EntryOption::Verbose(option) => option.tokenize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -865,3 +866,24 @@ impl Tokenizable for CaCertificateOption {
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for VerboseOption {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.append(
|
||||
&mut self
|
||||
.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
tokens.append(&mut self.space0.tokenize());
|
||||
tokens.push(Token::String("verbose".to_string()));
|
||||
tokens.append(&mut self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
tokens.append(&mut self.space2.tokenize());
|
||||
tokens.push(Token::Boolean(self.value.to_string()));
|
||||
tokens.append(&mut self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user