0

I have a User model and Authentications model, which is a basic omniauth setup. Essentially, users can sign up through oauth without setting a password.

I have a Authentication.is_destroyable? method that returns true if the user has a password or has more than one authentication. Essentially, this prevents users deleting their one and only way of authentication.

def is_destroyable?
  if user.encrypted_password.present? || user.authentications.count > 1
    true
  else
    errors.add :base, 'not allowed'
    false
  end
end

When testing this in development it works as expected under all conditions. However, my unit tests are failing:

describe "Authentication#is_destroyable?" do

  before(:each) do
    # This creates a user with no password and a single authentication
    @user = FactoryGirl.create(:user_with_oauth)
    @auth = @user.authentications.first
  end

  # This spec passes :)
  it "should return false when is users only authentication method" do
    @auth.is_destroyable?.should be_false
  end

  # This FAILS - I have no idea why :(
  it "should return true when user has multiple authentications" do
    @user.authentications.create FactoryGirl.attributes_for(:authentication, :provider => 'twitter')
    @auth.is_destroyable?.should be_true
  end

  # This FAILS - I have no idea why :(
  it "should return true when user has a password" do
    @user.update_attributes :password => 'password'
    @auth.is_destroyable?.should be_true
  end

end

I've spent the best part of 3 hours banging my head against the wall. I can't for the life of me understand why this works when I manually test the functionality (and Cucumber stories pass also testing the functionality), but in rspec the unit tests are failing. Is there something obvious that I'm missing?

Edit

As requested, here's some further detail.

Both failing specs fail with:

Failure/Error: @auth.is_destroyable?.should be_true
  expected false to be true

The Factories:

FactoryGirl.define do

  factory :user do
    username  { FactoryGirl.generate(:username) }
    name      'Test User'
    email     { FactoryGirl.generate(:email) }
    password  'password'
  end

  factory :user_with_oauth, :parent => :user do
    password        nil
    authentications [ FactoryGirl.build(:authentication) ]
  end

  factory :authentication do
    provider  'facebook'
    uid       SecureRandom.hex(16)
  end

end

Also, maybe relevant, am using DatabaseCleaner with the truncation strategy.

aaronrussell
  • 9,389
  • 5
  • 38
  • 62
  • what's the factory for the auth? – apneadiving Nov 04 '11 at 13:19
  • can you show the failure output? – jaydel Nov 04 '11 at 13:22
  • Have edited the original post – aaronrussell Nov 04 '11 at 13:26
  • Have added all the relevant factories now. – aaronrussell Nov 04 '11 at 13:52
  • Just for a check, does using `@user.authentications.create!` (note the use of `!`) throw an exception? And in the line after that, try reloading `@auth` instead of depending on the one assigned in the `before(:each)`? And also check if the second auth record did actually get created in the database. – Zabba Nov 04 '11 at 13:59
  • @zabba, using `create!` raises no exceptions so that must be OK. Following that I've reloaded `@auth` with `@auth = Authentication.last`. Still fails. – aaronrussell Nov 04 '11 at 14:32
  • @Zabba OK this is interesting - in that same test after the `create!` method I've added two further assertions: `@user.authentications.should have(2).auths` which passes; `Authentication.all.should have(2).auths` which fails, saying only 1 auth exists. Seems like the authentication is not getting save in the DB? – aaronrussell Nov 04 '11 at 14:37

1 Answers1

1

I can answer my own question (after 2 more hours of hitting my head against the wall)...

My :user_with_oath Factory was to blame; I wasn't wrapping the authentications association in a block:

factory :user_with_oauth, :parent => :user do
  password        nil
  authentications { [FactoryGirl.build(:authentication)] }
end
aaronrussell
  • 9,389
  • 5
  • 38
  • 62