Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to check if rails set permanent cookie?

I'm using TestUnit and I want to test the remember me functionality (when user login).

the cookies variable(and also require/response .cookies) contain only the cookie value without the expire time.

Rails somehow tell the web-browser when the cookie should be expire, so I assume there must be a way to check the cookie expire time.

EDIT

test "set permanent cookie" do
  post :create, email: 'email', password: 'password', remember_me: true
  # cookies[:auth_token] = random_string
  # @request.cookies[:auth_token] = also_random_string
  # @response.cookies[:auth_token] = also_random_string
end

the problem is that I can get only the values of the cookies and not the hash that contain the expire time.

like image 696
gilsilas Avatar asked Aug 16 '11 14:08

gilsilas


2 Answers

As you've noticed, the cookies Hash only contains the values, not the expiration times, when you inspect it after your post call (this has been the behavior since at least Rails 2.3).

You have two options:

First, you could inspect the response.headers["Set-Cookie"] item instead. It will include the expiration time in there. However, the Set-Cookie value is just a single string, which you would need to then parse. For example, cookies["foo"] = {:value => "bar", :expires => Time.now + 10.years } would give you:

response.headers["Set-Cookie"]
# => "foo=bar; path=/; expires=Mon, 16-Aug-2021 21:54:30 GMT"

The other option (taken from This Question/Answer), would be to stub the cookie jar and make sure it is sent an expires value:

stub_cookie_jar = HashWithIndifferentAccess.new
controller.stub(:cookies) { stub_cookie_jar }
post :create, email: 'email', password: 'password', remember_me: true
expiring_cookie = stub_cookie_jar['expiring_cookie']
expiring_cookie[:expires].to_i.should be_within(1).of(1.hour.from_now.to_i)
like image 163
Dylan Markow Avatar answered Oct 05 '22 18:10

Dylan Markow


Unfortunately, I couldn't get the solutions presented or linked in @DylanMarkow's answer working, so here is how I tested that a "permanent" cookie was being set when a Remember Me checkbox was checked (the tests are influenced/blatantly copied from the Test::Unit tests that DHH made in the commit that added cookies.permanent to Rails).

Tests use RSpec and FactoryGirl.

spec/requests/authentication_requests_spec.rb

require 'spec_helper'

describe "Authentication Requests" do
  # ...
  describe "authorization" do
    # ...
    describe "cookies" do
      let(:user) { FactoryGirl.create(:user) }

      subject { response.headers["Set-Cookie"] }

      context "when remember me is set" do
        before { sign_in_request(user) }
        it { should =~ %r(.+expires.+#{20.years.from_now.year}) }
      end

      context "when remember me is not set" do
        before { sign_in_request(user, remember_me: false) }
        it { should_not =~ %r(expires) }
      end
    end
  end
end

spec/utilities.rb

def sign_in_request(user, remember_me: "true")
  post session_path(
    session: {
      email: user.email,
      password: user.password,
      remember_me: remember_me
    }
  )
end

App code snippets below use i18n, Haml, Bootstrap, and Simple Form syntax:

app/views/sessions/new.html.haml

= simple_form_for :session, url: session_path, html: {class: 'form-vertical' } do |f|
  = f.input :email
  = f.input :password
  .checkbox
    = f.input :remember_me, as: :boolean, label: false do
      = check_box_tag :remember_me, 1, true
      = label_tag :remember_me
  = f.submit t('.signin'), class: "btn btn-large btn-primary"

app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
  # ...
  def create
    if user = User.authenticate(params[:session][:email],
                                params[:session][:password])
      sign_in user
      flash[:success] = t('flash.successful_signin')
      redirect_to root_url
    else
      flash.now[:error] = t('flash.invalid_credentials')
      render 'new'
    end
  end
  # ...
end

app/models/user.rb

class User < ActiveRecord::Base
  has_secure_password
  # ...
  before_create :generate_authentication_token

  def self.authenticate(email, password)
    find_by_email(email).try(:authenticate, password)
  end

  private

    def generate_authentication_token
      begin
        self.authentication_token = SecureRandom.urlsafe_base64
      end while User.exists?(authentication_token: self.authentication_token)
    end
end

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  # ...
  private

    # A cookie that does not have an expiry will automatically be expired by
    # the browser when browser's session is finished.
    # cookies.permanent sets the expiry to 20 years
    # Booleans seem to get passed from forms as strings
    def sign_in(user)
      if remember_me
        cookies.permanent[:authentication_token] = user.authentication_token
      else
        cookies[:authentication_token] = user.authentication_token
      end
      self.current_user = user
    end
    helper_method :sign_in

    def remember_me
      params[:session].try(:[], :remember_me) == "true" ||
        params[:remember_me] == "true"
    end
    # ...
end
like image 29
Paul Fioravanti Avatar answered Oct 05 '22 19:10

Paul Fioravanti