diff --git a/integration/tests/assert_status_code.exit b/integration/tests/assert_status_code.exit new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/integration/tests/assert_status_code.exit @@ -0,0 +1 @@ +0 diff --git a/integration/tests/assert_status_code.hurl b/integration/tests/assert_status_code.hurl new file mode 100644 index 000000000..929375ee3 --- /dev/null +++ b/integration/tests/assert_status_code.hurl @@ -0,0 +1,11 @@ +GET http://localhost:8000/assert-status-code +HTTP/1.0 201 + +# simply check that the status code is not 200 +# do not run implicit assert of http response version and code +GET http://localhost:8000/assert-status-code +HTTP/* * +[Asserts] +status not equals 200 + + diff --git a/integration/tests/assert_status_code.out b/integration/tests/assert_status_code.out new file mode 100644 index 000000000..e69de29bb diff --git a/integration/tests/assert_status_code.py b/integration/tests/assert_status_code.py new file mode 100644 index 000000000..adf54de4c --- /dev/null +++ b/integration/tests/assert_status_code.py @@ -0,0 +1,8 @@ +# coding=utf-8 +from tests import app +from flask import Response + +@app.route("/assert-status-code") +def assert_status_code(): + return Response('', status=201) + diff --git a/packages/hurl/src/runner/response.rs b/packages/hurl/src/runner/response.rs index cd59630a9..2f5104998 100644 --- a/packages/hurl/src/runner/response.rs +++ b/packages/hurl/src/runner/response.rs @@ -44,11 +44,13 @@ pub fn eval_asserts( }); 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, - }); + if let StatusValue::Specific(v) = status.value { + asserts.push(AssertResult::Status { + actual: http_response.status as u64, + expected: v as u64, + source_info: status.source_info, + }); + } for header in response.clone().headers { match eval_template(header.value.clone(), variables) { @@ -247,7 +249,7 @@ mod tests { }, space0: whitespace.clone(), status: Status { - value: 200, + value: StatusValue::Specific(200), source_info: SourceInfo::init(2, 10, 2, 13), }, space1: whitespace.clone(), diff --git a/packages/hurl/tests/runner.rs b/packages/hurl/tests/runner.rs index 28a1865b5..b0ace7c79 100644 --- a/packages/hurl/tests/runner.rs +++ b/packages/hurl/tests/runner.rs @@ -186,7 +186,7 @@ fn test_hello() { }, space0: whitespace.clone(), status: Status { - value: 200, + value: StatusValue::Specific(200), source_info: source_info.clone(), }, space1: whitespace.clone(), diff --git a/packages/hurl_core/src/ast/core.rs b/packages/hurl_core/src/ast/core.rs index 789b355d4..4b7eea556 100644 --- a/packages/hurl_core/src/ast/core.rs +++ b/packages/hurl_core/src/ast/core.rs @@ -146,12 +146,6 @@ impl Method { } } -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Status { - pub value: u64, - pub source_info: SourceInfo, -} - #[derive(Clone, Debug, PartialEq, Eq)] pub struct Version { pub value: VersionValue, @@ -177,6 +171,27 @@ impl VersionValue { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Status { + pub value: StatusValue, + pub source_info: SourceInfo, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum StatusValue { + Any, + Specific(u64), +} + +impl fmt::Display for StatusValue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + StatusValue::Any => write!(f, "*"), + StatusValue::Specific(v) => write!(f, "{}", v.to_string()), + } + } +} + type Header = KeyValue; #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/packages/hurl_core/src/parser/parsers.rs b/packages/hurl_core/src/parser/parsers.rs index d4101c8c5..0b61f7a9d 100644 --- a/packages/hurl_core/src/parser/parsers.rs +++ b/packages/hurl_core/src/parser/parsers.rs @@ -264,23 +264,25 @@ fn version(reader: &mut Reader) -> ParseResult<'static, Version> { } fn status(reader: &mut Reader) -> ParseResult<'static, Status> { - let start = reader.state.clone(); - match natural(reader) { - Ok(value) => Ok(Status { - value, - source_info: SourceInfo::init( - start.pos.line, - start.pos.column, - reader.state.pos.line, - reader.state.pos.column, - ), - }), - Err(_) => Err(Error { - pos: start.pos, - recoverable: false, - inner: ParseError::Status {}, - }), - } + let start = reader.state.pos.clone(); + let value = match try_literal("*", reader) { + Ok(_) => StatusValue::Any, + Err(_) => match natural(reader) { + Ok(value) => StatusValue::Specific(value), + Err(_) => { + return Err(Error { + pos: start.clone(), + recoverable: false, + inner: ParseError::Status {}, + }) + } + }, + }; + let end = reader.state.pos.clone(); + Ok(Status { + value, + source_info: SourceInfo { start, end }, + }) } fn body(reader: &mut Reader) -> ParseResult<'static, Body> { @@ -351,7 +353,7 @@ mod tests { let mut reader = Reader::init("GET http://google.fr\nHTTP/1.1 200"); let e = entry(&mut reader).unwrap(); assert_eq!(e.request.method, Method::Get); - assert_eq!(e.response.unwrap().status.value, 200); + assert_eq!(e.response.unwrap().status.value, StatusValue::Specific(200)); } #[test] @@ -560,7 +562,7 @@ mod tests { let r = response(&mut reader).unwrap(); assert_eq!(r.version.value, VersionValue::Version11); - assert_eq!(r.status.value, 200); + assert_eq!(r.status.value, StatusValue::Specific(200)); } #[test] @@ -715,9 +717,13 @@ mod tests { #[test] fn test_status() { + let mut reader = Reader::init("*"); + let s = status(&mut reader).unwrap(); + assert_eq!(s.value, StatusValue::Any); + let mut reader = Reader::init("200"); let s = status(&mut reader).unwrap(); - assert_eq!(s.value, 200); + assert_eq!(s.value, StatusValue::Specific(200)); let mut reader = Reader::init("xxx"); let result = status(&mut reader); diff --git a/packages/hurlfmt/src/format/token.rs b/packages/hurlfmt/src/format/token.rs index d45c3c190..60b187e64 100644 --- a/packages/hurlfmt/src/format/token.rs +++ b/packages/hurlfmt/src/format/token.rs @@ -124,7 +124,7 @@ impl Tokenizable for Response { add_tokens(&mut tokens, self.space0.tokenize()); add_tokens(&mut tokens, self.version.tokenize()); add_tokens(&mut tokens, self.space1.tokenize()); - tokens.push(Token::Status(self.status.value.to_string())); + add_tokens(&mut tokens, self.status.tokenize()); add_tokens(&mut tokens, self.line_terminator0.tokenize()); add_tokens( &mut tokens, @@ -141,6 +141,17 @@ impl Tokenizable for Response { } } +impl Tokenizable for Status { + fn tokenize(&self) -> Vec { + let mut tokens: Vec = vec![]; + match self.value.clone() { + StatusValue::Any => tokens.push(Token::Status("*".to_string())), + StatusValue::Specific(v) => tokens.push(Token::Status(v.to_string())), + } + tokens + } +} + impl Tokenizable for Version { fn tokenize(&self) -> Vec { let mut tokens: Vec = vec![];