Karate - how to access an array element by UUID during a 'retry until' statement - karate

I have an endpoint which returns this JSON response:
{
"jobs": [
{
"name": "job1",
"id": "d6bd9aa1-0708-436a-81fd-cf22d5042689",
"status": "pending"
},
{
"name": "job2",
"id": "4fdaf09f-51de-4246-88fd-08d4daef6c3e",
"status": "pending"
}
]
I would like to repeatedly GET call this endpoint until the job I care about ("job2") has a "status" of "completed", but I'd like to check this by using a UUID stored in a variable from a previous call.
i.e. by doing something like this:
#NB: code for previous API call is executed
* def uuidVar = response.jobRef
#NB: uuidVar equates to '4fdaf09f-51de-4246-88fd-08d4daef6c3e' for this scenario
* configure retry = { count: 5, interval: 10000 }
Given path /blah
And retry until response.jobs[?(#.id==uuidVar)].status == 'completed'
When method GET
Could anyone suggest the correct syntax for the retry until?
I've tried referencing the fantastic Karate docs & examples (in particular, js-arrays.feature) and some questions on SO (including this one: Karate framework retry until not working as expected) but sadly I haven't been able to get this working.
I also tried using karate.match here as suggested in the link above, but no cigar.
Apologies in advance if I am missing something obvious.

First I recommend you read this answer on Stack Overflow, it is linked from the readme actually, and is intended to be the definitive reference. Let me know if it needs to be improved: https://stackoverflow.com/a/55823180/143475
Short answer, you can't use JsonPath in the retry until expression, it has to be pure JavaScript.
While you can use karate.jsonPath() to bridge the worlds of JsonPath and JS, JsonPath can get very hard to write and comprehend. Which is why I recommend using karate.filter() to do the same thing, but break down the steps into simple, readable chunks. Here is what you can try in a fresh Scenario:. Hint, this is a good way to troubleshoot your code without making any "real" requests.
* def getStatus = function(id){ var temp = karate.filter(response.jobs, function(x){ return x.id == id }); return temp[0].status }
* def response =
"""
{
"jobs": [
{
"name": "job1",
"id": "d6bd9aa1-0708-436a-81fd-cf22d5042689",
"status": "pending"
},
{
"name": "job2",
"id": "4fdaf09f-51de-4246-88fd-08d4daef6c3e",
"status": "pending"
}
]
}
"""
* def selected = '4fdaf09f-51de-4246-88fd-08d4daef6c3e'
* print getStatus(selected)
So if you have getStatus defined up-front, you can do this:
* retry until getStatus(selected) == 'completed'
Note you can use multiple lines for a JS function if you don't like squeezing it all into one line, or even read it from a file.

Related

How to handle multiple conditions for responseStatus [duplicate]

I'm using Karate framework with JUnit.
Using this feature:
Given path 'save_token'
And request
"""
{
"token": "test_token"
}
"""
And retry until response.tokens ==
"""
[
"test_token"
]
"""
When method POST
I'm having this exception:
java.lang.ArrayIndexOutOfBoundsException: 1
at com.intuit.karate.core.MethodMatch.convertArgs(MethodMatch.java:60)
at com.intuit.karate.core.Engine.executeStep(Engine.java:141)
at com.intuit.karate.core.ScenarioExecutionUnit.execute(ScenarioExecutionUnit.java:171)
When response.tokens list is empty:
{
"tokens": []
}
I don't understand why == does not work in this case (it should return false, and keep retrying).
Thanks in advance!
The retry until expression has to be pure JavaScript and the special Karate match keywords such as contains are not supported, and you can't do a "deep equals" like how you are trying, as that also is not possible in JS.
EDIT: in 0.9.6. onwards you can do a complex match in JS: https://stackoverflow.com/a/50350442/143475
Also note that JsonPath is not supported, which means * or .. cannot appear in the expression.
So if your response is { "tokens": [ "value1" ] }, you can do this:
And retry until response.tokens.includes('value1')
Or:
And retry until response.tokens[0] == 'value1'
To experiment, you can try expressions like this:
* def response = { "tokens": [ "value1" ] }
* assert response.tokens.includes('value1')
At run time, you can use JS to take care of conditions when the response is not yet ready while polling:
And retry until response.tokens && response.tokens.length
EDIT: actually a more elegant way to do the above is shown below, because karate.get() gracefully handles a JS or JsonPath evaluation failure and returns null:
And retry until karate.get('response.tokens.length')
Or if you are dealing with XML, you can use the karate.xmlPath() API:
And retry until karate.xmlPath(response, '//result') == 5
And if you really want to use the power of Karate's match syntax, you can use the JS API:
And retry until karate.match(response, { tokens: '##[_ > 0]' }).pass
Note that if you have more complex logic, you can always wrap it into a re-usable function:
* def isValid = function(x){ return karate.match(x, { tokens: '##[_ > 0]' }).pass }
# ...
And retry until isValid(response)
Finally if none of the above works, you can always switch to a custom polling routine: polling.feature
EDIT: also see this answer for an example of how to use karate.filter() instead of JsonPath: https://stackoverflow.com/a/60537602/143475
EDIT: in version 0.9.6 onwards, Karate can do a match in JS, which can simplify some of the above: https://stackoverflow.com/a/50350442/143475

Karate: I would like to use retry more efficiently [duplicate]

I'm using Karate framework with JUnit.
Using this feature:
Given path 'save_token'
And request
"""
{
"token": "test_token"
}
"""
And retry until response.tokens ==
"""
[
"test_token"
]
"""
When method POST
I'm having this exception:
java.lang.ArrayIndexOutOfBoundsException: 1
at com.intuit.karate.core.MethodMatch.convertArgs(MethodMatch.java:60)
at com.intuit.karate.core.Engine.executeStep(Engine.java:141)
at com.intuit.karate.core.ScenarioExecutionUnit.execute(ScenarioExecutionUnit.java:171)
When response.tokens list is empty:
{
"tokens": []
}
I don't understand why == does not work in this case (it should return false, and keep retrying).
Thanks in advance!
The retry until expression has to be pure JavaScript and the special Karate match keywords such as contains are not supported, and you can't do a "deep equals" like how you are trying, as that also is not possible in JS.
EDIT: in 0.9.6. onwards you can do a complex match in JS: https://stackoverflow.com/a/50350442/143475
Also note that JsonPath is not supported, which means * or .. cannot appear in the expression.
So if your response is { "tokens": [ "value1" ] }, you can do this:
And retry until response.tokens.includes('value1')
Or:
And retry until response.tokens[0] == 'value1'
To experiment, you can try expressions like this:
* def response = { "tokens": [ "value1" ] }
* assert response.tokens.includes('value1')
At run time, you can use JS to take care of conditions when the response is not yet ready while polling:
And retry until response.tokens && response.tokens.length
EDIT: actually a more elegant way to do the above is shown below, because karate.get() gracefully handles a JS or JsonPath evaluation failure and returns null:
And retry until karate.get('response.tokens.length')
Or if you are dealing with XML, you can use the karate.xmlPath() API:
And retry until karate.xmlPath(response, '//result') == 5
And if you really want to use the power of Karate's match syntax, you can use the JS API:
And retry until karate.match(response, { tokens: '##[_ > 0]' }).pass
Note that if you have more complex logic, you can always wrap it into a re-usable function:
* def isValid = function(x){ return karate.match(x, { tokens: '##[_ > 0]' }).pass }
# ...
And retry until isValid(response)
Finally if none of the above works, you can always switch to a custom polling routine: polling.feature
EDIT: also see this answer for an example of how to use karate.filter() instead of JsonPath: https://stackoverflow.com/a/60537602/143475
EDIT: in version 0.9.6 onwards, Karate can do a match in JS, which can simplify some of the above: https://stackoverflow.com/a/50350442/143475

Unable to find the duplicate elements from API response

We were testing an API and recently got an issue, some of the customers cannot log in to the website.
We found the issue and it is because of Duplicate keys in the API response, it is giving response even if the API contains the duplicate key.
So tests are not helping for the duplicate key conditions,So can anyone please help me or guide how I can find whether there is a duplicate element in the API response.
Tool: postman
Below is the sample API output,
In the below JSON output from API we can find there are duplicates for "operatingSystem",like this duplicate key is coming for different elements.
Since there is no way to debug the API for a while due to some reasons,so need to find out these duplicate cases.
Any idea or suggestions will be much appreciated.Thanks in advance.
JSON
eg: {
"code": 2,
"deviceId": "ID",
"deviceName": "Test",
"platform": "x64",
"operatingSystem": "test",
"operatingSystem": "test",
"gde": 000,
"productVersion": "0.0",
"build": "00000",
"receipt": null
}
How could we handle such a situation. Do we have any method to automate/test this case?
Here's something you can try although it's a bit convoluted. pm.response.json() will normalize the response and remove any duplicates i.e. you won't be able to detect any. So what you can do is take the response in text then manipulate it into a list and look for duplicates there. I used a map object so that if the map already contains a given key then set a flag and fail the test.
This is not thoroughly tested but it should give you an idea or at least a starting point to tackle the problem:
var jsonBody = pm.response.text();
var str = jsonBody.substring(1, jsonBody.length-1);
var keyArr = str.split(",");
var keyMap = {};
var foundDups = false;
for (var i = 0; i < keyArr.length; i++) {
var key = keyArr[i].split(":")[0];
if(!(key in keyMap)) {
keyMap[key] = key;
console.log("added key " + key);
}
else {
console.log("found duplicate: " + key);
foundDups = true;
break;
}
}
pm.test("Look for dups", function() {
pm.expect(foundDups).to.eql(false);
});

Karate framework retry until not working as expected

I'm using Karate framework with JUnit.
Using this feature:
Given path 'save_token'
And request
"""
{
"token": "test_token"
}
"""
And retry until response.tokens ==
"""
[
"test_token"
]
"""
When method POST
I'm having this exception:
java.lang.ArrayIndexOutOfBoundsException: 1
at com.intuit.karate.core.MethodMatch.convertArgs(MethodMatch.java:60)
at com.intuit.karate.core.Engine.executeStep(Engine.java:141)
at com.intuit.karate.core.ScenarioExecutionUnit.execute(ScenarioExecutionUnit.java:171)
When response.tokens list is empty:
{
"tokens": []
}
I don't understand why == does not work in this case (it should return false, and keep retrying).
Thanks in advance!
The retry until expression has to be pure JavaScript and the special Karate match keywords such as contains are not supported, and you can't do a "deep equals" like how you are trying, as that also is not possible in JS.
EDIT: in 0.9.6. onwards you can do a complex match in JS: https://stackoverflow.com/a/50350442/143475
Also note that JsonPath is not supported, which means * or .. cannot appear in the expression.
So if your response is { "tokens": [ "value1" ] }, you can do this:
And retry until response.tokens.includes('value1')
Or:
And retry until response.tokens[0] == 'value1'
To experiment, you can try expressions like this:
* def response = { "tokens": [ "value1" ] }
* assert response.tokens.includes('value1')
At run time, you can use JS to take care of conditions when the response is not yet ready while polling:
And retry until response.tokens && response.tokens.length
EDIT: actually a more elegant way to do the above is shown below, because karate.get() gracefully handles a JS or JsonPath evaluation failure and returns null:
And retry until karate.get('response.tokens.length')
Or if you are dealing with XML, you can use the karate.xmlPath() API:
And retry until karate.xmlPath(response, '//result') == 5
And if you really want to use the power of Karate's match syntax, you can use the JS API:
And retry until karate.match(response, { tokens: '##[_ > 0]' }).pass
Note that if you have more complex logic, you can always wrap it into a re-usable function:
* def isValid = function(x){ return karate.match(x, { tokens: '##[_ > 0]' }).pass }
# ...
And retry until isValid(response)
Finally if none of the above works, you can always switch to a custom polling routine: polling.feature
EDIT: also see this answer for an example of how to use karate.filter() instead of JsonPath: https://stackoverflow.com/a/60537602/143475
EDIT: in version 0.9.6 onwards, Karate can do a match in JS, which can simplify some of the above: https://stackoverflow.com/a/50350442/143475

Payload data manipulation

My payload looks something like this. I am not posting the entire request.
{
"Package": {
"#UniqueID": "Some383",
"Content": {
"Application": {
"#UniqueID": "SUB1536201342468",
"PersonApplicant": [
{
"#ApplicantType": "Whateever",
"#Citizenship": "IND",
I am able to set values to the payload
* set request $.Package.Content.Application.#UniqueID = someID
But, I am unable to get a value from the request for a second service call. Below is the code I use.
* set type= request $.Package.Content.Application.PersonApplicant.#ApplicantType
I get the below error, Please advice.
jdk.nashorn.internal.runtime.ParserException: <eval>:1:13 Expected ; but found $
lixiInitiate $.Package.Content.Application.PersonApplicant.#ApplicantType
Please read the docs and examples carefully: https://github.com/intuit/karate#karate-expressions
* set type = request.Package.Content.Application.PersonApplicant.#ApplicantType