Implements isObject predicate.

This commit is contained in:
Jean-Christophe Amiel 2025-11-05 17:47:49 +01:00
parent ca482dc36f
commit 7ae81bc339
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
16 changed files with 84 additions and 37 deletions

View File

@ -469,9 +469,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
50 | jsonpath "$.not-exist" isDate
50 | jsonpath "$.not-exist" isObject
| actual: none
| expected: date
| expected: object
|
error: Assert failure
@ -479,9 +479,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
51 | jsonpath "$.not-exist" exists
51 | jsonpath "$.not-exist" isDate
| actual: none
| expected: something
| expected: date
|
error: Assert failure
@ -489,9 +489,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
52 | jsonpath "$.not-exist" isEmpty
52 | jsonpath "$.not-exist" exists
| actual: none
| expected: empty
| expected: something
|
error: Assert failure
@ -499,9 +499,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
53 | jsonpath "$.not-exist" isIpv4
53 | jsonpath "$.not-exist" isEmpty
| actual: none
| expected: ipv4
| expected: empty
|
error: Assert failure
@ -509,9 +509,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
54 | jsonpath "$.not-exist" isIpv6
54 | jsonpath "$.not-exist" isIpv4
| actual: none
| expected: ipv6
| expected: ipv4
|
error: Assert failure
@ -519,9 +519,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
55 | jsonpath "$.not-exist" isUuid
55 | jsonpath "$.not-exist" isIpv6
| actual: none
| expected: uuid
| expected: ipv6
|
error: Assert failure
@ -529,9 +529,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
56 | jsonpath "$.not_a_date" isIsoDate
| actual: 2018
| expected: string with format YYYY-MM-DDTHH:mm:ss.sssZ
56 | jsonpath "$.not-exist" isUuid
| actual: none
| expected: uuid
|
error: Assert failure
@ -539,9 +539,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
57 | jsonpath "$.is_a_date" not isIsoDate
| actual: 2018-12-10T13:45:00.000Z
| expected: not string with format YYYY-MM-DDTHH:mm:ss.sssZ
57 | jsonpath "$.not_a_date" isIsoDate
| actual: 2018
| expected: string with format YYYY-MM-DDTHH:mm:ss.sssZ
|
error: Assert failure
@ -549,9 +549,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
58 | jsonpath "$.not_a_date" isNumber
| actual: string <2018>
| expected: number
58 | jsonpath "$.is_a_date" not isIsoDate
| actual: 2018-12-10T13:45:00.000Z
| expected: not string with format YYYY-MM-DDTHH:mm:ss.sssZ
|
error: Assert failure
@ -559,9 +559,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
59 | jsonpath "$.is_a_date" isIpv4
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv4 format
59 | jsonpath "$.not_a_date" isNumber
| actual: string <2018>
| expected: number
|
error: Assert failure
@ -569,9 +569,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
60 | jsonpath "$.is_a_date" isIpv6
60 | jsonpath "$.is_a_date" isIpv4
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv6 format
| expected: string in IPv4 format
|
error: Assert failure
@ -579,8 +579,8 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
61 | jsonpath "$.ipv4" isIpv6
| actual: 127.0.0.1
61 | jsonpath "$.is_a_date" isIpv6
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv6 format
|
@ -589,9 +589,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
62 | jsonpath "$.ipv4" not isIpv4
62 | jsonpath "$.ipv4" isIpv6
| actual: 127.0.0.1
| expected: not string in IPv4 format
| expected: string in IPv6 format
|
error: Assert failure
@ -599,9 +599,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
63 | jsonpath "$.ipv6" isIpv4
| actual: 2001:db8::1
| expected: string in IPv4 format
63 | jsonpath "$.ipv4" not isIpv4
| actual: 127.0.0.1
| expected: not string in IPv4 format
|
error: Assert failure
@ -609,7 +609,17 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
64 | jsonpath "$.ipv6" not isIpv6
64 | jsonpath "$.ipv6" isIpv4
| actual: 2001:db8::1
| expected: string in IPv4 format
|
error: Assert failure
--> tests_failed/predicate/predicate.hurl:65:0
|
| GET http://localhost:8000/predicate/error/type
| ...
65 | jsonpath "$.ipv6" not isIpv6
| actual: 2001:db8::1
| expected: not string in IPv6 format
|

View File

@ -47,6 +47,7 @@ jsonpath "$.not-exist" isBoolean
jsonpath "$.not-exist" isString
jsonpath "$.not-exist" isCollection
jsonpath "$.not-exist" isList
jsonpath "$.not-exist" isObject
jsonpath "$.not-exist" isDate
jsonpath "$.not-exist" exists
jsonpath "$.not-exist" isEmpty

View File

@ -18,6 +18,7 @@ header "Set-Cookie" contains "cookie1=value1; Path=/"
header "Set-Cookie" not contains "cookie4=value4; Path=/"
header "X-Fruit" isList
header "X-Fruit" isCollection
header "X-Fruit" not isObject
header "x-fruit" count == 4
header "X-Fruit" nth 0 == "Banana"
header "x-fruit" nth 1 == "Lemon"
@ -38,6 +39,7 @@ header "X-Fruit" isList
header "X-Fruit" isCollection
variable "fruits" isList
variable "fruits" isCollection
variable "fruits" not isObject
variable "fruits" count == 4
variable "fruits" nth 0 == "Banana"
variable "fruits" nth 3 == "Strawberry"

File diff suppressed because one or more lines are too long

View File

@ -29,9 +29,11 @@ jsonpath "$.success" isBoolean
jsonpath "$.errors" count == 2
jsonpath "$.errors" isList
jsonpath "$.errors" isCollection
jsonpath "$.errors" not isObject
jsonpath "$.failures" count == 1
jsonpath "$.failures" isList
jsonpath "$.failures" isCollection
jsonpath "$.failures" not isObject
jsonpath "$.warnings" count == 0
jsonpath "$.warnings" isEmpty
jsonpath "$.message" == "Bob says \"Hello\""
@ -42,6 +44,7 @@ jsonpath "$.warnings" exists
jsonpath "$.errors[0]" exists
jsonpath "$.errors[0]" not isList
jsonpath "$.errors[0]" isCollection
jsonpath "$.errors[0]" isObject
jsonpath "$.errors[0].id" == "error1"
jsonpath "$.errors[0].id" isString
jsonpath "$.errors[0]['id']" == "error1"

View File

@ -18,6 +18,7 @@
<span class="query-type">jsonpath</span> <span class="string">"$.succeeded"</span> <span class="predicate-type">isBoolean</span> <span class="comment"># isBoolean</span>
<span class="query-type">jsonpath</span> <span class="string">"$.books"</span> <span class="predicate-type">isList</span> <span class="comment"># isList</span>
<span class="query-type">jsonpath</span> <span class="string">"$.books"</span> <span class="predicate-type">isCollection</span> <span class="comment"># isCollection</span>
<span class="query-type">jsonpath</span> <span class="string">"$.books"</span> <span class="predicate-type">isObject</span> <span class="comment"># isObject</span>
<span class="query-type">certificate</span> <span class="string">"Expire-Date"</span> <span class="predicate-type">isDate</span> <span class="comment"># isDate</span>
<span class="query-type">jsonpath</span> <span class="string">"$.publication_date"</span> <span class="predicate-type">isIsoDate</span> <span class="comment"># isIsoDate</span>
<span class="query-type">jsonpath</span> <span class="string">"$.movies"</span> <span class="predicate-type">isEmpty</span> <span class="comment"># isEmpty</span>

View File

@ -18,6 +18,7 @@ jsonpath "$.nooks" contains "Dune" # contains
jsonpath "$.succeeded" isBoolean # isBoolean
jsonpath "$.books" isList # isList
jsonpath "$.books" isCollection # isCollection
jsonpath "$.books" isObject # isObject
certificate "Expire-Date" isDate # isDate
jsonpath "$.publication_date" isIsoDate # isIsoDate
jsonpath "$.movies" isEmpty # isEmpty

View File

@ -1 +1 @@
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/dummy"},"response":{"status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.book"},"predicate":{"not":true,"type":"==","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.book"},"predicate":{"type":"==","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.color"},"predicate":{"type":"!=","value":"red"}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":">","value":1978}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":">=","value":1978}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":"<","value":1978}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":"<=","value":1978}},{"query":{"type":"jsonpath","expr":"$.movie"},"predicate":{"type":"contains","value":"Empire"}},{"query":{"type":"bytes"},"predicate":{"type":"contains","value":"vu8=","encoding":"base64"}},{"query":{"type":"jsonpath","expr":"$.movie"},"predicate":{"type":"endsWith","value":"Back"}},{"query":{"type":"bytes"},"predicate":{"type":"endsWith","value":"qxI0Vg==","encoding":"base64"}},{"query":{"type":"jsonpath","expr":"$.book"},"predicate":{"type":"exists"}},{"query":{"type":"jsonpath","expr":"$.nooks"},"predicate":{"type":"includes","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.nooks"},"predicate":{"type":"contains","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.succeeded"},"predicate":{"type":"isBoolean"}},{"query":{"type":"jsonpath","expr":"$.books"},"predicate":{"type":"isList"}},{"query":{"type":"jsonpath","expr":"$.books"},"predicate":{"type":"isCollection"}},{"query":{"type":"certificate","expr":"Expire-Date"},"predicate":{"type":"isDate"}},{"query":{"type":"jsonpath","expr":"$.publication_date"},"predicate":{"type":"isIsoDate"}},{"query":{"type":"jsonpath","expr":"$.movies"},"predicate":{"type":"isEmpty"}},{"query":{"type":"jsonpath","expr":"$.height"},"predicate":{"type":"isFloat"}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"isInteger"}},{"query":{"type":"jsonpath","expr":"$.name"},"predicate":{"type":"isString"}},{"query":{"type":"jsonpath","expr":"$.release"},"predicate":{"type":"matches","value":"\\d{4}"}},{"query":{"type":"jsonpath","expr":"$.release"},"predicate":{"type":"matches","value":"\\d{4}","encoding":"regex"}},{"query":{"type":"jsonpath","expr":"$.movie"},"predicate":{"type":"startsWith","value":"The"}},{"query":{"type":"bytes"},"predicate":{"type":"startsWith","value":"77u/","encoding":"base64"}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"isNumber"}},{"query":{"type":"ip"},"predicate":{"type":"isIpv6"}},{"query":{"type":"ip"},"predicate":{"type":"isIpv4"}},{"query":{"type":"jsonpath","expr":"$.uuid"},"predicate":{"type":"isUuid"}}]}}]}
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/dummy"},"response":{"status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.book"},"predicate":{"not":true,"type":"==","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.book"},"predicate":{"type":"==","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.color"},"predicate":{"type":"!=","value":"red"}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":">","value":1978}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":">=","value":1978}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":"<","value":1978}},{"query":{"type":"jsonpath","expr":"$.year"},"predicate":{"type":"<=","value":1978}},{"query":{"type":"jsonpath","expr":"$.movie"},"predicate":{"type":"contains","value":"Empire"}},{"query":{"type":"bytes"},"predicate":{"type":"contains","value":"vu8=","encoding":"base64"}},{"query":{"type":"jsonpath","expr":"$.movie"},"predicate":{"type":"endsWith","value":"Back"}},{"query":{"type":"bytes"},"predicate":{"type":"endsWith","value":"qxI0Vg==","encoding":"base64"}},{"query":{"type":"jsonpath","expr":"$.book"},"predicate":{"type":"exists"}},{"query":{"type":"jsonpath","expr":"$.nooks"},"predicate":{"type":"includes","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.nooks"},"predicate":{"type":"contains","value":"Dune"}},{"query":{"type":"jsonpath","expr":"$.succeeded"},"predicate":{"type":"isBoolean"}},{"query":{"type":"jsonpath","expr":"$.books"},"predicate":{"type":"isList"}},{"query":{"type":"jsonpath","expr":"$.books"},"predicate":{"type":"isCollection"}},{"query":{"type":"jsonpath","expr":"$.books"},"predicate":{"type":"isObject"}},{"query":{"type":"certificate","expr":"Expire-Date"},"predicate":{"type":"isDate"}},{"query":{"type":"jsonpath","expr":"$.publication_date"},"predicate":{"type":"isIsoDate"}},{"query":{"type":"jsonpath","expr":"$.movies"},"predicate":{"type":"isEmpty"}},{"query":{"type":"jsonpath","expr":"$.height"},"predicate":{"type":"isFloat"}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"isInteger"}},{"query":{"type":"jsonpath","expr":"$.name"},"predicate":{"type":"isString"}},{"query":{"type":"jsonpath","expr":"$.release"},"predicate":{"type":"matches","value":"\\d{4}"}},{"query":{"type":"jsonpath","expr":"$.release"},"predicate":{"type":"matches","value":"\\d{4}","encoding":"regex"}},{"query":{"type":"jsonpath","expr":"$.movie"},"predicate":{"type":"startsWith","value":"The"}},{"query":{"type":"bytes"},"predicate":{"type":"startsWith","value":"77u/","encoding":"base64"}},{"query":{"type":"jsonpath","expr":"$.count"},"predicate":{"type":"isNumber"}},{"query":{"type":"ip"},"predicate":{"type":"isIpv6"}},{"query":{"type":"ip"},"predicate":{"type":"isIpv4"}},{"query":{"type":"jsonpath","expr":"$.uuid"},"predicate":{"type":"isUuid"}}]}}]}

View File

@ -18,6 +18,7 @@ jsonpath "$.nooks" contains "Dune" # contains
jsonpath "$.succeeded" isBoolean # isBoolean
jsonpath "$.books" isList # isList
jsonpath "$.books" isCollection # isCollection
jsonpath "$.books" isObject # isObject
certificate "Expire-Date" isDate # isDate
jsonpath "$.publication_date" isIsoDate # isIsoDate
jsonpath "$.movies" isEmpty # isEmpty

View File

@ -200,8 +200,9 @@ fn expected_no_value(
PredicateFuncValue::IsIpv4 => Ok("ipv4".to_string()),
PredicateFuncValue::IsIpv6 => Ok("ipv6".to_string()),
PredicateFuncValue::IsIsoDate => Ok("date".to_string()),
PredicateFuncValue::IsNumber => Ok("number".to_string()),
PredicateFuncValue::IsList => Ok("list".to_string()),
PredicateFuncValue::IsNumber => Ok("number".to_string()),
PredicateFuncValue::IsObject => Ok("object".to_string()),
PredicateFuncValue::IsString => Ok("string".to_string()),
PredicateFuncValue::IsUuid => Ok("uuid".to_string()),
}
@ -281,6 +282,7 @@ fn eval_predicate_func(
PredicateFuncValue::IsIsoDate => eval_is_iso_date(value),
PredicateFuncValue::IsList => eval_is_list(value),
PredicateFuncValue::IsNumber => eval_is_number(value),
PredicateFuncValue::IsObject => eval_is_object(value),
PredicateFuncValue::IsString => eval_is_string(value),
PredicateFuncValue::IsUuid => eval_is_uuid(value),
}
@ -537,6 +539,16 @@ fn eval_is_list(actual: &Value) -> Result<PredicateResult, RunnerError> {
})
}
/// Evaluates if an `actual` value is an object.
fn eval_is_object(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {
success: actual.is_object(),
actual: actual.repr(),
expected: "object".to_string(),
type_mismatch: false,
})
}
/// Evaluates if an `actual` value is a date.
fn eval_is_date(actual: &Value) -> Result<PredicateResult, RunnerError> {
Ok(PredicateResult {

View File

@ -112,6 +112,11 @@ impl Value {
self.kind() == ValueKind::Bytes || self.kind() == ValueKind::List
}
/// Returns `true` the value is an object, otherwise `false`.
pub fn is_object(&self) -> bool {
self.kind() == ValueKind::Nodeset || self.kind() == ValueKind::Object
}
/// Returns `true` the value is a collection, otherwise `false`.
pub fn is_collection(&self) -> bool {
self.kind() == ValueKind::Bytes

View File

@ -414,6 +414,7 @@ pub enum PredicateFuncValue {
IsIsoDate,
IsList,
IsNumber,
IsObject,
IsString,
IsUuid,
}
@ -445,6 +446,7 @@ impl PredicateFuncValue {
PredicateFuncValue::IsIsoDate => "isIsoDate",
PredicateFuncValue::IsList => "isList",
PredicateFuncValue::IsNumber => "isNumber",
PredicateFuncValue::IsObject => "isObject",
PredicateFuncValue::IsString => "isString",
PredicateFuncValue::IsUuid => "isUuid",
}

View File

@ -695,6 +695,7 @@ pub fn walk_predicate<V: Visitor>(visitor: &mut V, pred: &Predicate) {
| PredicateFuncValue::IsIsoDate
| PredicateFuncValue::IsList
| PredicateFuncValue::IsNumber
| PredicateFuncValue::IsObject
| PredicateFuncValue::IsString
| PredicateFuncValue::IsUuid => {}
}

View File

@ -88,6 +88,7 @@ fn predicate_func_value(reader: &mut Reader) -> ParseResult<PredicateFuncValue>
string_predicate,
collection_predicate,
is_list_predicate,
is_object_predicate,
date_predicate,
iso_date_predicate,
exist_predicate,
@ -330,6 +331,11 @@ fn is_list_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
Ok(PredicateFuncValue::IsList)
}
fn is_object_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
try_literal("isObject", reader)?;
Ok(PredicateFuncValue::IsObject)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -571,6 +571,7 @@ impl ToJson for Predicate {
| PredicateFuncValue::IsIsoDate
| PredicateFuncValue::IsList
| PredicateFuncValue::IsNumber
| PredicateFuncValue::IsObject
| PredicateFuncValue::IsString
| PredicateFuncValue::IsUuid => {}
}

View File

@ -632,6 +632,7 @@ impl Lint for PredicateFuncValue {
| PredicateFuncValue::IsIsoDate
| PredicateFuncValue::IsList
| PredicateFuncValue::IsNumber
| PredicateFuncValue::IsObject
| PredicateFuncValue::IsString
| PredicateFuncValue::IsUuid => {}
}