require File.dirname(__FILE__) + '/../test_helper'
require 'account_controller'
require 'mocha'

# Re-raise errors caught by the controller.
class AccountController; def rescue_action(e) raise e end; end

class AccountControllerTest < Test::Rails::TestCase

  fixtures :users, :settings

  def setup
    @controller = AccountController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    
    # for testing action mailer
    @emails = ActionMailer::Base.deliveries 
    @emails.clear
    ApplicationPolicy.stubs(:guests_allowed?).returns(true)
  end

  def test_should_login_and_redirect
    post :login, :email => 'quentin@example.com', :password => 'quentin'
    assert session[:user]
    assert_instance_of Participant, User.find(session[:user])
    assert_response :redirect
  end

  def test_should_fail_login_and_not_redirect
    post :login, :email => 'quentin@example.com', :password => 'bad password'
    assert_is_guest
    assert_response :success
  end

  def test_should_allow_signup
    assert_difference(Participant, :count) do
      create_user
      assert_response :redirect
    end
  end
  
  %w{email password password_confirmation}.each do |field|
    define_method("test_create_user_should_require_#{field}".to_sym) do
      create_user(field.to_sym => nil)
      assert assigns(:user).errors.on(field), "should error on mising #{field}"
      assert_response :success, "should render the same page, due to missing #{field}"
    end
  end
  
  def test_should_logout
    login_as :quentin
    get :logout
    assert_nil session[:user]
    assert_response :redirect
  end
  
  # def test_logout_sets_flash_js # TODO create a Setting or ApplicationPolicy to control the use of urchinTracker
  #   login_as :quentin
  #   get :logout
  #   
  #   assert flash[:javascript]
  #   assert_match 'urchinTracker("/funnel/account/logout")', flash[:javascript]
  # end
  
  def test_login_bounces_if_already_logged_in
    login_as :ernest
    get :login
    assert_response :redirect
  end
  
  def test_signup_bounces_if_already_logged_in
    login_as :ernest
    get :signup
    assert_response :redirect
  end
  
  # user numbers suspended
  #def test_arrive_at_signup_with_id_becomes_user_number
  #  get :signup, :id => '392109'
  #  assert assigns(:user).user_number
  #  assert_equal '392109', assigns(:user).user_number
  #end
  #
  #
  ## If a user_number has already been taken, redirect to login with message
  ## "you've already signed up, please login"
  #def test_arrives_at_signup_user_number_already_spoken_for
  #  get :signup, :id => users(:grace).user_number
  #  assert_response :redirect
  #  assert flash[:notice]
  #end
  #
  ## GET signup without id results in redirect to error page
  #def test_signup_without_an_id_gets_error_message
  #  get :signup
  #  assert_template 'no_user_number'
  #end
  
  def test_signup_sends_email
    create_user
    assert_equal 1, @emails.length
  end
  
  def test_signup_redirects_to_main
    create_user
    assert_response :redirect
    #assert_redirected_to :controller => '/topics' # damnit, the redirect is correct. This test gives a false negative
  end
  
  def test_get_remind_password
    get :remind_password
    assert_response :success
  end

  def test_remind_password_redirects_to_login
    request_password_reminder
    assert_response :redirect
    assert_redirected_to :action => 'login'
  end

  def test_remind_password_redirects_with_flash
    request_password_reminder
    assert flash[:notice]
    assert_equal "Your password has been sent to your email account", flash[:notice]
  end

  def test_remind_password_sends_email
    request_password_reminder
    assert_equal 1, @emails.length
  end
  
  # test remind password when email not found doesn't send any email
  def test_remind_password_email_not_found_sends_no_email
    post :remind_password, :email => 'nofound@nowhere.com'
    assert_equal 0, @emails.length
  end
  
  # test remind password when email not found renders the page again
  def test_remind_password_email_not_found_renders_page_again
    request_password_reminder 'nofound@nowhere.com'
    assert_response :success
  end
  
  # test remind password when email not found shows notice
  def test_remind_password_email_not_found_has_flash
    request_password_reminder 'nofound@nowhere.com'
    assert flash[:notice]
    assert_equal "No account was found for that email address", flash[:notice]
  end
  
  def test_remember_me_auto_logged_in
    @request.cookies["login_token"] = CGI::Cookie.new("login_token", "6;login_key_hash")
    get :index
    assert session[:user]
    assert_equal users(:ernest).id, session[:user]
  end
  
  def test_remember_me_login_sets_cookie
    post :login, :email => 'quentin@example.com', :password => 'quentin', :remember_me => '1'
    assert session[:user]
    assert_instance_of Participant, User.find(session[:user])
    assert cookies['login_token']
  end
  
  def test_show_login_when_participation_disabled
    ApplicationPolicy.stubs(:participation_enabled?).returns(false)
    get :login
    assert_response :success
  end
  
  def test_should_fail_login_when_user_not_verified
    post_login_not_verified    
    assert !session[:user].is_a?(RegisteredUser)
  end
  
  def test_should_fail_login_when_user_not_verified_set_flash
    post_login_not_verified    
    assert flash[:notice]
    assert_equal "Your account has not been verified. Please check your email.", flash[:notice]
  end
  
  def test_verify_should_set_verify_flag
    get_verify
    assert users(:arthur).reload.verified?
  end

  def test_verify_should_login_user
    get_verify
    assert session[:user]
    assert_equal users(:arthur).id, session[:user]
  end
  
  def test_verify_should_set_flash
    get_verify
    assert flash[:notice]
    assert_equal "Account Verified", flash[:notice]
  end
  
  # def test_verify_should_set_js_flash
  #   get_verify
  #   assert flash[:javascript]
  #   assert_match %r{/funnel/account/verified}, flash[:javascript]
  # end
  # 
  # def test_verify_should_not_set_js_flash_if_fails
  #   quentin = users(:quentin)
  #   sanity_check "quentin is already verified", quentin.verified?
  #   
  #   get :verify_account, :key => "doesn't matter"
  #   assert_nil flash[:javascript]
  # end
  
  def test_should_create_guest_account
    get :login
    assert session[:user], "user ID missing from session"
    assert_kind_of Guest, User.find(session[:user]), "user in session should be a Guest"
  end
  
  def test_guest_account_should_set_cookie
    get :login
    assert cookies['guest_id'], "guest_id cookie is missing"
  end
  
  def test_should_resume_previous_guest_account
    @request.cookies['guest_id'] = CGI::Cookie.new("guest_id", users(:visitor1).id)
    get :login
    assert_equal users(:visitor1).id, session[:user], "wrong user ID -- should have resumed Guest account"
  end
  
  def test_suspended_user_cannot_login
    post_login_suspended
    
    assert_is_guest
  end
  
  def test_suspended_user_sees_message_on_failed_login
    post_login_suspended
    
    assert flash[:notice]
    assert_equal "Your account has been suspended.", flash[:notice]
  end

  def test_remember_me_not_logged_in_if_suspended
    users(:ernest).toggle!(:suspended)
    @request.cookies["login_token"] = CGI::Cookie.new("login_token", "6;login_key_hash")
    get :index
    assert_is_guest
  end
  
  def test_refresh_on_login_triggers_refresh
    xhr_login(true)
    assert_response :success
    assert_match('window.location.reload()', @response.body)
  end
  
  def test_refresh_on_login_sets_flash
    xhr_login(true)
    assert flash[:notice]
  end
  
  def test_refresh_on_login_false_shows_regular_rjs
    xhr_login(nil)
    assert_response :success
    assert_template 'login.rjs'
    assert_match('Logged in successfully', @response.body)
  end
  
  def test_refresh_on_login_false_sets_no_flash
    xhr_login(nil)
    assert_response :success
    assert !flash[:notice]
  end
  
  protected
  def create_user(options = {})
    post :signup, :user => { :email => 'quire@example.com', 
                             :password => 'quyre', 
                             :name => 'Jeremy',
                             :password_confirmation => 'quyre',
                             :user_number => '392109',
                             :postal_code => 'k1r',
                             :year_of_birth => '1970',
                             :gender => 1 }.merge(options)
  end
  
  def request_password_reminder(email = nil)
     post :remind_password, :email => (email || users(:arthur).email)
  end
  
  def get_verify
    arthur = users(:arthur)
    sanity_check "arthur is not verified", !arthur.verified?
    
    get :verify_account, :key => arthur.login_key
  end
  
  def post_login_not_verified
    arthur = users(:arthur)
    sanity_check "arthur is not verified", !arthur.verified?
    
    post :login, :email => arthur.email, :password => arthur.password
  end
  
  def post_login_suspended
    sally = users(:suspended_sally)
    sanity_check "sally is suspended", sally.suspended?
    
    post :login, :email => sally.email, :password => sally.password 
  end
  
  def assert_is_guest
    assert session[:user], 'no user in session'
    assert_instance_of Guest, User.find(session[:user])
  end
  
  def xhr_login(refresh_on_login=nil)
    xml_http_request :post, :login, {:email => 'quentin@example.com', :password => 'quentin', :refresh_on_login => refresh_on_login}
  end
end
