Support custom has_secure_password attributes
Starting with version 6, Rails accepts custom attributes for has_secure_password macro. Without one it defaults to `password`, so everything should still work without breaking for older versions of Rails as well.
This commit is contained in:
parent
4ab77e8b46
commit
4868da7503
|
@ -10,49 +10,49 @@ module Shoulda
|
|||
# include ActiveModel::Model
|
||||
# include ActiveModel::SecurePassword
|
||||
# attr_accessor :password
|
||||
# attr_accessor :reset_password
|
||||
#
|
||||
# has_secure_password
|
||||
# has_secure_password :reset_password
|
||||
# end
|
||||
#
|
||||
# # RSpec
|
||||
# RSpec.describe User, type: :model do
|
||||
# it { should have_secure_password }
|
||||
# it { should have_secure_password(:reset_password) }
|
||||
# end
|
||||
#
|
||||
# # Minitest (Shoulda)
|
||||
# class UserTest < ActiveSupport::TestCase
|
||||
# should have_secure_password
|
||||
# should have_secure_password(:reset_password)
|
||||
# end
|
||||
#
|
||||
# @return [HaveSecurePasswordMatcher]
|
||||
#
|
||||
def have_secure_password
|
||||
HaveSecurePasswordMatcher.new
|
||||
def have_secure_password(attr = :password)
|
||||
HaveSecurePasswordMatcher.new(attr)
|
||||
end
|
||||
|
||||
# @private
|
||||
class HaveSecurePasswordMatcher
|
||||
attr_reader :failure_message
|
||||
|
||||
CORRECT_PASSWORD = "aBcDe12345"
|
||||
INCORRECT_PASSWORD = "password"
|
||||
|
||||
EXPECTED_METHODS = [
|
||||
:authenticate,
|
||||
:password=,
|
||||
:password_confirmation=,
|
||||
:password_digest,
|
||||
:password_digest=,
|
||||
]
|
||||
CORRECT_PASSWORD = "aBcDe12345".freeze
|
||||
INCORRECT_PASSWORD = "password".freeze
|
||||
|
||||
MESSAGES = {
|
||||
authenticated_incorrect_password: "expected %{subject} to not authenticate an incorrect password",
|
||||
did_not_authenticate_correct_password: "expected %{subject} to authenticate the correct password",
|
||||
authenticated_incorrect_password: "expected %{subject} to not authenticate an incorrect %{attribute}",
|
||||
did_not_authenticate_correct_password: "expected %{subject} to authenticate the correct %{attribute}",
|
||||
method_not_found: "expected %{subject} to respond to %{methods}"
|
||||
}
|
||||
}.freeze
|
||||
|
||||
def initialize(attribute)
|
||||
@attribute = attribute.to_sym
|
||||
end
|
||||
|
||||
def description
|
||||
"have a secure password"
|
||||
"have a secure password, defined on #{@attribute} attribute"
|
||||
end
|
||||
|
||||
def matches?(subject)
|
||||
|
@ -71,21 +71,41 @@ module Shoulda
|
|||
attr_reader :subject
|
||||
|
||||
def validate
|
||||
missing_methods = EXPECTED_METHODS.select {|m| !subject.respond_to?(m) }
|
||||
missing_methods = expected_methods.reject {|m| subject.respond_to?(m) }
|
||||
|
||||
if missing_methods.present?
|
||||
[:method_not_found, { methods: missing_methods.to_sentence }]
|
||||
else
|
||||
subject.password = CORRECT_PASSWORD
|
||||
subject.password_confirmation = CORRECT_PASSWORD
|
||||
subject.send("#{@attribute}=", CORRECT_PASSWORD)
|
||||
subject.send("#{@attribute}_confirmation=", CORRECT_PASSWORD)
|
||||
|
||||
if not subject.authenticate(CORRECT_PASSWORD)
|
||||
[:did_not_authenticate_correct_password, {}]
|
||||
elsif subject.authenticate(INCORRECT_PASSWORD)
|
||||
[:authenticated_incorrect_password, {}]
|
||||
if not subject.send(authenticate_method, CORRECT_PASSWORD)
|
||||
[:did_not_authenticate_correct_password, { attribute: @attribute }]
|
||||
elsif subject.send(authenticate_method, INCORRECT_PASSWORD)
|
||||
[:authenticated_incorrect_password, { attribute: @attribute }]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expected_methods
|
||||
@expected_methods ||= %I[
|
||||
#{authenticate_method}
|
||||
#{@attribute}=
|
||||
#{@attribute}_confirmation=
|
||||
#{@attribute}_digest
|
||||
#{@attribute}_digest=
|
||||
]
|
||||
end
|
||||
|
||||
def authenticate_method
|
||||
if @attribute == :password
|
||||
:authenticate
|
||||
else
|
||||
"authenticate_#{@attribute}".to_sym
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,5 +32,9 @@ module UnitTests
|
|||
def active_model_supports_full_attributes_api?
|
||||
active_model_version >= '5.2'
|
||||
end
|
||||
|
||||
def active_model_supports_custom_has_secure_password_attribute?
|
||||
active_model_version >= '6.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,18 +1,39 @@
|
|||
require 'unit_spec_helper'
|
||||
|
||||
describe Shoulda::Matchers::ActiveModel::HaveSecurePasswordMatcher, type: :model do
|
||||
it 'matches when the subject configures has_secure_password with default options' do
|
||||
working_model = define_model(:example, password_digest: :string) { has_secure_password }
|
||||
expect(working_model.new).to have_secure_password
|
||||
context "with no arguments passed to has_secure_password" do
|
||||
it 'matches when the subject configures has_secure_password with default options' do
|
||||
working_model = define_model(:example, password_digest: :string) { has_secure_password }
|
||||
expect(working_model.new).to have_secure_password
|
||||
end
|
||||
|
||||
it 'does not match when the subject does not authenticate a password' do
|
||||
no_secure_password = define_model(:example)
|
||||
expect(no_secure_password.new).not_to have_secure_password
|
||||
end
|
||||
|
||||
it 'does not match when the subject is missing the password_digest attribute' do
|
||||
no_digest_column = define_model(:example) { has_secure_password }
|
||||
expect(no_digest_column.new).not_to have_secure_password
|
||||
end
|
||||
end
|
||||
|
||||
it 'does not match when the subject does not authenticate a password' do
|
||||
no_secure_password = define_model(:example)
|
||||
expect(no_secure_password.new).not_to have_secure_password
|
||||
end
|
||||
if active_model_supports_custom_has_secure_password_attribute?
|
||||
context "when custom attribute is given to has_secure_password" do
|
||||
it 'matches when the subject configures has_secure_password with correct options' do
|
||||
working_model = define_model(:example, reset_password_digest: :string) { has_secure_password :reset_password }
|
||||
expect(working_model.new).to have_secure_password :reset_password
|
||||
end
|
||||
|
||||
it 'does not match when the subject is missing the password_digest attribute' do
|
||||
no_digest_column = define_model(:example) { has_secure_password }
|
||||
expect(no_digest_column.new).not_to have_secure_password
|
||||
it 'does not match when the subject does not authenticate a password' do
|
||||
no_secure_password = define_model(:example)
|
||||
expect(no_secure_password.new).not_to have_secure_password :reset_password
|
||||
end
|
||||
|
||||
it 'does not match when the subject is missing the custom digest attribute' do
|
||||
no_digest_column = define_model(:example) { has_secure_password :reset_password }
|
||||
expect(no_digest_column.new).not_to have_secure_password :reset_password
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue