ActionMailer not delivering confirmation emails in test environment - Rails 4 - ruby-on-rails-4

I'm using Rails (4.2.6), Ruby (2.2.4), Devise (4.1.1), Capybara (2.7.1), Capybara-email (2.5.0), Email_spec (2.1.0), Rspec (3.4.0), and Postgres (0.18.4)
After I upgraded the application from Rails 4.1.15 to 4.2.6, several authentification & registration tests fail. Before the upgrade all tests were properly passing. The code works as expected in the development environment (for example, confirmation emails are sent & viewable in the Rails server terminal). The problem of non-delivered emails only occurs in the test environment.
Here's the failing rspec ./spec/features/users/authentification_spec.rb:56:
#Sign up User
visit "/"
click_link "Sign up"
find(:css, "#user_email").set("tester9#example.com")
find(:css, "#user_password").set("password900")
find(:css, "#user_password_confirmation").set("password900")
expect {
click_button "Sign up"
}.to change{ ActionMailer::Base.deliveries.size}.by(1)
When a user completes the form and clicks the "Sign up" button, the page redirects to the "About" page and as expected, the following flash message appears: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account."
Using save_and_open_page, I confirmed the aforementioned behavior. However, the spec fails with the following error:
Failure/Error:
expect {
click_button "Sign up"
}.to change{ ActionMailer::Base.deliveries.size}.by(1)
expected result to have changed by 1, but was changed by 0
# ./spec/features/users/authentification_spec.rb:56:in `block (2 levels) in <top (required)>'
The error indicates that there are no message objects in the ActionMailer::Base.deliveries array. The results of Pry confirm that the ActionMailer::Base.deliveries array is indeed empty:
[1] pry(main)> mail = ActionMailer::Base.deliveries
=> []
Here's the test log when the spec is run:
Started POST "/users" for 127.0.0.1 at 2016-06-09 16:16:25 -0700
Processing by RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "user"=>{"email"=>"tester9#example.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
[1m[35m (0.8ms)[0m SAVEPOINT active_record_1
[1m[36mUser Exists (3.3ms)[0m [1mSELECT 1 AS one FROM "users" WHERE "users"."email" = 'tester9#example.com' LIMIT 1[0m
[1m[35mUser Exists (1.9ms)[0m SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER('tester9#example.com') LIMIT 1
[1m[36mSQL (2.1ms)[0m [1mINSERT INTO "users" ("email", "encrypted_password", "signup_as", "created_at", "updated_at", "confirmation_token", "confirmation_sent_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"[0m [["email", "tester9#example.com"], ["encrypted_password", "$2a $04$Vruf8j0A.DdOZLPe0qjVp.4PxzdR7sCdLF4FfyAr4dQSxcQAjpAwy"], ["created_at", "2016-06-09 23:16:25.095386"], ["updated_at", "2016-06-09 23:16:25.095386"], ["confirmation_token", "EmY8JyaVAxAfiq7oQ98z"], ["confirmation_sent_at", "2016-06-09 23:16:25.097581"]]
[1m[35m (0.4ms)[0m RELEASE SAVEPOINT active_record_1
Redirected to http://www.example.com/about
Completed 302 Found in 113ms (ActiveRecord: 9.6ms)
The log indicates that a confirmation email was sent, but the deliveries array is empty. Why is that happening? Is the record not being committed or persisted in the test database for some reason? I've read related posts about non-delivery of mail, but have not found a solution to my problem.
Relevant code from test.rb:
# Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = true
Relevant contents from rails_helper.rb:
require 'rspec/rails'
require 'devise'
require 'capybara/rails'
require 'database_cleaner'
require 'capybara/poltergeist'
require 'capybara/email/rspec'
require 'email_spec'
# Includes Devise test helpers (e.g., lets you use Devise's "sign_in" method in tests)
config.include Devise::TestHelpers, :type => :controller
config.include Warden::Test::Helpers
Relevant commented-out code from devise.rb:
# Configure the class responsible to send e-mails.
#config.mailer = 'Devise::Mailer'
Mail delivery works as expected in development & production. What is going wrong in the test environment? How can I fix it? Thanks!

Looking at devise - https://github.com/plataformatec/devise/blob/4-1-stable/lib/devise/models/confirmable.rb#L48 - it sends the confirmation email in an after_commit callback - If you're running with transactional testing enabled the after_commit callback will never be called (because the DB transaction is rolled back and never committed) so the email is never sent. Disable transaction based testing for that test and it will probably work.

Just to summarize the discussion between Tom Walpole and codeinspired above into a top-level answer for browsers who might not look into comments, their solution (assuming you use DatabaseCleaner) is to make the following modifications:
rails_helper.rb
# lots of boilerplate and
# other things at the top of this file, but you'll
# eventually see....
RSpec.configure do |config|
# ... other config stuff ...
# sometime after this key line, you want to add:
config.before(:each, truncation: true) do
DatabaseCleaner.strategy = :truncation
end
# ... other config stuff ...
end
For the test that is in question here, you want to modify it to look like the following (note the addition of "truncation: true" in the line starting with the word "it")
something_spec.rb
# ... other stuff ...
describe "some function" do
it "performs a function correctly", truncation: true do
# your test
# goes here
end
end

Related

rails redirecting from expired assets

I have a rails (4.1) app running on Heroku with cloudflare as the CDN. In the error logs from NewRelic I see a constant trickle of requests for expired css and js assets, primarily application-<fingerprint>.js and application-<fingerprint>.css(with fingerprints that have been expired).
I am wondering about a solution to redirect these requests to the current asset but I am uncertain if this is a good/safe thing to do.
In my routes I'd add
get "assets/:asset_name" => "assets#show"
and then add an assets_controller.rb with:
class AssetsController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token, :only => [:show]
def show
begin
asset_name = params[:asset_name].gsub(/-[0-9a-f]{32}$/, "") << ".#{params[:format]}"
if ["css", "js"].include?(params[:format])
redirect_to "/assets/" + Rails.application.assets.find_asset(asset_name).digest_path
else
return asset_not_found!
end
rescue
return asset_not_found!
end
end
private
def asset_not_found!
render :text => "asset #{params[:asset_name]}.#{params[:format]} not found", :status => 404
end
end
I've tried this out on a stage environment and it works but I'm not sure if this is the right way.
In particular the need to have skip_before_action :verify_authenticity_token bothers me, but without it requests for .js assets result in a InvalidCrossOriginRequest error.
I only see requests for expired css and js assets, not for any image assets, hence the above check for the request format being either "css" or "js", but maybe that's an unnecessary step.
So my question is; would doing this be bad practice? Is there a better way to handle requests for expired assets?
i consider this a bad practice.
not particular the way that you implemented id (even though i think there are better ways using rack middlewares), but more because of the fact that you should not redirect those expired assets anywhere.
if a user requests a css or js file with a stale fingerprint they likely have a html document that is out of date and you will probably want him to reload what he has: "there is a new version, please reload your site".
deploying is taken care of at heroku since sprockets stores up to 3 versions of the assets for you https://devcenter.heroku.com/articles/rails-4-asset-pipeline#only-generate-digest-assets

'Sign up' process fails

I'm relatively new to rails, so please keep this in mind when answering. :)
I've added the clearance gem to my rails app. I executed rails g clearance:views, no hassles. Next I executed rails g clearance:specs. All the clearance specs pass except for one:
/spec/features/clearance/visitor_signs_up_spec.rb:13 # Visitor signs up with valid email and password
Here's the rspec error message:
Visitor signs up with valid email and password
Failure/Error: expect_user_to_be_signed_in
expected to find button "Sign out" but there were no matches
# ./spec/support/features/clearance_helpers.rb:35:in `expect_user_to_be_signed_in'
# ./spec/features/clearance/visitor_signs_up_spec.rb:16:in `block (2 levels) in <top (required)>'
# -e:1:in `<main>'
Interestingly, when I attempt to follow the spec manually, the sign up process completes but it doesn't create a new session. In other words, I have to sign in after signing up. This appears to match the behavior that occurs in /spec/features/clearance/visitor_signs_up_spec.rb:13, although it's not the expected behavior, according to that spec.
None of the other specs that use the same method, expect_user_to_be_signed_in, have this problem.
Also, my rails server log doesn't indicate any errors.
I don't know where else to look to narrow down the problem. What do you think?
UPDATE: Found an error!
Started POST "/users" for ::1 at 2015-10-18 15:15:44 -0600
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"[FILTERED]", "user"=>{"first_name"=>"Michael", "last_name"=>"Hargiss", "username"=>"mycargus", "email"=>"valid#example.com", "password"=>"[FILTERED]"}, "commit"=>"Sign up"}
Redirected to http://localhost:3000/sign_in
Filter chain halted as :require_login rendered or redirected
Completed 302 Found in 1ms (ActiveRecord: 0.0ms)
UPDATE: I fixed the previous error by changing the redirect url after successfully saving a new user. I also had to add the following to my UsersController:
before_action :require_login, only: [:index, :show, :edit, :update, :destroy]
Any ideas?
Still can't get it to sign in automatically after signing up. Instead I implemented a workaround inside Users#create:
if #user.save
format.html { redirect_to sign_in_path, notice: "Welcome, #{#user.first_name}!\nSign in to continue." }
...
end

cucumber test in rails application does not seem to be submitting a form to log a user in

I am trying to test my rails app using cucumber and webrat. I'm trying to get the test to login as a user who is already created and stored in the database, and I'm getting the following error:
Then I see a login error message # features/step_definitions/user_steps.rb:16
expected the following element's content to include "Welcome admin":
Csa
Welcome Guest
Forgot password?
or
Register
HomeJobs
English
Cymraeg
Welcome to CS-Alumni News
The is where the home page text will go.
(RSpec::Expectations::ExpectationNotMetError)
./features/step_definitions/user_steps.rb:18:in `/^I see a login error message$/'
features/users/login.feature:9:in `Then I see a login error message'
I changed the code to get it to login a user instead of failing to log in a user due to invalid details, as that wasn't working, and I wasn't sure if it was because it didn't pick up the flash message or not.
Here is the step_definitions file (user_steps.rb)
require 'webrat'
Given /^I do not have an account$/ do
#user = FactoryGirl.create(:user)
#user ||= User.where(:email >= #user[:email]).first
#user.destroy unless #user.nil?
end
When /^I attempt to login with my details$/ do
visit new_session_path
fill_in "login-input", :with => "admin"
fill_in "password", :with => "taliesin"
clicks_button "Login"
end
Then /^I see a login error message$/ do
visit home_path
response.should contain("Welcome admin")
end
And /^I should not be logged in$/ do
#response.should_not contain("Welcome admin")
end
Here is the login.feature file:
Feature: Login
In order to access my account
As a user
I want to be able to login
Scenario: User does not have an account
Given I do not have an account
When I attempt to login with my details
Then I see a login error message
And I should not be logged in
Scenario: User enters incorrect Login
Given I have an account
And I am not logged in
When I enter an incorrect Login
Then I see a login error message
And I should not be logged in
Scenario: User enters incorrect password
Given I have an account
And I am not logged in
When I enter an incorrect password
Then I see a login error message
And I should not be logged in
Scenario: User logs in successfully
Given I have an account
And I am not logged in
When I enter my correct login
And I enter my correct password
Then I see a successful login message
When I return to the application
Then I should still be logged in
Here is the env.rb file;
require 'cucumber/rails'
require 'webrat'
require 'cucumber/rails/world'
require 'webrat/core/matchers'
Webrat.configure do |config|
config.mode = :rack
config.open_error_files = false
end
World(Webrat::Methods)
World(Webrat::Matchers)
ActionController::Base.allow_rescue = false
begin
DatabaseCleaner.strategy = :transaction
rescue NameError
raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
end
Cucumber::Rails::World.use_transactional_fixtures = false
class ActiveSupport::TestCase
setup do |session|
session.host! "localhost:3001"
end
end
rails/blob/master/features/choose_javascript_database_strategy.feature
Cucumber::Rails::Database.javascript_strategy = :truncation
Here is the gemfile:
# Use jquery as the JavaScript library
gem "jquery-rails", "~> 2.3.0"
gem 'jquery-ui-rails', '4.1.2'
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 1.2'
group :doc do
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', require: false
end
group :development, :test do
gem 'rspec-rails', '~> 3.0.0'
gem 'cucumber-rails', require: false
gem "webrat"
gem 'factory_girl_rails'
gem 'selenium-client'
gem 'capybara'
gem 'database_cleaner'
end
Ok, so your step is this:
Then /^I see a login error message$/ do
visit home_path
response.should contain("Welcome admin")
end
And the error you're getting is: RSpec::Expectations::ExpectationNotMetError
I think it's just a case of you're expectation is wrong, but is at least a bit misleading. We should actually see an error (something like "Cannot login") but we are looking for "Welcome admin" which seems more like a welcome message.
We can debug this by placing puts response.body before the expectation to see what is being rendered.
It may also be that the cucumber isn't waiting for the request to finish. We could try adding find('#some-element') which will wait for a few seconds before failing if it cannot find an element.
Edit: Also, I just noticed the feature that you were running isn't the one you included (login.feature) so you're trying to see an error message without attempting an incorrect login.

Implementing resource_owner_authenticator doesnt work

I'm writing an API aside with an web page. I want to have Oauth2 login so i can keep a session from the mobile device, i've already install the GEM doorkeeper and run the migration how it's explained on this site.
where i'm getting stuck is in the resource_owner_from_credentials parts, since i have an User model which has authenticate method given by the has_secure_password helper from rails. this is how my /config/initializers/doorkeeper.rb file looks like
Doorkeeper.configure do
# Change the ORM that doorkeeper will use.
# Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_from_credentials do
User.find_by_email(params[:email]).authenticate(params[:password])
end
##lots of comments
end
and when go to
localhost:3000/oauth/authorize
i get this:
config/initializers/doorkeeper.rb:8:in `block (2 levels) in <top (required)>'
then i tried:
http://127.0.0.1:3000/oauth/authorize?email=puca#gmail.com&password=porche
and the same
what i'm i doing wrong? how should i configuresource_owner_authenticator block? how do i get the token?
As per this doorkeeper wiki page you need to send a POST request to /oauth/token API with the following params:
{
"grant_type" : "password",
"username" : "user#example.com",
"password" : "sekret"
}
When this request is processed, doorkeeper calls the resource_owner_from_credentials block and passes the params to it. So you have access to the param named username and not email.
To summarize, fix the API endpoint to /oauth/token , change params[:email] to params[:username] and everything should work.

Signed cookies and Test:unit don't mix: "NoMethodError"

I am using signed cookies to maintain my user across pages, pretty much implemented as here.
I use two methods, sign_in(account) and sign_out to manipulate my cookies, creating or destroying them as you would expect ...
module SessionsHelper
def sign_in account
cookies.signed[:auth_account] = {
:expires => 1.week.from_now,
:value => [account.id, account.hash]
}
end
def sign_out
cookies.delete(:auth_account)
end
end
However, when trying to use this method, or the authenticate method that matches it in the ApplicationController from the functional tests, I get a NoMethodError:
NoMethodError: undefined method `signed' for {}:ActiveSupport::HashWithIndifferentAccess
I realise from this and this that this is an issue with the way cookies are defined differently in the test case, but I can't get any of these solutions to work. For completeness, an example test that fails with the above error:
require 'test_helper'
class AccountsControllerTest < ActionController::TestCase
include SessionsHelper
setup do
# We need to fake authentication by manually
# assigning an account to the sign_in process
sign_in accounts(:ia)
#account = accounts(:ia)
end
test "should 403 on index if unauthenticated" do
sign_out
get :index
assert_response :forbidden
end
test "should 200 on index if authenticated" do
get :index
assert_response :success
end
end
You can't set cookies in your test with the cookies variable like you do in your controller.
The cookies variable in your tests is use for read cookies after doing requests call (get/post...) and not for writing
In order to spoof login within your tests you should set cookies via #request.cookies[:auth]
you can put the following method in your test_helper and use it in all your tests
def signin user
#request.cookies[:auth] = user.auth
end
and in your tests
test "get dashboard if logged in" do
signin #user
get :index
end