Karate way of accessing a value in JSON object with number string as property (key) - testing

I'm having a JSON response from an API as per below:
{
"lanes": [
{
"laneId": "6ef9deb2-de6d-43f7-baed-fe00de3a11d4",
"name": "LaneTest",
"dayShifts": {
"1": [
"Morning Shift"
]
}
},
{
"laneId": "559165e9-d675-4537-99e7-a0f3b73de3c8",
"name": "testaaa",
"dayShifts": {
"1": [
"11am shift",
"5:00 pm shift"
]
}
}]
}
Based on the response, I'm trying to find a lane object which contains the value "Morning Shift" under the dayShift.1 array and below is my attempt:
* def shiftFilter = function(x) { return x == 'Morning Shift' }
* def laneIdFilter = function(x) { return karate.filter(x.dayShifts.1, shiftFilter) }
* def laneObj = karate.filter(response.lanes, laneIdFilter)
Upon executing the above code, I'm getting the following error:
>>> failed features:
js failed:
>>>>
01: (function(x) { return karate.filter(x.dayShifts.1, shiftFilter) })
<<<<
org.graalvm.polyglot.PolyglotException: SyntaxError: Unnamed:1:47 Expected , but found .1
(function(x) { return karate.filter(x.dayShifts.1, shiftFilter) })
^
Unnamed:1:65 Expected ; but found )
(function(x) { return karate.filter(x.dayShifts.1, shiftFilter) })
^
Unnamed:1:66 Expected } but found eof
(function(x) { return karate.filter(x.dayShifts.1, shiftFilter) })
^
- org.graalvm.polyglot.Context.eval(Context.java:425)
- com.intuit.karate.graal.JsEngine.evalForValue(JsEngine.java:139)
- com.intuit.karate.graal.JsEngine.eval(JsEngine.java:135)
- com.intuit.karate.core.ScenarioEngine.evalJs(ScenarioEngine.java:1190)
- com.intuit.karate.core.ScenarioEngine.evalKarateExpression(ScenarioEngine.java:2143)
- com.intuit.karate.core.ScenarioEngine.evalKarateExpression(ScenarioEngine.java:2062)
- com.intuit.karate.core.ScenarioEngine.evalAndCastTo(ScenarioEngine.java:1251)
However, when I attempt to filter based on other properties of the lane object (i.e: name), it works correctly. It seems that Karate is unhappy with the usage of 1 in the dayShifts object.
I'm new to Karate and I would really appreciate if someone can shed some lights on this.
Karate version: v1.3.0
Thank you!

The reason is 1 is interpreted as a number, so you need to use the alternate way of referring to JSON using keys in square-brackets - which is: x.dayShifts['1']
I'm giving you a more concise solution, the latest version of Karate has JS "baked in" so normal Array operations work:
* def lane = response.lanes.find(x => x.dayShifts['1'][0] == 'Morning Shift')

Related

How to validate an object inside a JSON schema in Karate whether its empty or contains a series of key:value pairs?

I am trying to validate an API response using Karate for either of these two states.
Scenario 1 (when it returns a contractData object that contains a fee key):
{
"customer": {
"financialData": {
"totalAmount": 55736.51,
"CreateDate": "2022-04-01",
"RequestedBy": "user1#test.com"
},
"contractData": {
"Fee": 78.00
}
}
}
Scenario 2 (when it returns an empty contractData object):
{
"customer": {
"financialData": {
"totalAmount": 55736.51,
"CreateDate": "2022-04-01",
"RequestedBy": "user1#test.com"
},
"contractData": {}
}
}
How can I write my schema validation logic to validate both states?
The best thing I could have done is to write it like this:
* def schema = {"customer":{"financialData":{"totalAmount":"#number","CreateDate":"#?isValidDate(_)","RequestedBy":"#string"},"contractData":{"Fee": ##number}}}
* match response == schema
And it seems like it works for both above scenarios, but I am not sure whether this is the best approach or not. Problem with this approach is if I have more than one key:value pair inside "contractData" object and I want to be sure all those keys are present in there when it is not empty, I cannot check it via this approach because for each individual key:value pair, this approach assumes that they could either be present or not and will match the schema even if some of those keys will be present.
Wow, I have to admit I've never come across this case ever, and that's saying something. I finally was able to figure out a possible solution:
* def chunk = { foo: 'bar' }
* def valid = function(x){ return karate.match(x, {}).pass || karate.match(x, chunk).pass }
* def schema = { hey: '#? valid(_)' }
* def response1 = { hey: { foo: 'bar' } }
* def response2 = { hey: { } }
* match response1 == schema
* match response2 == schema

Why is Date query with aggregate is not working in parse-server?

I want to query user where updatedAt is less than or equal today using aggregate because I'm doing other stuff like sorting by pointers.
I'm using cloud code to define the query from the server.
I first tried using mongoDB Compass to check my query using ISODate and it works, but using it in NodeJS seems not working correctly.
I also noticed about this problem that was already fix, they say. I also saw their tests.
Here's a link to that PR.
I'm passing date like this:
const pipeline = [
{
project: {
_id: true,
process: {
$substr: ['$_p_testdata', 12, -1]
}
}
},
{
lookup: {
from: 'Test',
localField: 'process',
foreignField: '_id',
as: 'process'
}
},
{
unwind: {
path: '$process'
}
},
{
match: {
'process._updated_at': {
$lte: new Date()
}
}
}
];
const query = new Parse.Query('data');
return query.aggregate(pipeline);
I expect value to be an array with length of 4 but only give me empty array.
I was able to fetch data without match date.
Please try this:
const pipeline = [
{
match: {
'editedBy.updatedAt': {
$lte: new Date()
}
}
}
];

Karate - Match two complex shuffled json

Below question is very similar to this: Karate - Validate json responses stored in different files I went through suggested contains-shortcuts and could not figure out the answer.
I need to compare two json files but using contains keyword. Why only contains? Because in some cases i need to match only some of the selected fields in json files. Below are the samples and codes.
Json File 1: Test.Json
{
"webServiceDetail":{
"feature":{
"featureCd":"ABCD",
"imaginaryInd":"100.0",
"extraInd1":"someRandomValue1"
},
"includefeatureList":[
{
"featureCd":"PQRS",
"featureName":"Checking SecondAddOn Service",
"extraInd1":"someRandomValue1",
"extraInd2":"someRandomValue1"
},
{
"featureCd":"XYZ",
"featureName":"Checking AddOn Service",
"imaginaryInd":"50.0"
}
]
}
}
Json File 2: Test1.json
{
"webServiceSummary":{
"service":{
"serviceCd":"ABCD"
},
"includeServicesList":[
{
"serviceCd":"XYZ",
"serviceDescription": "Checking AddOn Service"
},
{
"serviceDescription":"Checking SecondAddOn Service",
"serviceCd":"PQRS",
"randon":"FGDD"
}
]
}
}
My Code:
* def Test = read('classpath:PP1/data/test.json')
* def Test1 = read('classpath:PP1/data/Test1.json')
* def feature = Test.webServiceDetail.feature
* set expected.webServiceSummary.service
| path | value |
| serviceCd | feature.featureCd |
* def mapper = function(x){ return { serviceCd: x.featureCd, serviceDescription: x.featureName} }
* def expectedList = karate.map(Test.webServiceDetail.includefeatureList, mapper)
* set expected.webServiceSummary.includeServicesList = '#(^*expectedList)'
* match Test1.webServiceSummary.includeServicesList == expected.webServiceSummary.includeServicesList
Now, above code, perfectly works and i get success response as well. But my concern is I am matching with contains any here. I should verify with contains keyword. Because i need to ensure all the parameters in expected.webServiceSummary.includeServicesList are present in Test1.webServiceSummary.includeServicesList; not any or some of them. I tried using #(^expectedList) -- for contains; but didn't worked out. I know that these series of questions look silly, but i can't figure out the behavior!
This will always check that a value contains only all the array elements in expectedList.
'#(^^expectedList)'
Read the docs: https://github.com/intuit/karate#contains-short-cuts

Add a new element in each array of objects where array may have different length in mongodb

I have a following shema.
{
id:week
output:{
headerValues:[
{startDate:"0707",headers:"ID|week"},
{startDate:"0715",headers:"ID1|week1"},
{startDate:"0722",headers:"ID2|week2"}
]
}
}
I have to add a new field into headerValues array like this:
{
id:week
output:{
headerValues[
{startDate:"0707",headers:"ID|week",types:"used"},
{startDate:"0715",headers:"ID1|week1",types:"used"},
{startDate:"0722",headers:"ID2|week2",types:"used"}
]
}
}
I tried different approaches like this:
1)
db.CollectionName.find({}).forEach(function(data){
for(var i=0;i<data.output.headerValues.length;i++) {
db.CollectionName.update({
"_id": data._id, "output.headerValues.startDate":data.output.headerValues[i].startDate
},
{
"$set": {
"output.headerValues.$.types":"used"
}
},true,true
);
}
})
So, In this approach it is executing script and then failing. It is updating result with failed statement.
2)
Another approach I have followed using this link:
https://jira.mongodb.org/browse/SERVER-1243
db.collectionName.update({"_id":"week"},
{ "$set": { "output.headerValues.$[].types":"used" }
})
But it fails with error:
cannot use the part (headerValues of output.headerValues.$[].types) to
traverse the element ({headerValues: [ { startDate: "0707", headers:
"Id|week" } ]}) WriteError#src/mongo/shell/bulk_api.js:469:48
Bulk/mergeBatchResults#src/mongo/shell/bulk_api.js:836:49
Bulk/executeBatch#src/mongo/shell/bulk_api.js:906:13
Bulk/this.execute#src/mongo/shell/bulk_api.js:1150:21
DBCollection.prototype.updateOne#src/mongo/shell/crud_api.js:550:17
#(shell):1:1
I have searched with many different ways which can update different arrays object by adding new field to each object but no success. Can anybody please suggest that what am I doing wrong?
Your query is {"_id" : "week"} but in your data id field is week
So you can change {"_id" : "week"} to {"id" : "week"} and also update your mongodb latest version
db.collectionName.update({"id":"week"},
{ "$set": { "output.headerValues.$[].types":"used" }
})

How do I operate the m.withAttr tutorials code?

A contrived example of bi-directional data binding
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
m.render("body", [
m("input", {onchange: m.withAttr("value", controller.user.name), value: controller.user.name()})
]);
}
};
https://lhorie.github.io/mithril/mithril.withAttr.html
I tried the above code does not work nothing.
It was the first to try to append the following.
m.mount(document.body, user);
Uncaught SyntaxError: Unexpected token n
Then I tried to append the following.
var users = m.prop([]);
var error = m.prop("");
m.request({method: "GET", url: "/users/index.php"})
.then(users, error);
▼/users/index.php
<?php
echo '[{name: "John"}, {name: "Mary"}]';
Uncaught SyntaxError: Unexpected token n
How do I operate the m.withAttr tutorials code?
Try returning m('body', [...]) from your controller.
view: function (ctrl) {
return m("body", [
...
]);
}
render should not be used inside of Mithril components (render is only used to mount Mithril components on existing DOM nodes).
The example is difficult to operate because it's contrived, it's not meant to be working out-of-the-box. Here's a slightly modified, working version:
http://jsfiddle.net/ciscoheat/8dwenn02/2/
var user = {
model: function(name) {
this.name = m.prop(name);
},
controller: function() {
return {user: new user.model("John Doe")};
},
view: function(controller) {
return [
m("input", {
oninput: m.withAttr("value", controller.user.name),
value: controller.user.name()
}),
m("h1", controller.user.name())
];
}
};
m.mount(document.body, user);
Changes made:
m.mount injects html inside the element specified as first parameter, so rendering a body element in view will make a body inside a body.
Changed the input field event to oninput for instant feedback, and added a h1 to display the model, so you can see it changing when the input field changes.
Using m.request
Another example how to make an ajax request that displays the retrieved data, as per your modifications:
http://jsfiddle.net/ciscoheat/3senfh9c/
var userList = {
controller: function() {
var users = m.prop([]);
var error = m.prop("");
m.request({
method: "GET",
url: "http://jsonplaceholder.typicode.com/users",
}).then(users, error);
return { users: users, error: error };
},
view: function(controller) {
return [
controller.users().map(function(u) {
return m("div", u.name)
}),
controller.error() ? m(".error", {style: "color:red"}, "Error: " + controller.error()) : null
];
}
};
m.mount(document.body, userList);
The Unexpected token n error can happen if the requested url doesn't return valid JSON, so you need to fix the JSON data in /users/index.php to make it work with your own code. There are no quotes around the name field.