Skip to content

Commit

Permalink
Feature/configurable smtp error body, #19 (#37)
Browse files Browse the repository at this point in the history
* Implement Validate::Base#configuration
* Implement configurable smtp error body
* Update linter configuration
* Update tests
* Update readme
* Update gem version
  • Loading branch information
bestwebua authored May 10, 2019
1 parent 22ead56 commit ef26feb
Show file tree
Hide file tree
Showing 15 changed files with 76 additions and 20 deletions.
1 change: 1 addition & 0 deletions .reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ detectors:
exclude:
- Truemail::Validate::Smtp::Request#compose_from
- Truemail::Validator#select_validation_type
- Truemail::Validate::Base#configuration
- Truemail::Validate::Mx#null_mx?
- Truemail::Validate::Mx#a_record
- Truemail::Audit::Base#verifier_domain
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
truemail (0.1.9)
truemail (0.1.10)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -114,4 +114,4 @@ DEPENDENCIES
truemail!

BUNDLED WITH
1.16.6
1.17.3
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ Truemail.configure do |config|
# Optional parameter. You can override default regex pattern
config.email_pattern = /regex_pattern/

# Optional parameter. You can override default regex pattern
config.smtp_error_body_pattern = /regex_pattern/

# Optional parameter. Connection timeout is equal to 2 ms by default.
config.connection_timeout = 1

Expand Down Expand Up @@ -92,6 +95,7 @@ Truemail.configuration
=> #<Truemail::Configuration:0x000055590cb17b40
@connection_timeout=1,
@email_pattern=/regex_pattern/,
@smtp_error_body_pattern=/regex_pattern/,
@response_timeout=1,
@connection_attempts=3,
@validation_type_by_domain={},
Expand All @@ -114,6 +118,7 @@ Truemail.configuration
=> #<Truemail::Configuration:0x000055590cb17b40
@connection_timeout=3,
@email_pattern=/regex_pattern/,
@smtp_error_body_pattern=/regex_pattern/,
@response_timeout=4,
@connection_attempts=1,
@validation_type_by_domain={},
Expand Down Expand Up @@ -270,6 +275,7 @@ Truemail.validate('email@example.com')
#<Truemail::Configuration:0x0000000002d49930
@connection_timeout=2,
@email_pattern=/regex_pattern/,
@smtp_error_body_pattern=/regex_pattern/,
@response_timeout=2,
@connection_attempts=2,
@smtp_safe_check=false,
Expand Down Expand Up @@ -323,6 +329,7 @@ Truemail.validate('email@example.com')
#<Truemail::Configuration:0x0000000002c95b38
@connection_timeout=2,
@email_pattern=/regex_pattern/,
@smtp_error_body_pattern=/regex_pattern/,
@response_timeout=2,
@connection_attempts=2,
@smtp_safe_check=true,
Expand Down Expand Up @@ -360,6 +367,7 @@ Truemail.validate('email@example.com')
#<Truemail::Configuration:0x0000000002d49930
@connection_timeout=2,
@email_pattern=/regex_pattern/,
@smtp_error_body_pattern=/regex_pattern/,
@response_timeout=2,
@connection_attempts=2,
@smtp_safe_check=true,
Expand Down
10 changes: 7 additions & 3 deletions lib/truemail/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Configuration
DEFAULT_CONNECTION_ATTEMPTS = 2

attr_reader :email_pattern,
:smtp_error_body_pattern,
:verifier_email,
:verifier_domain,
:connection_timeout,
Expand All @@ -20,16 +21,19 @@ class Configuration

def initialize
@email_pattern = Truemail::RegexConstant::REGEX_EMAIL_PATTERN
@smtp_error_body_pattern = Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN
@connection_timeout = Truemail::Configuration::DEFAULT_CONNECTION_TIMEOUT
@response_timeout = Truemail::Configuration::DEFAULT_RESPONSE_TIMEOUT
@connection_attempts = Truemail::Configuration::DEFAULT_CONNECTION_ATTEMPTS
@validation_type_by_domain = {}
@smtp_safe_check = false
end

def email_pattern=(regex_pattern)
raise Truemail::ArgumentError.new(regex_pattern, Regexp) unless regex_pattern.is_a?(Regexp)
@email_pattern = regex_pattern
%i[email_pattern smtp_error_body_pattern].each do |method|
define_method("#{method}=") do |argument|
raise Truemail::ArgumentError.new(argument, __method__) unless argument.is_a?(Regexp)
instance_variable_set(:"@#{method}", argument)
end
end

def verifier_email=(email)
Expand Down
1 change: 1 addition & 0 deletions lib/truemail/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module RegexConstant
REGEX_EMAIL_PATTERN = /(?=\A.{6,255}\z)(\A([a-zA-Z0-9]+[\w|\-|\.|\+]*)@(#{REGEX_DOMAIN})\z)/
REGEX_DOMAIN_PATTERN = /(?=\A.{4,255}\z)(\A#{REGEX_DOMAIN}\z)/
REGEX_DOMAIN_FROM_EMAIL = /\A.+@(.+)\z/
REGEX_SMTP_ERROR_BODY_PATTERN = /(?=.*550)(?=.*(user|account|customer|mailbox)).*/i
end

module Audit
Expand Down
4 changes: 4 additions & 0 deletions lib/truemail/validate/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ def add_error(message)
def mail_servers
result.mail_servers
end

def configuration
Truemail.configuration
end
end
end
end
2 changes: 1 addition & 1 deletion lib/truemail/validate/regex.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class Regex < Truemail::Validate::Base
ERROR = 'email does not match the regular expression'

def run
return true if success(Truemail.configuration.email_pattern.match?(result.email))
return true if success(configuration.email_pattern.match?(result.email))
add_error(Truemail::Validate::Regex::ERROR)
false
end
Expand Down
7 changes: 3 additions & 4 deletions lib/truemail/validate/smtp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ module Truemail
module Validate
class Smtp < Truemail::Validate::Base
ERROR = 'smtp error'
ERROR_BODY = /(?=.*550)(?=.*(user|account|customer|mailbox)).*/i

attr_reader :smtp_results

Expand All @@ -31,7 +30,7 @@ def request

def attempts
@attempts ||=
mail_servers.one? ? { attempts: Truemail.configuration.connection_attempts } : {}
mail_servers.one? ? { attempts: configuration.connection_attempts } : {}
end

def rcptto_error
Expand All @@ -53,11 +52,11 @@ def success_response?
end

def not_includes_user_not_found_errors?
return unless Truemail.configuration.smtp_safe_check
return unless configuration.smtp_safe_check
result.smtp_debug.map(&:response).map(&:errors).all? do |errors|
next true unless errors.key?(:rcptto)
errors.slice(:rcptto).values.none? do |error|
Truemail::Validate::Smtp::ERROR_BODY.match?(error)
configuration.smtp_error_body_pattern.match?(error)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/truemail/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Truemail
VERSION = '0.1.9'
VERSION = '0.1.10'
end
1 change: 1 addition & 0 deletions spec/support/shared_examples/has_attr_accessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Truemail
RSpec.shared_examples 'has attr_accessor' do
%i[
email_pattern
smtp_error_body_pattern
verifier_email
verifier_domain
connection_timeout
Expand Down
3 changes: 2 additions & 1 deletion spec/support/shared_examples/sets_default_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
module Truemail
RSpec.shared_examples 'sets default configuration' do
it 'sets default configuration settings' do
expect(configuration_instance.email_pattern).to be_an_instance_of(Regexp)
expect(configuration_instance.email_pattern).to eq(Truemail::RegexConstant::REGEX_EMAIL_PATTERN)
expect(configuration_instance.smtp_error_body_pattern).to eq(Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN)
expect(configuration_instance.verifier_email).to be_nil
expect(configuration_instance.verifier_domain).to be_nil
expect(configuration_instance.connection_timeout).to eq(Truemail::Configuration::DEFAULT_CONNECTION_TIMEOUT)
Expand Down
29 changes: 25 additions & 4 deletions spec/truemail/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
context 'when auto configuration' do
let(:configuration_instance_expectaions) do
expect(configuration_instance.email_pattern).to eq(Truemail::RegexConstant::REGEX_EMAIL_PATTERN)
expect(configuration_instance.smtp_error_body_pattern).to eq(Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN)
expect(configuration_instance.connection_timeout).to eq(2)
expect(configuration_instance.response_timeout).to eq(2)
expect(configuration_instance.connection_attempts).to eq(2)
Expand All @@ -46,6 +47,7 @@
.and change(configuration_instance, :complete?)
.from(false).to(true)
.and not_change(configuration_instance, :email_pattern)
.and not_change(configuration_instance, :smtp_error_body_pattern)
.and not_change(configuration_instance, :connection_timeout)
.and not_change(configuration_instance, :response_timeout)
.and not_change(configuration_instance, :validation_type_by_domain)
Expand All @@ -63,6 +65,7 @@
.and change(configuration_instance, :complete?)
.from(false).to(true)
.and not_change(configuration_instance, :email_pattern)
.and not_change(configuration_instance, :smtp_error_body_pattern)
.and not_change(configuration_instance, :connection_timeout)
.and not_change(configuration_instance, :response_timeout)
.and not_change(configuration_instance, :validation_type_by_domain)
Expand All @@ -80,6 +83,7 @@
.and change(configuration_instance, :complete?)
.from(false).to(true)
.and not_change(configuration_instance, :email_pattern)
.and not_change(configuration_instance, :smtp_error_body_pattern)
.and not_change(configuration_instance, :connection_timeout)
.and not_change(configuration_instance, :response_timeout)
.and not_change(configuration_instance, :validation_type_by_domain)
Expand Down Expand Up @@ -150,12 +154,29 @@
end
end

context 'with invalid value' do
specify do
expect { configuration_instance.email_pattern = 'not_regex_object' }
.to raise_error(Truemail::ArgumentError)
context 'with invalid email pattern' do
let(:setter) { :email_pattern= }

include_examples 'raises argument error'
end
end

describe '#smtp_error_body_pattern=' do
context 'with valid value' do
let(:valid_smtp_error_body_pattern) { /\d+/ }

it 'sets custom email pattern' do
expect { configuration_instance.smtp_error_body_pattern = valid_smtp_error_body_pattern }
.to change(configuration_instance, :smtp_error_body_pattern)
.from(Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN).to(valid_smtp_error_body_pattern)
end
end

context 'with invalid smtp error body pattern' do
let(:setter) { :smtp_error_body_pattern= }

include_examples 'raises argument error'
end
end

describe '#connection_timeout=' do
Expand Down
11 changes: 11 additions & 0 deletions spec/truemail/core_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Truemail
specify { expect(described_class).to be_const_defined(:REGEX_EMAIL_PATTERN) }
specify { expect(described_class).to be_const_defined(:REGEX_DOMAIN_PATTERN) }
specify { expect(described_class).to be_const_defined(:REGEX_DOMAIN_FROM_EMAIL) }
specify { expect(described_class).to be_const_defined(:REGEX_SMTP_ERROR_BODY_PATTERN) }
end

describe 'Truemail::RegexConstant::REGEX_EMAIL_PATTERN' do
Expand Down Expand Up @@ -121,6 +122,16 @@ module Truemail
specify { expect(regex_pattern.match?(email)).to be(true) }
specify { expect(email[regex_pattern, 1]).to eq('domain') }
end

describe 'Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN' do
subject(:regex_pattern) { described_class::REGEX_SMTP_ERROR_BODY_PATTERN }

let(:smtp_error_context) { 'some smtp 550 error with' }

%w[user account customer mailbox].map { |item| [item, item.upcase] }.flatten.each do |account_name_type|
specify { expect(regex_pattern.match?("#{smtp_error_context} #{account_name_type}")).to be(true) }
end
end
end

RSpec.describe Truemail::Validate do
Expand Down
7 changes: 4 additions & 3 deletions spec/truemail/validate/smtp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

describe 'defined constants' do
specify { expect(described_class).to be_const_defined(:ERROR) }
specify { expect(described_class).to be_const_defined(:ERROR_BODY) }
specify { expect(described_class).to be_const_defined(:RESPONSE_ATTRS) }
specify { expect(described_class).to be_const_defined(:Response) }
specify { expect(described_class).to be_const_defined(:Request) }
Expand Down Expand Up @@ -206,8 +205,10 @@
end

context 'when smtp user error has been detected' do
let(:smtp_error_context) { 'some 550 error with user or account in body' }

context 'with error in rcptto response' do
let(:smtp_error_context_2) { 'some 550 error with user or account in body' }
let(:smtp_error_context_2) { smtp_error_context }

specify do
allow(result_instance.mail_servers).to receive(:each)
Expand All @@ -225,7 +226,7 @@

context 'with error in others smtp responses' do
context 'with error in rcptto response' do
let(:smtp_error_context_1) { 'some 550 error with user or account in body' }
let(:smtp_error_context_1) { smtp_error_context }

specify do
allow(result_instance.mail_servers).to receive(:each)
Expand Down
6 changes: 5 additions & 1 deletion spec/truemail_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
let(:new_email) { FFaker::Internet.email }
let(:new_domain) { FFaker::Internet.domain_name }
let(:new_regex_pattern) { /\A+.\z/ }
let(:new_smtp_error_body_pattern) { /\A\d+\z/ }

specify { expect(configuration).to be_instance_of(Truemail::Configuration) }

Expand All @@ -89,14 +90,17 @@
configuration.tap(&configuration_block(
verifier_email: new_email,
verifier_domain: new_domain,
email_pattern: new_regex_pattern
email_pattern: new_regex_pattern,
smtp_error_body_pattern: new_smtp_error_body_pattern
)
)
end
.to change(configuration, :verifier_email).from(email).to(new_email)
.and change(configuration, :verifier_domain).from(domain).to(new_domain)
.and change(configuration, :email_pattern)
.from(Truemail::RegexConstant::REGEX_EMAIL_PATTERN).to(new_regex_pattern)
.and change(configuration, :smtp_error_body_pattern)
.from(Truemail::RegexConstant::REGEX_SMTP_ERROR_BODY_PATTERN).to(new_smtp_error_body_pattern)
end
end

Expand Down

0 comments on commit ef26feb

Please sign in to comment.