Implement predicate `isUuid`

This commit is contained in:
TheoForger 2025-07-16 12:38:48 -04:00
parent 4e9ef9a523
commit e5e51cae12
No known key found for this signature in database
GPG Key ID: 790561A9A7CBFA0F
19 changed files with 217 additions and 119 deletions

View File

@ -406,6 +406,7 @@ Predicates consist of a predicate function and a predicate value. Predicate func
| __`isString`__ | Query returns a string | `jsonpath "$.name" isString` |
| __`isIpv4`__ | Query returns an IPv4 address | `ip isIpv4` |
| __`isIpv6`__ | Query returns an IPv6 address | `ip isIpv6` |
| __`isUuid`__ | Query returns a UUID | `ip isUuid` |
Each predicate can be negated by prefixing it with `not` (for instance, `not contains` or `not exists`)

View File

@ -181,7 +181,8 @@ Short description:
<span class="grammar-symbol">|</span><a href="#date-predicate">date-predicate</a><br>
<span class="grammar-symbol">|</span><a href="#iso-date-predicate">iso-date-predicate</a><br>
<span class="grammar-symbol">|</span><a href="#is-ipv4-predicate">is-ipv4-predicate</a><br>
<span class="grammar-symbol">|</span><a href="#is-ipv6-predicate">is-ipv6-predicate</a></div></div>
<span class="grammar-symbol">|</span><a href="#is-ipv6-predicate">is-ipv6-predicate</a><br>
<span class="grammar-symbol">|</span><a href="#is-uuid-predicate">is-uuid-predicate</a></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="equal-predicate">equal-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">==</span>&nbsp;<a href="#sp">sp</a>&nbsp;<a href="#predicate-value">predicate-value</a></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="not-equal-predicate">not-equal-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">!=</span>&nbsp;<a href="#sp">sp</a>&nbsp;<a href="#predicate-value">predicate-value</a></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="greater-predicate">greater-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">&gt;</span>&nbsp;<a href="#sp">sp</a>&nbsp;<span class="grammar-symbol">(</span><a href="#number">number</a><span class="grammar-symbol">|</span><a href="#quoted-string">quoted-string</a><span class="grammar-symbol">|</span><a href="#placeholder">placeholder</a><span class="grammar-symbol">)</span></div></div>
@ -204,6 +205,7 @@ Short description:
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="iso-date-predicate">iso-date-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">isIsoDate</span></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="is-ipv4-predicate">is-ipv4-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">isIpv4</span></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="is-ipv6-predicate">is-ipv6-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">isIpv6</span></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="is-uuid-predicate">is-uuid-predicate</span><span class="grammar-usedby">(used by <a href="#predicate-func">predicate-func</a>)</span></div><div class="grammar-rule-expression"><span class="grammar-literal">isUuid</span></div></div>
<div class="grammar-rule"><div class="grammar-rule-declaration"><span class="grammar-rule-id" id="predicate-value">predicate-value</span><span class="grammar-usedby">(used by <a href="#equal-predicate">equal-predicate</a>,&nbsp;<a href="#not-equal-predicate">not-equal-predicate</a>,&nbsp;<a href="#include-predicate">include-predicate</a>)</span></div><div class="grammar-rule-expression">&nbsp;<a href="#boolean">boolean</a><br>
<span class="grammar-symbol">|</span><a href="#multiline-string">multiline-string</a><br>
<span class="grammar-symbol">|</span><a href="#null">null</a><br>

View File

@ -330,6 +330,7 @@ predicate-func:
| iso-date-predicate
| is-ipv4-predicate
| is-ipv6-predicate
| is-uuid-predicate
equal-predicate: "==" sp predicate-value
@ -375,6 +376,8 @@ is-ipv4-predicate: "isIpv4"
is-ipv6-predicate: "isIpv6"
is-uuid-predicate: "isUuid"
predicate-value:
boolean
| multiline-string

View File

@ -141,9 +141,10 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
17 | jsonpath "$.message" startsWith "hi"
| actual: string <0>
| expected: starts with string <hi>
17 | jsonpath "$.count" isUuid
| actual: integer <1>
| expected: string
| >>> types between actual and expected are not consistent
|
error: Assert failure
@ -151,9 +152,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
18 | jsonpath "$.message" endsWith "hi"
18 | jsonpath "$.message" startsWith "hi"
| actual: string <0>
| expected: ends with string <hi>
| expected: starts with string <hi>
|
error: Assert failure
@ -161,9 +162,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
19 | jsonpath "$.message" contains "hi"
19 | jsonpath "$.message" endsWith "hi"
| actual: string <0>
| expected: contains string <hi>
| expected: ends with string <hi>
|
error: Assert failure
@ -171,57 +172,57 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
20 | jsonpath "$.message" matches "hi"
20 | jsonpath "$.message" contains "hi"
| actual: string <0>
| expected: contains string <hi>
|
error: Assert failure
--> tests_failed/predicate.hurl:21:0
|
| GET http://localhost:8000/predicate/error/type
| ...
21 | jsonpath "$.message" matches "hi"
| actual: string <0>
| expected: matches regex <hi>
|
error: Invalid regex
--> tests_failed/predicate.hurl:21:22
--> tests_failed/predicate.hurl:22:22
|
| GET http://localhost:8000/predicate/error/type
| ...
21 | jsonpath "$.message" matches "hi{"
22 | jsonpath "$.message" matches "hi{"
| ^^^^^^^^^^^^^ regex expression is not valid
|
error: Assert failure
--> tests_failed/predicate.hurl:22:0
--> tests_failed/predicate.hurl:23:0
|
| GET http://localhost:8000/predicate/error/type
| ...
22 | jsonpath "$.message" isEmpty
23 | jsonpath "$.message" isEmpty
| actual: count equals to 1
| expected: count equals to 0
|
error: Filter error
--> tests_failed/predicate.hurl:23:22
--> tests_failed/predicate.hurl:24:22
|
| GET http://localhost:8000/predicate/error/type
| ...
23 | jsonpath "$.message" count == 1
24 | jsonpath "$.message" count == 1
| ^^^^^ invalid filter input: string
|
error: Assert failure
--> tests_failed/predicate.hurl:24:0
|
| GET http://localhost:8000/predicate/error/type
| ...
24 | jsonpath "$.message" isIsoDate
| actual: 0
| expected: string with format YYYY-MM-DDTHH:mm:ss.sssZ
|
error: Assert failure
--> tests_failed/predicate.hurl:25:0
|
| GET http://localhost:8000/predicate/error/type
| ...
25 | jsonpath "$.message" isIpv4
25 | jsonpath "$.message" isIsoDate
| actual: 0
| expected: string in IPv4 format
| expected: string with format YYYY-MM-DDTHH:mm:ss.sssZ
|
error: Assert failure
@ -229,9 +230,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
26 | jsonpath "$.message" isIpv6
26 | jsonpath "$.message" isIpv4
| actual: 0
| expected: string in IPv6 format
| expected: string in IPv4 format
|
error: Assert failure
@ -239,9 +240,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
27 | jsonpath "$.toto" exists
| actual: none
| expected: something
27 | jsonpath "$.message" isIpv6
| actual: 0
| expected: string in IPv6 format
|
error: Assert failure
@ -249,9 +250,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
28 | jsonpath "$.message" not exists
| actual: string <0>
| expected: not something
28 | jsonpath "$.message" isUuid
| actual: 0
| expected: string in UUID format
|
error: Assert failure
@ -259,9 +260,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
29 | jsonpath "$.list" count == 2
| actual: integer <3>
| expected: integer <2>
29 | jsonpath "$.toto" exists
| actual: none
| expected: something
|
error: Assert failure
@ -269,9 +270,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
30 | jsonpath "$.not-exist" == 2
| actual: none
| expected: integer <2>
30 | jsonpath "$.message" not exists
| actual: string <0>
| expected: not something
|
error: Assert failure
@ -279,9 +280,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
31 | jsonpath "$.not-exist" > 3
| actual: none
| expected: greater than <integer <3>>
31 | jsonpath "$.list" count == 2
| actual: integer <3>
| expected: integer <2>
|
error: Assert failure
@ -289,9 +290,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
32 | jsonpath "$.not-exist" >= 3
32 | jsonpath "$.not-exist" == 2
| actual: none
| expected: greater than or equals to <integer <3>>
| expected: integer <2>
|
error: Assert failure
@ -299,9 +300,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
33 | jsonpath "$.not-exist" < 1
33 | jsonpath "$.not-exist" > 3
| actual: none
| expected: less than <integer <1>>
| expected: greater than <integer <3>>
|
error: Assert failure
@ -309,18 +310,19 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
34 | jsonpath "$.not-exist" <= 1
34 | jsonpath "$.not-exist" >= 3
| actual: none
| expected: less than or equals to <integer <1>>
| expected: greater than or equals to <integer <3>>
|
error: Filter error
--> tests_failed/predicate.hurl:35:24
error: Assert failure
--> tests_failed/predicate.hurl:35:0
|
| GET http://localhost:8000/predicate/error/type
| ...
35 | jsonpath "$.not-exist" count == 1
| ^^^^^ missing value to apply filter
35 | jsonpath "$.not-exist" < 1
| actual: none
| expected: less than <integer <1>>
|
error: Assert failure
@ -328,19 +330,18 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
36 | jsonpath "$.not-exist" startsWith "foo"
36 | jsonpath "$.not-exist" <= 1
| actual: none
| expected: starts with string <foo>
| expected: less than or equals to <integer <1>>
|
error: Assert failure
--> tests_failed/predicate.hurl:37:0
error: Filter error
--> tests_failed/predicate.hurl:37:24
|
| GET http://localhost:8000/predicate/error/type
| ...
37 | jsonpath "$.not-exist" startsWith hex,efbbbf;
| actual: none
| expected: starts with 3 bytes
37 | jsonpath "$.not-exist" count == 1
| ^^^^^ missing value to apply filter
|
error: Assert failure
@ -348,9 +349,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
38 | jsonpath "$.not-exist" endsWith "foo"
38 | jsonpath "$.not-exist" startsWith "foo"
| actual: none
| expected: ends with string <foo>
| expected: starts with string <foo>
|
error: Assert failure
@ -358,9 +359,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
39 | jsonpath "$.not-exist" endsWith hex,00;
39 | jsonpath "$.not-exist" startsWith hex,efbbbf;
| actual: none
| expected: ends with 1 byte
| expected: starts with 3 bytes
|
error: Assert failure
@ -368,9 +369,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
40 | jsonpath "$.not-exist" contains 1
40 | jsonpath "$.not-exist" endsWith "foo"
| actual: none
| expected: contains integer <1>
| expected: ends with string <foo>
|
error: Assert failure
@ -378,9 +379,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
41 | jsonpath "$.not-exist" matches /foo/
41 | jsonpath "$.not-exist" endsWith hex,00;
| actual: none
| expected: matches regex <foo>
| expected: ends with 1 byte
|
error: Assert failure
@ -388,9 +389,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
42 | jsonpath "$.not-exist" isInteger
42 | jsonpath "$.not-exist" contains 1
| actual: none
| expected: integer
| expected: contains integer <1>
|
error: Assert failure
@ -398,9 +399,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
43 | jsonpath "$.not-exist" isFloat
43 | jsonpath "$.not-exist" matches /foo/
| actual: none
| expected: float
| expected: matches regex <foo>
|
error: Assert failure
@ -408,9 +409,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
44 | jsonpath "$.not-exist" isBoolean
44 | jsonpath "$.not-exist" isInteger
| actual: none
| expected: boolean
| expected: integer
|
error: Assert failure
@ -418,9 +419,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
45 | jsonpath "$.not-exist" isString
45 | jsonpath "$.not-exist" isFloat
| actual: none
| expected: string
| expected: float
|
error: Assert failure
@ -428,9 +429,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
46 | jsonpath "$.not-exist" isCollection
46 | jsonpath "$.not-exist" isBoolean
| actual: none
| expected: collection
| expected: boolean
|
error: Assert failure
@ -438,9 +439,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
47 | jsonpath "$.not-exist" isDate
47 | jsonpath "$.not-exist" isString
| actual: none
| expected: date
| expected: string
|
error: Assert failure
@ -448,9 +449,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
48 | jsonpath "$.not-exist" exists
48 | jsonpath "$.not-exist" isCollection
| actual: none
| expected: something
| expected: collection
|
error: Assert failure
@ -458,9 +459,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
49 | jsonpath "$.not-exist" isEmpty
49 | jsonpath "$.not-exist" isDate
| actual: none
| expected: empty
| expected: date
|
error: Assert failure
@ -468,9 +469,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
50 | jsonpath "$.not-exist" isIpv4
50 | jsonpath "$.not-exist" exists
| actual: none
| expected: ipv4
| expected: something
|
error: Assert failure
@ -478,9 +479,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
51 | jsonpath "$.not-exist" isIpv6
51 | jsonpath "$.not-exist" isEmpty
| actual: none
| expected: ipv6
| expected: empty
|
error: Assert failure
@ -488,9 +489,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
52 | jsonpath "$.not_a_date" isIsoDate
| actual: 2018
| expected: string with format YYYY-MM-DDTHH:mm:ss.sssZ
52 | jsonpath "$.not-exist" isIpv4
| actual: none
| expected: ipv4
|
error: Assert failure
@ -498,9 +499,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
53 | jsonpath "$.is_a_date" not isIsoDate
| actual: 2018-12-10T13:45:00.000Z
| expected: not string with format YYYY-MM-DDTHH:mm:ss.sssZ
53 | jsonpath "$.not-exist" isIpv6
| actual: none
| expected: ipv6
|
error: Assert failure
@ -508,9 +509,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
54 | jsonpath "$.not_a_date" isNumber
| actual: string <2018>
| expected: number
54 | jsonpath "$.not-exist" isUuid
| actual: none
| expected: uuid
|
error: Assert failure
@ -518,9 +519,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
55 | jsonpath "$.is_a_date" isIpv4
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv4 format
55 | jsonpath "$.not_a_date" isIsoDate
| actual: 2018
| expected: string with format YYYY-MM-DDTHH:mm:ss.sssZ
|
error: Assert failure
@ -528,9 +529,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
56 | jsonpath "$.is_a_date" isIpv6
56 | jsonpath "$.is_a_date" not isIsoDate
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv6 format
| expected: not string with format YYYY-MM-DDTHH:mm:ss.sssZ
|
error: Assert failure
@ -538,9 +539,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
57 | jsonpath "$.ipv4" isIpv6
| actual: 127.0.0.1
| expected: string in IPv6 format
57 | jsonpath "$.not_a_date" isNumber
| actual: string <2018>
| expected: number
|
error: Assert failure
@ -548,9 +549,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
58 | jsonpath "$.ipv4" not isIpv4
| actual: 127.0.0.1
| expected: not string in IPv4 format
58 | jsonpath "$.is_a_date" isIpv4
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv4 format
|
error: Assert failure
@ -558,9 +559,9 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
59 | jsonpath "$.ipv6" isIpv4
| actual: 2001:db8::1
| expected: string in IPv4 format
59 | jsonpath "$.is_a_date" isIpv6
| actual: 2018-12-10T13:45:00.000Z
| expected: string in IPv6 format
|
error: Assert failure
@ -568,7 +569,37 @@ error: Assert failure
|
| GET http://localhost:8000/predicate/error/type
| ...
60 | jsonpath "$.ipv6" not isIpv6
60 | jsonpath "$.ipv4" isIpv6
| actual: 127.0.0.1
| expected: string in IPv6 format
|
error: Assert failure
--> tests_failed/predicate.hurl:61:0
|
| GET http://localhost:8000/predicate/error/type
| ...
61 | jsonpath "$.ipv4" not isIpv4
| actual: 127.0.0.1
| expected: not string in IPv4 format
|
error: Assert failure
--> tests_failed/predicate.hurl:62:0
|
| GET http://localhost:8000/predicate/error/type
| ...
62 | jsonpath "$.ipv6" isIpv4
| actual: 2001:db8::1
| expected: string in IPv4 format
|
error: Assert failure
--> tests_failed/predicate.hurl:63:0
|
| GET http://localhost:8000/predicate/error/type
| ...
63 | jsonpath "$.ipv6" not isIpv6
| actual: 2001:db8::1
| expected: not string in IPv6 format
|

View File

@ -14,6 +14,7 @@ jsonpath "$.count" contains "foo"
jsonpath "$.count" isIsoDate
jsonpath "$.count" isIpv4
jsonpath "$.count" isIpv6
jsonpath "$.count" isUuid
jsonpath "$.message" startsWith "hi"
jsonpath "$.message" endsWith "hi"
jsonpath "$.message" contains "hi"
@ -24,6 +25,7 @@ jsonpath "$.message" count == 1
jsonpath "$.message" isIsoDate
jsonpath "$.message" isIpv4
jsonpath "$.message" isIpv6
jsonpath "$.message" isUuid
jsonpath "$.toto" exists
jsonpath "$.message" not exists
jsonpath "$.list" count == 2
@ -49,6 +51,7 @@ jsonpath "$.not-exist" exists
jsonpath "$.not-exist" isEmpty
jsonpath "$.not-exist" isIpv4
jsonpath "$.not-exist" isIpv6
jsonpath "$.not-exist" isUuid
jsonpath "$.not_a_date" isIsoDate
jsonpath "$.is_a_date" not isIsoDate
jsonpath "$.not_a_date" isNumber

View File

@ -66,7 +66,7 @@ jsonpath "$.profile-id" == "123abc"
jsonpath "$['profile-id']" == "123abc"
jsonpath "$.errors[0]" not isEmpty
jsonpath "$.empty" isEmpty
jsonpath "$.*" count == 12
jsonpath "$.*" count == 13
jsonpath "$.errors..*" count == 4
jsonpath "$..id" count == 3
jsonpath "$.dates[0]" isIsoDate
@ -79,6 +79,7 @@ jsonpath "$.dates[0]" toDate "%+" < {{ date1 }}
jsonpath "$.dates[1]" toDate "%+" != {{ date0 }}
jsonpath "$.dates[1]" toDate "%+" >= {{ date0 }}
jsonpath "$.dates[1]" toDate "%+" > {{ date0 }}
jsonpath "$.uuid" isUuid
# FIXME do we accept count filter on object?
@ -98,6 +99,7 @@ jsonpath "$.dates[1]" toDate "%+" > {{ date0 }}
"2022-10-31T09:00:00.594Z",
"2024-03-20T11:23:56.773+02:00"
],
"uuid": "01cc0f54-8885-4a1d-9121-ae5d316a33c5",
"empty": {}
}

View File

@ -20,6 +20,7 @@ def assert_json():
"2022-10-31T09:00:00.594Z",
"2024-03-20T11:23:56.773+02:00"
],
"uuid": "01cc0f54-8885-4a1d-9121-ae5d316a33c5",
"empty": {}
}""",
mimetype="application/json",

File diff suppressed because one or more lines are too long

View File

@ -30,4 +30,5 @@
<span class="query-type">jsonpath</span> <span class="string">"$.count"</span> <span class="predicate-type">isNumber</span> <span class="comment"># isNumber</span>
<span class="query-type">ip</span> <span class="predicate-type">isIpv6</span> <span class="comment"># isIpv6</span>
<span class="query-type">ip</span> <span class="predicate-type">isIpv4</span> <span class="comment"># isIpv4</span>
<span class="query-type">jsonpath</span> <span class="string">"$.uuid"</span> <span class="predicate-type">isUuid</span> <span class="comment"># isUuid</span>
</span></span></code></pre>

View File

@ -30,3 +30,4 @@ bytes startsWith hex,efbbbf; # startsWith
jsonpath "$.count" isNumber # isNumber
ip isIpv6 # isIpv6
ip isIpv4 # isIpv4
jsonpath "$.uuid" isUuid # isUuid

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":"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"}}]}}]}
{"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":"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"}}]}}]}

View File

@ -30,3 +30,4 @@ bytes startsWith hex,efbbbf; # startsWith
jsonpath "$.count" isNumber # isNumber
ip isIpv6 # isIpv6
ip isIpv4 # isIpv4
jsonpath "$.uuid" isUuid # isUuid

View File

@ -202,6 +202,7 @@ fn expected_no_value(
PredicateFuncValue::IsNumber => Ok("number".to_string()),
PredicateFuncValue::IsIpv4 => Ok("ipv4".to_string()),
PredicateFuncValue::IsIpv6 => Ok("ipv6".to_string()),
PredicateFuncValue::IsUuid => Ok("uuid".to_string()),
}
}
@ -279,6 +280,7 @@ fn eval_predicate_func(
PredicateFuncValue::IsNumber => eval_is_number(value),
PredicateFuncValue::IsIpv4 => eval_is_ipv4(value),
PredicateFuncValue::IsIpv6 => eval_is_ipv6(value),
PredicateFuncValue::IsUuid => eval_is_uuid(value),
}
}
@ -642,6 +644,24 @@ fn eval_is_ipv6(actual: &Value) -> Result<PredicateResult, RunnerError> {
}
}
/// Evaluates if an `actual` value is a UUID.
fn eval_is_uuid(actual: &Value) -> Result<PredicateResult, RunnerError> {
match actual.is_uuid() {
Ok(success) => Ok(PredicateResult {
success,
actual: actual.to_string(),
expected: "string in UUID format".to_string(),
type_mismatch: false,
}),
_ => Ok(PredicateResult {
success: false,
actual: actual.repr(),
expected: "string".to_string(),
type_mismatch: true,
}),
}
}
fn assert_values_equal(actual: &Value, expected: &Value) -> PredicateResult {
let success = actual == expected;
let actual = actual.repr();

View File

@ -192,6 +192,21 @@ impl Value {
}
}
/// Returns `true` the value is a UUID.
///
/// Returns `false` if it is not.
///
/// Returns a [`EvalError::Type`] if the given value is not a String.
pub fn is_uuid(&self) -> Result<bool, EvalError> {
match self {
Value::String(value) => {
let is_uuid = uuid::Uuid::try_parse(value).is_ok();
Ok(is_uuid)
}
_ => Err(EvalError::Type),
}
}
/// Returns `true` the string value represents a RFC339 date (format YYYY-MM-DDTHH:mm:ss.sssZ).
///
/// Returns `false` if it does not.
@ -343,6 +358,12 @@ mod tests {
assert!(!value2.is_collection());
assert!(value2.is_number());
assert!(value2.is_integer());
let value3 = Value::String("01cc0f54-8885-4a1d-9121-ae5d316a33c5".to_string());
assert!(!value3.is_boolean());
assert!(!value3.is_collection());
assert!(value3.is_string());
assert!(value3.is_uuid().unwrap());
}
#[test]

View File

@ -414,6 +414,7 @@ pub enum PredicateFuncValue {
IsNumber,
IsIpv4,
IsIpv6,
IsUuid,
}
impl PredicateFuncValue {
@ -443,6 +444,7 @@ impl PredicateFuncValue {
PredicateFuncValue::IsNumber => "isNumber",
PredicateFuncValue::IsIpv4 => "isIpv4",
PredicateFuncValue::IsIpv6 => "isIpv6",
PredicateFuncValue::IsUuid => "isUuid",
}
}
}

View File

@ -686,7 +686,8 @@ pub fn walk_predicate<V: Visitor>(visitor: &mut V, pred: &Predicate) {
| PredicateFuncValue::IsEmpty
| PredicateFuncValue::IsNumber
| PredicateFuncValue::IsIpv4
| PredicateFuncValue::IsIpv6 => {}
| PredicateFuncValue::IsIpv6
| PredicateFuncValue::IsUuid => {}
}
}

View File

@ -94,6 +94,7 @@ fn predicate_func_value(reader: &mut Reader) -> ParseResult<PredicateFuncValue>
is_number_predicate,
is_ipv4_predicate,
is_ipv6_predicate,
is_uuid_predicate,
],
reader,
) {
@ -318,6 +319,11 @@ fn is_ipv6_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
Ok(PredicateFuncValue::IsIpv6)
}
fn is_uuid_predicate(reader: &mut Reader) -> ParseResult<PredicateFuncValue> {
try_literal("isUuid", reader)?;
Ok(PredicateFuncValue::IsUuid)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -568,7 +568,8 @@ impl ToJson for Predicate {
| PredicateFuncValue::IsEmpty
| PredicateFuncValue::IsNumber
| PredicateFuncValue::IsIpv4
| PredicateFuncValue::IsIpv6 => {}
| PredicateFuncValue::IsIpv6
| PredicateFuncValue::IsUuid => {}
}
JValue::Object(attributes)
}

View File

@ -623,7 +623,8 @@ impl Lint for PredicateFuncValue {
| PredicateFuncValue::IsEmpty
| PredicateFuncValue::IsNumber
| PredicateFuncValue::IsIpv4
| PredicateFuncValue::IsIpv6 => {}
| PredicateFuncValue::IsIpv6
| PredicateFuncValue::IsUuid => {}
}
s
}