Ruby on Rails - help needed in writting test cases usinng test:unit - ruby-on-rails

I am new to Ruby on Rails.
I have created an sample app where I have written test cases using rails test:unit.
Now I am not able to find a way to test my http API calls using test:unit.
I have two method in my model, one is to GET response from API and another to POST request(JSON data) to API. Here are my methods as follows
class RestApi
def self.reqSecure(apiMethod, qryString)
uri = URI.parse("https://test.my-api.com/test.json?apiKey=abc1234&userId=123456")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
hashOfResponse = ActiveSupport::JSON.decode(response.body)
return hashOfResponse
end
def self.postSecure
#host = 'localhost'
#port = '8099'
#path = "/posts"
#body ={
"bbrequest" => "BBTest",
"reqid" => "44",
"data" => {"name" => "test"}
}.to_json
request = Net::HTTP::Post.new(#path, initheader = {'Content-Type' =>'application/json'})
request.body = #body
response = Net::HTTP.new(#host, #port).start {|http| http.request(request) }
puts "Response #{response.code} #{response.message}: #{response.body}"
end
end

If you're wondering why your test is failing, it might be because the :new_password and :confirm_password values don't match.
Regarding a good book, I can recommend this one: http://pragprog.com/titles/nrtest/rails-test-prescriptions it covers testing in detail, and is a great resource to learn TDD. I actually used it to learn TDD, and it helped me a lot.
I've since decided to use RSpec and Cucumber for testing, mainly due to 2 other books (http://pragprog.com/titles/achbd/the-rspec-book and http://www.manning.com/katz/).
EDIT:
If you want to test the failing condition, the line after post should be something like
assert_nil #user
assert_template :change_password
In addition, for clarity's sake, your test name should be something like test_password_change_failure.
The last line in your original test shouldn't be there: that's not what your controller is supposed to be doing.

Related

How to run cURL commands in Rails

I'm using Ruby on Rails 5 and I need to execute the following command in my application:
curl -F 'client_id=126581840734567' -F 'client_secret=678ebe1b3b8081231aab27dff738313' -F 'grant_type=authorization_code' -F 'redirect_uri=https://uri.com/' -F 'code=AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q' https://api.instagram.com/oauth/access_token
so that it returns something like:
{"access_token": "IGQVJYS0k8V6ZACRC10WjYxQWtyMVRZAN8VXamh0RVBZAYi34RkFlOUxXZnTJsbjlEfnFJNmprQThmQ4hTckpFUmJEaXZAnQlNYa25aWURnX3hpO12NV1VMWDNMWmdIT3FicnJfZAVowM3VldlVWZAEViN1ZAidHlyU2VDMUNuMm2V", "user_id": 17231445640157812}
Is there a way to make Rails execute those types of commands? I was trying the following:
uri = URI.parse('https://api.instagram.com/oauth/access_token')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({
"client_id" => "126581840734567",
"client_secret" => "678ebe1b3b8081231aab27dff738313",
"grant_type" => "authorization_code",
"redirect_uri" => "http://nace.network/",
"code" => params[:code]
})
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(request)
end
but I get the following error:
end of file reached
in this line:
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(request)
end
You're using HTTPS, so you need to add this to your code:
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
res = http.request(request)
end
But if you don't need persistent connections, you could also use this:
res = Net::HTTP.post_form(uri,
"client_id" => "126581840734567",
"client_secret" => "678ebe1b3b8081231aab27dff738313",
"grant_type" => "authorization_code",
"redirect_uri" => "http://nace.network/",
"code" => params[:code]
)
Also, you could consider using a library like Faraday, which is a lot easier to deal with.
Edit
This is from TinMan's comment below, sound points.
Using cURL from inside Ruby or Rails is extremely valuable. There is an incredible amount of functionality inside cURL that isn't implemented in Rails or Ruby; Even Ruby's HTTP clients have a hard time replicating it, so cURL is very acceptable depending on the needs of the application. And, depending on the application, because cURL is in compiled C, it could easily outrun pure Ruby clients.
Curl is a means of issuing HTTP (or HTTPs) requests from the command line.
You don't want to use CURL in Rails. You want to issue HTTP requests from within Rails. Using curl is okay, it's one way to issue HTTP requests from with Rails.
We can refine that down further to, you want to issue HTTP requests from Ruby. Narrowing/distilling down to the most basic version of the problem is always good to do.
We knew all this already probably - still worth writing down for us all to benefit from!
Use HTTP in Ruby
We want to use a HTTP Client. There are many but, for this I'm going to use Faraday (a gem) 'cause I like it.
You've made a good start with Ruby's built in NET:HTTP but I prefer Faraday's DSL. It results in more readable and extendable code.
So, here is a class! I barely tested this so, use as a starting point. Make sure you write some unit tests for it.
# This is a Plain Old Ruby Object (PORO)
# It will work in Rails but, isn't Rails specific.
require 'faraday' # This require is needed as it's a PORO.
class InstagramOAuth
attr_reader :code
# The code parameter will likely change frequently, so we provide it
# at run time.
def initialize(code)
#code = code
end
def get_token
connection.get('/oauth/access_token') do |request|
request.params[:code] = code
end
end
private
def connection
#connection ||= Faraday.new(
url: instagram_api_url,
params: params,
ssl: { :ca_path => https_certificate_location }
)
end
def instagram_api_url
#url ||= 'https://api.instagram.com'
end
# You need to find out where these are for your self.
def https_certificate_location
'/usr/lib/ssl/certs'
end
def params
# These params likely won't change to often so we set a write time
# in the class like this.
{
client_id: '126581840734567',
client_secret: '678ebe1b3b8081231aab27dff738313',
grant_type: 'authorization_code',
redirect_uri: 'https://uri.com/'
}
end
end
# How do we use it? Like so
# Your big old authorisation code from your question
code = 'AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU'\
'7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz'\
'5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H'\
'-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q'
# This will return a Faraday::Response object but, what is in it?
response = InstagramOAuth.new(code).get_token
# Now we've got a Hash
response_hash = response.to_hash
puts 'Request made'
puts "Request full URL: #{response_hash[:url]}"
puts "HTTP status code: #{response_hash[:status]}"
puts "HTTP response body: #{response_hash[:body]}"
When I ran the snippet above I got the following. The class works, you just need to tweak the request params until you get what you want. Hopefully the class demonstrates how to send HTTP requests in Ruby/Rails.
Request made
Request full URL: https://api.instagram.com/oauth/access_token?client_id=126581840734567&client_secret=678ebe1b3b8081231aab27dff738313&code=AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q&grant_type=authorization_code&redirect_uri=https%3A%2F%2Furi.com%2F
HTTP status code: 405
HTTP response body:
Additional Reading
. https://lostisland.github.io/faraday/usage/
. https://github.com/lostisland/faraday/wiki/Setting-up-SSL-certificates

Make http POST request that return xml response and parsing XML fields

I want to make a http POST request that parse XML response and return the value of SessionId field that is inside XML. This is what I tried so far.
Ps: is there a way I can run this class from the console, in the way that I can see the response?
class Documents::CreateSession
def initialize()
#username = Rails.secrets.legal_doc.username
#password= Rails.secrets.legal_doc.password
end
def start
require "net/http"
require "uri"
uri = URI.parse("http://example.com/search")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"userid" => #username, "password" => #password})
response = http.request(request)
end
end
I think that you can run your code the way that you have it now. Start a console and do the following:
obj = Documents::CreateSession.new
obj.start
For debugging purposes, you could put a binding.pry in the start method before you make your request.

Consuming paginated resources using HTTP with Ruby on Rails

I'm building out a platform for displaying data in charts that pulls from Zendesk's API. I'm running into trouble in that only 100 records at a time can be pulled with one call. How do I pull multiple pages of records from this resource?
Here is the code I use to make the call:
require 'net/http'
require 'uri'
require 'json'
#imports User data from the zendesk api and populates the database with it.
uri = URI.parse("https://samplesupport.zendesk.com/api/v2/users.json")
request = Net::HTTP::Get.new(uri)
request.content_type = "application/json"
request.basic_auth("sampleguy#sample.com", "samplepass")
req_options = {
use_ssl: uri.scheme == "https",
}
#response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts #response.body
puts #response.message
puts #response.code
This works fine for calling down one 'page' of resources...any help with grabbing multiple pages using my script would be greatly appreciated. Thank you!
Based on ZenDesk's documentation they return a next_page attribute in their payload. So you should just check for its existence and then query again if it exists. Repeat as needed.
require 'json'
# setup to query for the first page
results = JSON.parse(#response.body)
users = results['users'] #to get the users
if results['next_page']
# Do another query to results['next_page'] URL and add to users list

Rails listener Paypal IPN always return INVALID

I've build a listener for my IPN. I can receive the request via IPN Simulator and all the parameters are correct, but when I send it back to link for sending my request to make validations it will ALWAYS return INVALID.
I've tried changing the encoding on my PayPal account to UTF-8 but it didn't work.
I'm not using any gem for this and I'm not looking forward to this.
class PaymentNotificationController [:create] #Otherwise the request from PayPal wouldn't make it to the controller
def create
response = validate_IPN_notification(request.raw_post)
case response
when "VERIFIED"
P 'worked'
when "INVALID"
p 'did not work'
else
end
render :nothing => true, :status => 200, :content_type => 'text/html'
end
protected
def validate_IPN_notification(raw)
uri = URI.parse('https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
#uri = URI.parse('https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = 60
http.read_timeout = 60
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.use_ssl = true
response = http.post(uri.request_uri, raw,
'Content-Length' => "#{raw.size}",
'User-Agent' => "My custom user agent"
).body
end
end
Please help me, I've been stuck with it all afternoon and can't get it figured out.
I think your problem is not follow exactly what papal requires
From Paypal document
Your listener HTTP POSTs the complete, unaltered message back to PayPal.
Note This message must contain the same fields, in the same order, as the original IPN from PayPal, all preceded by cmd=_notify-validate. Further, this message must use the same encoding as the original.
So you validate function would be:
def validate_IPN_notification(ipn_url)
validate_url = "#{ipn_url}?cmd=_notify-validate"
# Post validate_url
...
end
Note that the ipn_url is the same url of IPN
It turned out to be a Windows problem which I don't know how to fix.
I tried the same thing on my Ubuntu 15 and it worked perfectly.

Why Rails (current 4.0) fails to interpret nested JSON (from a HTTP POST)?

I am writing a simple client server application (using only JSON API) with Ruby (client) and Rails (server).
When trying to create a game from client, I am using:
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"tttgame" => {"name" => "Marius"}})
resp = http.request(request)
On server side (tttgames_controller.rb) I have:
# POST /tttgames
# POST /tttgames.json
def create
#tttgame = Tttgame.new(tttgame_params)
...
end
...
def tttgame_params
params.require(:tttgame).permit(:name)
end
Logs on server are:
Started POST "/tttgames.json" for 127.0.0.1 at 2013-10-05 12:58:44 +0300
Processing by TttgamesController#create as JSON
Parameters: {"tttgame"=>"{\"name\"=>\"Marius\"}"}
Completed 500 Internal Server Error in 0ms
NoMethodError (undefined method `stringify_keys' for "{\"name\"=>\"Marius\"}":String):
app/controllers/tttgames_controller.rb:33:in `create'
How can I fix this? All examples from the Internet are looking the same. Thanks!
Both methods set_form_data and post_form are encoding data using format x-www-form-urlencoded. Check here.
Examples that are provided do not contain nested hashes.
I have found here an example, under the REST methods section, which works very well.
Thus, in order to get on server a valid structure with nested hashes, the client should use square brackets:
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"tttgame[name]" => “Marius”)
resp = http.request(request)
or much simpler:
uri = URI.parse(url)
resp = Net::HTTP.post_form(uri, {"tttgame[name]" => “Marius”})
This will generate on server
Parameters: {"tttgame"=>{"name"=>"Marius"}}
You might want to do this instead. It's even more compact.
uri = URI.parse(url)
resp = Net::HTTP.post_form(uri, "tttgame" => {"name" => "Marius"})
From http://ruby-doc.org/stdlib-2.0.0/libdoc/net/http/rdoc/Net/HTTP.html#label-POST+with+Multiple+Values
UPDATE: In addition, your String is not a valid JSON. It needs to be "{\"name\":\"Marius\"}" instead.
You need to parse that response, because right now it is a String ("{\"name\"=>\"Marius\"}") but you actually need a Hash ({"name" => "Marius"}).
Therefore #stringify_keys fails because it is a method that operates on a Hash.
So do a:
#tttgame = Tttgame.new(JSON.parse(tttgame_params))
instead. This will turn your serialized JSON response into a Hash from a String.

Resources