Can I do a "before hook" on rspec get / post - ruby-on-rails

I'm trying to write tests for an API which requires an hmac signature on each request.
describe Api::V2::HmacController, :type => :controller do
render_views
it 'GET' do
get :index, timestamp: Time.now.to_i, format: :json
expect(response.status).to eq(200)
end
end
I would like to add
request.env['x-api-key'] = API_KEY
request.env['x-api-hmac'] = "Encode"(API_SECRET, "parameters of the request")
to each request.
I'm open to any type of solution.
Question: How can I wedge a hook after the request has been formed, but hasn't sent?
I'm thinking of overwriting rspec get / post method, but I'm not sure how.

You can do it from in the block
request.headers['x-api-key'] = API_KEY
You shouldn't set access the request headers through the env.
See here

Related

How to set the request.body for an Rspec request spec with a GET request

rails 5.0.0.1
rspec 3.5
I have inherited a code base. I am busy writing integration tests to tie down the app functionality before I consider refactoring.
I have the following lines in a controller concern before_action. It seems to read the request body. The json value here is used to extract an identifier used to authenticate the request.
request.body.rewind
body = request.body.read
json = JSON.parse(body) unless body.empty?
I need to test that the authentication happens correctly.
How can I set the request.body for a GET request spec?
I think you should be able to do this via the request env RAW_POST_DATA
get root_path, {}, 'RAW_POST_DATA' => 'raw json string'
request.raw_post # "raw json string"
See:
How to send raw post data in a Rails functional test?
https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec
#rails_post_5
require "rails_helper"
RSpec.describe "Widget management", :type => :request do
it "creates a Widget and redirects to the Widget's page" do
headers = { "CONTENT_TYPE" => "application/json" }
post "/widgets", :params => '{ "widget": { "name":"My Widget" } }', :headers => headers
expect(response).to redirect_to(assigns(:widget))
end
end
or just
post "/widgets", params: '{ "widget": { "name":"My Widget" } }'

`expected 200` error in Rspec test for `get` API request

I'm trying to write some rspec tests to check API endpoints for an API-only application.
Testing error
Failure/Error: expect( res ).to be_success
expected 200 to respond to `success?`
But if the same call (with full api url) is made from another application it works fine and returns a response.
Example from other application:
res = RestClient.get "site.io/api/v1/projects/1"
p JSON.parse(res)
Blog example I'm trying to follow: (http://matthewlehner.net/rails-api-testing-guidelines/).
# spec/requests/api/v1/messages_spec.rb
describe "Messages API" do
it 'sends a list of messages' do
FactoryGirl.create_list(:message, 10)
get '/api/v1/messages'
json = JSON.parse(response.body)
# test for the 200 status-code
expect(response).to be_success
# check to make sure the right amount of messages are returned
expect(json['messages'].length).to eq(10)
end
end
My Application
/requests/projects_spec.rb
require 'rails_helper'
RSpec.describe Project do
describe "show_project" do
before do
#project1 = create(:project)
end
it "Checks if responds successfully" do
res = get '/api/v1/projects/1'
expect( res ).to be_success
end
end
end
/factories/projects.rb
FactoryGirl.define do
factory :project do
name "Thing"
key "123123"
end
end
routes.rb
namespace :api, :defaults => { :format => 'json'} do
namespace :v1 do
resources :projects, only: [:create, :show]
end
end
end
I don't have much experience with testing, so if anyone can point me in the correct direction I would really really appreciate it.
When using Rspec Request Specs, your call to get '/api/v1/projects/1' doesn't need to captured by your res variable. Spec Request tests automatically set the value of response when get '/api/v1/projects/1' is run. The example you're following is correct, it just looks like your missing some knowledge about how much Rspec is handling for you behind the scenes. This makes your test simpler:
it "Checks if responds successfully" do
get '/api/v1/projects/1'
expect(response).to be_success
end
In Rspec Request tests, response is automatically setup by the call the get without you needing to do anything extra.

Rspec Rails 4.2.5 Request test pass with basic http auth

The setup is the following. For each http request the manager sends his credentials in the header(name,pw). These get checked against the entries in the db and if they succeed return the desired user object. How is it possible to implement basic http_auth in the request tests? I would like to add only the password and username and test the return value? Which is the goal of request tests,right? I tried the following without much success:
I created an AuthHelper module in spec/support/auth_helper.rb with
module AuthHelper
def http_login
user = 'test'
pw = 'test'
request.ENV['HTTP_AUTHORIZATION'] =ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
end
end
and use it in the requests/api/user_spec.rb as follows
include AuthHelper
describe "User API get 1 user object" do
before(:each) do
http_login
end
but i receive this error message. How can i fix this and enable my tests to pass http_auth? I read lot of similar topis and questions also here but
they apply mostly to older versions of rspec and rails and are not applying to my case
Thanks in advance!
Failure/Error: request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
NoMethodError:
undefined method `env' for nil:NilClass# ./spec/support
# ./spec/support/auth_helper.rb:5:in `http_login'
# ./spec/requests/api/user_spec.rb:8:in `block (2 levels) in <top (required)>'
Update
I moved the header generation inside a request. I looked up the Auth verb, so i think the assignment should work. I tested the ActionController call in rails console and received a string. I suppose this is also correct.My whole test now looks like this:
describe "User API get 1 user object", type: :request do
it 'Get sends back one User object ' do
headers = {
"AUTHORIZATION" =>ActionController::HttpAuthentication::Basic.encode_credentials("test","test")
# "AUTHORIZATION" =>ActionController::HttpAuthentication::Token.encode_credentials("test","test")
}
FactoryGirl.create(:user)
get "/api/1/user", headers
#json = JSON.parse(response.body)
expect(response).to be_success
# expect(response.content_type).to eq("application/json")
end
end
receiving the following error:
which incudles the line #buf=["HTTP Basic: Access denied.\n"] so access is still denied.
Failure/Error: expect(response).to be_success
expected `#<ActionDispatch::TestResponse:0x000000070d1d38 #mon_owner=nil, #mon_count=0, #mon_mutex=#<Thread::Mutex:0x000000070d1c98>, #stream=#<ActionDispatch::Response::Buffer:0x000000070d1c48 #response=#<ActionDispatch::TestResponse:0x000000070d1d38 ...>,
#buf=["HTTP Basic: Access denied.\n"], #closed=false>, #header={"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff", "WWW-Authenticate"=>"Basic realm=\"Application\"", "Content-Type"=>"text/html; charset=utf-8", "Cache-Control"=>"no-cache", "X-Request-Id"=>"9c27d4e9-84c0-4ef3-82ed-cccfb19876a0", "X-Runtime"=>"0.134230", "Content-Length"=>"27"}, #status=401, #sending_file=false, #blank=false,
#cv=#<MonitorMixin::ConditionVariable:0x000000070d1bf8 #monitor=#<ActionDispatch::TestResponse:0x000000070d1d38 ...>, #cond=#<Thread::ConditionVariable:0x000000070d1bd0>>, #committed=false, #sending=false, #sent=false, #content_type=#<Mime::Type:0x00000002af78f8 #synonyms=["application/xhtml+xml"], #symbol=:html, #string="text/html">, #charset="utf-8", #cache_control={:no_cache=>true}, #etag=nil>.success?`
to return true, got false
SOLUTION
This test is not polished (yet) but at least it passes now.
describe "User API get 1 user object", type: :request do
it 'Get sends back one User object ' do
#env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
FactoryGirl.create(:user)
get "/api/1/user", {}, #env
JSON.parse(response.body)
expect(response).to be_success
expect(response.status).to eq 200
end
end
Read the error carefully: undefined method `env' for nil:NilClass means request is nil. Are you trying to set the header before a test while you are defining the request later on in the test?
You might want to look at the documentation for an example on how to set headers.
If you're still stuck, post one of your tests as well.
This line looks suspicious:
request.ENV['HTTP_AUTHORIZATION'] =ActionController::HttpAuthentication::Basic.encode_credentials(user,pw)
Are you sure that "ENV" should be capitalized? I think it should be written like "env".

How to rspec ActionDispatch::ParamsParser::ParseError in Rails Api

I am trying to write a failing spec for passing a invalid json when making a request to a rails api endpoint.
In a full rail api I would have used capybara and curl to make the actual request.
How do I pass a invalid json via a rspec request spec?
I ended up finding a solution to this, you need to use request specs, something along this lines:
require 'rails_helper'
RSpec.describe 'JSON formatting', :type => :request do
context 'with bad json' do
it 'returns 400 on bad json' do
headers = {
'ACCEPT' => 'application/json',
'CONTENT_TYPE' => 'application/json'
}
post '/some/path', "{", headers
expect(response.content_type).to eq("application/json")
end
end
end

Grape Rails RSpec test returning 400

I am trying to test my Grape API, but I am receiving a 400 error in my tests, but when I run the action the test is supposed to test, I get a 201 HTTP response as expected. Not sure what is going on here. Below is the specific RSpec test, but you can view the whole project with the factories and the actual Grape API on GitHub at hackcentral/hackcentral. The test below is testing the POST create action on Alpha::Applications. (app/api/alpha/applications.rb)
describe 'POST #create' do
before :each do
#oauth_application = FactoryGirl.build(:oauth_application)
#token = Doorkeeper::AccessToken.create!(:application_id => #oauth_application.id, :resource_owner_id => user.id)
end
context "with valid attributes" do
it "creates a new application" do
expect{
post "http://api.vcap.me:3000/v1/applications?access_token=#{#token.token}", application: FactoryGirl.attributes_for(:application), :format => :json
} .to change(Application, :count).by(1)
end
it "creates a new application, making sure response is #201" do
post "http://api.vcap.me:3000/v1/applications", application: FactoryGirl.attributes_for(:application), :format => :json, :access_token => #token.token
response.status.should eq(201)
end
end
end
I don't understand why are you testing http://api.vcap.me an not localhost?
You usually test the app on the local enviroment. And this is not the right why to test if the server is working either.
Here is an example of how your test should look like.
https://github.com/dblock/grape-on-rails/blob/master/spec/api/ping_spec.rb from an example project

Resources