Can't stub request to Flickr API - ruby-on-rails

I can't stub request to Flickr API for my
controller test. I use gem 'flickraw' for getting data from Flickr API.
flickr_search_controller.rb:
module Dashboard
class FlickrSearchController < Dashboard::BaseController
respond_to :js
def search
#search_tag = params[:search]
photos_list = if #search_tag.blank?
flickr.photos.getRecent(per_page: 10)
else
flickr.photos.search(text: #search_tag, per_page: 10)
end
#photos = photos_list.map { |photo| FlickRaw.url_q(photo) }
end
end
end
flickr_search_controller_spec.rb:
require 'rails_helper'
describe Dashboard::FlickrSearchController do
let(:user) { FactoryGirl.create(:user) }
before(:each) do
stub_request(:post, "https://api.flickr.com/services/rest").to_return(status: 200)
#controller.send(:auto_login, user)
end
describe 'when user didn\'t set search tag' do
it 'returns recend photo'do
get :search, search: ' '
expect(response.status).to eq(200)
end
end
end
I get in console next error:
Failures:
1) Dashboard::FlickrSearchController when user didn't set search tag returns recend photo
Failure/Error: flickr.photos.getRecent(per_page: 10)
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: POST https://api.flickr.com/services/rest/ with body 'method=flickr.reflection.getMethods&format=json&nojsoncallback=1' with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'OAuth realm="https://api.flickr.com/services/rest/", oauth_consumer_key="32904448e7d40c7e833c7b381c86cd31", oauth_nonce="lCL%2FUM9o8go5XNVy4F7p%2FNxHJrY%2BvFNLhlzueFq8Juc%3D", oauth_signature="1b77fc6af54b2b51%26", oauth_signature_method="PLAINTEXT", oauth_timestamp="1455128674", oauth_token="", oauth_version="1.0"', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'FlickRaw/0.9.8'}
You can stub this request with the following snippet:
stub_request(:post, "https://api.flickr.com/services/rest/").
with(:body => {"format"=>"json", "method"=>"flickr.reflection.getMethods", "nojsoncallback"=>"1"},
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'OAuth realm="https://api.flickr.com/services/rest/", oauth_consumer_key="32904448e7d40c7e833c7b381c86cd31", oauth_nonce="lCL%2FUM9o8go5XNVy4F7p%2FNxHJrY%2BvFNLhlzueFq8Juc%3D", oauth_signature="1b77fc6af54b2b51%26", oauth_signature_method="PLAINTEXT", oauth_timestamp="1455128674", oauth_token="", oauth_version="1.0"', 'Content-Type'=>'application/x-www-form-urlencoded', 'User-Agent'=>'FlickRaw/0.9.8'}).
to_return(:status => 200, :body => "", :headers => {})
============================================================
Does somebody have idea how can I stub this request?

Note the slash in uri, the request you're stubbing is not the one being made

Related

Passing bearer token in RSPEC (no implicit conversion of nil into String)

require 'rails_helper'
RSpec.describe "User management", :type => :request do
describe "Example::V2::Users" do
describe "GET /api/v2/users/" do
it 'returns status 200, authorized' do
#token = "Bearer 123"
#url = "https://api.example-v2.com/v2/users/me"
#headers = { "AUTHORIZATION" => #token}
get #url, as: :json, headers: {:Authorization => #token}
expect(response.status).to eq 200
end
end
end
end
I am trying to pass the #token but I am getting this error
Failure/Error: get #url, as: :json, headers: {:Authorization => #token}
TypeError: no implicit conversion of nil into String
I can make a get request without the params and headers and it works but as soon as I add params or headers I get the error, I even tried writing it like so
1 - get #url, {}, { Authorization: #token}
ArgumentError: wrong number of arguments (given 2, expected 1)
2 - get #url, params: {}, headers: { Authorization: #token}
TypeError: no implicit conversion of nil into String
Some smart people please point me in the right direction =).
Gems:
gem 'rspec-rails', '~> 3.8'
gem 'rails', '~> 6.0.2.2'
get #url, as: :json, headers: { Authorization: #token }
this works in my environment when type: reuqst.
I suggest you can use byebug just before the GET request
and check the #token and the #url.
If everything looks good.
Check the get if it can make request to root url?
Check the rails_helper if it requires any suspicious file?
Please check this its working for me.
require 'rails_helper'
RSpec.describe "User management", :type => :request do
describe "PeopleGoal::V2::Users" do
describe "GET /api/v2/users/" do
it 'returns status 200, authorized' do
request.headers["AUTHORIZATION"] = "Basic #{user.id}"
#url = "https://api.peoplegoal-v2.com/v2/users/me"
get #url, format: :json
expect(response.status).to eq 200
end
end
end
end

How to do request spec for JWT authenticate app using RSpec

I have a Rails 5 API only app and using knock to do JWT authenticate.
After complete the model and model spec, I start to do the request spec.
But I have no idea how to complete the authentication inside the request spec in the right way,
My users controller,
module V1
class UsersController < ApplicationController
before_action :authenticate_user, except: [:create]
end
end
Application controller,
class ApplicationController < ActionController::API
include Knock::Authenticable
include ActionController::Serialization
end
My stupidest solution (call the get token request to get the JWT before the rest request),
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
post '/user_token', params: {"auth": {"email": user.email, "password": user.password }}
body = response.body
puts body # {"jwt":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0ODgxMDgxMDYsInN1YiI6MX0.GDBHPzbivclJfwSTswXhDkV0TCFCybJFDrjBnLIfN3Q"}
# use the retrieved JWT for future requests
end
end
Any advice is appreciated.
def authenticated_header(user)
token = Knock::AuthToken.new(payload: { sub: user.id }).token
{ 'Authorization': "Bearer #{token}" }
end
describe 'GET /users?me=true' do
URL = '/v1/users?me=true'
AUTH_URL = '/user_token'
context 'when the request with NO authentication header' do
it 'should return unauth for retrieve current user info before login' do
get URL
expect(response).to have_http_status(:unauthorized)
end
end
context 'when the request contains an authentication header' do
it 'should return the user info' do
user = create(:user)
get URL, headers: authenticated_header(user)
puts response.body
end
end
end
With the help of Lorem's answer, I was able to implement something similar for my request spec. Sharing it here for others to see an alternate implementation.
# spec/requests/locations_spec.rb
require 'rails_helper'
RSpec.describe 'Locations API' do
let!(:user) { create(:user) }
let!(:locations) { create_list(:location, 10, user_id: user.id) }
describe 'GET /locations' do
it 'reponds with invalid request without JWT' do
get '/locations'
expect(response).to have_http_status(401)
expect(response.body).to match(/Invalid Request/)
end
it 'responds with JSON with JWT' do
jwt = confirm_and_login_user(user)
get '/locations', headers: { "Authorization" => "Bearer #{jwt}" }
expect(response).to have_http_status(200)
expect(json.size).to eq(10)
end
end
end
confirm_and_login_user(user) is defined in a request_spec_helper which is included as a module in rails_helper.rb:
# spec/support/request_spec_helper.rb
module RequestSpecHelper
def json
JSON.parse(response.body)
end
def confirm_and_login_user(user)
get '/users/confirm', params: {token: user.confirmation_token}
post '/users/login', params: {email: user.email, password: 'password'}
return json['auth_token']
end
end
I'm using the jwt gem for generating my tokens as described in this SitePoint tutorial (https://www.sitepoint.com/introduction-to-using-jwt-in-rails/)
Lorem's answer mostly worked for me. I got unrecognized keyword setting headers: on the get. I modified the authenticated_header method and put it in support/api_helper.rb so I could reuse it. The modification is to merge the auth token into request.headers.
# spec/support/api_helper.rb
module ApiHelper
def authenticated_header(request, user)
token = Knock::AuthToken.new(payload: { sub: user.id }).token
request.headers.merge!('Authorization': "Bearer #{token}")
end
end
In each spec file testing the api, I include api_helper.rb. And I call authenticated_header just before the get statement when testing the case of valid authentication...
# spec/controllers/api/v2/search_controller_spec.rb
RSpec.describe API::V2::SearchController, type: :controller do
include ApiHelper
...
describe '#search_by_id' do
context 'with an unauthenticated user' do
it 'returns unauthorized' do
get :search_by_id, params: { "id" : "123" }
expect(response).to be_unauthorized
end
end
context 'with an authenticated user' do
let(:user) { create(:user) }
it 'renders json listing resource with id' do
expected_result = { id: 123, title: 'Resource 123' }
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }
expect(response).to be_successful
expect(JSON.parse(response.body)).to eq expected_result
end
end
The key lines in this second test are...
authenticated_header(request, user)
get :search_by_id, params: { "id" : "123" }

Rspec Test for JSON post Rails

Controller: payments_controller.rb
class PaymentsController < ApplicationController
# This is needed to have Postman work
skip_before_action :verify_authenticity_token
rescue_from ActiveRecord::RecordNotFound do |exception|
render json: 'not_found', status: :not_found
def create
new_payment = Payment.new(new_params)
current_loan = Loan.find(new_params[:loan_id])
if Payment.valid?(new_payment, current_loan)
Payment.received(new_payment, current_loan)
current_loan.save
new_payment.save
redirect_to '/loans'
else
raise 'Amount entered is above the remaining balance'
end
end
end
This method works when I test it in Postman. However, I can't seem to write a test for it that passes. I currently have:
payments_controller_spec.rb
require 'rails_helper'
RSpec.describe PaymentsController, type: :controller do
describe "#create", :type => :request do
let!(:loan) {Loan.create!(id: 1, funded_amount: 500.0)}
params = '{"payment":{"amount":400, "loan_id":2}}'
it 'creates and saves a payment while saving the associated fund_amount of the loan' do
post "/payments", params.to_json, {'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'}
expect(loan.funded_amount).to eql(600.0)
end
end
end
The error is:
Failure/Error: post "/payments", params.to_json, {'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'}
ActionController::ParameterMissing:
param is missing or the value is empty: payment
Valid parameters (that work with Postman) are:
{"payment":{"amount":400,"loan_id":2}}
Any help would be appreciated!
*** UPDATE ****
After messing around with this for a while, I finally got it to work with this:
describe "#create", :type => :request do
let!(:loan) {Loan.create!(id: 1, funded_amount: 500.0)}
it 'creates and saves a payment while saving the associated fund_amount of the loan' do
json = { :format => 'json', :payment => { :amount => 200.0, :loan_id => 1 } }
post '/payments', json
loan.reload
expect(loan.funded_amount).to eql(300.0)
end
end
You can pass in your params like this.
it 'creates and saves a payment while saving the associated fund_amount of the loan' do
post "/payments", payment: { amount: 400, loan_id: 1 }, {'CONTENT_TYPE' => 'application/json', 'ACCEPT' => 'application/json'}
expect(loan.funded_amount).to eql(600.0)
end

Integration Testing a rails API using devise_token_auth for authenticaton and cancancan for authorization using Rspec

I have a rails-api application that I'm testing using Rspec. The Application uses devise_token_auth gem for authentication and cancancan gem for authorization.
devise_token_auth requires that the client include these authentication headers in every request:
access-token, client, expiry, uid. These headers are available in a response after successful authentication using email and password.
I have decided to use a solution provided in this answer to set these headers during testing.
In ability.rb I have this:
## models/ability.rb
class Ability
include CanCan::Ability
def initialize(user)
if user.role? :registered
can :create, Post, user_id: user.id
can :update, Post, user_id: user.id
can :destroy, Post, user_id: user.id
can :read, Post
end
end
end
posts#show action in PostsController looks like this:
## controllers/posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!
load_and_authorize_resource
def show
render json: #post
end
end
I have rescued CanCan::AccessDenied error to render a json message and a status of 403 forbidden in ApplicationController
rescue_from CanCan::AccessDenied do |exception|
render json: {"message" => "unauthorized"}.to_json, :status => 403
end
I have this in spec/support/session_helper.rb
module SessionHelper
def retrieve_access_headers
##I have a user with these credentials and has a "registered" role. in the the test db.
post "/auth/sign_in", params: {:email => "registered_user#gmail.com", :password => "g00dP#ssword"}, headers: {'HTTP_ACCEPT' => "application/json"}
##These two pass
expect(response.response_code).to eq 200
expect(response.body).to match(/"email": "registered_user#gmail.com"/)
access_headers = {"access-token" => response.headers["access-token"],
"client" => response.headers["client"],
"expiry" => response.headers["expiry"],
"uid" => response.headers["uid"],
"token-type" => response.headers["token-type"],
'HTTP_ACCEPT' => "application/json"
}
return access_headers
end
end
I have this in spec/support/requests_helper.rb
module RequestsHelper
def get_with_token(path, params={}, headers={})
headers.merge!(retrieve_access_headers)
get path, params: params, headers: headers
#### this outputs the expected headers on a json string and they seem fine ####
puts "headers: "+headers.to_json
end
end
I have included the two helpers in rails_helper.rb as shown below:
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.include SessionHelper, type: :request
config.include RequestsHelper, type: :request
end
Finally I have a request spec in spec/request/posts/show_spec.rb
require 'rspec/its'
require 'spec_helper'
require 'rails_helper'
RSpec.describe 'GET /posts/:id', :type => :request do
let(:post) {create(:post)}
let(:id) {post.id}
let(:request_url) {"/posts/#{id}"}
context 'with a registered user' do
it 'has a status code of 200' do
get_with_token request_url
expect(response).to have_http_status(:success)
end
end
end
I expect this to pass but it fails with message:
Failure/Error: expect(response).to have_http_status(:success)
expected the response to have a success status code (2xx) but it was 403
The application works as expected on a browser.
What I'm a doing wrong?

How can I configure access token in doorkeeper-gem on Rails4?

doorkeeper's spec with token through http header
My goal is creating secure API between iOS and Rails4. So I've been trying doorkeeper-gem for a while. But I'm wasting time for testing and configuration now. In detail, the problem is doorkeeper_for method and token transferring through HTTP header. How using http request parameter was successful, but sending token through parameter is not good. So I want to send token with HTTP header, but doorkeeper does not see where request.header["My_TOKEN_PLACE"].
Circumstance
Now, I have api_controller.rb, communities_controller.rb, communities_controller_spec.rb, doorkeeper.rb.
api_controller.rb
class ApiController < ApplicationController
before_action :create_token
doorkeeper_for :all, scopes: [:app]
respond_to :json, handler: :jbuilder
private
def error_handle
raise 'Failed.'
end
def create_token
params[:access_token] = request.headers["HTTP_AUTHENTICATION"]
# this does not read by doorkeeper
end
end
and communities_controller.rb
class CommunitiesController < ApiController
def show
#community = Community.find params[:id]
end
def search
Query.create q: #search_form.q if #search_form.q.present?
community_search = Community.search title_or_description_cont: #search_form.q
#communities = community_search.result(distinct: true).page params[:page]
end
end
and communities_controller_spec.rb
require 'spec_helper'
describe CommunitiesController do
let!(:application) { Doorkeeper::Application.create!(name: "MyApp", redirect_uri: "http://app.com") }
let(:user){ create :user }
let!(:access_token) { Doorkeeper::AccessToken.create! application_id: application.id, resource_owner_id: user.id, scopes: "app" }
before(:each) do
request.env["HTTP_ACCEPT"] = 'application/json'
end
describe "#show" do
let(:community) { create :community }
before do
request.headers["HTTP_AUTHENTICATION"] = access_token.token
get :show, id: community
end
it { expect(response).to be_success }
it { expect(response.status).to be 200 }
end
describe "#search" do
before { get :search, access_token: access_token.token }
it { expect(response).to be_success }
it { expect(response.status).to be 200 }
end
end
and config/initializer/doorkeeper.rb
Doorkeeper.configure do
orm :active_record
resource_owner_authenticator do
User.find id: session[:user_id]
default_scopes :app
end
and result of rspec communities_controller.rb is here.
/Users/shogochiai/Documents/anyll% be rspec spec/controllers/communities_controller_spec.rb
FF..
Failures:
1) CommunitiesController#show should be success
Failure/Error: it { expect(response).to be_success }
expected success? to return true, got false
# ./spec/controllers/communities_controller_spec.rb:20:in `block (3 levels) in <top(required)>'
2) CommunitiesController#show should equal 200
Failure/Error: it { expect(response.status).to be 200 }
expected #<Fixnum:401> => 200
got #<Fixnum:803> => 401
Compared using equal?, which compares object identity,
but expected and actual are not the same object. Use
`expect(actual).to eq(expected)` if you don't care about
object identity in this example.
# ./spec/controllers/communities_controller_spec.rb:21:in `block (3 levels) in <top(required)>'
Finished in 0.33439 seconds
4 examples, 2 failures
Failed examples:
rspec ./spec/controllers/communities_controller_spec.rb:20 # CommunitiesController#show should be success
rspec ./spec/controllers/communities_controller_spec.rb:21 # CommunitiesController#show should equal 200
Randomized with seed 18521
It seems
before do
request.headers["HTTP_AUTHENTICATION"] = access_token.token
get :show, id: community
end
is not authenticated
and
before { get :search, access_token: access_token.token }
is authenticated.
Supplementary
I did pp debug in controller, pp request and pp responce result had have a pair of key-value that "HTTP_AUTHENTICATION": "xrfui24j53iji34.....(some Hash value)".

Resources