From 4bfa98eb7c827b006600617de0b545a413be0b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 16 Jul 2010 12:03:48 +0200 Subject: [PATCH] More and more tests to DeviseOauth. --- test/integration/oauthable_test.rb | 229 +++++++++++++++++- test/rails_app/app/views/home/index.html.erb | 4 + .../migrate/20100401102949_create_tables.rb | 2 + test/rails_app/db/schema.rb | 68 ++---- test/rails_app/lib/shared_user.rb | 18 +- 5 files changed, 261 insertions(+), 60 deletions(-) diff --git a/test/integration/oauthable_test.rb b/test/integration/oauthable_test.rb index 9e077696..bb7dde6e 100644 --- a/test/integration/oauthable_test.rb +++ b/test/integration/oauthable_test.rb @@ -17,10 +17,20 @@ class OAuthableTest < ActionController::IntegrationTest teardown do Devise::Oauth.unshort_circuit_authorizers! Devise::Oauth.reset_stubs! + User.singleton_class.remove_possible_method(:find_for_github_oauth) + end + + def stub_github!(times=1) + def User.find_for_github_oauth(*); end + + Devise::Oauth.stub!(:github) do |b| + b.post('/login/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] } + end end def stub_facebook!(times=1) - data = (times == 1) ? FACEBOOK_INFO : FACEBOOK_INFO.except(:email) + # If times != 1, use invalid data + data = (times != 1) ? FACEBOOK_INFO.except(:email) : FACEBOOK_INFO Devise::Oauth.stub!(:facebook) do |b| b.post('/oauth/access_token') { [200, {}, ACCESS_TOKEN.to_json] } @@ -30,7 +40,7 @@ class OAuthableTest < ActionController::IntegrationTest end end - test "basic setup with persisted user" do + test "[BASIC] setup with persisted user" do stub_facebook! assert_difference "User.count", 1 do @@ -43,9 +53,10 @@ class OAuthableTest < ActionController::IntegrationTest assert warden.authenticated?(:user) assert_not warden.authenticated?(:admin) + assert "plataformatec", warden.user(:user).facebook_token end - test "basic setup with not persisted user and follow up" do + test "[BASIC] setup with not persisted user and follow up" do stub_facebook!(2) assert_no_difference "User.count" do @@ -68,5 +79,217 @@ class OAuthableTest < ActionController::IntegrationTest assert warden.authenticated?(:user) assert_not warden.authenticated?(:admin) + assert "plataformatec", warden.user(:user).facebook_token + end + + test "[BASIC] setup updating an existing user in database" do + stub_facebook! + user = create_user + + assert_no_difference "User.count" do + visit "/users/sign_in" + click_link "Sign in with Facebook" + end + + assert_current_url "/" + assert_contain "Successfully authorized from Facebook account." + + assert_equal user, warden.user(:user) + assert_equal "plataformatec", user.reload.facebook_token + end + + test "[BASIC] setup updating an existing user in session" do + stub_facebook! + + # Create an user and change his e-mail + user = sign_in_as_user + user.update_attribute(:email, "another@test.com") + + assert_no_difference "User.count" do + visit "/" + click_link "Sign in with Facebook" + end + + assert_current_url "/" + assert_contain "Successfully authorized from Facebook account." + + assert_equal user, warden.user(:user) + assert_equal "another@test.com", warden.user(:user).email + assert_equal "plataformatec", user.reload.facebook_token + end + + test "[BASIC] setup skipping oauth callback" do + stub_github! + + assert_no_difference "User.count" do + visit "/users/sign_in" + click_link "Sign in with Github" + end + + assert_current_url "/users/sign_in" + assert_contain "Skipped Oauth authorization for Github." + + assert_not warden.authenticated?(:user) + assert_not warden.authenticated?(:admin) + end + + test "[SESSION CLEANUP] ensures session is cleaned up after sign up" do + stub_facebook!(2) + + assert_no_difference "User.count" do + visit "/users/sign_in" + click_link "Sign in with Facebook" + end + + assert_contain "1 error prohibited this user from being saved" + fill_in "Email", :with => "user.form@test.com" + click_button "Sign up" + + assert_contain "You have signed up successfully." + visit "/users/sign_out" + + user = sign_in_as_user + assert_nil warden.user(:user).facebook_token + assert_equal user, warden.user(:user) + end + + test "[SESSION CLEANUP] ensures session is cleaned up on cancel" do + stub_facebook!(2) + + assert_no_difference "User.count" do + visit "/users/sign_in" + click_link "Sign in with Facebook" + end + + assert_contain "1 error prohibited this user from being saved" + visit "/users/cancel" + + user = sign_in_as_user + assert_nil warden.user(:user).facebook_token + assert_equal user, warden.user(:user) + end + + test "[SESSION CLEANUP] ensures session is cleaned up on sign in" do + stub_facebook!(2) + + assert_no_difference "User.count" do + visit "/users/sign_in" + click_link "Sign in with Facebook" + end + + assert_contain "1 error prohibited this user from being saved" + + user = sign_in_as_user + assert_nil warden.user(:user).facebook_token + assert_equal user, warden.user(:user) + end + + test "[I18N] scopes messages based on oauth callback for success" do + stub_facebook! + + store_translations :en, :devise => { :oauth_callbacks => { + :facebook => { :success => "Welcome facebooker" } } } do + visit "/users/sign_in" + click_link "Sign in with Facebook" + assert_contain "Welcome facebooker" + end + end + + test "[I18N] scopes messages based on oauth callback and resource name for success" do + stub_facebook! + + store_translations :en, :devise => { :oauth_callbacks => { + :user => { :facebook => { :success => "Welcome facebooker user" } }, + :facebook => { :success => "Welcome facebooker" } } } do + visit "/users/sign_in" + click_link "Sign in with Facebook" + assert_contain "Welcome facebooker user" + end + end + + test "[I18N] scopes messages based on oauth callback for skipped" do + stub_github! + + store_translations :en, :devise => { :oauth_callbacks => { + :github => { :skipped => "Skipped github" } } } do + visit "/users/sign_in" + click_link "Sign in with Github" + assert_contain "Skipped github" + end + end + + test "[I18N] scopes messages based on oauth callback and resource name for skipped" do + stub_github! + + store_translations :en, :devise => { :oauth_callbacks => { + :user => { :github => { :skipped => "Skipped github user" } }, + :github => { :skipped => "Skipped github" } } } do + visit "/users/sign_in" + click_link "Sign in with Github" + assert_contain "Skipped github user" + end + end + + test "[FAILURE] shows 404 if no code or error are given as params" do + assert_raise AbstractController::ActionNotFound do + visit "/users/oauth/facebook/callback" + end + end + + test "[FAILURE] raises an error if model does not implement a hook" do + begin + visit "/users/oauth/github/callback?code=123456" + raise "Expected visit to raise an error" + rescue Exception => e + assert_match "User does not respond to find_for_github_oauth", e.message + end + end + + test "[FAILURE] handles callback error parameter according to the specification" do + visit "/users/oauth/facebook/callback?error=access_denied" + assert_current_url "/users/sign_in" + assert_contain 'Could not authorize you from Facebook because "Access denied".' + end + + test "[FAILURE] handles callback error_reason just for Facebook compatibility" do + visit "/users/oauth/facebook/callback?error_reason=access_denied" + assert_current_url "/users/sign_in" + assert_contain 'Could not authorize you from Facebook because "Access denied".' + end + + test "[FAILURE][I18N] uses I18n for custom messages" do + store_translations :en, :devise => { :oauth_callbacks => { :access_denied => "Access denied bro" } } do + visit "/users/oauth/facebook/callback?error=access_denied" + assert_current_url "/users/sign_in" + assert_contain "Access denied bro" + end + end + + test "[FAILURE][I18N] uses I18n with oauth callback scope for custom messages" do + store_translations :en, :devise => { :oauth_callbacks => { + :facebook => { :access_denied => "Access denied bro" } } } do + visit "/users/oauth/facebook/callback?error=access_denied" + assert_current_url "/users/sign_in" + assert_contain "Access denied bro" + end + end + + test "[FAILURE][I18N] uses I18n with oauth callback scope and resource name for custom messages" do + store_translations :en, :devise => { :oauth_callbacks => { + :user => { :facebook => { :access_denied => "Access denied user" } }, + :facebook => { :access_denied => "Access denied bro" } } } do + visit "/users/oauth/facebook/callback?error=access_denied" + assert_current_url "/users/sign_in" + assert_contain "Access denied user" + end + end + + test "[FAILURE][I18N] trim messages to avoid long symbols lookups" do + store_translations :en, :devise => { :oauth_callbacks => { + :facebook => { ("a"*25) => "Access denied bro" } } } do + visit "/users/oauth/facebook/callback?error=#{"a"*100}" + assert_current_url "/users/sign_in" + assert_contain "Access denied bro" + end end end \ No newline at end of file diff --git a/test/rails_app/app/views/home/index.html.erb b/test/rails_app/app/views/home/index.html.erb index c3942a09..b0d8fcd5 100644 --- a/test/rails_app/app/views/home/index.html.erb +++ b/test/rails_app/app/views/home/index.html.erb @@ -1 +1,5 @@ Home! + +<%- User.oauth_providers.each do |provider| %> + <%= link_to "Sign in with #{provider.to_s.titleize}", user_oauth_authorize_url(provider) %>
+<% end =%> \ No newline at end of file diff --git a/test/rails_app/db/migrate/20100401102949_create_tables.rb b/test/rails_app/db/migrate/20100401102949_create_tables.rb index 545cc0bc..13bc8e6b 100644 --- a/test/rails_app/db/migrate/20100401102949_create_tables.rb +++ b/test/rails_app/db/migrate/20100401102949_create_tables.rb @@ -2,6 +2,8 @@ class CreateTables < ActiveRecord::Migration def self.up create_table :users do |t| t.string :username + t.string :facebook_token + t.database_authenticatable :null => false t.confirmable t.recoverable diff --git a/test/rails_app/db/schema.rb b/test/rails_app/db/schema.rb index a4579fa2..0e4db5c6 100644 --- a/test/rails_app/db/schema.rb +++ b/test/rails_app/db/schema.rb @@ -1,81 +1,47 @@ -# This file is auto-generated from the current state of the database. Instead of editing this file, -# please use the migrations feature of Active Record to incrementally modify your database, and -# then regenerate this schema definition. +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your database schema. If you need -# to create the application database on another system, you should be using db:schema:load, not running -# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations # you'll amass, the slower it'll run and the greater likelihood for issues). # # It's strongly recommended to check this file into your version control system. ActiveRecord::Schema.define(:version => 20100401102949) do - create_table "accounts", :force => true do |t| - t.string "email", :default => "", :null => false - t.string "encrypted_password", :default => "", :null => false - t.string "password_salt", :default => "", :null => false - t.string "username" - t.string "confirmation_token" - t.datetime "confirmed_at" - t.datetime "confirmation_sent_at" - t.string "reset_password_token" - t.string "remember_token" - t.datetime "remember_created_at" - t.integer "sign_in_count", :default => 0 - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.integer "failed_attempts", :default => 0 - t.string "unlock_token" - t.datetime "locked_at" - t.string "authentication_token" - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "admins", :force => true do |t| - t.string "email", :default => "" - t.string "encrypted_password", :default => "" - t.string "password_salt", :default => "" - t.string "username" - t.string "confirmation_token" - t.datetime "confirmed_at" - t.datetime "confirmation_sent_at" + t.string "email", :default => "" + t.string "encrypted_password", :limit => 128, :default => "" + t.string "password_salt", :default => "" t.string "reset_password_token" - t.string "remember_token" - t.datetime "remember_created_at" - t.integer "sign_in_count", :default => 0 - t.datetime "current_sign_in_at" - t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.integer "failed_attempts", :default => 0 + t.integer "failed_attempts", :default => 0 t.string "unlock_token" t.datetime "locked_at" - t.string "authentication_token" t.datetime "created_at" t.datetime "updated_at" end create_table "users", :force => true do |t| - t.string "email", :default => "", :null => false - t.string "encrypted_password", :default => "", :null => false - t.string "password_salt", :default => "", :null => false t.string "username" + t.string "facebook_token" + t.string "email", :default => "", :null => false + t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "password_salt", :default => "", :null => false t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "reset_password_token" t.string "remember_token" t.datetime "remember_created_at" - t.integer "sign_in_count", :default => 0 + t.integer "sign_in_count", :default => 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.integer "failed_attempts", :default => 0 + t.integer "failed_attempts", :default => 0 t.string "unlock_token" t.datetime "locked_at" t.string "authentication_token" diff --git a/test/rails_app/lib/shared_user.rb b/test/rails_app/lib/shared_user.rb index 880e1fb6..0aa0ebb4 100644 --- a/test/rails_app/lib/shared_user.rb +++ b/test/rails_app/lib/shared_user.rb @@ -12,7 +12,11 @@ module SharedUser module ExtendMethods def find_for_facebook_oauth(access_token, signed_in_resource=nil) - User.create { |u| u.update_with_facebook_oauth(access_token) } + data = ActiveSupport::JSON.decode(access_token.get('/me')) + user = signed_in_resource || User.find_by_email(data["email"]) || User.new + user.update_with_facebook_oauth(access_token, data) + user.save + user end def new_with_session(params, session) @@ -25,18 +29,20 @@ module SharedUser end end - def update_with_facebook_oauth(access_token) - data = ActiveSupport::JSON.decode(access_token.get('/me')) + def update_with_facebook_oauth(access_token, data=nil) + data ||= ActiveSupport::JSON.decode(access_token.get('/me')) self.username = data["username"] unless username.present? self.email = data["email"] unless email.present? - self.confirmed_at ||= Time.now - unless password.present? + self.confirmed_at ||= Time.now + self.facebook_token = access_token.token + + unless encrypted_password.present? self.password = Devise.friendly_token self.password_confirmation = nil end - yield self if block_given? + yield self if block_given? end end