rails api: send curl in controller test - ruby-on-rails

I'm building a Rails API, and I'm writing a test that sending a curl request works. This seems to be a testing issue, because an actual curl request works:
$ curl -X POST -d temperature=68 localhost:3000/temperature_readings.json
# => {"status":200}
Here's the controller method:
def create
TemperatureReading.create(temperature: params[:temperature])
render json: { status: 200 }
end
Here's the test:
context 'works via a curl request' do
it 'works' do
system "curl -X POST -d 'temperature=68' #{temperature_readings_url(format: 'json')}"
expect(TemperatureReading.last.temperature).to eq(68)
end
end
I'm getting an error from curl that test.host does not resolve:
curl: (6) Could not resolve host: test.host
That makes sense, because when I call temperature_readings_url(format: 'json') from within pry in that method, I get http://test.host/temperatures.json.
Is there a better way to test that my controller can successfully handle a curl request and create the correct record with the correct attributes?
What I've Tried
I made sure that protect_from_forgery with: :null_session is set in ApplicationController so that it doesn't fail with an exception when it can't find the token
I tried adding -H 'Content-Type:application/json' to the curl request to make sure it's hitting the json format in the controller. But it really seems to boil down to the fact that the working URL in the testing environment is test.host.
So I explicitly set request.host to localhost:3000 in the test, which felt a little hacky. But then of course it just posted to my dev database and not my test database, so the test still failed.

Your app doesn't know its host outside the request context, so you have to provide it anyway. This is also hacky:
temperature_readings_url(format: 'json', host: 'localhost:3000').
But you can define it globally in config/enviroments/test.rb:
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
so in test enviroment all url helpers will return localhost:3000 as host by default.

Related

Rails 'unsafe redirect' from strange curl request

I am getting occasional errors of this type in production and staging:
Unsafe redirect to "https://${ip}:${port}/businesses/new", pass allow_other_host: true to redirect anyway.
It is being caused by Curl requests from a random IP:
User-Agent: "curl/7.64.1"
Accept: "*/*"
Host: "${ip}:${port}"
Version: "HTTP/1.1"
I do not get it with any other URLs processed within the app.
When I try the same curl request in development I get this (which is correct):
Rails - [ActionDispatch::HostAuthorization::DefaultResponseApp] Blocked host: "${ip}:${port}"
I cannot find where the difference is that makes this throw an exception in production.
Any insights on this issue would be appreciated.

Standalone MockServer: Where do I implement expectations?

I am trying to mock an external (REST) server used by my system under test.
I am choosing MockServer (http://www.mock-server.com/) for mocking the external REST server.
I am running mock server standalone as in:
$ java -jar ./mockserver-netty-5.3.0-jar-with-dependencies.jar
-serverPort 1080 -proxyPort 1090 -proxyRemotePort 80 -proxyRemoteHost www.mock-server.com 2018-05-23 14:05:57,703 INFO o.m.m.MockServer
MockServer started on port: 1080 2018-05-23 14:05:57,747 INFO
o.m.p.d.DirectProxy MockServer started on port: 1090
I am not sure, having read the documentation, where I should define the expectations (viz., the responses the mock should yield based on incoming requests).
Can anyone explain?
Thanx,
R
It can be done by PUT, ie:
curl -v -X PUT "http://localhost:1080/expectation" -d '{
"httpRequest" : {
"path" : "/some/path"
},
"httpResponse" : {
"body" : "some_response_body"
}
}'
More info https://www.mock-server.com/mock_server/creating_expectations.html and go for REST API type of expectation
I used Postman to create the expectation. For creating expectations send a PUT request to http://localhost:portnumber/mockserver/expectation.
You can check the expectation and logs using this URL http://localhost:portnumber/mockserver/dashboard in the browser.

Can't get header request Rails 5 API on production stage (AWS elasticbeanstalk)

I have rails 5 API. Tested on my local it running perfect. But after deploy to elastic beanstalk I have problem, I can't getting Authentication token which I put it on header, and this is how I set token when call API and get/ read on my app :
Set when call api :
curl -H "Content-Type: application/json" -X POST http://example.com/users -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGc"
And Read/ get :
auth_header = request.headers['Authorization'] and token = auth_header.split(' ').last
Any ideas what's going on?
I resolved by using dash("-") instead of underscore("_") in the
header.
example:
authentication-token
instead of
authentication_token
It will work 100%

Server not available on localhost with response code 403 & RuntimeError in Ruby on Rails

After changing proxy settings in open_uri.rb and server_manage.rb I finally managed to install neo4j behind a proxy server. The neo4j server is running on port 7000 ( It opens in the browser) but when i enter :
$rails generate scaffold post title body
Error:
/.rvm/gems/ruby-2.2.3/gems/neo4j-core-5.1.6/lib/neo4j-server/cypher_session.rb:51:in `open': Server not available on http://localhost:7000 (response code 403) (RuntimeError)
What should I do ?
Any help is appreciated!!
$ ruby --version
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
$ rails --version
Rails 4.2.2
My guess - proxy issues. Things may behave differently in your browser and code (because those are 2 different environment).
To check what exactly is going on with your database, you should try to make request to Neo4j manually, from command line.
Example with using curl:
# if auth enabled
curl -i --user username:password http://localhost:7000/db/data/
# if auth disabled
curl -i http://localhost:7000/db/data/
This will give you more details on what exactly is not working.
Also you can assemble basic ruby script that will make HTTP request, to check what you receive in response in this case.
A 403 might mean that your Neo4j authentication credentials are wrong. See http://neo4jrb.readthedocs.org/en/5.1.x/Setup.html#rails-configuration for details but basically, adding something like this to application.rb might do the trick:
config.neo4j.session_options = { basic_auth: { username: 'foo', password: 'bar'} }
Also, since you mentioned needing help with the proxy, you can add an initialize key to set that.
init = { proxy: { uri: 'http://myproxy', user: 'username', password: 'password' }}
auth = { username: 'neo4j', password: 'pwhere'}
config.neo4j.session_options = { basic_auth: auth, initialize: init }

Trying to make curl requests in ruby

is there a ruby curl library that will allow me to duplicate this request:
curl -d '<hello xmlns="http://checkout.google.com/schema/2"/>' https://S_MERCHANT_ID:S_MERCHANT_KEY#sandbox.google.com/checkout/api/checkout/v2/request/Merchant/S_MERCHANT_ID
i have tried curb, but their PostField.content class is not cooperating with google's checkout api. here is the code from my curb request:
c = Curl::Easy.new("https://MY_ID:MY_KEY#sandbox.google.com/checkout/api/checkout/v2/request/Merchant/MY_ID_AGAIN")
c.http_auth_types = :basic
c.username = 'MY_ID'
c.password = 'MY_KEY'
# c.headers["data"] = '<?xml version="1.0" encoding="UTF-8"?><hello xmlns="http://checkout.google.com/schema/2"/>'
c.http_post(Curl::PostField.content('', '<?xml version="1.0" encoding="UTF-8"?><hello xmlns="http://checkout.google.com/schema/2"/>'))
c.perform
i HAVE managed to get it working using ruby's system command, but im not sure how to handle the response from it.
req = system("curl -d '<hello xmlns=\"http://checkout.google.com/schema/2\"/>' https://MY_ID:MY_KEY#sandbox.google.com/checkout/api/checkout/v2/request/Merchant/MY_ID")
I have been at it for 2 hours now. any help would be greatly appreciated, thanks!
You can use IO.popen to read from the child process:
IO.popen(['curl', '-o', '-', '-d', ..., err: [:child, :out]]) do |io|
response = io.read
end
This example combines standard out and standard error into one stream in the child process, and it forces curl to redirect output to standard out via -o. You would specify your other options in place of the ....
I always use Rest Client gem for such use cases, it is very simple in use and have all REST requests out-of-box with whole batch of tuning parameters.
Your code will look like something similar to this:
url = "sandbox.google.com/checkout/api/checkout/v2/request/Merchant/#{S_MERCHANT_ID}"
credentials = "#{S_MERCHANT_ID}:#{S_MERCHANT_KEY}"
RestClient.post "https://credentials##{url}", '<hello xmlns="http://checkout.google.com/schema/2"/>'
Alternatively, you can use a HTTP request library such as Typheous (https://github.com/typhoeus/typhoeus). Is there anything that binds you with "curl"?
I would have curl put the result in a file, and then open the file using ruby and read it ( File.open)
Or us httparty
I figured it out (YAAAAY!)
if anyone else is having this problem, here is the solution.
executable commands work fine in the command line, but if you are trying to render the output of an executable command from a controller in rails, make sure you use render :json instead of render :text to print the results.
for some reason the render :text was only outputting bits and pieces of my command's output (and driving me insane in the process).
For those of you trying to integrate with google checkout in rails, here is how you make http requests to google:
First step: add rest-client to your Gemfile. here is how to do it from the command line:
$ cd /path/to/your/rails/app
$ sudo nano Gemfile
Next, add the gem to your gemfile by placing the following somewhere in your Gemfile
$ gem "rest-client"
next, run bundle install
$ bundle install
restart your server. if apache2:
$ sudo service apache2 reload
if webrick:
$ rails s
then, in your controller (assuming you have rails set up and are able to access a controller from the browser) write the following code:
$ url = "https://YOUR_GOOGLE_CHECKOUT_MERCHANT_ID:YOUR_GOOGLE_CHECKOUT_KEY#sandbox.google.com/checkout/api/checkout/v2/request/Merchant/YOUR_GOOGLE_CHECKOUT_MERCHANT_ID"
$ req = RestClient.post(url, '<hello xmlns="http://checkout.google.com/schema/2"/>')
render :json => req
Please don't forget to replace YOUR_GOOGLE_MERCHANT_ID with your actual merchant id and YOUR_GOOGLE_CHECKOUT_KEY with your actual google checkout key
<?xml version="1.0" encoding="UTF-8"?>
<bye xmlns="http://checkout.google.com/schema/2" serial-number="1dfc3b90-1fa6-47ea-a585-4d5482b6c785" />
(answer courtesy of nexo)

Resources