Karate: Is it possible to compare two service responses & exclude some keys in comparison - karate

I am trying to compare responses from two service call(for Migration project) using Karate.
Is it possible to exclude or ignore some keys while comparing whole response at one go for following cases:
1)There are few elements in response where the values is different(marked in bold).
2)There are few elements which is not present in one response but present in other(marked in bold).
Service 1 response -
{
"userKey": "string",
"enabled": true,
"locked": true,
"profile": {},
"roles": [
{
"roleKey": 3,
"name": "Role",
**"links": []**
}
],
"links": [
{
"rel": "self",
**"href": "https://starhub1.com"**
},
{
"rel": "self",
**"href": "https://singtel1.com"**
}
]
}
Service 2 response -
{
"userKey": "string",
"enabled": true,
"locked": true,
"profile": {},
"roles": [
{
"roleKey": 3,
"name": "Role"
}
],
"links": [
{
"rel": "self",
**"href": "https://starhub22.com"**
},
{
"rel": "self",
**"href": "https://singtel22.com"**
}`enter code here`
]
}

Yes Karate is pretty good at this. Just do a transform on one payload before comparison.
Please see this answer for details: https://stackoverflow.com/a/53120851/143475
For your specifc example maybe all you need to do is change one "side" to #string and then do the comparison.
* def response1 = { foo: 'hello' }
* def response2 = { foo: 'world' }
* response2.foo = '#string'
* match response1 == response2

Related

JSON element extraction from response based on scenario outline examples or external file

This is my api response. Want to extract the value of the Id based on the displayNumber. This display number is a given in the list of values in examples/csv file.
{
"Acc": [
{
"Id": "2b765368696b3441673633325",
"code": "SGD",
"val": 406030.83,
"displayNumber": "8957",
"curval": 406030.83
},
{
"Id": "4e676269685a73787472355776764b50717a4",
"code": "GBP",
"val": 22.68,
"displayNumber": "1881",
"curval": 22.68
},
{
"Id": "526e666d65366e67626244626e6266467",
"code": "SGD",
"val": 38404.44,
"displayNumber": "1004",
"curval": 38404.44
},
],
"combinations": [
{
"displayNumber": "3444",
"Code": "SGD",
"Ids": [
{
"Id": "2b765368696b34416736333254462"
},
{
"Id": "4e676269685a7378747235577"
},
{
"Id": "526e666d65366e6762624d"
}
],
"destId": "3678434b643530456962435272d",
"curval": 3.85
},
{
"displayNumber": "8957",
"code": "SGD",
"Ids": [
{
"Id": "3678434b6435304569624357"
},
{
"Id": "4e676269685a73787472355776764b50717a4"
},
{
"Id": "526e666d65366e67626244626e62664679"
}
],
"destId": "2b765368696b344167363332544",
"curval": 406030.83
},
{
"displayNumber": "1881",
"code": "GBP",
"Ids": [
{
"Id": "3678434b643530456962435275"
},
{
"Id": "2b765368696b3441673"
},
{
"Id": "526e666d65366e67626244626e626"
}
],
"destId": "4e676269685a7378747d",
"curval": 22.68
},
]
}
Examples
|displayNumber|
|8957|
|3498|
|4943|
Below expression works if i give the value
* def tempid = response
* def fromAccount = get[0] tempid.Acc[?(#.displayNumber==8957].Id
I'm not sure how to make this comparison value (i.e. 1881) as a variable which can be read from examples (scenario outline) or a csv file. Went through the documentation, which recommends, karate filters or maps. However, not able to follow how to implement.
You almost got it :-). This is the way you want to solve this
Scenario Outline: Testing SO question for Navneeth
* def tempid = response
* def fromAccount = get[0] tempid.Acc[?(#.displayNumber == <displayNumber>)]
* print fromAccount
Examples:
|displayNumber|
|8957|
|1881|
|3444|
You need to pass the placeholder in examples as -
'<displayNumber>'

How to check a particular value in karate on basis of condition when there are more than one key in response

There are multiple keys present in x.details[0].user so how to compare in such condition. It works fine where there is only one key while when there are more than one key it fails with error as there are multiple keys for user.
Please guide
* def array =
"""
{
"type": "1",
"array": 2,
"details": [
{
"path": "path",
"user": {
"school": {
"name": [
{
"value": "this is school",
"codeable": {
"details": [
{
"hello": "yty",
"condition": "check1"
}
],
"text": "123S"
}
}
]
},
"sample": "test1",
"id": "22222"
},
"data": {
"check": "abc"
}
},
{
"path": "path",
"user": {
"school": {
"name": [
{
"value": "this is school",
"codeable": {
"details": [
{
"hello": "def",
"condition": "check2"
}
],
"text": "123O"
}
}
]
},
"sample": "test",
"id": "11111"
},
"data": {
"check": "xyz"
}
}
]
}
"""
* def lookup = { 'check1': 'yty', 'check2': 'def' }
* match each array.details contains { user: { school: { name[0]: { codeable: { details[0]: { hello: '#(lookup[_$.user.school.name[0].codeable.details[0].condition])' } } } } } }
I have tried multiple ways but not sure how to make it work when there are more than one keys getting returned.
First let me say this. Please, please do not try to be "too clever" in your tests. Read this please: https://stackoverflow.com/a/54126724/143475
Just have simple tests that test for specific, predictable responses and stop there. The maximum complexity should be match contains, that's it. What you are trying to do is in my opinion a waste of time, and will just cause grief for anyone who tries to maintain these tests in future.
That said, I'm giving you a completely different approach here. There are a few different ways, this is just one. Here we attack the problem piece by piece instead of trying to write one massive match.
* def allXyz = array.details.filter(x => x.data.check == 'xyz')
* match each allXyz..details == [{ hello: 'def', condition: 'check2' }]
You should be able to extend this to do all the weird assertions that you want. If you are wondering what allXyz..details does, you can print it like this:
* def temp = $allXyz..details
* print temp

Karate For loop to get ids based on pattern and then use a delete feature

I have response from an API call that gives me a list of devices each with an id. Some of these devices are test devices with the id starting with the prefix 'Test' Example Test319244.
I wish to only retrieve those ids with the prefix 'Test', may be in an array and be able to pass them to another feature file which takes the device ID as the parameter to delete it. Basically I want to delete all the testdevices.
Here is the sample response that contains all the device IDs
{
"items": [
{
"deviceId": "004401784033074000",
"deviceType": "AVMAP_TMR",
"disabled": false,
"metadata": {
"createdAt": "2020-07-20T00:00:00.000+00:00",
"modifiedAt": "2020-07-20T00:00:00.000+00:00"
}
},
{
"deviceId": "Test319246",
"deviceType": "AVMAP_TMR",
"disabled": false,
"metadata": {
"createdAt": "2020-07-21T00:00:00.000+00:00",
"modifiedAt": "2020-07-21T00:00:00.000+00:00"
}
},
{
"deviceId": "Test319245",
"deviceType": "AVMAP_TMR",
"disabled": false,
"metadata": {
"createdAt": "2020-07-21T00:00:00.000+00:00",
"modifiedAt": "2020-07-21T00:00:00.000+00:00"
}
},
{
"deviceId": "Test319244",
"deviceType": "AVMAP_TMR",
"disabled": false,
"metadata": {
"createdAt": "2020-07-21T00:00:00.000+00:00",
"modifiedAt": "2020-07-21T00:00:00.000+00:00"
}
},
{
"deviceId": "command-service",
"deviceType": "service",
"disabled": false,
"metadata": {
"createdAt": "2020-07-20T00:00:00.000+00:00",
"modifiedAt": "2020-07-20T00:00:00.000+00:00"
}
},
{
"deviceId": "kafka-connect-all",
"deviceType": "kafka-connect",
"disabled": false,
"metadata": {
"createdAt": "2020-07-20T00:00:00.000+00:00",
"modifiedAt": "2020-07-20T00:00:00.000+00:00"
}
}
],
"metadata": {
"pagination": {
"limit": 50,
"offset": 0,
"previousOffset": 0,
"nextOffset": 0,
"totalCount": 15
},
"sortedBy": [
{
"field": "deviceId",
"order": "ASC"
}
]
}
}
Here in the above example I only want to delete the devices with ids - Test319244,Test319245 and Test319246
How can I get an array of ids based on the pattern(Testxxxxxx) and pass that on to another feature file
I need help to define an array of ids like:
* def ids = extract the ids based on the pattern
# pass the ids to the delete feature which would send the id one at a time and delete the device.
* def delete = call(delete.feature) ids
This is how the delete scenario feature file looks:
Scenario: Delete Device
# device_registry_url defined in karate-config.js
Given url device_registry_url
And path '/device/'+DeviceID
And header Authorization = authheader
And request ''
When method delete
Then status 200
Would this be the right approach or could we do it in a better way? If so, can someone kindly help in how to do it please?
Just use karate.filter() and then you know what to do:
* def fun = function(x){ return x.deviceId.startsWith('Test') }
* def filtered = karate.filter(response.items, fun)
* call read('delete.feature') filtered

Karate: Get all users and delete with the reference id

After my tests are completed, I would like to delete all the users that were created. I am able to delete one user at a time. Can we do this using conditional logic?
Feature file:
Given path 'users'
And header Authorization = authId
And header Accept = 'application/json;version=2'
When method Get
Then status 200
Then print 'Response Time: '+ responseTime + ' milliseconds'
* def resp = $
* def size = karate.sizeOf(resp)
* print 'Number of users: ' + size
This gives me below response:
[
{
"firstName": "Edit User",
"lastName": "API Test",
"emailAddress": "edituserapitesting#gmail.com",
"ada": true,
"isDeleted": false,
"alerts": [],
"links": [
{
"href": "https://someurl/api/users/0219360d-5ca6-42af-9e9c-10be1e32d219",
"rel": "self"
}
]
},
{
"firstName": "Create",
"lastName": "Test",
"emailAddress": "api#test.com",
"ada": true,
"isDeleted": false,
"alerts": [],
"links": [
{
"href": "https://someurl/api/users/5e3c9be1-2863-4a2d-85e9-966582b127ac",
"rel": "self"
}
]
},
{
"firstName": "Create",
"lastName": "Test",
"emailAddress": "api#test.com",
"ada": true,
"isDeleted": false,
"alerts": [],
"links": [
{
"href": "https://someurl/api/users/b6feb126-bca2-43e6-ba2d-87aaae81fef0",
"rel": "self"
}
]
},
{
"firstName": "Create",
"lastName": "Test",
"emailAddress": "api#test.com",
"ada": true,
"isDeleted": false,
"alerts": [],
"links": [
{
"href": "https://someurl/api/users/4f07f234-9606-4cf5-94ce-3a42fb11a6d7",
"rel": "self"
}
]
}
]
I have to get the id [Eg:4f07f234-9606-4cf5-94ce-3a42fb11a6d7] from each json and pass it to delete path.
Please read about transforms: https://github.com/intuit/karate#json-transforms
Now in one line you can get an array of ids:
* def ids = karate.map(response, function(x){ var link = x.links[0].href; return link.substring(link.lastIndexOf('/') + 1) })
Now you can use this in a loop or data driven test and do what you want.

Max Response Limitation im OTA_AirLowFareSearchRQ

I'm working with Sabre REST API. I have a issue with the OTA_AirLowFareSearchRQ, I try limit the response number using the MaxResponses in the json structure but seems that I make something wrong because the response give to me 95 answers in the cert environment (https://api.cert.sabre.com/).
The json request that I use is:
{
"OTA_AirLowFareSearchRQ": {
"Target": "Production",
"PrimaryLangID": "ES",
"MaxResponses": "15",
"POS": {
"Source": [{
"RequestorID": {
"Type": "1",
"ID": "1",
"CompanyName": {}
}
}]
},
"OriginDestinationInformation": [{
"RPH": "1",
"DepartureDateTime": "2016-04-01T11:00:00",
"OriginLocation": {
"LocationCode": "BOG"
},
"DestinationLocation": {
"LocationCode": "CTG"
},
"TPA_Extensions": {
"SegmentType": {
"Code": "O"
}
}
}],
"TravelPreferences": {
"ValidInterlineTicket": true,
"CabinPref": [{
"Cabin": "Y",
"PreferLevel": "Preferred"
}],
"TPA_Extensions": {
"TripType": {
"Value": "Return"
},
"LongConnectTime": {
"Min": 780,
"Max": 1200,
"Enable": true
},
"ExcludeCallDirectCarriers": {
"Enabled": true
}
}
},
"TravelerInfoSummary": {
"SeatsRequested": [1],
"AirTravelerAvail": [{
"PassengerTypeQuantity": [{
"Code": "ADT",
"Quantity": 1
}]
}]
},
"TPA_Extensions": {
"IntelliSellTransaction": {
"RequestType": {
"Name": "10ITINS"
}
}
}
}
}
MaxResponses could be something for internal development which is part of the schema but does not affect the response.
What you can modify is in the IntelliSellTransaction. You used 10ITINS, but the values that will work should be 50ITINS, 100ITINS and 200ITINS.
EDIT2 (as Panagiotis Kanavos said):
RequestType values depend on the business agreement between your company and Sabre. You can't use 100 or 200 without modifying the agreement.
"TPA_Extensions": {
"IntelliSellTransaction": {
"RequestType": {
"Name": "50ITINS"
}
}
}
EDIT1:
I have searched a bit more and found:
OTA_AirLowFareSearchRQ.TravelPreferences.TPA_Extensions.NumTrips
Required: false
Type: object
Description: This element allows a user to specify the number of itineraries returned.