Rest Assured Body handling ArrayList - rest-assured

I has a response body like this
enter code here
{
"applicationName": "Service MyService",
"someData": [
{
"name": "check1",
"props": [
"AAaa"
]
},
{
"name": "check2",
"props": [
"BBbb",
"CCcc"
]
}
]
}
Now I can use the following code and the test passes.
given().log().all()
.accept(JSON).expect().statusCode(SC_OK)
.when().log().all()
.get(contextPath + "/test")
.then().log().all()
.body("someData.name",
IsCollectionWithSize.hasSize(2))
.body("someData.name",
allOf(hasItems("check1", "check2")))
.body("someData.findAll {it.name == 'check1'}.props",
IsCollectionWithSize.hasSize(1))
.body("healthReports.findAll {it.name == 'check2'}.props",
IsCollectionWithSize.hasSize(2)));
However if I then attempt to check the values in the props field it fails I think because a ArrayList is returned and the matchers are checking on String.
given().log().all()
.accept(JSON).expect().statusCode(SC_OK)
.when().log().all()
.get(contextPath + "/test")
.then().log().all()
.body("someData.name",
IsCollectionWithSize.hasSize(2))
.body("healthReports.findAll {it.name == 'check1'}.props",
IsCollectionContaining.hasItems(startsWith("AA")));
I'm not sure how from the findAll ...props I can check the contents of the ArrayList.
The error displayed is:
JSON path someData.findAll {it.name == 'check1'}.props doesn't match.
Expected: (a collection containing a string starting with "AA")
Actual: [[AAaa]]
Any idea's ?

The findall return an Array of Array containing one element which is AA (which is why you have [[AAaa]] instead of [AAaa].
You have to flatten or extract one level of array to solve the problem I think.

Related

Elasticsearch saves document as string of array, not array of strings

I am trying to contain array as a document value.
I succeed it in "tags" field as below;
This document contains array of strings.
curl -XGET localhost:9200/MY_INDEX/_doc/132328908
#=> {
"_index":"MY_INDEX",
"_type":"_doc",
"_id":"132328908",
"found":true,
"_source": {
"tags": ["food"]
}
}
However, when I am putting items in the same way as above,
the document is SOMETIMES like that;
curl -XGET localhost:9200/MY_INDEX/_doc/328098989
#=> {
"_index":"MY_INDEX",
"_type":"_doc",
"_id":"328098989",
"found":true,
"_source": {
"tags": "[\"food\"]"
}
}
This is string of array, not array of strings, which I expected.
"tags": "[\"food\"]"
It seems that this situation happens randomly and I could not predict it.
How could it happen?
Note:
・I use elasticsearch-ruby client to index a document.
This is my actual code;
es_client = Elasticsearch::Client.new url: MY_ENDPOINT
es_client.index(
index: MY_INDEX,
id: random_id, # defined elsewhere
body: {
doc: {
"tags": ["food"]
},
}
)
Thank you in advance.

How to validate response in one attempt

I want to test the response for POST request to petstore.swagger.io. I receive a response, why cannot I validate the body with path "id"? I always got errors, but regex is right and tested.
Test method:
#Test
public void postPet() {
Pattern pt = Pattern.compile("<(\\d*)>");
Response response = given()
.contentType("application/json")
.body(jsonObject)
.when()
.post(String.format("https://petstore.swagger.io/v2/pet"))
.then()
.statusCode(200)
.log().body()
.and()
.assertThat()
.body("id", matchesPattern(pt))
Error:
java.lang.AssertionError: 1 expectation failed.
JSON path id doesn't match.
Expected: a string matching the pattern '<(\d*)>'
Actual: <9223372000666122518L>
The response body is:
{
"id": 9223372000666122443,
"category": {
"id": 0,
"name": "string"
},
"name": "doggie",
"photoUrls": [
"string"
],
"tags": [
{
"id": 0,
"name": "string"
}
],
"status": "available"
}
And what is the L at the end of the id string? In swagger, there is no L as well as in the Postman. I tried regex without "< , >".
It's kind of clear that
your id is 9223372000666122518L, L means long (data type)
you use regex, but it only works for String
--> type mistmach when comparing long vs String
Solution:
Convert id to String first then assert
long id = ...log().body()
.extract().path("id");
assertThat(String.valueOf(id), Matchers.matchesPattern("\\d+"));
or
Write a custom Matcher, to check isLong, like this Is there a way in Hamcrest to test for a value to be a number?

Restassured: How Can we compare each element in Json array to one particular Same value in Java using Hemcrest Matchers, not using Foreach loop

Restassured: How Can we compare each element in Json array to one particular Same value in Java using Hemcrest Matchers, not using Foreach loop.
{
"id": 52352,
"name": "Great Apartments",
"floorplans": [
{
"id": 5342622,
"name": "THE STUDIO",
"fpCustomAmenities": [
{
"displaySequence": 2,
"amenityPartnerId": "gadasd",
"display": true,
"leased": true
},
{
"displaySequence": 13,
"amenityPartnerId": "sdfsfd",
"display": true,
"leased": true
}
]
},
{
"id": 4321020,
"name": "THE First Bed",
"fpCustomAmenities": [
{
"displaySequence": 4,
"amenityPartnerId": "gadasd",
"display": true,
"leased": true
},
{
"displaySequence": 15,
"amenityPartnerId": "hsfdsdf",
"display": true,
"leased": true
}
]
}
]
}
I want to compare that Leased=true for all the leased nodes at all the levels in the json response...
I have working code...
List<List<Boolean>> displayedvaluesfpStandardAmenities =
when().get(baseUrl + restUrl).
then().statusCode(200).log().ifError().
extract().body().jsonPath().getList("floorplans.fpCustomAmenities.display");
for (List<Boolean> displayedStandardList : displayedvaluesfpStandardAmenities) {
for (Boolean isDisplayedTrue : displayedStandardList) {
softAssert.assertTrue(isDisplayedTrue);
}
}
But the issue is I need the code to be in simple format using either Hemcrest Matchers or Restaussred Matchers and try simplistic way like Below, ( which is not working)
when().get(baseUrl + restUrl).
then().assertThat().body("floorplans.fpCustomAmenities.display",equalTo("true"));
The error I am getting is
java.lang.AssertionError: 1 expectation failed.
JSON path floorplans.fpCustomAmenities.display doesn't match.
Expected: true
Actual: <[[true, true], [true, true]]>
So what I need is the that all thes 'display' nodes in the json response where ever it is need to compared with "true", so that my test can Pass.
I have an alternate solution like mentioned above, but All I need is working solution using matchers.
Assuming fpCustomAmenities arrays are not empty, you can use the following solution;
when().get(baseUrl + restUrl).then()
.body("floorplans.findAll { it }.fpCustomAmenities" + // 1st line
".findAll { it }.leased.each{ a -> println a }" + // 2nd line
".grep{ it.contains(false) }.size()", equalTo(0)); // 3rd line
Here from the 1st line, we return each object in fpCustomAmenities array.
From the 2nd line we get boolean value of leased in each fpCustomAmenities object to a boolean array ([true, true]).
Each boolean array is printed from .each{ a -> println a }. I added it only to explain the answer. It is not relevant to the solution.
From 3rd line we check whether, if there is a false in each boolean array. grep() will return only the arrays which has a false. And then we get the filtered array count. Then we check whether it is equal to 0.
Check groovy documentation for more details.
Or
This solution does not use any Matchers. But this works.
String responseBody = when().get(baseUrl + restUrl).
then().extract().response().getBody().asPrettyString();
Assert.assertFalse(responseBody.contains("\"leased\": false"));

How to add content to table cell via google-docs-api request?

I want to add a content to a table cell in google doc, but the way described in the documentation doesn't work.
What is wrong with my request? When I provide 1 for index parameter of insertText request it just pastes text before table. When I provide 2 as value of index parameter I get an error: "Invalid requests[1].insertText: The insertion index must be inside the bounds of an existing paragraph. You can still create new paragraphs by inserting newlines."
{
"requests": [
{
"insertTable": {
"endOfSegmentLocation": {
"segmentId": ""
},
"columns": 1,
"rows": 1
}
},
{
"insertText": {
"location": {
"index": 1
},
"text": "Cell content"
}
}
]
}
I expect that text must be inserted into the only cell of the table.
You want to append a table (1 x 1) to the last body.
You want to insert a text to the 1st cell.
From your request body, I could understand like above. If my understanding is correct, how about this flow? I think that there might be several solutions. So please think of this as just one of them.
In the case that new table is appended to the last body ("segmentId": "" means that the table is appended to the last body.), at first, the start index of the table is required to be known. So how about the following flows?
Flow 1:
In this flow, it supposes that the index of last body is not known.
Append a table using the following request body.
{
"requests": [
{
"insertTable": {
"endOfSegmentLocation": {
"segmentId": ""
},
"columns": 1,
"rows": 1
}
}
]
}
Retrieve the start index of table using the following endpoint. At that time, you can also retrieve the start index of the cell.
GET https://docs.googleapis.com/v1/documents/{fileId}?fields=body(content(startIndex%2Ctable))
Insert the text to the cell. In this case, it supposes that the retrieved start index of the appended table is 10. The start index of the 1st cell is 14 (I think that the start index of the 1st cell can be retrieved by start index of table + 4.). In this case, the request body for inserting the text to the cell is as follows.
{
"requests": [
{
"insertText":
{
"location":
{
"index": 14
},
"text": "Cell content"
}
}
]
}
Flow 2:
In this flow, it supposes that the index of last body is known. For example, when the table is appended to the new Document, you can create the table with the text using the following request body. In this case, the start index of the table and the cell are 1 and 5, respectively.
{
"requests": [
{
"insertTable":
{
"endOfSegmentLocation":
{
"segmentId": ""
},
"columns": 1,
"rows": 1
}
},
{
"insertText":
{
"location":
{
"index": 5
},
"text": "Cell content"
}
}
]
}
References:
Inserting or deleting table rows
Thread: Insert table in Google Docs API Python
If I misunderstood your question and this was not the direction you want, I apologize.
This is a follow up to the answers posted already. #ANewb hinted the the flow ends after the 5th cell. Ensure the values/entries in the payload aren't empty strings. For anyone encountering the same challenge, you can try using tenary conditions - for php $value == "" ? "N/A : $value;, for js let value = checkValue == "" ? "N?A": checkValue;

iOS Swit 3 - filter array inside filter

I would like to filter array inside a filter. First I have a big array of Staff object (self.bookingSettings.staffs). Inside this array I have multiple object like this :
"staffs": [
{
"id": 1,
"name": "Brian",
"services": [
{
"id": 1
},
{
"id": 2
},
{
"id": 3
},
{
"id": 4
}
],
"pos": 1
},...
I would like to filter this array in order to have only services with id = 3.
I succeed to have if first object is equal to 3 with this code :
self.bookingSettings.staffs.filter({ $0.services.first?.id == self.bookingService.id })
but that takes only the first item.
I think I have to filter inside my filter function, something like this to loop over all object inside services :
self.bookingSettings.staffs.filter({ $0.services.filter({ $0.id == self.bookingService.id }) })
but I've the following error: Cannot convert value of type [BookingService] to closure result type Bool.
Is this a good idea ? How can I achieve this ?
You could use filter, which would look something like this:
self.bookingSettings.staffs.filter {
!$0.services.filter{ $0.id == self.bookingService.id }.isEmpty
}
This code is constructing an entire array of filtered results, only to check if its empty and immediately discard it. Since filter returns all items that match the predicate from the list, it won't stop after it finds a match (which is really what you're looking for). So even if the first element out of a list of a million elements matches, it'll still go on to check 999,999 more elements. If the other 999,999 elements also match, then they will all be copied into filter's result. That's silly, and can use way more CPU and RAM than necessary in this case.
You just need contains(where:):
self.bookingSettings.staffs.filter {
$0.services.contains(where: { $0.id == self.bookingService.id })
}
contains(where:) short-circuits, meaning that it won't keep checking elements after a match is found. It stops and returns true as soon as find a match. It also doesn't both copying matching elements into a new list.

Resources