require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')

describe UsersController do
  integrate_views
  
  before(:each) do
    controller.instance_eval { flash.stub!(:sweep) }
  end  
  
  describe "GET login_form" do
    it "should render the 'login_form' template" do
      get :login_form
      response.should render_template(:login_form)
    end

    it "should render 'login_form' template for JAVASCRIPT" do
      xhr :get, :login_form
      response.should render_template(:login_form)
    end
    
    describe "already logged in" do
      before(:each) do
        controller.stub!(:logged_in?).and_return(true)
      end

      it "should set a success message if already lodded in" do
        get :login_form
        flash[:success].should_not be_blank
      end

      it "should redirect to the root_path" do
        get :login_form
        response.should redirect_to(root_path)
      end
      
      it "should redirect to the root_path JAVASCRIPT" do
        xhr :get, :login_form
        response.body.should == "window.location.href = '#{root_path}';"
      end
    end
  end
    
  describe "POST login" do
    before do
      # User.stub!(:new).and_return @user = mock_model(User)
      @user = User.make(:access_level => User::ACCESS_LEVEL_ADMIN )
      User.stub!(:authenticate_by_email).and_return(@user)
      @user.stub(:remember_token).and_return('mock_remember_token')
      @user.stub(:remember_token_expires_at).and_return(Time.now)        
    end

    it "should authenticate by email" do
      User.should_receive(:authenticate_by_email).with("mock_email", "mock_password")
      post :login, :user => { :name => "mock_email", :password => "mock_password"}
    end

    it "should set the current user to the authenticated user" do
      controller.should_receive(:current_user=).with(@user)
      post :login, :user => { :name => "mock_email", :password => "mock_password"}
    end

    describe "authentication passed" do
      before(:each) do
        controller.stub!(:logged_in?).and_return(true)
      end
      
      it "should redirect to the root_path" do
        post :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.should redirect_to(root_path)
      end

      it "should redirect to the session[:return_to] path" do
        controller.stub(:session).and_return(:return_to => '/mock_return_to')
        post :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.should redirect_to('/mock_return_to')
      end

      it "should redirect to the root_path JAVASCRIPT" do
        xhr :post, :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.body.should == "window.location.href = '#{root_path}';"
      end

      it "should redirect to the session[:return_to] path ror JAVASCRIPT" do
        controller.stub(:session).and_return(:return_to => '/mock_return_to')
        xhr :post, :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.body.should == "window.location.href = '/mock_return_to';"
      end
      
      describe ":remember me set" do
        it "should set the remember me token" do
          @user.should_receive(:set_remember_token)
          post :login, :user => { :name => "mock_email", :password => "mock_password"}, :remember_me => "1"
        end
        
        it "should set the auth_token cookie" do
          cookies = {}
          controller.stub(:cookies).and_return(cookies)
          @user.should_receive(:set_remember_token)
          post :login, :user => { :name => "mock_email", :password => "mock_password"}, :remember_me => "1"
        
          cookies[:auth_token][:value].should_not be_blank
          cookies[:auth_token][:expires].should_not be_blank
        end
      end                
    end  
    
    describe "authentication failed" do
      before(:each) do
        User.stub!(:authenticate_by_email).and_return(nil)
        User.stub!(:authenticate_by_login).and_return(nil)
      end

      it "should set an error message" do
        post :login, :user => { :name => "mock_email", :password => "mock_password"}
        # flash.now[:error].should_not be_blank
        response.should have_tag("div.error")
      end
      
      it "should render the login form" do
        post :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.should render_template(:login_form)
      end
      
      it "should render the login form JAVASCRIPT" do
        xhr :post, :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.should render_template(:login_form)
      end      
    end
    
    describe "user account has been disabled" do
      before(:each) do
        @user.stub!(:access_level).and_return(User::ACCESS_LEVEL_DISABLED)
        User.stub!(:authenticate_by_email).and_return(@user)
      end

      it "should set an error message" do
        post :login, :user => { :name => "mock_email", :password => "mock_password"}
        flash[:error].should_not be_blank
      end
      
      it "should redirect to the root_path" do
        post :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.should redirect_to(root_path)
      end
      
      it "should redirect to the root_path JAVASCRIPT" do
        xhr :post, :login, :user => { :name => "mock_email", :password => "mock_password"}
        response.body.should == "window.location.href = '#{root_path}';"
      end      
    end    
  end
    
  describe "GET signup_form" do
    it "should render the 'signup_form' template" do
      get :signup_form
      response.should render_template(:signup_form)
    end

    it "should render 'signup_form' template for JAVASCRIPT" do
      xhr :get, :signup_form
      response.should render_template(:signup_form)
    end
    
    describe "already logged in" do
      before(:each) do
        controller.stub!(:logged_in?).and_return(true)
      end

      it "should set a success message if already lodded in" do
        get :signup_form
        flash[:success].should_not be_blank
      end

      it "should redirect to the root_path" do
        get :signup_form
        response.should redirect_to(root_path)
      end
      
      it "should redirect to the root_path JAVASCRIPT" do
        xhr :get, :signup_form
        response.body.should == "window.location.href = '#{root_path}';"
      end
    end
  end    
    
  describe "GET logout" do
    describe "the user is logged in" do
      it "should reset the current users remember me token" do
        user = mock_model(User)
        controller.stub!(:current_user).and_return(user)
        controller.stub!(:logged_in?).and_return(true)
  
        user.should_receive(:reset_remember_token)
        get :logout
      end        
    end
    
    it "should delete the auth_token cookie" do
      cookies = {:auth_token => 'mock_token'}
      controller.stub!(:cookies).and_return(cookies)
      get :logout      
      cookies[:auth_token].should be_blank
    end
    
    it "should reset the session variables" do
      controller.should_receive(:reset_session)
      get :logout      
    end
          
    it "should redirec to the root page" do
      get :logout
      response.should redirect_to(root_path)
    end            
  end
    
  describe "GET details" do
    before(:each) do
      controller.stub!(:login_required)
      @user = User.make
      controller.stub!(:current_user).and_return(@user)      
    end
    
    it "should require the user to be logged_in" do
      controller.should_receive(:login_required)
      get :details
    end
       
    it "should set the user to the current user" do
      get :details
      assigns[:user].should == @user     
    end
    
    it "should render the details template" do
      get :details
      response.should render_template(:details)
    end
  end
  
  describe "GET change_form" do
    before(:each) do
      controller.stub!(:login_required)
      @user = User.make
      controller.stub!(:current_user).and_return(@user)
    end
    
    it "should require the user to be logged_in" do
      controller.should_receive(:login_required)
      get :change_form
    end
       
    it "should set the user to the current user" do
      get :change_form
      assigns[:user].should == @user     
    end
    
    it "should render the user_change_form template" do
      get :change_form
      response.should render_template(:change_form)
    end
  end

  describe "POST change" do
    before do
      controller.stub!(:login_required)
      @user = User.make
      @user.stub!(:update_attributes).and_return(true)
      controller.stub!(:current_user).and_return(@user)
    end
    
    it "should require the user to be logged_in" do
      controller.should_receive(:login_required)
      post :change, :user => {}
    end
    
    it "should not change the access level" do
      @user.should_receive(:update_attributes).with({})
      post :change, :user => {:access_level => 202}
    end
  
    it "should not change the password" do
      @user.should_receive(:update_attributes).with({})
      post :change, :user => {:password => 'new'}
    end
  
    it "should update the user fields" do
      @user.should_receive(:update_attributes).with("field_name" => 'field_value')
      post :change, :user => {:field_name => 'field_value'}
    end
      
    describe "update failed" do
      before do
        @user.stub!(:update_attributes).and_return(false)
      end
      
      it "should render the 'user_change_form' template" do
        post :change, :user => { :name => "value" }
        response.should render_template(:change_form)
      end
    end
    
    describe "user updated" do
      it "should redirect to the user_details path" do
        post :change, :user => { :name => "value" }
        response.should redirect_to(user_details_path)
      end
      
      it "should have a success flash message" do
        post :change, :user => { :name => "value" }
        flash[:success].should_not be_blank
      end
    end
  end

  describe "GET pswd_change_form" do
    before(:each) do
      controller.stub!(:login_required)
      @user = User.make
      controller.stub!(:current_user).and_return(@user)
    end
    
    it "should require the user to be logged_in" do
      controller.should_receive(:login_required)
      get :pswd_change_form
    end
       
    it "should set the user to the current user" do
      get :pswd_change_form
      assigns[:user].should == @user     
    end
    
    it "should render the pswd_change_form template" do
      get :pswd_change_form
      response.should render_template(:pswd_change_form)
    end
  end

  describe "POST pswd_change" do
    before do
      controller.stub!(:login_required)
      @user = User.make(:email => 'mock_email')
      @user.stub!(:update_attributes).and_return(true)
      controller.stub!(:current_user).and_return(@user)

      User.stub!(:authenticate_by_email).and_return(true)
      
      @valid_params = {:old_password => 'old password', :password => 'new password', :password_confirmation => 'new password'}
    end
    
    it "should require the user to be logged_in" do
      controller.should_receive(:login_required)
      post :pswd_change, :user => @valid_params
    end
  
    it "should require the old_password" do
      post :pswd_change, :user => @valid_params.except(:old_password)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_change_form)
    end

    it "should require the password" do
      post :pswd_change, :user => @valid_params.except(:password)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_change_form)
    end

    it "should require the password confirmation" do
      post :pswd_change, :user => @valid_params.except(:password_confirmation)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_change_form)
    end
    
    it "should confirm the user's old password is correct" do
      User.should_receive(:authenticate_by_email).with("mock_email", "old password")
      post :pswd_change, :user => @valid_params
    end
    
    it "should require the old password to be correct" do
      User.stub!(:authenticate_by_email).and_return(false)
      post :pswd_change, :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_change_form)
    end

    it "should update the password fields only" do
      @user.should_receive(:update_attributes).with(:password => "new password", :password_confirmation => "new password")
      post :pswd_change, :user => @valid_params
    end
    
    it "should confirm the update of the password" do
      @user.should_receive(:update_attributes).and_return(false)
      post :pswd_change, :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_change_form)
    end

    it "should redirect to the user_details_path" do
      @user.should_receive(:update_attributes).and_return(true)
      post :pswd_change, :user => @valid_params
      flash[:success].should_not be_blank
      response.should redirect_to(user_details_path)
    end    
  end  
  
    
  describe "GET pswd_forgot_form" do
    it "should render the pswd_forgot_form template" do
      get :pswd_forgot_form
      response.should render_template(:pswd_forgot_form)
    end
    
    it "should render 'pswd_forgot_form' template for JAVASCRIPT" do
      xhr :get, :pswd_forgot_form
      response.should render_template(:pswd_forgot_form)
    end
    
    describe "already logged in" do
      before(:each) do
        controller.stub!(:logged_in?).and_return(true)
      end

      it "should set a success message if already lodded in" do
        get :pswd_forgot_form
        flash[:success].should_not be_blank
      end

      it "should redirect to the root_path" do
        get :pswd_forgot_form
        response.should redirect_to(root_path)
      end
      
      it "should redirect to the root_path JAVASCRIPT" do
        xhr :get, :pswd_forgot_form
        response.body.should == "window.location.href = '#{root_path}';"
      end
    end
  end    

  
  describe "POST pswd_forgot" do
    it "should set the reset token" do
      User.should_receive(:set_reset_token).with('mock_email')
        post :pswd_forgot, :user => {:email => "mock_email"}
    end
      
    describe "reset token set" do
      before(:each) do
        User.stub!(:set_reset_token).and_return(true)
      end
      
      it "should set a success message" do
        post :pswd_forgot, :user => {:email => "mock_email"}
        flash[:success].should_not be_blank
      end
          
      it "should redirect to the user login page" do
        post :pswd_forgot, :user => {:email => "mock_email"}
        response.should redirect_to(user_login_path)
      end
      
      it "should redirect to the user login for JAVASCRIPT" do
        xhr :post, :pswd_forgot, :user => {:email => "mock_email"}
        response.body.should == "window.location.href = '#{user_login_path}';"
      end
    end

    describe "reset token not set" do
      before(:each) do
        User.stub!(:set_reset_token).and_return(false)
      end
      
      it "should render the pswd_forgot_form" do
        post :pswd_forgot, :user => {:email => "mock_email"}
        # flash.now[:error].should_not be_blank
        response.should have_tag("div.error")
        response.should render_template(:pswd_forgot_form)
      end
      
      it "should render the pswd_forgot_form for JAVASCRIPT" do
        xhr :post, :pswd_forgot, :user => {:email => "mock_email"}
        response.should render_template(:pswd_forgot_form)
      end      
    end
  end

  describe "GET pswd_reset_form" do    
    it "should require a reset token" do
      get :pswd_reset_form
      # flash[:error].should_not be_blank
      response.should redirect_to(user_pswd_forgot_path)
    end
    
    it "should authenticate the reset token" do
      User.should_receive(:authenticate_by_reset_token).with('mock_token')
      get :pswd_reset_form, :token => "mock_token"
    end

    it "should assign the reset token" do
      get :pswd_reset_form, :token => "mock_token"
      assigns[:token].should == "mock_token"
    end

    it "should redirect to password forgot form if authentication failed" do
      User.stub!(:authenticate_by_reset_token).and_return(false)
      get :pswd_reset_form, :token => "mock_token"
      flash[:error].should_not be_blank
      response.should redirect_to(user_pswd_forgot_path)
    end

    it "should render the pswd_reset_form" do
      User.stub!(:authenticate_by_reset_token).and_return(true)
      get :pswd_reset_form, :token => "mock_token"
      response.should render_template(:pswd_reset_form)
    end
  end    

  describe "POST pswd_reset" do
    before do
      @user = User.make(:email => 'mock_email')
      @user.stub!(:update_attributes).and_return(true)
      User.stub!(:authenticate_by_reset_token).and_return(@user)
      @valid_params = {:email => 'mock_email', :password => 'new password', :password_confirmation => 'new password'}
    end
    
    it "should require a reset token" do
      post :pswd_reset, :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should render_template(:pswd_forgot_form)
    end
    
    it "should require an email address" do
      post :pswd_reset, :token => 'mock_token', :user => @valid_params.except(:email)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_reset_form)
    end

    it "should require a password" do
      post :pswd_reset, :token => 'mock_token', :user => @valid_params.except(:password)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_reset_form)
    end

    it "should require a password to match the password confirmation" do
      post :pswd_reset, :token => 'mock_token', :user => @valid_params.merge(:password_confirmation => 'wrong')
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_reset_form)
    end

    it "should authenticate the reset token" do
      User.should_receive(:authenticate_by_reset_token).with("mock_token")
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
    end

    it "should require the reset token to be authenticated" do
      User.stub!(:authenticate_by_reset_token).and_return(false)
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_reset_form)
    end

    it "should require the authenticated user to have the same email address" do
      @user.stub!(:email).and_return('wrong')
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_reset_form)
    end

    it "should update the user's password" do
      @user.should_receive(:update_attributes).with(:password => 'new password', :password_confirmation => 'new password')
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
    end

    it "should require the password update to be successfull" do
      @user.stub!(:update_attributes).and_return(false)
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_reset_form)
    end

    it "should set the authenticated user to the current user" do
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
      controller.current_user.should == @user
    end

    it "should redirect to the root_page" do
      post :pswd_reset, :token => 'mock_token', :user => @valid_params
      flash[:success].should_not be_blank
      response.should redirect_to(root_path)
    end
  end

  describe "GET welcome_form" do    
    it "should require a reset token" do
      get :welcome_form
      response.should redirect_to(user_pswd_forgot_path)
    end
    
    it "should authenticate the reset token" do
      User.should_receive(:authenticate_by_reset_token).with('mock_token')
      get :welcome_form, :token => "mock_token"
    end

    it "should redirect to password forgot form if authentication failed" do
      User.stub!(:authenticate_by_reset_token).and_return(false)
      get :welcome_form, :token => "mock_token"
      flash[:error].should_not be_blank
      response.should redirect_to(user_pswd_forgot_path)
    end

    it "should render the welcome_form" do
      User.stub!(:authenticate_by_reset_token).and_return(true)
      get :welcome_form, :token => "mock_token"
      response.should render_template(:welcome_form)
    end
  end    

  describe "POST welcome" do
    before do
      @user = User.make(:email => 'mock_email')
      @user.stub!(:update_attributes).and_return(true)
      User.stub!(:authenticate_by_reset_token).and_return(@user)
      @valid_params = {:email => 'mock_email', :password => 'new password', :password_confirmation => 'new password'}
    end
    
    it "should require a reset token" do
      post :welcome, :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:pswd_forgot_form)
    end
    
    it "should require an email address" do
      post :welcome, :token => 'mock_token', :user => @valid_params.except(:email)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:welcome_form)
    end

    it "should require a password" do
      post :welcome, :token => 'mock_token', :user => @valid_params.except(:password)
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:welcome_form)
    end

    it "should require a password to match the password confirmation" do
      post :welcome, :token => 'mock_token', :user => @valid_params.merge(:password_confirmation => 'wrong')
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:welcome_form)
    end

    it "should authenticate the reset token" do
      User.should_receive(:authenticate_by_reset_token).with("mock_token")
      post :welcome, :token => 'mock_token', :user => @valid_params
    end

    it "should require the reset token to be authenticated" do
      User.stub!(:authenticate_by_reset_token).and_return(false)
      post :welcome, :token => 'mock_token', :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:welcome_form)
    end

    it "should require the authenticated user to have the same email address" do
      @user.stub!(:email).and_return('wrong')
      post :welcome, :token => 'mock_token', :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:welcome_form)
    end

    it "should update the user's password" do
      @user.should_receive(:update_attributes).with(:password => 'new password', :password_confirmation => 'new password')
      post :welcome, :token => 'mock_token', :user => @valid_params
    end

    it "should require the password update to be successfull" do
      @user.stub!(:update_attributes).and_return(false)
      post :welcome, :token => 'mock_token', :user => @valid_params
      # flash.now[:error].should_not be_blank
      response.should have_tag("div.error")
      response.should render_template(:welcome_form)
    end

    it "should set the authenticated user to the current user" do
      post :welcome, :token => 'mock_token', :user => @valid_params
      controller.current_user.should == @user
    end

    it "should redirect to the root_page" do
      post :welcome, :token => 'mock_token', :user => @valid_params
      flash[:success].should_not be_blank
      response.should redirect_to(root_path)
    end
  end  
end