Merge tag 'v4.3.0-rc.1'

This commit is contained in:
Mike Barnes 2024-10-02 10:34:27 +10:00
commit 26c9b9ba39
3459 changed files with 130932 additions and 69993 deletions

View file

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe DisallowedHashtagsValidator, type: :validator do
RSpec.describe DisallowedHashtagsValidator do
let(:disallowed_tags) { [] }
describe '#validate' do

View file

@ -0,0 +1,125 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe DomainValidator do
let(:record) { record_class.new }
context 'with no options' do
let(:record_class) do
Class.new do
include ActiveModel::Validations
attr_accessor :domain
validates :domain, domain: true
end
end
describe '#validate_each' do
context 'with a nil value' do
it 'does not add errors' do
record.domain = nil
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with a valid domain' do
it 'does not add errors' do
record.domain = 'example.com'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with a domain that is too long' do
it 'adds an error' do
record.domain = "#{'a' * 300}.com"
expect(record).to_not be_valid
expect(record.errors.where(:domain)).to_not be_empty
end
end
context 'with a domain with an empty segment' do
it 'adds an error' do
record.domain = '.example.com'
expect(record).to_not be_valid
expect(record.errors.where(:domain)).to_not be_empty
end
end
context 'with a domain with an invalid character' do
it 'adds an error' do
record.domain = '*.example.com'
expect(record).to_not be_valid
expect(record.errors.where(:domain)).to_not be_empty
end
end
context 'with a domain that would fail parsing' do
it 'adds an error' do
record.domain = '/'
expect(record).to_not be_valid
expect(record.errors.where(:domain)).to_not be_empty
end
end
end
end
context 'with acct option' do
let(:record_class) do
Class.new do
include ActiveModel::Validations
attr_accessor :acct
validates :acct, domain: { acct: true }
end
end
describe '#validate_each' do
context 'with a nil value' do
it 'does not add errors' do
record.acct = nil
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with no domain' do
it 'does not add errors' do
record.acct = 'hoge_123'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with a valid domain' do
it 'does not add errors' do
record.acct = 'hoge_123@example.com'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with an invalid domain' do
it 'adds an error' do
record.acct = 'hoge_123@.example.com'
expect(record).to_not be_valid
expect(record.errors.where(:acct)).to_not be_empty
end
end
end
end
end

View file

@ -2,26 +2,21 @@
require 'rails_helper'
describe EmailMxValidator do
RSpec.describe EmailMxValidator do
describe '#validate' do
let(:user) { instance_double(User, email: 'foo@example.com', sign_up_ip: '1.2.3.4', errors: instance_double(ActiveModel::Errors, add: nil)) }
let(:resolv_dns_double) { instance_double(Resolv::DNS) }
context 'with an e-mail domain that is explicitly allowed' do
around do |block|
tmp = Rails.configuration.x.email_domains_whitelist
Rails.configuration.x.email_domains_whitelist = 'example.com'
tmp = Rails.configuration.x.email_domains_allowlist
Rails.configuration.x.email_domains_allowlist = 'example.com'
block.call
Rails.configuration.x.email_domains_whitelist = tmp
Rails.configuration.x.email_domains_allowlist = tmp
end
it 'does not add errors if there are no DNS records' do
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
configure_resolver('example.com')
subject.validate(user)
expect(user.errors).to_not have_received(:add)
@ -29,13 +24,7 @@ describe EmailMxValidator do
end
it 'adds no error if there are DNS records for the e-mail domain' do
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([Resolv::DNS::Resource::IN::A.new('192.0.2.42')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
configure_resolver('example.com', a: resolv_double_a('192.0.2.42'))
subject.validate(user)
expect(user.errors).to_not have_received(:add)
@ -58,13 +47,7 @@ describe EmailMxValidator do
end
it 'adds an error if the email domain name contains empty labels' do
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::A).and_return([Resolv::DNS::Resource::IN::A.new('192.0.2.42')])
allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
configure_resolver('example..com', a: resolv_double_a('192.0.2.42'))
user = instance_double(User, email: 'foo@example..com', sign_up_ip: '1.2.3.4', errors: instance_double(ActiveModel::Errors, add: nil))
subject.validate(user)
@ -72,51 +55,64 @@ describe EmailMxValidator do
end
it 'adds an error if there are no DNS records for the e-mail domain' do
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
configure_resolver('example.com')
subject.validate(user)
expect(user.errors).to have_received(:add)
end
it 'adds an error if a MX record does not lead to an IP' do
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources)
.with('example.com', Resolv::DNS::Resource::IN::MX)
.and_return([instance_double(Resolv::DNS::Resource::MX, exchange: 'mail.example.com')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
configure_resolver('example.com', mx: resolv_double_mx('mail.example.com'))
configure_resolver('mail.example.com')
subject.validate(user)
expect(user.errors).to have_received(:add)
end
it 'adds an error if the MX record is blacklisted' do
it 'adds an error if the MX record has an email domain block' do
EmailDomainBlock.create!(domain: 'mail.example.com')
resolver = instance_double(Resolv::DNS)
allow(resolver).to receive(:getresources)
.with('example.com', Resolv::DNS::Resource::IN::MX)
.and_return([instance_double(Resolv::DNS::Resource::MX, exchange: 'mail.example.com')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([instance_double(Resolv::DNS::Resource::IN::A, address: '2.3.4.5')])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([instance_double(Resolv::DNS::Resource::IN::A, address: 'fd00::2')])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
configure_resolver(
'example.com',
mx: resolv_double_mx('mail.example.com')
)
configure_resolver(
'mail.example.com',
a: instance_double(Resolv::DNS::Resource::IN::A, address: '2.3.4.5'),
aaaa: instance_double(Resolv::DNS::Resource::IN::AAAA, address: 'fd00::2')
)
subject.validate(user)
expect(user.errors).to have_received(:add)
end
end
def configure_resolver(domain, options = {})
allow(resolv_dns_double)
.to receive(:getresources)
.with(domain, Resolv::DNS::Resource::IN::MX)
.and_return(Array(options[:mx]))
allow(resolv_dns_double)
.to receive(:getresources)
.with(domain, Resolv::DNS::Resource::IN::A)
.and_return(Array(options[:a]))
allow(resolv_dns_double)
.to receive(:getresources)
.with(domain, Resolv::DNS::Resource::IN::AAAA)
.and_return(Array(options[:aaaa]))
allow(resolv_dns_double)
.to receive(:timeouts=)
.and_return(nil)
allow(Resolv::DNS)
.to receive(:open)
.and_yield(resolv_dns_double)
end
def resolv_double_mx(domain)
instance_double(Resolv::DNS::Resource::MX, exchange: domain)
end
def resolv_double_a(domain)
Resolv::DNS::Resource::IN::A.new(domain)
end
end

View file

@ -0,0 +1,83 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ExistingUsernameValidator do
let(:record_class) do
Class.new do
include ActiveModel::Validations
attr_accessor :contact, :friends
def self.name
'Record'
end
validates :contact, existing_username: true
validates :friends, existing_username: { multiple: true }
end
end
let(:record) { record_class.new }
describe '#validate_each' do
context 'with a nil value' do
it 'does not add errors' do
record.contact = nil
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'when there are no accounts' do
it 'adds errors to the record' do
record.contact = 'user@example.com'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:contact)
expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found')
end
end
context 'when there are accounts' do
before { Fabricate(:account, domain: 'example.com', username: 'user') }
context 'when the value does not match' do
it 'adds errors to the record' do
record.contact = 'friend@other.host'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:contact)
expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found')
end
context 'when multiple is true' do
it 'adds errors to the record' do
record.friends = 'friend@other.host'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:friends)
expect(record.errors.first.type).to eq I18n.t('existing_username_validator.not_found_multiple', usernames: 'friend@other.host')
end
end
end
context 'when the value does match' do
it 'does not add errors to the record' do
record.contact = 'user@example.com'
expect(record).to be_valid
expect(record.errors).to be_empty
end
context 'when multiple is true' do
it 'does not add errors to the record' do
record.friends = 'user@example.com'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
end
end
end
end

View file

@ -2,48 +2,76 @@
require 'rails_helper'
RSpec.describe FollowLimitValidator, type: :validator do
RSpec.describe FollowLimitValidator do
describe '#validate' do
before do
allow_any_instance_of(described_class).to receive(:limit_reached?).with(account) do
limit_reached
end
context 'with a nil account' do
it 'does not add validation errors to base' do
follow = Fabricate.build(:follow, account: nil)
described_class.new.validate(follow)
end
follow.valid?
let(:follow) { instance_double(Follow, account: account, errors: errors) }
let(:errors) { instance_double(ActiveModel::Errors, add: nil) }
let(:account) { instance_double(Account, nil?: _nil, local?: local, following_count: 0, followers_count: 0) }
let(:_nil) { true }
let(:local) { false }
context 'with follow.account.nil? || !follow.account.local?' do
let(:_nil) { true }
it 'not calls errors.add' do
expect(errors).to_not have_received(:add).with(:base, any_args)
expect(follow.errors[:base]).to be_empty
end
end
context 'with !(follow.account.nil? || !follow.account.local?)' do
let(:_nil) { false }
let(:local) { true }
context 'with a non-local account' do
it 'does not add validation errors to base' do
follow = Fabricate.build(:follow, account: Account.new(domain: 'host.example'))
context 'when limit_reached?' do
let(:limit_reached) { true }
follow.valid?
it 'calls errors.add' do
expect(errors).to have_received(:add)
.with(:base, I18n.t('users.follow_limit_reached', limit: FollowLimitValidator::LIMIT))
expect(follow.errors[:base]).to be_empty
end
end
context 'with a local account' do
let(:account) { Account.new }
context 'when the followers count is under the limit' do
before do
allow(account).to receive(:following_count).and_return(described_class::LIMIT - 100)
end
it 'does not add validation errors to base' do
follow = Fabricate.build(:follow, account: account)
follow.valid?
expect(follow.errors[:base]).to be_empty
end
end
context 'with !limit_reached?' do
let(:limit_reached) { false }
context 'when the following count is over the limit' do
before do
allow(account).to receive(:following_count).and_return(described_class::LIMIT + 100)
end
it 'not calls errors.add' do
expect(errors).to_not have_received(:add).with(:base, any_args)
context 'when the followers count is low' do
before do
allow(account).to receive(:followers_count).and_return(10)
end
it 'adds validation errors to base' do
follow = Fabricate.build(:follow, account: account)
follow.valid?
expect(follow.errors[:base]).to include(I18n.t('users.follow_limit_reached', limit: described_class::LIMIT))
end
end
context 'when the followers count is high' do
before do
allow(account).to receive(:followers_count).and_return(100_000)
end
it 'does not add validation errors to base' do
follow = Fabricate.build(:follow, account: account)
follow.valid?
expect(follow.errors[:base]).to be_empty
end
end
end
end

View file

@ -2,7 +2,7 @@
require 'rails_helper'
describe LanguageValidator do
RSpec.describe LanguageValidator do
let(:record_class) do
Class.new do
include ActiveModel::Validations

View file

@ -0,0 +1,46 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe LinesValidator do
let(:record_class) do
Class.new do
include ActiveModel::Validations
attr_accessor :text
validates :text, lines: { maximum: 5 }
end
end
let(:record) { record_class.new }
describe '#validate_each' do
context 'with a nil value' do
it 'does not add errors' do
record.text = nil
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with lines below the limit' do
it 'does not add errors' do
record.text = "hoge\n" * 5
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'with more lines than limit' do
it 'adds an error' do
record.text = "hoge\n" * 6
expect(record).to_not be_valid
expect(record.errors.where(:text)).to_not be_empty
end
end
end
end

View file

@ -2,11 +2,11 @@
require 'rails_helper'
describe NoteLengthValidator do
RSpec.describe NoteLengthValidator do
subject { described_class.new(attributes: { note: true }, maximum: 640) }
describe '#validate' do
it 'adds an error when text is over 640 characters' do
it 'adds an error when text is over configured character limit' do
text = 'a' * 650
account = instance_double(Account, note: text, errors: activemodel_errors)
@ -14,16 +14,16 @@ describe NoteLengthValidator do
expect(account.errors).to have_received(:add)
end
it 'counts URLs as 23 characters flat' do
text = ('a' * 476) + " http://#{'b' * 30}.com/example"
it 'reduces calculated length of auto-linkable space-separated URLs' do
text = [starting_string, example_link].join(' ')
account = instance_double(Account, note: text, errors: activemodel_errors)
subject.validate_each(account, 'note', text)
expect(account.errors).to_not have_received(:add)
end
it 'does not count non-autolinkable URLs as 23 characters flat' do
text = ('a' * 476) + "http://#{'b' * 30}.com/example"
it 'does not reduce calculated length of non-autolinkable URLs' do
text = [starting_string, example_link].join
account = instance_double(Account, note: text, errors: activemodel_errors)
subject.validate_each(account, 'note', text)
@ -32,6 +32,14 @@ describe NoteLengthValidator do
private
def starting_string
'a' * 476
end
def example_link
"http://#{'b' * 30}.com/example"
end
def activemodel_errors
instance_double(ActiveModel::Errors, add: nil)
end

View file

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe PollValidator, type: :validator do
RSpec.describe PollValidator do
describe '#validate' do
before do
validator.validate(poll)

View file

@ -2,7 +2,7 @@
require 'rails_helper'
describe ReactionValidator do
RSpec.describe ReactionValidator do
let(:announcement) { Fabricate(:announcement) }
describe '#validate' do
@ -19,8 +19,9 @@ describe ReactionValidator do
expect(reaction.errors).to be_empty
end
it 'adds error when 8 reactions already exist' do
%w(🐘 ❤️ 🙉 😍 😋 😂 😞 👍).each do |name|
it 'adds error when reaction limit count has already been reached' do
stub_const 'ReactionValidator::LIMIT', 2
%w(🐘 ❤️).each do |name|
announcement.announcement_reactions.create!(name: name, account: Fabricate(:account))
end

View file

@ -2,64 +2,72 @@
require 'rails_helper'
describe StatusLengthValidator do
RSpec.describe StatusLengthValidator do
describe '#validate' do
before { stub_const("#{described_class}::MAX_CHARS", 500) } # Example values below are relative to this baseline
it 'does not add errors onto remote statuses' do
status = instance_double(Status, local?: false)
allow(status).to receive(:errors)
subject.validate(status)
expect(status).to_not receive(:errors)
expect(status).to_not have_received(:errors)
end
it 'does not add errors onto local reblogs' do
status = instance_double(Status, local?: false, reblog?: true)
allow(status).to receive(:errors)
subject.validate(status)
expect(status).to_not receive(:errors)
expect(status).to_not have_received(:errors)
end
it 'adds an error when content warning is over 500 characters' do
status = instance_double(Status, spoiler_text: 'a' * 520, text: '', errors: activemodel_errors, local?: true, reblog?: false)
it 'adds an error when content warning is over character limit' do
status = status_double(spoiler_text: 'a' * 520)
subject.validate(status)
expect(status.errors).to have_received(:add)
end
it 'adds an error when text is over 500 characters' do
status = instance_double(Status, spoiler_text: '', text: 'a' * 520, errors: activemodel_errors, local?: true, reblog?: false)
it 'adds an error when text is over character limit' do
status = status_double(text: 'a' * 520)
subject.validate(status)
expect(status.errors).to have_received(:add)
end
it 'adds an error when text and content warning are over 500 characters total' do
status = instance_double(Status, spoiler_text: 'a' * 250, text: 'b' * 251, errors: activemodel_errors, local?: true, reblog?: false)
it 'adds an error when text and content warning are over character limit total' do
status = status_double(spoiler_text: 'a' * 250, text: 'b' * 251)
subject.validate(status)
expect(status.errors).to have_received(:add)
end
it 'counts URLs as 23 characters flat' do
text = ('a' * 476) + " http://#{'b' * 30}.com/example"
status = instance_double(Status, spoiler_text: '', text: text, errors: activemodel_errors, local?: true, reblog?: false)
it 'reduces calculated length of auto-linkable space-separated URLs' do
text = [starting_string, example_link].join(' ')
status = status_double(text: text)
subject.validate(status)
expect(status.errors).to_not have_received(:add)
end
it 'does not count non-autolinkable URLs as 23 characters flat' do
text = ('a' * 476) + "http://#{'b' * 30}.com/example"
status = instance_double(Status, spoiler_text: '', text: text, errors: activemodel_errors, local?: true, reblog?: false)
it 'does not reduce calculated length of non-autolinkable URLs' do
text = [starting_string, example_link].join
status = status_double(text: text)
subject.validate(status)
expect(status.errors).to have_received(:add)
end
it 'does not count overly long URLs as 23 characters flat' do
it 'does not reduce calculated length of count overly long URLs' do
text = "http://example.com/valid?#{'#foo?' * 1000}"
status = instance_double(Status, spoiler_text: '', text: text, errors: activemodel_errors, local?: true, reblog?: false)
status = status_double(text: text)
subject.validate(status)
expect(status.errors).to have_received(:add)
end
it 'counts only the front part of remote usernames' do
text = ('a' * 475) + " @alice@#{'b' * 30}.com"
status = instance_double(Status, spoiler_text: '', text: text, errors: activemodel_errors, local?: true, reblog?: false)
status = status_double(text: text)
subject.validate(status)
expect(status.errors).to_not have_received(:add)
@ -67,7 +75,7 @@ describe StatusLengthValidator do
it 'does count both parts of remote usernames for overly long domains' do
text = "@alice@#{'b' * 500}.com"
status = instance_double(Status, spoiler_text: '', text: text, errors: activemodel_errors, local?: true, reblog?: false)
status = status_double(text: text)
subject.validate(status)
expect(status.errors).to have_received(:add)
@ -76,6 +84,25 @@ describe StatusLengthValidator do
private
def starting_string
'a' * 476
end
def example_link
"http://#{'b' * 30}.com/example"
end
def status_double(spoiler_text: '', text: '')
instance_double(
Status,
spoiler_text: spoiler_text,
text: text,
errors: activemodel_errors,
local?: true,
reblog?: false
)
end
def activemodel_errors
instance_double(ActiveModel::Errors, add: nil)
end

View file

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe StatusPinValidator, type: :validator do
RSpec.describe StatusPinValidator do
describe '#validate' do
before do
subject.validate(pin)
@ -45,8 +45,8 @@ RSpec.describe StatusPinValidator, type: :validator do
end
end
context 'when pin.account.status_pins.count > 4 && pin.account.local?' do
let(:count) { 5 }
context 'when pin account is local and has too many pins' do
let(:count) { described_class::PIN_LIMIT + 1 }
let(:local) { true }
it 'calls errors.add' do

View file

@ -2,7 +2,7 @@
require 'rails_helper'
describe UniqueUsernameValidator do
RSpec.describe UniqueUsernameValidator do
describe '#validate' do
context 'when local account' do
it 'does not add errors if username is nil' do

View file

@ -2,41 +2,118 @@
require 'rails_helper'
RSpec.describe UnreservedUsernameValidator, type: :validator do
describe '#validate' do
before do
allow(validator).to receive(:reserved_username?) { reserved_username }
validator.validate(account)
RSpec.describe UnreservedUsernameValidator do
let(:record_class) do
Class.new do
include ActiveModel::Validations
attr_accessor :username
validates_with UnreservedUsernameValidator
end
end
let(:record) { record_class.new }
let(:validator) { described_class.new }
let(:account) { instance_double(Account, username: username, errors: errors) }
let(:errors) { instance_double(ActiveModel::Errors, add: nil) }
describe '#validate' do
context 'when username is nil' do
it 'does not add errors' do
record.username = nil
context 'when @username is blank?' do
let(:username) { nil }
it 'not calls errors.add' do
expect(errors).to_not have_received(:add).with(:username, any_args)
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'when @username is not blank?' do
let(:username) { 'f' }
context 'when PAM is enabled' do
before do
allow(Devise).to receive(:pam_authentication).and_return(true)
end
context 'with reserved_username?' do
let(:reserved_username) { true }
context 'with a pam service available' do
let(:service) { double }
let(:pam_class) do
Class.new do
def self.account(service, username); end
end
end
it 'calls errors.add' do
expect(errors).to have_received(:add).with(:username, :reserved)
before do
stub_const('Rpam2', pam_class)
allow(Devise).to receive(:pam_controlled_service).and_return(service)
end
context 'when the account exists' do
before do
allow(Rpam2).to receive(:account).with(service, 'username').and_return(true)
end
it 'adds errors to the record' do
record.username = 'username'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:username)
expect(record.errors.first.type).to eq(:reserved)
end
end
context 'when the account does not exist' do
before do
allow(Rpam2).to receive(:account).with(service, 'username').and_return(false)
end
it 'does not add errors to the record' do
record.username = 'username'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
end
context 'when username is not reserved' do
let(:reserved_username) { false }
context 'without a pam service' do
before do
allow(Devise).to receive(:pam_controlled_service).and_return(false)
end
it 'not calls errors.add' do
expect(errors).to_not have_received(:add).with(:username, any_args)
context 'when there are not any reserved usernames' do
before do
stub_reserved_usernames(nil)
end
it 'does not add errors to the record' do
record.username = 'username'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
context 'when there are reserved usernames' do
before do
stub_reserved_usernames(%w(alice bob))
end
context 'when the username is reserved' do
it 'adds errors to the record' do
record.username = 'alice'
expect(record).to_not be_valid
expect(record.errors.first.attribute).to eq(:username)
expect(record.errors.first.type).to eq(:reserved)
end
end
context 'when the username is not reserved' do
it 'does not add errors to the record' do
record.username = 'chris'
expect(record).to be_valid
expect(record.errors).to be_empty
end
end
end
def stub_reserved_usernames(value)
allow(Setting).to receive(:[]).with('reserved_usernames').and_return(value)
end
end
end

View file

@ -2,7 +2,7 @@
require 'rails_helper'
describe URLValidator do
RSpec.describe URLValidator do
let(:record_class) do
Class.new do
include ActiveModel::Validations

View file

@ -2,9 +2,9 @@
require 'rails_helper'
RSpec.describe BlacklistedEmailValidator, type: :validator do
RSpec.describe UserEmailValidator do
describe '#validate' do
subject { described_class.new.validate(user); errors }
subject { described_class.new.validate(user) }
let(:user) { instance_double(User, email: 'info@mail.com', sign_up_ip: '1.2.3.4', errors: errors) }
let(:errors) { instance_double(ActiveModel::Errors, add: nil) }
@ -18,7 +18,8 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do
let(:blocked_email) { true }
it 'adds error' do
described_class.new.validate(user)
subject
expect(errors).to have_received(:add).with(:email, :blocked).once
end
end
@ -27,7 +28,8 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do
let(:blocked_email) { false }
it 'does not add errors' do
described_class.new.validate(user)
subject
expect(errors).to_not have_received(:add)
end
@ -39,7 +41,8 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do
end
it 'adds error' do
described_class.new.validate(user)
subject
expect(errors).to have_received(:add).with(:email, :taken).once
end
end