How to simulate a POST request in Ruby with the webmock gem? - ruby

I am trying to simulate a POST request with rspec. I thought about using the webmock gem, after searching how it worked, my code looks like this:
Gist_Request_spec.rb:
require_relative '../Gist_Request.rb'
require 'spec_helper'
require 'webmock'
include WebMock::API
WebMock.enable!
RSpec.describe GistRequest do
describe "#post" do
it "crear gist" do
stub_request(:post, "https://api.github.com/gists").
with(
body: {"description" => "description","public" => true,"files" => { "file.txt" => { "content" => "content" }}},
headers: {
'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Authorization'=>'Basic Sm9lbEFsYXlvbjEyMzo0NzVmYjI1ZWU0MGQyNGQzZGM1NWQ0MzQ5YjI4MTU3MjM1ZjdmZDBk',
'Host'=>'api.github.com',
'User-Agent'=>'Ruby'}).to_return(status: 201, body: "", headers: {})
expect(WebMock).to have_requested(:post, "https://api.github.com/gists").with { |req| req.status == 201 }
end
end
end
I added code to my file spec_helper.rb and now it looks like this:
The first lines spec_helper.rb:
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
But when I run rspec spec/Gist_Request_spec.rb, it returns the following error:
GistRequest
#post
crear gist (FAILED - 1)
Failures:
1) GistRequest#post crear gist
Failure/Error: expect(WebMock).to have_requested(:post, "https://api.github.com/gists").with { |req| req.status == 201 }
The request POST https://api.github.com/gists with given block was expected to execute 1 time but it executed 0 times
The following requests were made:
No requests were made.
============================================================
# ./spec/Gist_Request_spec.rb:21:in `block (3 levels) in <top (required)>'

Related

Ruby: How to access an api using HTTParty

I'm new to Ruby, and to using HTTParty, and was trying to follow the HTTParty examples from their github page to execute a basic POST. When I run the code below I get an error:
require 'pp'
require 'HTTParty'
require 'pry'
class Partay
include HTTParty
base_uri "http://<myapidomain>/search/semanticsearch/query/"
end
options= {
query: {
version: "0.4",
query: "lawyer"
}}
response = Partay.post(options)
puts response
The error I get is:
rbenv/versions/2.2.0/lib/ruby/2.2.0/uri/common.rb:715:in `URI': bad argument (expected URI object or URI string) (ArgumentError)
from ~/.ruby/2.2.0/gems/httparty-0.13.3/lib/httparty/request.rb:47:in `path='
from ~/.ruby/2.2.0/gems/httparty-0.13.3/lib/httparty/request.rb:34:in `initialize'
from ~/.ruby/2.2.0/gems/httparty-0.13.3/lib/HTTParty.rb:539:in `new'
from ~/.ruby/2.2.0/gems/httparty-0.13.3/lib/HTTParty.rb:539:in `perform_request'
from ~/.ruby/2.2.0/gems/httparty-0.13.3/lib/HTTParty.rb:491:in `post'
from json-to-csv.rb:16:in `<main>'
What I am looking for is calling a post that receives JSON in the same way that calling this URL works:
http://somedomain.com/search/semanticsearch/query/?version=0.4&query=lawyer
Noting a solution with the suggested gem - unirest:
require 'unirest'
url = "http://somedomain.com/search/semanticsearch/query"
response = Unirest.post url,
headers:{ "Accept" => "application/json" },
parameters:{ :version => 0.4, :query => "lawyer" }

how to specify namespace in SOAP request with 'savon' gem?

I am using recent version of savon gem and trying to send a SOAP request i am getting this error about invalid url:
Invalid URL: %7Bendpoint%20address%7D (ArgumentError)
from /home/vagrant/.rvm/gems/ruby-2.1.0/gems/httpi-2.3.0/lib/httpi/request.rb:27:in `url='
from /home/vagrant/.rvm/gems/ruby-2.1.0/gems/savon-2.8.0/lib/savon/operation.rb:103:in `build_request'
from /home/vagrant/.rvm/gems/ruby-2.1.0/gems/savon-2.8.0/lib/savon/operation.rb:51:in `call'
from /home/vagrant/.rvm/gems/ruby-2.1.0/gems/savon-2.8.0/lib/savon/client.rb:36:in `call'
My code is:
require "savon"
require "excon"
Excon.defaults[:ssl_verify_peer] = false
class Payback
attr_reader :connection, :client, :operation, :message
SOAP_URL = "https://partnertest.payback.in/PBExternalServices/v1/soap?wsdl"
def initialize operation, message
#client = Savon.client(wsdl: "https://partnertest.payback.in/PBExternalServices/v1/soap?wsdl", ssl_verify_mode: :none)
#operation = operation
#message = message
end
def response
#response ||= client.call(operation, message: message)
end
end
this is how i am using it.(I know something is wrong with namespaces)
payback = Payback.new :get_account_balance,
{"typ:Authentication" => { "typ1:Principal" => { "typ1:PrincipalValue" => 9899012182,
"typ1:PrincipalClassifier" => 3 }}}
payback.response
I need to construct this XML with savon
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://www.payback.net/lmsglobal/ws/v1/extint/types" xmlns:typ1="http://www.payback.net/lmsglobal/xsd/v1/types">
<soapenv:Header/>
<soapenv:Body>
<typ:GetAccountBalanceRequest>
<typ:Authentication>
<typ1:Principal>
<typ1:PrincipalValue>9899012182</typ1:PrincipalValue>
<typ1:PrincipalClassifier>3</typ1:PrincipalClassifier>
</typ1:Principal>
</typ:Authentication>
</typ:GetAccountBalanceRequest>
</soapenv:Body>
</soapenv:Envelope>
i don't know what i am doing wrong here, please help.
I noticed that on line 5820 of the WSDL file the location looks like this:
<soap:address location="{endpoint address}"/>
Could it be the problem?
Edited 1.
Open up in your browser: https://partnertest.payback.in/PBExternalServices/v1/soap?wsdl
Search for {endpoint address} string on the page.
I'm far from understand this particular WSDL document, but I wonder if there must be something else in place of {endpoint address}, like a real URI. E.g. could it be the WSDL document that has the problem?
Edited 2
Tried to remove the typ and typ1 which were not recognized by the service. Ended up with a working code that returns a valid response:
puts Savon.client(
wsdl: 'https://partnertest.payback.in/PBExternalServices/v1/soap?wsdl',
endpoint: 'https://partnertest.payback.in/PBExternalServices/v1/soap',
ssl_verify_mode: :none
).call(
:get_account_balance,
:message => {
'Authentication' => {
'Principal' => {
'PrincipalValue' => 9899012182,
'PrincipalClassifier' => 3
}
}
}
)

Ruby stubbing with faraday, can't get it to work

Sorry for the title, I'm too frustrated to come up with anything better right now.
I have a class, Judge, which has a method #stats. This stats method is supposed to send a GET request to an api and get some data as response. I'm trying to test this and stub the stats method so that I don't perform an actual request. This is what my test looks like:
describe Judge do
describe '.stats' do
context 'when success' do
subject { Judge.stats }
it 'returns stats' do
allow(Faraday).to receive(:get).and_return('some data')
expect(subject.status).to eq 200
expect(subject).to be_success
end
end
end
end
This is the class I'm testing:
class Judge
def self.stats
Faraday.get "some-domain-dot-com/stats"
end
end
This currently gives me the error: Faraday does not implement: get
So How do you stub this with faraday? I have seen methods like:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('http://stats-api.com') { [200, {}, 'Lorem ipsum'] }
end
But I can't seem to apply it the right way. What am I missing here?
Note that Faraday.new returns an instance of Faraday::Connection, not Faraday. So you can try using
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return("some data")
Note that I don't know if returning "some data" as shown in your question is correct, because Faraday::Connection.get should return a response object, which would include the body and status code instead of a string. You might try something like this:
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(
double("response", status: 200, body: "some data")
)
Here's a rails console that shows the class you get back from Faraday.new
$ rails c
Loading development environment (Rails 4.1.5)
2.1.2 :001 > fara = Faraday.new
=> #<Faraday::Connection:0x0000010abcdd28 #parallel_manager=nil, #headers={"User-Agent"=>"Faraday v0.9.1"}, #params={}, #options=#<Faraday::RequestOptions (empty)>, #ssl=#<Faraday::SSLOptions (empty)>, #default_parallel_manager=nil, #builder=#<Faraday::RackBuilder:0x0000010abcd990 #handlers=[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]>, #url_prefix=#<URI::HTTP:0x0000010abcd378 URL:http:/>, #proxy=nil>
2.1.2 :002 > fara.class
=> Faraday::Connection
Coming to this late, but incase anyone else is too, this is what worked for me - a combination of the approaches above:
let(:json_data) { File.read Rails.root.join("..", "fixtures", "ror", "501100000267.json") }
before do
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(
double(Faraday::Response, status: 200, body: json_data, success?: true)
)
end
Faraday the class has no get method, only the instance does. Since you are using this in a class method what you can do is something like this:
class Judge
def self.stats
connection.get "some-domain-dot-com/stats"
end
def self.connection=(val)
#connection = val
end
def self.connection
#connection ||= Faraday.new(some stuff to build up connection)
end
end
Then in your test you can just set up a double:
let(:connection) { double :connection, get: nil }
before do
allow(connection).to receive(:get).with("some-domain-dot-com/stats").and_return('some data')
Judge.connection = connection
end
I ran into the same problem with Faraday::Adapter::Test::Stubs erroring with Faraday does not implement: get. It seems you need to set stubs to a Faraday adapter, like so:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get("some-domain-dot-com/stats") { |env| [200, {}, 'egg'] }
end
test = Faraday.new do |builder|
builder.adapter :test, stubs
end
allow(Faraday).to receive(:new).and_return(test)
expect(Judge.stats.body).to eq "egg"
expect(Judge.stats.status).to eq 200
A better way to do this, rather than using allow_any_instance_of, is to set the default connection for Faraday, so that Faraday.get will use the connection you setup in your tests.
For example:
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
before do
stubs.get('/maps/api/place/details/json') do |_env|
[
200,
{ 'Content-Type': 'application/json' },
{ 'result' => { 'photos' => [] } }.to_json
]
end
Faraday.default_connection = conn
end
after do
Faraday.default_connection = nil
end

Obtaining a render Argument Error: What is the correct syntax or usage?

I am new to Ruby, Sinatra and Pusher so this is a basic question. I am attempting to authenticate a Pusher Client (using iOS demo code https://github.com/lukeredpath/libPusher). The server code below fails with error when the iOS client attempts to join a presence channel:
ArgumentError - wrong number of arguments (1 for 2):
/Users/waveocean/.rvm/gems/ruby-1.9.3-p327/gems/sinatra-1.3.3/lib/sinatra/base.rb:665:in `render'
web.rb:13:in `auth'
web.rb:26:in `block in <main>'
/Users/waveocean/.rvm/gems/ruby-1.9.3-p327/gems/sinatra-1.3.3/lib/sinatra/base.rb:1265:in `call'
... snipped for brevity ...
Here is the code:
require 'sinatra'
require 'pusher'
require 'thin'
Thin::HTTP_STATUS_CODES[403] = "FORBIDDEN"
Pusher.app_id = 'MY-APP-ID'
Pusher.key = 'MY-KEY'
Pusher.secret = 'MY-SECRET'
def auth
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {:user_id => 101})
render :json => response
end
use Rack::Auth::Basic, "Protected Area" do |username, password|
username == 'foo' && password == 'bar'
end
post '/presence/auth' do
if params[:channel_name] == 'presence-demo'
auth
else
# render :text => "Forbidden", :status => '403'
response.status = 403
end
end
Can someone provide a suggestion or correct usage of render?
Here's is what I discovered. render is associated with Rails, and not strictly Ruby. To respond to the Sinatra route, use the following in the auth method:
def auth
response = Pusher[params[:channel_name]].authenticate(params[:socket_id], {:user_id => 101})
[200, {"Content-Type" => "application/json"}, response.to_json]
end
As it turns out, the Pusher iOS project demo provides a Scripts/auth_server.rb file with the required implementation. It is documented with the installation instructions here: https://github.com/lukeredpath/libPusher/wiki/Adding-libPusher-to-your-project .

Using RSpec2 to test a controller's show action

I have a fairly simple Rails 3 project where I've defined a custom route:
get 'factions/:name' => 'factions#show', :as => :factions
get 'factions' => 'factions#index'
... which when running rails s gives me the expected page (http://localhost:3000/factions/xyz is HTTP 200 with the app/views/factions/show.html.haml being displayed). However I've tried multiple different ways of expressing a spec that will work, below is my latest incarnation:
require 'spec_helper'
describe FactionsController do
render_views
describe "GET 'show'" do
before { get '/xyz' }
subject { controller }
it { should respond_with(:success) }
it { should render_template(:show) }
end
describe "GET 'index'" do
it "should be successful" do
get 'index'
response.should be_success
end
end
end
The GET 'index' spec passes without complaint but no matter what I do the GET 'show' specs cannot pass - even if they do succeed when I am browsing to them locally.
1) FactionsController GET 'show'
Failure/Error: before { get '/xyz' }
ActionController::RoutingError:
No route matches {:controller=>"factions", :action=>"/xyz"}
# ./spec/controllers/factions_controller_spec.rb:7:in `block (3 levels) in <top (required)>'
The action really should be show but my routes.rb configuration must be incorrect or something. What gives?
(Additional context: I am using bundle exec spork for speeding up my specs and I have restarted the spork server multiple times just to make sure I'm not completely insane.)
Change:
before { get '/xyz' }
To:
before { get :show, :name => 'xyz' }

Resources