Как использовать omniauth в rspec для Sinatra?

Сокращенная версия:

Используя omniauth gem для sinatra, я не могу заставить rspec войти в систему и сохранить сеанс для последующих запросов.

Основано на предложениях с http://benprew.posterous.com/testing-sessions-with-sinatra и отключив сеансы, я определил проблему следующим образом:

  app.send(:set, :sessions, false)    # From http://benprew.posterous.com/testing-sessions-with-sinatra
  get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
  # last_request.session => {"uid"=>"222222222222222222222", :flash=>{:success=>"Welcome"}}
  # last_response.body => ""

  follow_redirect!
  # last_request.session => {:flash=>{}}
  # last_response.body => Html for the homepage, which is what I want

Как заставить rspec следовать перенаправлению и сохранять переменные сеанса? Возможно ли это в Синатре?

Из http://benprew.posterous.com/testing-sessions-with-sinatra похоже, мне придется отправлять переменные сеанса при каждом запросе на получение/отправку, для которого мне требуется вход в систему, но это не сработает в случае перенаправления.


Подробности:

Я пытаюсь использовать драгоценный камень omniauth в Sinatra со следующей настройкой:

spec_helper.rb

ENV['RACK_ENV'] = 'test'

# Include web.rb file
require_relative '../web'
# Include factories.rb file
require_relative '../test/factories.rb'

require 'rspec'
require 'rack/test'
require 'factory_girl'
require 'ruby-debug'

# Include Rack::Test in all rspec tests
RSpec.configure do |conf|
  conf.include Rack::Test::Methods
  conf.mock_with :rspec
end

web_spec.rb

describe "Authentication:" do
  before do
    OmniAuth.config.test_mode = true
    OmniAuth.config.add_mock(:google_oauth2, {
      :uid => '222222222222222222222',
      :info => {
        :email => "[email protected]",
        :name => 'Someone'
      }
    })
  end

  describe "Logging in as a new user" do
    it "should work" do
      get '/auth/google_oauth2/'

      last_response.body.should include("Welcome")
    end
  end
end

При попытке аутентификации я получаю ответ <h1>Not Found</h1>. Что мне не хватает?

На странице Интеграционное тестирование документации omniauth упоминается добавление двух переменных среды:

before do 
  request.env["devise.mapping"] = Devise.mappings[:user] 
  request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:twitter] 
end

Но, похоже, только для рельсов, как я добавил

request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2] 

к моему блоку before в моей спецификации, и я получаю эту ошибку:

Failure/Error: request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:google_oauth2]
 ArgumentError:
   wrong number of arguments (0 for 1)

Редактировать:

Звоню get с

get '/auth/google_oauth2/', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}

кажется, дает мне last_request.env["omniauth.auth"] равное

 {"provider"=>"google_oauth2", "uid"=>"222222222222222222222", "info"=>{"email"=>"[email protected]", "name"=>"Someone"}}

что кажется правильным, но last_response.body все еще возвращает

<h1>Not Found</h1>

person zlog    schedule 07.12.2011    source источник
comment
Я нашел этот stackoverflow.com/a/3892401/111884, который кажется на правильном пути, но я не могу заставить его Работа.   -  person zlog    schedule 08.12.2011


Ответы (2)


Частичный ответ...

URL-адрес обратного вызова работает лучше с добавленными переменными среды запроса:

get '/auth/google_oauth2/callback', nil, {"omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2]}
follow_redirect!

last_response.body.should include("Welcome")

Однако это не работает с сеансами после перенаправления, которое требуется моему приложению, чтобы знать, что кто-то вошел в систему. Обновлен вопрос, чтобы отразить это.

person zlog    schedule 07.12.2011
comment
На самом деле мне нужно, чтобы сеансы работали для входа в систему, поэтому ответ на самом деле не работает для моего приложения. - person zlog; 08.12.2011

Используя эту суть (исходя из https://stackoverflow.com/a/3892401/111884) для хранения данных сеанса, я получил свои тесты для хранения сеанса, что позволяет мне передавать сеанс для дальнейших запросов.

Хотя может есть более простой способ.

Код установки:

# Omniauth settings
OmniAuth.config.test_mode = true
OmniAuth.config.add_mock(:google_oauth2, {
  :uid => '222222222222222222222',
  :info => {
    :email => "[email protected]",
    :name => 'Someone'
  }
})


# Based on https://gist.github.com/375973 (from https://stackoverflow.com/a/3892401/111884)
class SessionData
  def initialize(cookies)
    @cookies = cookies
    @data = cookies['rack.session']
    if @data
      @data = @data.unpack("m*").first
      @data = Marshal.load(@data)
    else
      @data = {}
    end
  end

  def [](key)
    @data[key]
  end

  def []=(key, value)
    @data[key] = value
    session_data = Marshal.dump(@data)
    session_data = [session_data].pack("m*")
    @cookies.merge("rack.session=#{Rack::Utils.escape(session_data)}", URI.parse("//example.org//"))
    raise "session variable not set" unless @cookies['rack.session'] == session_data
  end
end

def login!(session)
  get '/auth/google_oauth2/callback', nil, { "omniauth.auth" => OmniAuth.config.mock_auth[:google_oauth2] }
  session['uid'] = last_request.session['uid']

  # Logged in user should have the same uid as login credentials
  session['uid'].should == OmniAuth.config.mock_auth[:google_oauth2]['uid']
end

# Based on Rack::Test::Session::follow_redirect!
def follow_redirect_with_session_login!(session)
  unless last_response.redirect?
    raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
  end

  get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url, "rack.session" => {"uid" => session['uid']} })
end

def get_with_session_login(path)
  get path, nil, {"rack.session" => {"uid" => session['uid']}}
end

Пример кода rspec:

describe "Authentication:" do

  def session
    SessionData.new(rack_test_session.instance_variable_get(:@rack_mock_session).cookie_jar)
  end

  describe "Logging in as a new user" do
    it "should create a new account with the user's name" do
      login!(session)
      last_request.session[:flash][:success].should include("Welcome")

      get_with_session_login "/"
      follow_redirect_with_session_login!(session)
      last_response.body.should include("Someone")
    end
  end
end
person zlog    schedule 08.12.2011