Orange-OpenSource / hurl

Hurl, run and test HTTP requests with plain text.
https://hurl.dev
Apache License 2.0
13.2k stars 495 forks source link

Support default values when capturing variables or enable a way to dynamically generate boolean variables #3436

Open lambrospetrou opened 1 week ago

lambrospetrou commented 1 week ago

Problem to solve

I am writing a test suite for an API, and I want to use the skip option to conditionally run some requests based on the response of a previous request.

The condition depends on the previous response containing a certain field with a specific value, e.g. $.result.id, or the response containing (or not) certain items.

However, if I use the following, it fails when the ID doesn't exist (e.g. indicates a failure of resource creation):

[Captures]
db_id: jsonpath "$.result.id"

Example error:

32 | db_id: jsonpath "$.result.uuid"
   |        ^^^^^^^^^^^^^^^^^^^^^^^^ The query didn't return any result

I cannot even proceed to the next step, which will conditionally create the resource if it doesn't exist. In other test suites, I might want to delete it if it already exists to get back to a clean state.

I tried using the filter syntax with recursive descent, which gives me back an empty array (e.g. db_id: jsonpath "$.result[?(@.uuid)]..uuid"). But this doesn't work either, since the empty array is not a valid type to be put into the skip option.

Proposal

Allow captures to provide a default value to variables if the specified filter results in a missing value. Or provide an option that will make the captures without valid value resolutions to not fail the script and just leave the variable with its previous value (so that I can preset it to a default).

In the end, what I need is a way to declare a variable with a boolean value that can be passed to skip, based on the response of a previous request.

Additional context and resources

Tasks to complete

lambrospetrou commented 1 week ago

Another option instead of allowing defaults in variable capturing is to allow predicates (e.g. db_exsts: jsonpath "$.result[?(@.name == 'some-name')].uuid" count > 1).

For example, storing the result of a predicate into a variable would allow me to set a variable to true/false so that I can use that later in the skip option.

Or, if none of the above options are wanted, the skip option should support query/filters/predicates and not just a raw boolean, otherwise it's not easy to use it flexibly since there is no way to generate a boolean value dynamically with the existing filters. Adding a toBoolean is another way but it will be messy and inflexible.

jcamiel commented 1 week ago

Hi guys,

We had a very clear model with queries, captures and asserts:

Now, I think we're going to a model where there are expressions everywhere. You can correct me @fabricereix if I'm mistaken but the model will be:

We want to express "naturally" this kind of file:

GET https:// foo.com
[Options]
skip: {{ results isEmpty }}

Or

GET https:// foo.com
HTTP 200
[Captures]
foo: jsonpath "$.results" count >= 4

It seems natural so we want to support it. Default values can fit into the expression model, for instance (syntax TBD):

GET https:// foo.com
HTTP 200
[Captures]
foo: jsonpath "$.results" count or 4

We're going to make small steps towards this model, starting with the next version where we're introducing functions. We want to have a clean model and the code that's reflecting that so everything is maintainable and can be augmented.

@fabricereix maybe we can write all this in a spec doc, it's helped a lot for previous features and everybody can improve it if we miss some things/usecases.

lambrospetrou commented 1 week ago

@jcamiel Love this end game! If we can get this flexibility, Hurl will be top-notch and will be able to model most of the common user journey flows.

Let me know if there is any way I can help, even if just for testing 😅

fabricereix commented 1 week ago

Yes!! it will be nice to rely only on expressions for everything. We like our model with query/predicate, but it could even better without them! Using just expressions to define captures and asserts. Predicates replaced by filters that return a bool value, but also queries replaced by functions.

This more generic model will unlock many use cases. Spec doc to gather many examples/use cases seems indeed a good idea.

Another uniformization, we might consider, is choosing only one syntax between the current query syntax or the placeholder one.

[Options]
skip: {{ results isEmpty }}

and

[Captures]
foo: jsonpath "$.results" count >= 4

If both of them have an expression after the ':', we might want to have exactly the same syntax. I will tend to think that the 2nd one is more hurly. We could still keep the placeholder {{...}} for template strings (such as the url)