I followed the rails cast of omniauth to create authentication for twitter ( http://railscasts.com/episodes/235-omniauth-part-1?view=comments ). It works fine in development but I can't get rspec to detect that I have created the authentication. Here is my snippet for create function in my Authentication controller:
def create
begin
auth_hash = request.env["omniauth.auth"]
#auth = current_user.authentications.build( :provider => auth_hash['provider'],
:uid => auth_hash['uid'],
:name => auth_hash['user_info']['name'],
:nickname => auth_hash['user_info']['nickname'],
:image => auth_hash['user_info']['image']
)
if #auth.provider.downcase == "twitter"
#auth.auth_token = auth_hash['credentials']['token']
#auth.secret_token = auth_hash['credentials']['secret']
#auth.site = auth_hash['user_info']['urls']['Twitter']
elsif #auth.provider == "Facebook"
end
rescue
redirect_to current_user, :flash => { :error => "Missing oauth data!! Check with administrator!"}
else
if #auth.save
msg = "Authentication success"
else
msg = "Already have authentication"
end
redirect_to current_user, :notice => msg
end
end
included in my routes:
match '/auth/:provider/callback' => 'authentications#create'
I have the following setup in my rspec_helper:
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:twitter, { :provider => "twitter",
:uid => "1234",
:user_info => { :name => "Bob hope",
:nickname => "bobby",
:urls => {:Twitter => "www.twitter.com/bobster"}},
:credentials => { :auth_token => "lk2j3lkjasldkjflk3ljsdf"} })
Here is my rspec code that is not working:
describe "Post 'create'" do
before(:each) do
#user = Factory(:user)
sign_in #user
end
describe "success" do
before(:each) do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
it "should create authentication" do
lambda do
post :create, :provider => "twitter"
response.should redirect_to(#user)
end.should change(Authentication, :count).by(1)
end
end
end
error i get is:
1) AuthenticationsController Post 'create' success should create authentication
Failure/Error: lambda do
count should have been changed by 1, but was changed by 0
# ./spec/controllers/authentications_controller_spec.rb:57
I've checked everything and cannot figure what I am doing wrong. Can anyone help?
i finally figured what was wrong. in my mock :auth_token is suppose to be :token. That was causing the failed validation.
Shouldn't that be a get request?
describe "success" do
before(:each) do
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter]
end
it "should create authentication" do
lambda do
get :create, :provider => "twitter"
response.should redirect_to(#user)
end.should change(Authentication, :count).by(1)
end
end
Related
I'm attempting to write a test for the creation of a session with the omniauth-google-oauth2 gem. Do I need to pass the env["omniauth.auth"] variable with the post :create? Perhaps when I was attempting to do that I was doing it incorrectly. The error I'm getting is shown below...
Rake Test Error
1) Error:
SessionsControllerTest#test_should_get_create:
NoMethodError: undefined method `provider' for nil:NilClass
app/models/user.rb:6:in `from_omniauth'
app/controllers/sessions_controller.rb:4:in `create'
test/controllers/sessions_controller_test.rb:13:in `block in <class:SessionsControllerTest>'
The following is my attempt at writing the test...
SessionsControllerTest
require 'test_helper'
class SessionsControllerTest < ActionController::TestCase
setup :prepare_omniauth
test "should get create" do
post :create
redirect_to root_path, notice: "Signed in!"
end
test "should get destroy" do
get :destroy
assert session[:user_id].blank?, "user_id should no longer exist"
assert_redirected_to root_path, notice: "Signed out!"
end
private
def prepare_omniauth
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:google] = OmniAuth::AuthHash.new({
:provider => 'google',
:uid => '123545'
})
request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google]
end
end
Sessions Controller
class SessionsController < ApplicationController
def create
user = User.from_omniauth(env["omniauth.auth"])
session[:user_id] = user.id
redirect_to root_path
end
def destroy
session[:user_id] = nil unless session[:user_id].blank?
redirect_to root_path
end
end
User Model
class User < ActiveRecord::Base
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.save!
end
end
end
I believe this blog has the answer to your question: http://natashatherobot.com/rails-test-omniauth-sessions-controller/
Basically you need to edit your rails_helper/spec_helper to set Omniauth's test mode to true and create an omniauth_hash to be used in your tests:
OmniAuth.config.test_mode = true
omniauth_hash = { 'provider' => 'github',
'uid' => '12345',
'info' => {
'name' => 'natasha',
'email' => 'hi#natashatherobot.com',
'nickname' => 'NatashaTheRobot'
},
'extra' => {'raw_info' =>
{ 'location' => 'San Francisco',
'gravatar_id' => '123456789'
}
}
}
OmniAuth.config.add_mock(:github, omniauth_hash)
Then you require it before any tests:
before do
request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:github]
end
Now you should be able to create users using Omniauth like in this example:
describe SessionsController do
it "should successfully create a user" do
expect {
post :create, provider: :github
}.to change{ User.count }.by(1)
end
end
All credits to Natasha from http://natashatherobot.com for posting this answer in her blog.
I'm trying to use devise to authenticate a simple email/password login so users can access the rest of the API via auth tokens. I'm running into trouble with devise simply returning You need to sign in or sign up before continuing. Here's my code:
class LoginController < ApplicationController
respond_to :json
def login
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
render :status => 200,
:json => { :success => true,
:info => "Logged in",
:user => current_user
}
end
def failure
render :status => 401,
:json => { :success => false,
:info => "Login Failed",
:data => {} }
end
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
routes.rb
devise_scope :user do
post 'register' => 'registration#create', :as => :registers
post 'login' => 'login#login', :as => :login
end
I'm sending the following post data:
{
"user" : {
"email" : "testPost4#fake.com",
"password" : "Password1"
}
}
Having browsed various posts I've added:
config.navigational_formats = [:json]
to the devise.rb file but it didn't solve the problem.
Adding skip_before_filter :authenticate_user! doesn't work either.
I wasn't able to get this working so I have reverted to the much simpler approach of checking and signing in manually.
def login
params = login_params
user = User.find_by_email(params['email'].downcase)
if user.valid_password?(params['password']) then
sign_in(user)
success
else
failure
end
end
I'm building an API using Rails and Devise. My sessions controller inherits from the following base controller
api/base_controller.rb
module Api
class BaseController < ApplicationController
skip_before_filter :verify_authenticity_token
before_filter :authenticate_user_from_token!
respond_to :json
private
def authenticate_user_from_token!
user_token = params[:auth_token].presence
user = user_token && User.find_by_authentication_token(user_token)
if user
sign_in user, store: false
else
render :json => {:success => false, :message => "Error with your credentials", :status => 401}
end
end
end
end
My sessions controller's destroy action below:
api/sessions_controller.rb
before_filter :authenticate_user_from_token!, :except => [:create]
def destroy
current_user.reset_authentication_token
render :json => {
:success => true,
:status => 200
}
end
This works perfectly when testing the api via curl. But, I can't get my Rspec tests for the destroy action to pass. From Rspec the sign_in user call is failing, so the response is a redirect. I haven't had any success trying to stub the sign_in method.
Rspec test:
describe "DELETE destroy" do
before(:each) do
#user1 = User.create!(:email => 'example#gmail.com', :password => 'helloworld', :password_confirmation => 'helloworld')
end
it "should render success json" do
delete :destroy, :auth_token => #user1.authentication_token
json = JSON.parse(response.body)
json.should include('success' => true, 'status' => 200)
end
###this fails because the response is a redirect to the sign_in page
end
How should I go about mocking the sign_in method called from within the base controller?
Add a spec/support/devise.rb file with this content:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Also, check in your test.log wether it's actually using the json format. I had a similar problem and found out that I had to force the format :json in my spec call parameters.
Andreamazz pointed me to the test.logs which revealed that the user I had created was confirmed (I'm using Devise confirmable). I use user.confirm! in the before(:each) and everything is passing.
describe "DELETE destroy" do
before(:each) do
#user1 = User.create!(:email => 'example#gmail.com', :password => 'helloworld', :password_confirmation => 'helloworld')
#user1.confirm!
end
it "should render success json" do
delete :destroy, :auth_token => #user1.authentication_token
json = JSON.parse(response.body)
json.should include('success' => true, 'status' => 200)
end
end
Thanks!
I testing my app for create a new user car, later that the user creates the new car the app must redirect to the user_car_path (I post my routes):
user_cars GET /users/:user_id/cars(.:format) cars#index
POST /users/:user_id/cars(.:format) cars#create
new_user_car GET /users/:user_id/cars/new(.:format) cars#new
edit_user_car GET /users/:user_id/cars/:id/edit(.:format) cars#edit
user_car GET /users/:user_id/cars/:id(.:format) cars#show
PUT /users/:user_id/cars/:id(.:format) cars#update
DELETE /users/:user_id/cars/:id(.:format) cars#destroy
so I'm testing my app with this rspec:
describe "POST 'create' car" do
describe "car created success" do
before(:each) do
#user = User.create!(:email => "foo#example.com", :password => "foobar", :password_confirmation => "foobar" )
#car = Car.create!(:brand => "example", :color => "foobar", :model => "foobar", :year =>"2012")
end
it "should create a car" do
lambda do
post :create, :cars => #car, :user_id => #user.id
end.should change(Car, :count).by(1)
end
it "should redirect to the user cars page" do
post :create, :cars => #car, :user_id => #user.id
response.should redirect_to user_car_path(#user, #car)
end
end
end
but i got 2 errors
Failures:
1) CarsController POST 'create' car car created success should create a car
Failure/Error: lambda do
count should have been changed by 1, but was changed by 0
# ./spec/controllers/car_controller_spec.rb:20
2) CarsController POST 'create' car car created success should redirect to the user cars page
Failure/Error: response.should redirect_to user_car_path(#user, #car)
Expected response to be a redirect to <http://test.host/users/115/cars/40> but was a redirect to <http://test.host/users/115/cars/new>.
# ./spec/controllers/car_controller_spec.rb:27
but my app works normally; here is my CarController
class CarsController < ApplicationController
....
def create
#user = User.find(params[:user_id])
#car = #user.cars.build(params[:car])
if #car.save
redirect_to user_car_path(#user, #car), :flash => { :notice => " car created!" }
else
redirect_to new_user_car_path ,:flash => { :notice => " sorry try again :(" }
end
end
def show
#user = User.find(params[:user_id])
#car = #user.cars.find(params[:id])
end
....
You are creating the car in the database, rather than just creating a car object in the before(:each). Also you're passing the param as :cars rather than :car. Finally, I'd also personally use let. Try this.
describe "POST 'create' car" do
let(:user) { User.create!(:email => "foo#example.com", :password => "foobar", :password_confirmation => "foobar" ) }
let(:car) { Car.new(:brand => "example", :color => "foobar", :model => "foobar", :year =>"2012")}
it "should create a car" do
lambda do
post :create, :car => car, :user_id => user.id
end.should change(Car, :count).by(1)
end
it "should redirect to the user cars page" do
post :create, :cars => car, :user_id => user.id
# note: car doesn't have an ID, you have to fetch it from the db to get an id
response.should redirect_to user_car_path(user.id, user.cars.last.id)
end
end
As a final note, you will want to look into Factory Girl
Then you could do this instead:
let(:user){ FactoryGirl.create(:user) }
let(:car) { FactoryGirl.build(:car) } # note: BUILD not CREATE
I think it's because your validation fail. To know if it fails do this, before save in your controller :
puts #car.valid?
If it's false, your test will fail and it's normal. To set to true you can do this:
before(:each) do
...
Car.any_instance.stub(:valid?).and_return(true)
end
You can also use stubs and mocks to return instances for User.find and Car.build. See the doc : https://www.relishapp.com/rspec/rspec-mocks/v/2-6/docs/method-stubs
I think you want the following (:cars => :car and use params that would make the car creation in the controller valid. I am confused why you are creating the car in your spec because this seems to be a test of car creation)
it "should create a car" do
lambda do
post :create, :car => {:brand => "example", :color => "foobar", :model => "foobar", :year =>"2012"}, :user_id => #user.id
end.should change(Car, :count).by(1)
end
it "should redirect to the user cars page" do
post :create, :car => {:brand => "example", :color => "foobar", :model => "foobar", :year =>"2012"}, :user_id => #user.id
response.should redirect_to user_car_path(#user, #car)
end
I have such tests:
class LockableFlowTest < ActionDispatch::IntegrationTest
context 'A user' do
setup do
#organization = Factory(:organization)
#user = Factory(:user, :organization => #organization)
end
should "login after 4 attempts" do
4.times do
post_via_redirect '/users/sign_in', 'user[username]' => #user.username, 'user[password]' => "bad_password"
assert_equal '/users/sign_in', path
assert_equal 'Invalid email or password.', flash[:alert]
end
post_via_redirect '/users/sign_in', 'user[username]' => #user.username, 'user[password]' => "password"
assert_equal "/registrations/#{#user.id}/edit", path
assert_nil flash[:alert]
end
It doesn't work but the application is okay. I would like to test attempts to login into the application. After 4 attempts it should be possible to login.
The code controller:
class SessionsController < Devise::SessionsController
after_filter :log_failed_login, :only => :new
private
def log_failed_login
if request.filtered_parameters["user"]
user = User.find_by_username(request.filtered_parameters["user"]["username"])
if user
if user.first_failed_attempt.nil?
user.first_failed_attempt = Time.now
else
if user.first_failed_attempt + 15.minutes <= Time.now
user.failed_attempts = 1
user.first_failed_attempt = Time.now
end
end
user.save
end
end
end
def failed_login?
(options = env["warden.options"]) && options[:action] == "unauthenticated"
end
end
What's the counting mechanism to let the application know that it already executed four tries?
Cookies? Cookies do not persist in Rspec on rails 3.1