How can rspec test http calls with body and headers - ruby-on-rails

I'm using rails4 + rspec 3. I want to make HTTP calls, and pass both params (such as JSON body or query string), and also HTTP headers. I was able to pass one of these two, but not both.
when I try something like:
post api_v1_post_path(#myid), {} , {"X-Some-Header" => "MyValue"}
it works fine and the headers fine, but if I do something like:
post api_v1_post_path(#myid), {"myparam" => "myvalue"} , {"X-Some-Header" => "MyValue"}
I get the following error:
Failure/Error: post api_v1_post_path(#myid), {"myparam" =>"myvalue"}, headers
ActionDispatch::ParamsParser::ParseError:
795: unexpected token at 'myparam'
Any ideas?

It seems that the POST params are expected to be JSON encoded. 795: unexpected token at 'myparam' is caused when the app tries to JSON decode the params that are not encoded.
Use .to_json with the post params.
post api_v1_post_path(#myid), {"myparam" => "myvalue"}.to_json , {"X-Some-Header" => "MyValue"}
You may want to use let:
describe 'Test' do
let( :params ){{ myparam: 'myvalue' }}
let( :headers ){{ 'X-Some-Header' => 'MyValue' }}
it 'succeeds' do
post api_v1_post_path(#myid), params.to_json , headers

Related

Rails 5.2.3 converting all params datatype to string when testing using rspec

I am using rails 5.2.3 and testing using rspec-rails (3.8.2), when I send request to rails like this
let(:params) do
{
down_payment: 10_000,
asking_price: 100_000,
payment_schedule: 'weekly',
amortization_period: 5
}
end
it 'works' do
get :calculate, params: params, format: :json
expect(response.status).to eq 200
end
I also tried
it 'works' do
get :calculate, params: params, as: :json
expect(response.status).to eq 200
end
in rails all integers get converted to string like this
<ActionController::Parameters {"amortization_period"=>"5", "asking_price"=>"100000", "down_payment"=>"10000", "payment_schedule"=>"weekly", "format"=>"json", "controller"=>"payment_amount", "action"=>"calculate", "payment_amount"=>{}} permitted: false>
But if I use curl to send a request I can see integer not being converted to string.
curl -X GET -H "Content-Type: application/json" -d ‘{"asking_price": 100000 ,"payment_schedule": "monthly", "down_payment": 10000, "amortization_period": 5 }' http://localhost:3000/payment-amount
Thanks for any help!
JSON payloads can contain five value types: string, number, integer, boolean and null.
HTTP query strings are, by contrast, only strings.
By default, request specs use the encoding specified in the HTTP spec - i.e. all parameters are strings. This is why you see the parameters get converted.
If your production system is sending JSON, you need to tell the test to do so too - e.g. by adding as: :json as you did above.
Just add as: :json format to your requests
post(graphql_path, params: params, as: :json)

Rails RestClient POST request failing with "400 Bad Request"

Looking at the docs there aren't any good examples of how to make a POST request. I need to make a POST request with a auth_token parameter and get a response back:
response = RestClient::Request.execute(method: :post,
url: 'http://api.example.com/starthere',
payload: '{"auth_token" : "my_token"}',
headers: {"Content-Type" => "text/plain"}
)
400 bad request error:
RestClient::BadRequest: 400 Bad Request
from /Users/me/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rest-client-1.8.0/lib/restclient/abstract_response.rb:74:in `return!'
from /Users/me/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rest-client-1.8.0/lib/restclient/request.rb:495:in `process_result'
from /Users/me/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rest-client-1.8.0/lib/me/request.rb:421:in `block in transmit'
Any good examples how to make a POST request using RestClient?
EDIT:
This is how I make the request in the model:
def start
response = RestClient::Request.execute(method: :post,
url: 'http://api.example.com/starthere',
payload: '{"auth_token" : "my_token"}',
headers: {"Content-Type" => "text/plain"}
)
puts response
end
Try using a hash like this:
def start
url= 'http://api.example.com/starthere'
params = {auth_token: 'my_token'}.to_json
response = RestClient.post url, params
puts response
end
If you just want to replicate the curl request:
response = RestClient::Request.execute(method: :post, url: 'http://api.example.com/starthere', payload: {"auth_token" => "my_token"})
Both Curl and RestClient defaults to the same content type (application/x-www-form-urlencoded) when posting data the this format.
In case you land here having the same Issue, Just know that this is a common error that happens when your environment variables are not "set".
I put this in quotes because you might have set it but not available in the current terminal session!
You can check if the ENV KEY is available with:
printenv <yourenvkey>
if you get nothing then it means you need to re-add it or just put it in your bash files
FYI: Putting my ENV variables in my ~/.bash_profile fixed it

bad URI(is not URI?): {"message":"d64","type":"success"}

Am using httparty to send API request, I want to get output response after i sent request,
url = HTTParty.post("https://example.com/api/sendhttp.php",
:query => { :authkey => "authkeyvalue",
:mobiles => mobileNos,
:message => messages,
:sender => "senderid",
:route => "routeid",
:response => 'json'
})
response = HTTParty.get(url)
puts response.body, response.code, response.message, response.headers.inspect
But when i run above code it throws bad URI(is not URI?): {"message":"d64","type":"success"} error. How to solve it and get the response?
This looks like you are running a POST to https://example.com/api/sendhttp.php with JSON format, this succeeds, and you're getting a response of:
{"message":"d64","type":"success"}
So your url variable now contains {"message":"d64","type":"success"}, which is clearly not a valid URL, so when you try to do a GET on it, you get an error. You've already got a response from the first POST, you should perhaps parse this? You don't need to do
response = HTTPParty.get(url)
unless you're expecting a second GET request to a URL which is returned by the first.

Rails RestClient pass token in header fails

I do this curl in Terminal which works excellent:
$ curl https://myurl.com/api/v1/orders/53e0ae7f6630361c46060000 -H "Authorization: Token xxxxxxxxxxxxxxxxxxxxxx"
Output is json.
Now I want to access the json string via my rails app. I have tried RestClient to do this, but somehow I always get a 401 unauthorized error. I believe the token gets not send correctly via header. I have tried the following:
RestClient.get 'https://myurl.com/api/v1/orders/53e0ae7f6630361c46060000', {token: 'xxxxxxxxxxxxxxxxxxxxxx'}
and
RestClient.get 'https://myurl.com/api/v1/orders/53e0ae7f6630361c46060000', :params => {:token => 'xxxxxxxxxxxxxxxxxxxxxx'}
with no success. Maybe I use a wrong syntax for sending the token in the header?
Doku is here http://rubydoc.info/github/rest-client/rest-client - I could not find any mistakes.
# GET request with modified headers
RestClient.get 'http://example.com/resource', {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
# POST request with modified headers
RestClient.post 'http://example.com/resource', {:foo => 'bar', :baz => 'qux'}, {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
# DELETE request with modified headers
RestClient.delete 'http://example.com/resource', {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
Source: https://github.com/rest-client/rest-client#headers

Ruby: HTTParty: can't format XML POST data correctly?

NOTE: "object" is a placeholder work, as I don't think I should be saying what the controller does specifically.
so, I have multiple ways of calling my apps API, the following works in the command line:
curl -H 'Content-Type: application/xml' -d '<object><name>Test API object</name><password>password</password><description>This is a test object</description></object>' "http://acme.example.dev/objects.xml?api_key=1234"
the above command generates the following request in the devlog:
Processing ObjectsController#create to xml (for 127.0.0.1 at 2011-07-07 09:17:51) [POST]
Parameters: {"format"=>"xml", "action"=>"create", "api_key"=>"1234", "controller"=>"objects",
"object"=>{"name"=>"Test API object", "description"=>"This is a test object", "password"=>"[FILTERED]"}}
Now, I'm trying to write tests for the actions using the API, to make sure the API works, as well as the controllers.
Here is my current (broken) httparty command:
response = post("create", :api_key => SharedTest.user_api_key, :xml => data, :format => "xml")
this command generates the following request in the testlog:
Processing ObjectsController#create to xml (for 0.0.0.0 at 2011-07-07 09:37:35) [POST]
Parameters: {
"xml"=>"<object><name><![CDATA[first post]]></name>
<description><![CDATA[Things are not as they used to be]]></description>
<password><![CDATA[WHEE]]></password>
</object>",
"format"=>"xml",
"api_key"=>"the_hatter_wants_to_have_tea1",
"action"=>"create",
"controller"=>"objects
So, as you can see, the command line command actually generates the object hash from the xml, whereas the httparty command ends up staying in xml, which causes problems for the create method, as it needs a hash.
Any ideas / proper documentation?
Current documentation says that post takes an url, and "options" and then never says what options are available
**EDIT:
as per #Casper's suggestion, my method now looks like this:
def post_through_api_to_url(url, data, api_key = SharedTest.user_api_key)
response = post("create", {
:query => {
:api_key => api_key
},
:headers => {
"Content-Type" => "application/xml"
},
:body => data
})
ap #request.env["REQUEST_URI"]
assert_response :success
return response
end
unfortunately, the assert_response fails, because the authentication via the api key fails.
looking at the very of of the request_uri, the api_key isn't being set properly... it shows:
api_key%5D=the_hatter_wants_to_have_tea1"
but it should just be equals, without the %5D (right square bracket)
I think this is how you're supposed to use it:
options = {
:query => {
:api_key => 1234
},
:headers => {
"Content-Type" => "application/xml"
},
:body => "<xmlcode>goes here</xmlcode>"
}
post("/create", options)
Forgive me for being basic about it but if you only want to send one variable as a parameter, why don't you do as Casper suggests, but just do:
post("/create?api_key=1234", options)
Or rather than testing HTTParty's peculiarities in accessing your API, perhaps write your tests using Rack::Test? Very rough example...
require "rack/test"
require "nokogiri"
class ObjectsTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
MyApp.new
end
def create_an_object(o)
authorize "x", "1234" # or however you want to authenticate using query params
header 'Accept', 'text/xml'
header 'Content-Type', 'text/xml'
body o.to_xml
post "/create"
xml = Nokogiri::XML(last_response.body)
assert something_logic_about(xml)
end
end

Resources