Add toInt filter

This commit is contained in:
Fabrice Reix 2022-12-03 11:30:11 +01:00
parent 45050a4aed
commit ffd31a271c
No known key found for this signature in database
GPG Key ID: 8D3D9DBDD96B2D30
17 changed files with 139 additions and 6 deletions

View File

@ -418,6 +418,7 @@ filter:
| count-filter
| url-encode-filter
| url-decode-filter
| to-int
regex-filter: "regex" sp (quoted-string | regex)
@ -427,6 +428,8 @@ url-encode-filter: "urlEncode"
url-decode-filter: "urlDecode"
to-int: "toInt"
# Lexical Grammar

View File

@ -0,0 +1,21 @@
error: Filter Error
--> tests_failed/filter.hurl:4:17
|
4 | jsonpath "$.id" toInt == 123
| ^^^^^ Invalid Filter Input: string <123x>
|
error: Filter Error
--> tests_failed/filter.hurl:5:21
|
5 | jsonpath "$.status" toInt == 0
| ^^^^^ Invalid Filter Input: bool <true>
|
error: Filter Error
--> tests_failed/filter.hurl:6:22
|
6 | jsonpath "$.unknown" toInt == 1
| ^^^^^ Missing value to apply the filter
|

View File

@ -0,0 +1 @@
4

View File

@ -0,0 +1,6 @@
GET http://localhost:8000/error-filter
HTTP 200
[Asserts]
jsonpath "$.id" toInt == 123
jsonpath "$.status" toInt == 0
jsonpath "$.unknown" toInt == 1

View File

@ -0,0 +1,14 @@
from app import app
from flask import Response
@app.route("/error-filter")
def error_filter():
return Response(
"""{
"id":"123x",
"status": true
}
""",
mimetype="application/json",
)

View File

@ -74,7 +74,7 @@ error: Filter Error
--> tests_failed/predicate.hurl:14:22
|
14 | jsonpath "$.message" count == 1
| ^^^^^ Invalid Filter Input <string>
| ^^^^^ Invalid Filter Input: string
|
error: Assert failure

View File

@ -10,10 +10,14 @@
<span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.url"</span> <span class="filter-type">urlEncode</span> <span class="predicate-type">==</span> <span class="string">"https%3A//mozilla.org/%3Fx%3D%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"</span></span>
<span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.encoded_url"</span> <span class="filter-type">urlDecode</span> <span class="predicate-type">==</span> <span class="string">"https://mozilla.org/?x=шеллы"</span></span>
<span class="line"><span class="query-type">variable</span> <span class="string">"url"</span> <span class="filter-type">urlEncode</span> <span class="filter-type">urlDecode</span> <span class="predicate-type">==</span> <span class="string">"{{url}}"</span></span>
<span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.id"</span> <span class="filter-type">toInt</span> <span class="predicate-type">==</span> <span class="number">123</span></span>
<span class="line"><span class="query-type">jsonpath</span> <span class="string">"$.score"</span> <span class="filter-type">toInt</span> <span class="predicate-type">==</span> <span class="number">1</span></span>
<span class="json"><span class="line">{</span>
<span class="line"> "list": [1,2,3],</span>
<span class="line"> "message": "Hello Bob!",</span>
<span class="line"> "url": "https://mozilla.org/?x=шеллы",</span>
<span class="line"> "encoded_url": "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"</span>
<span class="line"> "encoded_url": "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B",</span>
<span class="line"> "id": "123",</span>
<span class="line"> "score": 1.6</span>
<span class="line">}</span></span>
</span></span></code></pre>

View File

@ -10,9 +10,13 @@ jsonpath "$.url" == "https://mozilla.org/?x=шеллы"
jsonpath "$.url" urlEncode == "https%3A//mozilla.org/%3Fx%3D%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
jsonpath "$.encoded_url" urlDecode == "https://mozilla.org/?x=шеллы"
variable "url" urlEncode urlDecode == "{{url}}"
jsonpath "$.id" toInt == 123
jsonpath "$.score" toInt == 1
{
"list": [1,2,3],
"message": "Hello Bob!",
"url": "https://mozilla.org/?x=шеллы",
"encoded_url": "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
"encoded_url": "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B",
"id": "123",
"score": 1.6
}

View File

@ -1 +1 @@
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/filter"},"response":{"status":200,"captures":[{"name":"url","query":{"type":"jsonpath","expr":"$.url"}}],"asserts":[{"query":{"type":"jsonpath","expr":"$.list"},"filters":[{"type":"count"}],"predicate":{"type":"equal","value":3}},{"query":{"type":"jsonpath","expr":"$.message"},"filters":[{"type":"regex","expr":{"type":"regex","value":"Hello (.*)!"}}],"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$.url"},"predicate":{"type":"equal","value":"https://mozilla.org/?x=шеллы"}},{"query":{"type":"jsonpath","expr":"$.url"},"filters":[{"type":"urlEncode"}],"predicate":{"type":"equal","value":"https%3A//mozilla.org/%3Fx%3D%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"}},{"query":{"type":"jsonpath","expr":"$.encoded_url"},"filters":[{"type":"urlDecode"}],"predicate":{"type":"equal","value":"https://mozilla.org/?x=шеллы"}},{"query":{"type":"variable","name":"url"},"filters":[{"type":"urlEncode"},{"type":"urlDecode"}],"predicate":{"type":"equal","value":"{{url}}"}}],"body":{"type":"json","value":{"list":[1,2,3],"message":"Hello Bob!","url":"https://mozilla.org/?x=шеллы","encoded_url":"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"}}}}]}
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/filter"},"response":{"status":200,"captures":[{"name":"url","query":{"type":"jsonpath","expr":"$.url"}}],"asserts":[{"query":{"type":"jsonpath","expr":"$.list"},"filters":[{"type":"count"}],"predicate":{"type":"equal","value":3}},{"query":{"type":"jsonpath","expr":"$.message"},"filters":[{"type":"regex","expr":{"type":"regex","value":"Hello (.*)!"}}],"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$.url"},"predicate":{"type":"equal","value":"https://mozilla.org/?x=шеллы"}},{"query":{"type":"jsonpath","expr":"$.url"},"filters":[{"type":"urlEncode"}],"predicate":{"type":"equal","value":"https%3A//mozilla.org/%3Fx%3D%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"}},{"query":{"type":"jsonpath","expr":"$.encoded_url"},"filters":[{"type":"urlDecode"}],"predicate":{"type":"equal","value":"https://mozilla.org/?x=шеллы"}},{"query":{"type":"variable","name":"url"},"filters":[{"type":"urlEncode"},{"type":"urlDecode"}],"predicate":{"type":"equal","value":"{{url}}"}},{"query":{"type":"jsonpath","expr":"$.id"},"filters":[{"type":"toInt"}],"predicate":{"type":"equal","value":123}},{"query":{"type":"jsonpath","expr":"$.score"},"filters":[{"type":"toInt"}],"predicate":{"type":"equal","value":1}}],"body":{"type":"json","value":{"list":[1,2,3],"message":"Hello Bob!","url":"https://mozilla.org/?x=шеллы","encoded_url":"https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B","id":"123","score":1.6}}}}]}

View File

@ -7,5 +7,7 @@ def filter():
"list": [1,2,3],
"message": "Hello Bob!",
"url": "https://mozilla.org/?x=шеллы",
"encoded_url": "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"
"encoded_url": "https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B",
"id": "123",
"score": 1.6
}"""

View File

@ -162,7 +162,7 @@ impl Error for runner::Error {
"Missing value to apply the filter".to_string()
}
RunnerError::FilterInvalidInput(message) => {
format!("Invalid Filter Input <{}>", message)
format!("Invalid Filter Input: {}", message)
}
RunnerError::FilterRegexNoCapture { .. } => "Capture not found".to_string(),
}

View File

@ -22,6 +22,7 @@ use percent_encoding::AsciiSet;
use regex::Regex;
use std::collections::HashMap;
// TODO: indicated whether you running the filter in an assert / this produce an "assert" error
pub fn eval_filters(
filters: &Vec<Filter>,
value: &Value,
@ -46,6 +47,7 @@ fn eval_filter(
FilterValue::Count {} => eval_count(value, &filter.source_info),
FilterValue::UrlEncode { .. } => eval_url_encode(value, &filter.source_info),
FilterValue::UrlDecode { .. } => eval_url_decode(value, &filter.source_info),
FilterValue::ToInt { .. } => eval_to_int(value, &filter.source_info),
}
}
@ -151,6 +153,26 @@ fn eval_url_decode(value: &Value, source_info: &SourceInfo) -> Result<Value, Err
}
}
fn eval_to_int(value: &Value, source_info: &SourceInfo) -> Result<Value, Error> {
match value {
Value::Integer(v) => Ok(Value::Integer(*v)),
Value::Float(v) => Ok(Value::Integer(*v as i64)),
Value::String(v) => match v.parse::<i64>() {
Ok(i) => Ok(Value::Integer(i)),
Err(_) => Err(Error {
source_info: source_info.clone(),
inner: RunnerError::FilterInvalidInput(value.display()),
assert: false,
}),
},
v => Err(Error {
source_info: source_info.clone(),
inner: RunnerError::FilterInvalidInput(v.display()),
assert: false,
}),
}
}
#[cfg(test)]
pub mod tests {
use super::*;
@ -321,4 +343,48 @@ pub mod tests {
Value::String("https://mozilla.org/?x=шеллы".to_string())
);
}
#[test]
pub fn eval_filter_to_int() {
let variables = HashMap::new();
let filter = Filter {
source_info: SourceInfo::new(1, 1, 1, 1),
value: FilterValue::ToInt {},
};
assert_eq!(
eval_filter(&filter, &Value::String("123".to_string()), &variables).unwrap(),
Value::Integer(123)
);
assert_eq!(
eval_filter(&filter, &Value::Integer(123), &variables).unwrap(),
Value::Integer(123)
);
assert_eq!(
eval_filter(&filter, &Value::Float(1.6), &variables).unwrap(),
Value::Integer(1)
);
}
#[test]
pub fn eval_filter_to_int_error() {
let variables = HashMap::new();
let filter = Filter {
source_info: SourceInfo::new(1, 1, 1, 1),
value: FilterValue::ToInt {},
};
let err = eval_filter(&filter, &Value::String("123x".to_string()), &variables)
.err()
.unwrap();
assert_eq!(
err.inner,
RunnerError::FilterInvalidInput("string <123x>".to_string())
);
let err = eval_filter(&filter, &Value::Bool(true), &variables)
.err()
.unwrap();
assert_eq!(
err.inner,
RunnerError::FilterInvalidInput("bool <true>".to_string())
);
}
}

View File

@ -877,4 +877,5 @@ pub enum FilterValue {
},
UrlEncode {},
UrlDecode {},
ToInt {},
}

View File

@ -1083,6 +1083,7 @@ impl Htmlable for FilterValue {
}
FilterValue::UrlEncode {} => "<span class=\"filter-type\">urlEncode</span>".to_string(),
FilterValue::UrlDecode {} => "<span class=\"filter-type\">urlDecode</span>".to_string(),
FilterValue::ToInt {} => "<span class=\"filter-type\">toInt</span>".to_string(),
}
}
}

View File

@ -54,6 +54,7 @@ pub fn filter(reader: &mut Reader) -> ParseResult<'static, Filter> {
regex_filter,
url_encode_filter,
url_decode_filter,
to_int_filter,
],
reader,
)
@ -97,6 +98,11 @@ fn url_decode_filter(reader: &mut Reader) -> ParseResult<'static, FilterValue> {
Ok(FilterValue::UrlDecode {})
}
fn to_int_filter(reader: &mut Reader) -> ParseResult<'static, FilterValue> {
try_literal("toInt", reader)?;
Ok(FilterValue::ToInt {})
}
#[cfg(test)]
mod tests {
use crate::ast::Pos;

View File

@ -535,6 +535,9 @@ impl ToJson for FilterValue {
FilterValue::UrlDecode { .. } => {
attributes.push(("type".to_string(), JValue::String("urlDecode".to_string())));
}
FilterValue::ToInt { .. } => {
attributes.push(("type".to_string(), JValue::String("toInt".to_string())));
}
}
JValue::Object(attributes)
}

View File

@ -1167,6 +1167,7 @@ impl Tokenizable for Filter {
FilterValue::Count { .. } => vec![Token::FilterType(String::from("count"))],
FilterValue::UrlEncode { .. } => vec![Token::FilterType(String::from("urlEncode"))],
FilterValue::UrlDecode { .. } => vec![Token::FilterType(String::from("urlDecode"))],
FilterValue::ToInt { .. } => vec![Token::FilterType(String::from("toInt"))],
}
}
}