Rails/Rspec JSON integers being converted to strings when testing post call - ruby-on-rails

I am testing a JSON request to our API, It will respond with JSON.
It seems like all the integers within the JSON get converted to strings as we post them to the controller consider action.
Controller
def consider
binding.pry # binding no# 2 used to check the params after post from test.
if ParametersValidator.is_valid?(params)
application_handler = ApplicationHandler.new(request_interactor)
render json: application_handler.result
else
render json: ParametersValidator.failed_params(params).to_json
end
end
The ParamaterValidator validates the structure and types of data coming in.
Test
render_views
let(:json) { JSON.parse(response.body) }
..
..
it 'returns the result in the correct format for the AUTOMATIC APPROVE decision' do
automatic_approve_params = relative_json_file(relative_file('automatic_approve_params'))
expected_approve_params = {
"status" => "accepted",
"automated" => true,
"rate" => 6,
"amount" => 30000,
"term" => 10,
"pre_approved_amount" => 2500,
"comments" => ""
}
#request.headers['HTTP_X_AUTH_SIG'] = Rails.application.secrets['authorization']['token']
request.env["HTTP_ACCEPT"] = 'application/json'
binding.pry # binding no# 1 to inspect the params before post
post :consider, automatic_approve_params, format: :json
expect(json).to eq(expected_approve_params)
end
Binding no#1
{
"student_id"=>1,
"age"=>22,
"name"=>"John",
"age_range"=>"22-25",
"criminal_record"=>false,
"declared_bankrupt"=>false,
"declared_insolvent"=>false,
"declared_sequestrated"=>false,
"defaulted_on_loan"=>false,
"post_study_salary"=>100000000,
"first_nationality"=>"PL",
"second_nationality"=>"",
"citizenship"=>"PL",
}
Binding no#2
{
"student_id"=>"1",
"age"=>"22",
"name"=>"John",
"age_range"=>"22-25",
"criminal_record"=>false,
"declared_bankrupt"=>false,
"declared_insolvent"=>false,
"declared_sequestrated"=>false,
"defaulted_on_loan"=>false,
"post_study_salary"=>"100000000",
"first_nationality"=>"PL",
"second_nationality"=>"",
"citizenship"=>"PL",
}
The test log is showing that the request is
Processing by Api::V1::CreditApplicationsController#consider as JSON
Inspecting the request just before the post action you will see the params are fine, then in the controller before I run anything I inspect the params and they are all strings.
Using postman to test the API with the JSON works as expected but it seems that rspec when posting to the consider action will convert all the params to strings. I have read a few dozen posts that claim by adding format: :json to the post action it will remedy this, however I have had no such luck.
I am obviously doing something wrong but I have tried pretty much everything I know.

After replicating the issue you are having I managed to resolve it in a controller spec using the following:
post :consider, automatic_approve_params.merge(format: :json)
In my local tests I removed the
request.env["HTTP_ACCEPT"] = 'application/json' and it still worked as you expect it to. Hope it helps.

In Rails 5, use as: :json instead of format: :json, e.g. post :consider, params: automatic_approve_params, as: :json

We can try this
post 'orders.json', JSON.dump(order: {boolean: true, integer: 123}), "CONTENT_TYPE" => "application/json"

Related

Rails 5 API integration tests don't work with jsonapi-resources

I can't test POST requests against my jsonapi-resources Rails 5.1 API. Rails does not seem to allow me to customize request content types, or is doing it wrong.
jsonapi-resources version 0.9.0, edge Rails (I think it's 5.2 beta2)
So, this IntegrationTest code:
require 'test_helper'
class EventsControllerTest < ActionDispatch::IntegrationTest
setup do
#event = events(:one)
end
test 'should get index' do
get events_url, as: 'application/vnd.api+json'
assert_response :success
end
test 'should create event' do
assert_difference('Event.count') do
post events_url, params: {
data: {
type: 'events',
attributes: {
name: #event.name,
body: #event.body
}
}
},
as: :api_json
end
assert_response 201
end
end
...produces this error:
$ bin/rails test
...
Failure:
EventsControllerTest#test_should_create_event [/Users/aljabear/Projects/visualist/test/controllers/events_controller_test.rb:26]:
...
The GET request works fine. The POST request is borked. Printing out the #request.body after the POST request gives this clue:
{
"errors":[
{"title":"Bad Request",
"detail":"765: unexpected token at 'data[type]=events\u0026data[attributes][name]=Book+1\u0026data[attributes][body]=This+is+body+text.'",
"code":"400",
"status":"400"
}
]
}
So, clearly the :api_json content type is not being respected by Rails;
I guess it's instead spitting out form URL encoded.
if I do this instead, and print the result:
...
as: :json,
headers: {
'Content-type': 'application/vnd.api+json',
}
...
I get the following, showing that jsonapi-resources is behaving properly (just very strictly); when I use as: :json, Rails correctly formats things as json, just not when I do :api_json.
{
"errors":[{
"title":"Not acceptable",
"detail":"All requests must use the 'application/vnd.api+json' Accept without media type parameters. This request specified 'application/json'.",
"code":"406",
"status":"406"
}]
}
Is Rails just not bothering to convert the MIME type as requested? Or is this just a serialization issue? How can I force it to do it? Thanks... any clues are welcome.
I'm successfully using code like the following:
test 'should create event' do
assert_difference('Event.count') do
post events_url, params: {
data: {
type: 'events',
attributes: {
name: #event.name,
body: #event.body
}
}
},
as: :json,
headers: {
'Accept' => JSONAPI::MEDIA_TYPE,
'Content-Type' => JSONAPI::MEDIA_TYPE
}
end
assert_response :created
end
I have a project on GitHub that you can look at to see my code.
Is Rails just not bothering to convert the MIME type as requested? Or is this just a serialization issue? How can I force it to do it?
tl;dr: Yes, yes and as_json :)
tests are a subset of rails functionality - they don't go through the full rails stack (and thus does no conversions based on MIME type).
Yes, I think so - ie you need to serialize it so that rails can unserialize it.
Try running `as_json` on your data... eg
test 'should create event' do
assert_difference('Event.count') do
post events_url, params: {
data: {
type: 'events',
attributes: {
name: #event.name,
body: #event.body
}
}.as_json
},
as: :api_json
end
assert_response 201
end
or similar on your actual data... ie... if you're telling Rails to expect json... then pass it json? :D

wrap_parameters adds parameters in application => {...} hash

When making POST/PATCH requests to my rails webapp, I send the data as json. For example this json data {"name" => "Hallo", "status" => "admin"} to an controller named UsersController. The setting for wrap_parameters in the wrap_parameters.rb file are the default values:
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
end
So every json data gets wrapped like this "user" => {"name"=> "Hallo", "status" => "admin"}. This is how it should be. However when I call a custom method called status which is located in ApplicationController to check the server status, all following requests json data won't get wrapped in their controllers/models name hash, but in a hash like this "application" => {"name"=> "Hallo", "status" => "admin"}.
This is for all following requests until I restart the server. Moreover if I want to keep the "right" behavior I can't call the status method in ApplicationController again which would lead to the "application" => {...}wrapping.
What I found out is when I set Content-Type = text/html (It was Content-Type = application/json before) for my request to the status method the following request are not polluted, meaning I get the right wrapping behavior.
However this is very disturbing, as someone just needs to call the status method, when Content-Type = application/json is set for the request and all the following POST/PATCH requests won't work anylonger.
I don't know if this matters, but I set the following in routes.rb:
namespace :api, defaults: {format: :json} do
namespace :v1 do
...
match "/status", to: "application#status", via: "get"
...
end
end
There's one tricky thing in the wrap_parameters method. You can specify the format as you did but ParamsWrapper is getting the format from request.content_mime_typeinstead of request.format so if you're trying to do POST/PUTrequest, ParamsWrapper format is multipart_form and not json.
So instead of:
wrap_parameters format: [:json]
you have to do:
wrap_parameters format: [:json, :multipart_form]
Source: https://github.com/rails/rails/blob/dda31d59a03adb7e5aa372e72bb66a3886632f2f/actionpack/lib/action_controller/metal/params_wrapper.rb#L281

Webservice POST not displaying requested parameters using rest client in rails 2.3.8

I have headers set as Accept application/json and content-type application/json for my POST webservice. Follwing are the requested parameters:
{
"OccupantID": 162921,
"BuildingID": 13294,
"QuizID": 397,
"Score": 3,
"Result": "fail"
}
routes.rb
map.connect '', :controller => 'quiz_v2', :action => 'record_quiz_result', :conditions => { :method => :post }
When I try to see params in my controller's action
def record_quiz_result
p params
end
It returned just controller_name and action_name
I dont know what is happening here, May be I missed something.
Any Help
Goto Headers => Custom Header
1.Content-Type as name and application/x-www-form-urlencoded as value and save it.
2.Now enter ur params in body which should be available and accessible through the params hash in rails
Hope it helps

How to send a http post request to a rails app to get json response

For GET request its:-
response = Typhoeus::Request.get("http://localhost:3000/users/1.json?oauth_token=12")
This returns Json response perfectly.
for Post request:-
response = Typhoeus::Request.post("http://localhost:3000/users/1.json?oauth_token=12",:params => {'[user][city]' => params[:location]})
is not working...
This is returning routing error.
Update:--
FOr login this api post call is working..
response = Typhoeus::Request.post(API_SERVER_ADDRESS + "user_sessions.json" + API_OAUTH_TOKEN, :params => {'[user_session][email]' => params[:email], '[user_session][password]' =>params[:password]})
In routes its
resources :users
and also web http request is working perfectly fine..
UPDATE
For example http request from rails log is:--
Parameters: {"commit"=>"Update", "authenticity_token"=>"8nvzCd0GF9IxjMcTfHOMJTPnycVPNIENMoMff8w4qAI=", "utf8"=>"✓", "id"=>"1", "user"=>{ "city"=>"abc"}}
Now i want to sent same kind of request..
The :params parameter should be a hash of your parms, meaning key-value pairs, so maybe something like this:
response = Typhoeus::Request.post("http://localhost:3000/users/1.json?oauth_token=12",:params => {:user => 'u', :city => 'c', :location => 'l'})
...or somesuch - whatever the parms are, whatever the values are. Your original doesn't translate into a meaningful hash for what you are wanting to do, I think.
Also, check your routing to make sure that what you are trying to do is properly routed.
Here is the solution
From this
response = Typhoeus::Request.put(API_SERVER_ADDRESS + "users/" +user_id + ".json" ,:params => {:oauth_token=>'12', :user=>{:city => params[:location]}})
Make sure you have declared a separate POST route in your routes.rb file. Even if the URLs are the same, different HTTP methods require different routes.
Using resources :users gives you the following by default:
GET /users/new # new
POST /users # create
GET /users/:id # show
GET /users/:id/edit # edit
PUT /users/:id # update
DELETE /users/:id # destroy

How to post JSON data in rails 3 functional test

I plan to use JSON data in both request and response in my project and having some problems in testing.
After searching for a while, I find the following code which uses curl to post JSON data:
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d '{ "foo" : "bar" }' localhost:3000/api/new
In the controller I can access the JSON data simply using params[:foo] which is really easy. But for functional testing, I only find post and xhr (alias for xml_http_request).
How can I write functional test in rails to achieve the same effect as using curl? Or should I do test in other ways?
Here's what I've tried. I find the implementation for xhr in action_controller/test_case.rb, and tried to add jhr method simply changing 'Conetent-Type' and 'HTTP_ACCEPT'. (Added in test/test_helpers.rb.)
def json_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
#request.env['Content-Type'] = 'Application/json'
#request.env['HTTP_ACCEPT'] ||= [Mime::JSON, Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
__send__(request_method, action, parameters, session, flash).tap do
#request.env.delete 'Content-Type'
#request.env.delete 'HTTP_ACCEPT'
end
end
alias jhr :json_http_request
I used this in the same way as xhr, but it does not work. I inspected the #response object and sees the body is " ".
I also find one similar question on Stack Overflow but it's for rails 2 and the answer for posting raw data does not work in rails 3.
As of Rails 5, the way to do this is:
post new_widget_url, as: :json, params: { foo: "bar" }
This will also set the Content-type header correctly (to application/json).
I found that this does exactly what I want – post JSON to a controller's action.
post :create, {:format => 'json', :user => { :email => "test#test.com", :password => "foobar"}}
Just specify appropriate content type:
post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json'
Json data should go as a string, not as a Hash.
Looking at stack trace running a test you can acquire more control on request preparation:
ActionDispatch::Integration::RequestHelpers.post => ActionDispatch::Integration::Session.process =>
Rack::Test::Session.env_for
Specifying :format does not work because request go as 'application/x-www-form-urlencoded' and json isn't parsed properly processing a request body.
Assuming you have a controller named api, a method named new, and you're in the test for the api controller:
#request.env["RAW_POST_DATA"] = '{ "foo" : "bar" }'
post :new
did the trick for me.
Here is a snippet that let me post json data to test my own app. rails 3
port = Rails.env.production? ? 80 : 3000
uri = URI.parse( Rails.application.routes.url_helpers.books_url(:host => request.host, :port => port, :format => :json) )
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.content_type = 'application/json'
request.body = #json_data
response = http.request( request )
#result = response.body
Hope this helps others
As #taro suggests in a comment above, the syntax that works for me in functional and integration tests is:
post :create, {param1: 'value1', param2: 'value2', format: 'json'}
(The curly braces aren't always necessary, but sometimes it doesn't work if they're missing, so I always add them.)
Here's what params and request.format look like for a post of that sort:
params:
{"param1"=>"value1", "param2"=>"value2", "format"=>"json", "controller"=>"things", "action"=>"create"}
request.format:
application/json
The best answer I can come up with to this is you don't
Whether or not it was intentional it s maybe good that rails doesn't implement this for you.
In functional tests you really want to just test your controller and not rails method of deserialization or even that routing and mime detection are all setup correctly, those all fall under an IntegrationTest.
So for your controllers, don't pass JSON just pass your params hash like you normally would. Maybe adding :format as an argument as well if you need to check that and respond differently.
If you want to test the full stack move to an IntegrationTest

Resources