How to use WebMock to mock Paperclip call in a Sinatra app? - ruby

This code works fine without WebMock.
Raising an exception:
Paperclip::AdapterRegistry::NoHandlerError:
No handler found for #<URI::HTTP:0x007ff3852cefb8 URL:http://www.example.com/images/foo.jpg>
# ./spec/support/api_mock.rb:34:in `process_image_for'
Test:
let( :image_url ) { 'http://www.example.com/images/foo.jpg' }
...
stub_request(:post, image_url)
.to_return(:status => 200, :body => File.read('spec/fixtures/image.jpg'), :headers => {})
...hit Sinatra app...
api_mock.rb:
def self.process_image_for suggestion, params
if params[:image]
suggestion.image = URI.parse( params[:image] ) # line 34
suggestion.save!
end
end

It works. FWIW, both File.read and File.open work:
stub_request(:post, image_url)
.to_return(
:status => 200,
:body => File.read('spec/fixtures/image.jpg'),
:headers => {}
)
Just remember to require 'webmock/rspec' at the top of the test.

it is required to use headers: {"Content-Type" => 'image/jpg'} or any valid content type expected by Paperclip
eg.
stub_request(:get, "http://img.youtube.com/vi/123123123123/0.jpg")
.to_return(
status: 200,
body: File.read('spec/fixtures/images/sample.jpg'),
headers: {"Content-Type" => 'image/jpg'}
)

Related

http client with custom params

I use this code to make network request:
request = HTTPClient.new()
request.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
request.set_auth(url, terminal['api_login'], terminal['api_password'])
response = request.post(url, request_body, {"Content-Type" => "application/xml", "cache-control" => "no-cache"}).body
But when I tried to implement rspec with stub request two params are added which are every time different on every test env:
stub_request(:post, "http://www.example.com/").
with(:headers => {'Accept'=>'*/*', 'Cache-Control'=>'no-cache', 'Content-Type'=>'application/xml', 'Date'=>'Thu, 08 Mar 2018 14:20:55 GMT', 'User-Agent'=>'HTTPClient/1.0 (2.8.3, ruby 2.2.2 (2015-04-13))'}).
to_return(:status => 200, :body => "", :headers => {})
How I can configure http client not to send Date and for User-Agent to send something simple like 'Ruby'?
Is it possible to configure these params?
There is a great gem to deal with this kind of situations, it's called Timecop https://github.com/travisjeffery/timecop. Basically, you can freeze time, or move to a specific date. In your case, I think you just need to freeze the time so you can set an arbitrary date in the stub headers.
Timecop.freeze
stub_request(:post, "http://www.example.com/").
with(:headers => {'Accept'=>'*/*', 'Cache-Control'=>'no-cache', 'Content-Type'=>'application/xml', 'Date'=> Time.now, 'User-Agent'=>'HTTPClient/1.0 (2.8.3, ruby 2.2.2 (2015-04-13))'}).
to_return(:status => 200, :body => "", :headers => {})
# the rest of your test here
end
Also, you can simplify your stub by omitting the headers and using a regex for the url, something like:
stub_request(:post, /.example.*\/).and_return(:status => 200, :body => "", :headers => {})

Can't mock website with Basic Authentication

Using Ruby, Mechanize, RSpec, and Webmock, I can not mock a website with Basic Authentication, my app keeps telling me that I got an unregistered stub.
The stubs:
stub_request(:get, "http://foo:bar#google.fr:80/").
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
Net::HTTP.start('www.google.fr') {|http|
req = Net::HTTP::Get.new('/')
req.basic_auth 'foo', 'bar'
http.request(req)
}
In the app:
url = 'http://www.google.fr'
agent = Mechanize.new
agent.add_auth(url, 'foo', 'bar')
agent.get(url)
The issue I get when running agent.get(url)
(rdb:1) agent.get(url)
*** WebMock::NetConnectNotAllowedError Exception: Real HTTP connections are disabled. Unregistered request: GET http://www.google.fr/ with headers {'Accept'=>'*/*', 'Accept-Charset'=>'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'Accept-Encoding'=>'gzip,deflate,identity', 'Accept-Language'=>'en-us,en;q=0.5', 'Connection'=>'keep-alive', 'Host'=>'www.google.fr', 'Keep-Alive'=>'300', 'User-Agent'=>'Mechanize/2.7.3 Ruby/1.9.3p194 (http://github.com/sparklemotion/mechanize/)'}
You can stub this request with the following snippet:
stub_request(:get, "http://www.google.fr/").
with(:headers => {'Accept'=>'*/*', 'Accept-Charset'=>'ISO-8859-1,utf-8;q=0.7,*;q=0.7', 'Accept-Encoding'=>'gzip,deflate,identity', 'Accept-Language'=>'en-us,en;q=0.5', 'Connection'=>'keep-alive', 'Host'=>'www.google.fr', 'Keep-Alive'=>'300', 'User-Agent'=>'Mechanize/2.7.3 Ruby/1.9.3p194 (http://github.com/sparklemotion/mechanize/)'}).
to_return(:status => 200, :body => "", :headers => {})
registered request stubs:
stub_request(:get, "http://foo:bar#www.google.fr/").
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'})
A few points:
Assigning 'http://foo:bar#google.fr/' to url does
not work neither (and that would be very ugly if it works anyways).
Last but not least, creating a stub with http://www.google.fr/' as a url would not use basic authentication, since if I do so, even if I change the credentials, I will still access to my mocked page and no errors would be rendered.
Some screenshots:
There exists an incompatibility between WebMock and net-http-persistent.
See https://github.com/bblimke/webmock#connecting-on-nethttpstart
Add WebMock.allow_net_connect!(:net_http_connect_on_start => true) to your test set up.
According to the documentation this should work:
stub_request(:get, "foo:bar#www.google.fr").
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})

httparty: how to log request?

How do I log requests being sent with with httparty?
HTTParty.post(
#application_url,
:headers => {
"Accept" => "application/json",
"Content-Type" => "application/json; charset=utf-8"
},
:body => {
"ApplicationProfileId" => application.applicationProfileId
}.to_json
)
Use debug_output at the class level:
class Foo
include HTTParty
debug_output $stdout
end
or per request
response = HTTParty.post(url, :body => body, :debug_output => $stdout)
You ca use a built-in logger:
my_logger = Rails.logger || Logger.new # use what you like
my_logger.info "The default formatter is :apache. The :curl formatter can also be used."
my_logger.info "You can tell which method to call on the logger too. It is info by default."
HTTParty.get "http://google.com", logger: my_logger, log_level: :debug, log_format: :curl
Use the class-level debug_output method to set an output stream where debugging information gets sent.

How can I use Digest and Basic authentications with HTTParty in the same class?

I have the following class performing some requests:
the first request uses a digest auth
the second request uses a basic auth
When I run the second request I have the following error:
only one authentication method, :basic_auth or :digest_auth may be used at a time
How can I invalidate the digest_auth prior to running the second request?
class Test
include HTTParty
debug_output $stdout
digest_auth 'login', 'pass'
def first_request(href)
self.class.base_uri "SERVER:PORT"
response = self.class.get(href, {:query => {}})
response
end
def second_request(href)
auth = {:username => "USERNAME", :password => "PASSWORD"}
options = { :body => xml_string, :basic_auth => auth }
response = self.class.post('', options)
response
end
end
When you use basic_auth or digest_auth, HTTParty stores the information internally in the #default_options hash. Here is the source for basic_auth:
# File 'lib/httparty.rb', line 102
def basic_auth(u, p)
default_options[:basic_auth] = {:username => u, :password => p}
end
You can get access to that hash using the default_options method:
# File 'lib/httparty.rb', line 452
def default_options #:nodoc:
#default_options
end
I'd try:
default_options.delete(:basic_auth)
or:
default_options.delete(:digest_auth)
prior to using the other authentication method.
This is untested code but looks 'bout right:
class Test
include HTTParty
debug_output $stdout
def first_request(href)
klass = self.class
klass.base_uri "SERVER:PORT"
klass.default_options.delete(:basic_auth)
klass.digest_auth 'login', 'pass'
klass.get(href, {:query => {}})
end
def second_request(href)
klass = self.class
klass.default_options.delete(:digest_auth)
klass.post(
'',
{
:body => xml_string,
:basic_auth => {
:username => "USERNAME",
:password => "PASSWORD"
}
}
)
end
end

Can I use rest-client to POST a binary file to HTTP without multipart?

I have tried to do the following, but the web-service is NOT REST and does not take multi-part. What do I do in order to POST the image?
#response = RestClient.post('http://www.postful.com/service/upload',
{:upload => {
:file => File.new("#{#postalcard.postalimage.path}",'rb')
}
},
{"Content-Type" => #postalcard.postalimage.content_type,
"Content-Length" => #postalcard.postalimage.size,
"Authorization" => 'Basic xxxxxx'
} # end headers
) #close arguments to Restclient.post
Got the answer: use I/O to stream as a string instead of using File.new....
:file => IO.read("#{#postalcard.postalimage.path}")

Resources