Karate test framework: retry until complex condition on arrays [duplicate] - cucumber

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

Related

Groovy script assertion that validates the presence of some values in a JSON response

So I'm using an testing tool called ReadyAPI and for scripting it uses the Groovy language. I'm not familiar with the language and the fact that it's based on Java it somehow makes it even worse.
Now I'm trying to validate a REST response in JSON with an assertion that checks that certain elements exist in the response.
This is the code that I have now:
import groovy.json.*
def response = context.expand( 'RequestResponseHere' )
def object = new JsonSlurper().parseText(response)
assert response.contains("CODE1")
assert response.contains("CODE2")
assert response.contains("CODE3")
assert response.contains("CODE4")
The assertion seems to work but I was wondering if there is maybe a simpler way to do it than to have to write so many lines and making it less 'bulky'?
Any help is greatly appreciated.
Thank you!
Added an example of the json data that I have to parse:
What I need is to check that the value of "code" is always part of a list of acceptable values e.i. CODE1, CODE2, etc.
{
"_embedded": {
"taskList": [
{
"code": "CODE1",
"key": 123
},
{
"code": "CODE2",
"key": "234"
},
{
"code": "CODE3",
"key": "2323"
},
{
"code": "CODE4",
"key": "7829"
},
{
"code": "CODE5",
"key": "8992"
}
]
}
}
If you want to check for certain things to be there, you can DRY that
code with:
["code1","code2"].each{ assert response.contains(it) }
And as stated in the comments, if you want to make sure, "all are there,
but I don't care for the order", extracting the values and comparing it
as results can shorten this:
assert response._embeded.taskList*.code.toSet() == ["code1", ...].toSet()
Note the use of *., which is the "spread operator". It is basically
the same as ...collect{ it.code }. Then comparing the string sets
does the rest (this is fine if you are comparing not to many items since
the power assert will print all the items along the failed assertion; if
you have to find the proverbial needle in the haystack, you are better
off writing something smarter to check the two sets).
The assertion seems to work but I was wondering if there is maybe a
simpler way to do it than to have to write so many lines and making it
less 'bulky'?
Unclear what it is that is bulky but if what you want is to lessen the number of statements, one thing you could do is use a single assert.
Instead of this:
assert response.contains("CODE1")
assert response.contains("CODE2")
assert response.contains("CODE3")
assert response.contains("CODE4")
You could have this:
assert response.contains("CODE1") &&
response.contains("CODE2") &&
response.contains("CODE3") &&
response.contains("CODE4") &&

How to force nodejs script argument to take value from choices list using commander?

I am using commander in node.js script. I am able to set default value to one argument.
var args = require('commander')
// set 'pending' as defaut value of status
args.option('-s --status <statusString>', 'Status to filter questions', 'pending').parse(process.argv)
console.log('status:', args.status)
How can I force status value to be from ["pending", "rejected", "accepted", "hold"] only? I did not find anything relevant in documention.
This is what I could achieve:
var options = ["pending", "rejected", "accepted", "hold"];
args.option(
'-s --status <statusString>',
`Status to filter questions: ${options}`,
function(val, _) {
if (!options.includes(val)) {
throw Error(`${val} is not from ${options}`);
}
return val;
},
'pending')
.parse(process.argv)
Not perfect, since you need to format help string and validate input value by yourself. Also, throwing an error from the validation function is not handled by Commander nicely and causes it to fail with the whole stacktrace in the output. I could not find a better way to tell Commander that the input is invalid.
In my case I finally just switched to argparse, which is a clone of the Python command line parser, and seems to be better thought over. This is how you limit choices with it:
const ArgumentParser = require('argparse').ArgumentParser;
const argparser = new ArgumentParser({ description: 'An example'});
argparser.addArgument('--status', {
choices: ['pending', 'rejected', 'accepted', 'hold'],
defaultValue: 'pending'});
const args = argparser.parseArgs();
This will do the job, including nice help message and input validation.
I think that in your case this is what you were looking for:
args
.addOption(
new Option('-s --status <statusString>', 'Status to filter questions')
.choices(["pending", "rejected", "accepted", "hold"])
.default('pending')
)
.parse(process.argv)
Also please take a look to another specific example I've prepared and tested successfully (NPM commander v8.2.0)
program
.addOption(
new Option('-s --status <statusString>', 'Status to filter questions')
.choices(["pending", "rejected", "accepted", "hold"])
.default('pending')
)
.action((args) => {
console.log(args)
})
Side note: please notice that for this second example I've used a slightly different naming convention for clarity: I've used program (vs original args) in the first line, as I was planning to use args name for the variable array received in the arrow function used in action() instead. Don't let that change confuse you! ;)
IMPORTANT: more on the official examples, direct link to a related example right here: https://github.com/tj/commander.js/blob/HEAD/examples/options-extra.js#L13

Math functions in Logstash

I am looking forward to use mathematical operations on the input received in Logstash, but unable to see any of such filter.
Input is as following:
{
"user_id": "User123",
"date": "2016 Jun 26 12:00:12",
"data": {
"doc_name": "mydocs.xls",
"doc_size": "8526587",
}
}
The "doc_size" field will have bytes, I would like to add a new field say "doc_size_mb" which will contain the size in MB's.
So I want a simple division operation here like:
doc_size_mb = doc_size/(1024*1024)
I could see a link which says Logstash has math filter, but this is not visible here .
The logstash-filter-math is not a core plugin but it is available here. You can follow the next steps in order or install it:
> git clone https://github.com/robin13/logstash-filter-math.git
> cd logstash-filter-math
> gem build
> $LS_HOME/bin/logstash-plugin install logstash-filter-math-0.2.gem
If you don't want to install a 3rd party plugin just for that, you can also easily achieve the same computation with a ruby filter:
filter {
ruby {
code => "event['data']['doc_size_mb'] = event['data']['doc_size'].to_i / (1024 * 1024)"
}
}
I tried using the above approach to multiply an existing field by a factor value and update the value of the existing field in the event by this new scaled value in Logstash 7.0.1, but it did not work as expected.
I modified it to use the Event API's set() and get() methods which worked out for me.
Initial approach (did not work) -
filter {
ruby {
code => "event['data']['myField'] = event['data']['myField'].to_i * 0.25"
}
}
Working solution -
filter {
ruby {
code => "event.set('myField',event.get('myField')* 0.25)
}
}
The math filter or ruby are options for the general case of doing math in logstash, but for this specific use-case (converting MB) there is the bytes filter.

Watch for element visibility change in angulardart

I have the same exact question that has already been posted for angularjs (Angular - Watch for ngShow/ngHide Changes in Ancestors that Affect Child DOM Element's Visibility) except in my case we are using angulardart. I cannot seem to watch a function in angulardart. When I try to, I get an error from the expression parser stating that "{" is unexpected. Is there a better way to do this in angulardart? If not, how can I watch a function similar to angularjs?
I've tried the following code with no success:
scope.watch("() { return _element.hidden; }", (value, _) {
print(value);
});
Parser Error: Unexpected token ) at column 2 in [() { return _element.hidden; }]
scope.watch("(scope) { return _element.hidden; }", (value, _) {
print(value);
});
Parser Error: '{' is an unexpected token at column 9 in [(scope) { return _element.hidden; }]
We are using angular.dart 1.1.2
You could use MutationObserver, it notifies about DOM changes. See for example In Dart why the code below (about MutationObserver) dose not work? (there should be more examples on SO)

Workaround for lack of generators/yield keyword in Groovy

Wondering if there is a way I can use sql.eachRow like a generator, to use it in a DSL context where a Collection or Iterator is expected. The use case I'm trying to go for is streaming JSON generation - what I'm trying to do is something like:
def generator = { sql.eachRow { yield it } }
jsonBuilder.root {
status "OK"
rows generator()
}
You would need continuation support (or similiar) for this to work to some extend. Groovy does not have continuations, the JVM also not. Normally continuation passing style works, but then the method eachRow would have to support that, which it of course does not. So the only way I see is a makeshift solution using threads or something like that. So maybe something like that would work for you:
def sync = new java.util.concurrent.SynchronousQueue()
Thread.start { sql.eachRow { sync.put(it) } }
jsonBuilder.root {
status "OK"
rows sync.take()
}
I am not stating, that this is a good solution, just a random consumer-producer-work-around for your problem.

Resources