Why is an Array in my payload being flattened in Sinatra / Rack::Test? - ruby

I'm trying to test a small Sinatra app using rspec. I want to pass a rather complex payload and am running into issues i do not understand: my payload contains an array of hashes. When I run the actual application this will work as expected, yet when I use the post helper to run my tests, the array will contain a merged hash:
post(
"/#{bot}/webhook",
sessionId: "test-session-#{session_counter}",
result: {
contexts: [
{ some: 'fixture' },
{ name: 'generic', parameters: { facebook_sender_id: 'zuck-so-cool' } }
]
}
)
In the sinatra handler I use params to access this payload:
post '/:bot/webhook' do |bot|
do_something_with(params)
end
When I now look at the structure of params when running the test suite, I will see the following structure:
[{"some" => "fixture", "name" => "generic", "parameters" => {"facebook_sender_id" => "zuck-so-cool"}}]
which I do not really understand. Is this a syntax issue (me being a ruby noob), am I using params wrong, or is this a bug?
EDIT: So i found out this is an "issue" with the way that Rack::Test will serialize the given payload when not specifying how to (i.e. as form data). If I pass JSON and pass the correct headers it will do what I expect it to do:
post(
"/#{bot}/webhook",
{
sessionId: "test-session-#{session_counter}",
result: {
contexts: [
{ some: 'fixture' },
{ name: 'generic', parameters: { facebook_sender_id: 'zuck-so-cool' } }
]
}
}.to_json,
{ 'HTTP_ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json' }
)
Still I am unsure of this is an issue with the passed data structure not being possible to be serialized into form data or if this is a bug in the way that Rack::Test serializes data.

Looking at the relevant portion of the specs it looks like this is is expected behavior.

Related

Correct way to expect/receive state with Enzyme & MockProvider

I'm testing a component that is using a graphql useLazyQuery. MockProvider is provided by the Apollo recommended library #apollo/react-testing. I want to test that a certain message is being rendered base off the length of the data that is returned from the query. I have html elements structured like this:
<div className="message" data-id={props.data ? props.data.specials.length > 0 ? 'valid' : 'invalid' : ''}>
...children
</div>
I read through Apollo's docs about testing and wrote up a test like this:
mock = {
request: {
query: GET_PRODUCTS,
variables: { zip: "91001" }
},
result: {
data: {
specials: [
{
"_id": "5ecf28c459d3781a2e99738e",
},
{
"_id": "5ecf28c459d3781a2e99738f",
}
]
}
}
}
wrapper = mount(
<MockedProvider mocks={[mock]} addTypename={false}>
<Store.Provider value={[{ loading: false, zip: null }]}>
<GetZipCode />
</Store.Provider>
</MockedProvider>
)
await wait(0)
expect(wrapper.find(message).prop('data-id')).toEqual('valid')
But I've found that the tests do not update based on the mock that I put it. I have a test about this where I'm passing this value as the mock:
mock = {
request: {
query: GET_PRODUCTS,
variables: { zip: "32005" }
},
result: {
data: { specials: [] }
}
}
...after tests
expect(wrapper.find(message).prop('data-id')).toEqual('invalid')
And for both of these tests the expected value is "" which is the initial value for my data-id prop in my html element. If I were to set the initial value to "invalid" compared to "" then the expected value in my test would output "invalid".
It seems that no matter what I passed my mock provider it doesn't wait for it to be passed. I'm using the wait package that Apollo recommends as well.
If you wanna mock using jest, you use the following approach
jest.mock("apollo-client", () => ({
__esModule: true,
useQuery: (query: any) => {
//other mocks if needed
},
useLazyQuery: jest.fn().mockReturnValue([
jest.fn(),
{
data: {
yourProperties: "Add Here",
},
loading: false,
},
]),
}));
As you see, this approach even returns the mock function that is called in the code to be tested.
I faced in similar issues while testing with useLazyQuery. I would suggest writing a custom hook on top of useLazyQuery. This will have two benefits:
No need to wrap your test instance in Mock provider. You can mock the entire module (custom hook) using jest and use mockReturnValue method to simulate different behaviours (loading, data, error)
You can easily switch between actual query or some mock data in local file system while development.
Refer this blog for implementation details and demo app.

Sending a payload through the nativescript background-http plugin, one of its properties being an array

Im trying to send a request to a dotnet core api using the nativescript background-http plugin and in the payload one of the properties represents an array.
Im trying to send the array like this:
let params = [
..........
...invitees.map((v,i) => { name: `invitees.${i}.email`, value: v.email }),
...invitees.map((v,i) => { name: `invitees.${i}.name`, value: v.email })
]
Also tried it like this:
let params = [
..........
...invitees.map((v) => { name: `invitees.email`, value: v.email }),
...invitees.map((v) => { name: `invitees.name`, value: v.email })
]
Neither way works when i debug the api to see how it parses the payload. The rest of the properties which is a mix of primitive types, objects and files parse fine. Any idea on what should be the format? The array is of an object with two properties named name and email.
Made it work like this:
let params = [
..........
...invitees.map((v,i) => { name: `invitees[${i}].email`, value: v.email }),
...invitees.map((v,i) => { name: `invitees[${i}].name`, value: v.email })
]

Chai assertion, unable to identify property using should / expect

Hello: Need your help on a chai assertion.
I have a JSON response as shown below. I want to assert that it contains "Lastname is mandatory" only.
I tried using this statement but the error i get is AssertionError: expected [ Array(2) ] to have a deep property '#text'. Please help how to write this correctly.
using expect
chai.expect(data.response.error).to.have.deep.property('#text', 'Lastname is mandatory.');
using should
data.response.error.should.have.deep.property('#text', 'Lastname is mandatory.');
Response JSON
{
response: {
error: [
{
'#id': '1000',
'#text': 'Firstname is mandatory.'
},
{
'#id': '10001',
'#text': 'Lastname is mandatory.'
}
],
result:
{
status: '0'
}
}
}
Prior to Chai version 4
The use of deep with property requires that you pass a complete path to the property you want to test. In other words, deep.property won't do a search through all the properties for you. As the documentation puts it:
If the deep flag is set, you can use dot- and bracket-notation for deep references into objects and arrays.
Something like:
data.response.should.have.deep.property("error[0].#text");
Or you can start the path to the property with an array index if the object on which you use should is an array:
data.response.error.should.have.deep.property("[0].#text");
Here is a complete example derived from the code you show:
const chai = require("chai");
chai.should();
const data = {
response: {
error: [
{
'#id': '1000',
'#text': 'Firstname is mandatory.'
},
{
'#id': '10001',
'#text': 'Lastname is mandatory.'
}
],
result:
{
status: '0'
}
}
};
it("works", () => {
data.response.should.have.deep.property("error[0].#text");
// Or this, which looks weird but is allowed...
data.response.error.should.have.deep.property("[0].#text");
});
Chai version 4 and later
The OP was using a release of Chai earlier than version 4. If you are using Chai version 4 and over, the flag to use to is not .deep anymore but .nested. So in earlier versions where you would use data.response.should.have.deep.property("error[0].#text"); in version 4 or later you'd use data.response.should.have.nested.property("error[0].#text");
Thanks to answer from #shvaikalesh at github. It has the relevant answer to my question which i provide here for future reference + the code extract is also below for quick reference.
chai.expect(data.response.error.some(e => e['#text'] == 'Lastname is mandatory.')).to.be.true

Rails4 + Json API : Increase detail of response

I'm a newbie on RoR (and Ruby). I need a little help about a json response (with Grape).
This is the sample:
{
events: [
{
'some data':'some data',
place_id: 1
}
]
}
Now this is the result of Events.all in Rails, but I want to make for each event a query for the place, to have more data instead only id. I'm sure that new lambda function can help me, but for now I have no idea about to make it. I'm trying without success...
Thanks in advance
UPDATE
Desired result
{
events: [
{
'some data':'some data',
place : {
id: 1,
name: 'Blablabla'
}
]
}
Consider using ActiveModelSerializers which allows you to define how your models should be serialized in a manner similar to ActiveRecord DSL (e.g. your problem would be solved by defining that event has_one :place)
:events => events.as_json(include: :place)
This is a useful for my problem. After add belongs_to, obviously.
from http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html

mutidimensional array from javascript/jquery to ruby/sinatra

how do I pass a 2-dimensional array from javascript to ruby, please? I have this on client side:
function send_data() {
var testdata = {
"1": {
"name": "client_1",
"note": "bigboy"
},
"2": {
"name": "client_2",
"note": "smallboy"
}
}
console.log(testdata);
$.ajax({
type: 'POST',
url: 'test',
dataType: 'json',
data: testdata
});
}
and this on server side:
post '/test' do p params end
but I can't get it right. The best I could get on server side is something like
{"1"=>"[object Object]", "2"=>"[object Object]"}
I tried to add JSON.stringify on client side and JSON.parse on server side, but the first resulted in
{"{\"1\":{\"name\":\"client_1\",\"note\":\"bigboy\"},\"2\":{\"name\":\"client_2\",\"note\":\"smallboy\"}}"=>nil}
while the latter has thrown a TypeError - can't convert Hash into String.
Could anyone help, or maybe post a short snippet of correct code, please? Thank you
You may want to build up the JSON manually, on the javascript side:
[[{'object':'name1'},{'object':'name2'}],[...],[...]]
This will build an array of arrays with objects.
It may look like this:
testdata = [[{
"1": {
"name": "client_1",
"note": "bigboy"
}],
[{"2": {
"name": "client_2",
"note": "smallboy"
}]
}]
I may have something off here, but this should be close to what it would look like.
I'm not sure if this will help but I've got two thoughts: serialize fields and/ or iterate the array.
I managed to get a json array into activerecord objects by setting serializing the fields which had to store sub-arrays:
class MyModel < ActiveRecord::Base
serialize :tags
end
and using an iterator to process the json array:
f = File.read("myarrayof.json")
jarray = JSON.load(f)
jarray.each { |j| MyModel.create.from_json(j.to_json).save }
The conversion back-and-forth seems a bit cumbersome but I found it the most obvious way to handle the array.

Resources