aussiegeek-ar_sendmail_delayed 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +52 -0
- data/LICENSE.txt +28 -0
- data/Manifest.txt +13 -0
- data/README.txt +39 -0
- data/Rakefile +14 -0
- data/bin/ar_sendmail +6 -0
- data/lib/action_mailer/ar_mailer.rb +98 -0
- data/lib/action_mailer/ar_sendmail.rb +522 -0
- data/lib/smtp_tls.rb +105 -0
- data/share/ar_sendmail +30 -0
- data/test/action_mailer.rb +191 -0
- data/test/test_armailer.rb +56 -0
- data/test/test_arsendmail.rb +648 -0
- metadata +72 -0
data/lib/smtp_tls.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Original code believed public domain from ruby-talk or ruby-core email.
|
|
2
|
+
# Modifications by Kyle Maxwell <kyle@kylemaxwell.com> used under MIT license.
|
|
3
|
+
|
|
4
|
+
require "openssl"
|
|
5
|
+
require "net/smtp"
|
|
6
|
+
|
|
7
|
+
# :stopdoc:
|
|
8
|
+
|
|
9
|
+
class Net::SMTP
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
send :remove_method, :start
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.start( address, port = nil,
|
|
16
|
+
helo = 'localhost.localdomain',
|
|
17
|
+
user = nil, secret = nil, authtype = nil, use_tls = false,
|
|
18
|
+
&block) # :yield: smtp
|
|
19
|
+
new(address, port).start(helo, user, secret, authtype, use_tls, &block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
alias tls_old_start start
|
|
23
|
+
|
|
24
|
+
def start( helo = 'localhost.localdomain',
|
|
25
|
+
user = nil, secret = nil, authtype = nil, use_tls = false ) # :yield: smtp
|
|
26
|
+
start_method = use_tls ? :do_tls_start : :do_start
|
|
27
|
+
if block_given?
|
|
28
|
+
begin
|
|
29
|
+
send start_method, helo, user, secret, authtype
|
|
30
|
+
return yield(self)
|
|
31
|
+
ensure
|
|
32
|
+
do_finish
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
send start_method, helo, user, secret, authtype
|
|
36
|
+
return self
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def do_tls_start(helodomain, user, secret, authtype)
|
|
43
|
+
raise IOError, 'SMTP session already started' if @started
|
|
44
|
+
check_auth_args user, secret, authtype if user or secret
|
|
45
|
+
|
|
46
|
+
sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
|
|
47
|
+
@socket = Net::InternetMessageIO.new(sock)
|
|
48
|
+
@socket.read_timeout = 60 #@read_timeout
|
|
49
|
+
@socket.debug_output = STDERR #@debug_output
|
|
50
|
+
|
|
51
|
+
check_response(critical { recv_response() })
|
|
52
|
+
do_helo(helodomain)
|
|
53
|
+
|
|
54
|
+
raise 'openssl library not installed' unless defined?(OpenSSL)
|
|
55
|
+
starttls
|
|
56
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock)
|
|
57
|
+
ssl.sync_close = true
|
|
58
|
+
ssl.connect
|
|
59
|
+
@socket = Net::InternetMessageIO.new(ssl)
|
|
60
|
+
@socket.read_timeout = 60 #@read_timeout
|
|
61
|
+
@socket.debug_output = STDERR #@debug_output
|
|
62
|
+
do_helo(helodomain)
|
|
63
|
+
|
|
64
|
+
authenticate user, secret, authtype if user
|
|
65
|
+
@started = true
|
|
66
|
+
ensure
|
|
67
|
+
unless @started
|
|
68
|
+
# authentication failed, cancel connection.
|
|
69
|
+
@socket.close if not @started and @socket and not @socket.closed?
|
|
70
|
+
@socket = nil
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def do_helo(helodomain)
|
|
75
|
+
begin
|
|
76
|
+
if @esmtp
|
|
77
|
+
ehlo helodomain
|
|
78
|
+
else
|
|
79
|
+
helo helodomain
|
|
80
|
+
end
|
|
81
|
+
rescue Net::ProtocolError
|
|
82
|
+
if @esmtp
|
|
83
|
+
@esmtp = false
|
|
84
|
+
@error_occured = false
|
|
85
|
+
retry
|
|
86
|
+
end
|
|
87
|
+
raise
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def starttls
|
|
92
|
+
getok('STARTTLS')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
alias tls_old_quit quit
|
|
96
|
+
|
|
97
|
+
def quit
|
|
98
|
+
begin
|
|
99
|
+
getok('QUIT')
|
|
100
|
+
rescue EOFError
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end unless Net::SMTP.private_method_defined? :do_tls_start or
|
|
105
|
+
Net::SMTP.method_defined? :tls?
|
data/share/ar_sendmail
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# PROVIDE: ar_sendmail
|
|
3
|
+
# REQUIRE: DAEMON
|
|
4
|
+
# BEFORE: LOGIN
|
|
5
|
+
# KEYWORD: FreeBSD shutdown
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# Add the following lines to /etc/rc.conf to enable ar_sendmail:
|
|
9
|
+
#
|
|
10
|
+
#ar_sendmail_enable="YES"
|
|
11
|
+
|
|
12
|
+
. /etc/rc.subr
|
|
13
|
+
|
|
14
|
+
name="ar_sendmail"
|
|
15
|
+
rcvar=`set_rcvar`
|
|
16
|
+
|
|
17
|
+
command="/usr/local/bin/ar_sendmail"
|
|
18
|
+
command_interpreter="/usr/local/bin/ruby18"
|
|
19
|
+
|
|
20
|
+
# set defaults
|
|
21
|
+
|
|
22
|
+
ar_sendmail_rails_env=${ar_sendmail_rails_env:-"production"}
|
|
23
|
+
ar_sendmail_chdir=${ar_sendmail_chdir:-"/"}
|
|
24
|
+
ar_sendmail_enable=${ar_sendmail_enable:-"NO"}
|
|
25
|
+
ar_sendmail_flags=${ar_sendmail_flags:-"-d"}
|
|
26
|
+
|
|
27
|
+
load_rc_config $name
|
|
28
|
+
export RAILS_ENV=$ar_sendmail_rails_env
|
|
29
|
+
run_rc_command "$1"
|
|
30
|
+
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'net/smtp'
|
|
2
|
+
require 'smtp_tls'
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Net::SMTP
|
|
7
|
+
|
|
8
|
+
@reset_called = 0
|
|
9
|
+
|
|
10
|
+
@deliveries = []
|
|
11
|
+
|
|
12
|
+
@send_message_block = nil
|
|
13
|
+
|
|
14
|
+
@start_block = nil
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
|
|
18
|
+
attr_reader :deliveries
|
|
19
|
+
attr_reader :send_message_block
|
|
20
|
+
attr_accessor :reset_called
|
|
21
|
+
|
|
22
|
+
send :remove_method, :start
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.start(*args)
|
|
27
|
+
@start_block.call if @start_block
|
|
28
|
+
yield new(nil)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.on_send_message(&block)
|
|
32
|
+
@send_message_block = block
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.on_start(&block)
|
|
36
|
+
@start_block = block
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.reset
|
|
40
|
+
deliveries.clear
|
|
41
|
+
on_start
|
|
42
|
+
on_send_message
|
|
43
|
+
@reset_called = 0
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
alias test_old_reset reset if instance_methods.include? 'reset'
|
|
47
|
+
|
|
48
|
+
def reset
|
|
49
|
+
self.class.reset_called += 1
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
alias test_old_send_message send_message
|
|
53
|
+
|
|
54
|
+
def send_message(mail, to, from)
|
|
55
|
+
return self.class.send_message_block.call(mail, to, from) unless
|
|
56
|
+
self.class.send_message_block.nil?
|
|
57
|
+
self.class.deliveries << [mail, to, from]
|
|
58
|
+
return "queued"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
# Stub for ActionMailer::Base
|
|
65
|
+
|
|
66
|
+
module ActionMailer; end
|
|
67
|
+
|
|
68
|
+
class ActionMailer::Base
|
|
69
|
+
|
|
70
|
+
@server_settings = {}
|
|
71
|
+
|
|
72
|
+
def self.logger
|
|
73
|
+
o = Object.new
|
|
74
|
+
def o.info(arg) end
|
|
75
|
+
return o
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.method_missing(meth, *args)
|
|
79
|
+
meth.to_s =~ /deliver_(.*)/
|
|
80
|
+
super unless $1
|
|
81
|
+
new($1, *args).deliver!
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.reset
|
|
85
|
+
server_settings.clear
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def self.server_settings
|
|
89
|
+
@server_settings
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def initialize(meth = nil)
|
|
93
|
+
send meth if meth
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def deliver!
|
|
97
|
+
perform_delivery_activerecord @mail
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# Stub for an ActiveRecord model
|
|
104
|
+
|
|
105
|
+
class Email
|
|
106
|
+
|
|
107
|
+
START = Time.parse 'Thu Aug 10 2006 11:19:48'
|
|
108
|
+
|
|
109
|
+
attr_accessor :from, :to, :mail, :last_send_attempt, :created_on, :id, :deliver_after
|
|
110
|
+
|
|
111
|
+
@records = []
|
|
112
|
+
@id = 0
|
|
113
|
+
|
|
114
|
+
class << self; attr_accessor :records, :id; end
|
|
115
|
+
|
|
116
|
+
def self.create(record)
|
|
117
|
+
record = new record[:from], record[:to], record[:mail],
|
|
118
|
+
record[:deliver_after], record[:last_send_attempt]
|
|
119
|
+
records << record
|
|
120
|
+
return record
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.destroy_all(conditions)
|
|
124
|
+
timeout = conditions.last
|
|
125
|
+
found = []
|
|
126
|
+
|
|
127
|
+
records.each do |record|
|
|
128
|
+
next if record.last_send_attempt == 0
|
|
129
|
+
next if record.created_on == 0
|
|
130
|
+
next unless record.created_on < timeout
|
|
131
|
+
record.destroy
|
|
132
|
+
found << record
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
found
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def self.find(_, conditions = nil)
|
|
139
|
+
if conditions.nil?
|
|
140
|
+
return records.select do |r|
|
|
141
|
+
r.deliver_after > Time.parse('Thu Aug 10 2006 11:19:48')
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
now = Time.now.to_i - 300
|
|
145
|
+
return records.select do |r|
|
|
146
|
+
r.last_send_attempt < now && r.deliver_after.to_i > Time.parse('Thu Aug 10 2006 11:19:48').to_i
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def self.reset
|
|
151
|
+
@id = 0
|
|
152
|
+
records.clear
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def initialize(from, to, mail, deliver_after, last_send_attempt = nil)
|
|
156
|
+
@from = from
|
|
157
|
+
@to = to
|
|
158
|
+
@mail = mail
|
|
159
|
+
@id = self.class.id += 1
|
|
160
|
+
@created_on = START + @id
|
|
161
|
+
@deliver_after=deliver_after || Time.now
|
|
162
|
+
@last_send_attempt = last_send_attempt || 0
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def destroy
|
|
166
|
+
self.class.records.delete self
|
|
167
|
+
self.freeze
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def ==(other)
|
|
171
|
+
other.id == id
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def save
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
Mail = Email
|
|
180
|
+
|
|
181
|
+
class String
|
|
182
|
+
def classify
|
|
183
|
+
self
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def tableize
|
|
187
|
+
self.downcase
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
end
|
|
191
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'action_mailer'
|
|
3
|
+
require 'action_mailer/ar_mailer'
|
|
4
|
+
|
|
5
|
+
class Time
|
|
6
|
+
def now
|
|
7
|
+
Time.parse('Thu Aug 10 2006 11:41:05')
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Pretend mailer
|
|
13
|
+
|
|
14
|
+
class Mailer < ActionMailer::ARMailer
|
|
15
|
+
|
|
16
|
+
def mail
|
|
17
|
+
@mail = Object.new
|
|
18
|
+
def @mail.encoded() 'email' end
|
|
19
|
+
def @mail.from() ['nobody@example.com'] end
|
|
20
|
+
def @mail.destinations() %w[user1@example.com user2@example.com] end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class TestARMailer < Test::Unit::TestCase
|
|
26
|
+
|
|
27
|
+
def setup
|
|
28
|
+
Mailer.email_class = Email
|
|
29
|
+
|
|
30
|
+
Email.records.clear
|
|
31
|
+
Mail.records.clear
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_self_email_class_equals
|
|
35
|
+
Mailer.email_class = Mail
|
|
36
|
+
|
|
37
|
+
Mailer.deliver_mail
|
|
38
|
+
|
|
39
|
+
assert_equal 2, Mail.records.length
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_perform_delivery_activerecord
|
|
43
|
+
Mailer.deliver_mail
|
|
44
|
+
|
|
45
|
+
assert_equal 2, Email.records.length
|
|
46
|
+
|
|
47
|
+
record = Email.records.first
|
|
48
|
+
assert_equal 'email', record.mail
|
|
49
|
+
assert_equal 'user1@example.com', record.to
|
|
50
|
+
assert_equal 'nobody@example.com', record.from
|
|
51
|
+
|
|
52
|
+
assert_equal 'user2@example.com', Email.records.last.to
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'action_mailer'
|
|
3
|
+
require 'action_mailer/ar_sendmail'
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'test/zentest_assertions'
|
|
6
|
+
|
|
7
|
+
class ActionMailer::ARSendmail
|
|
8
|
+
attr_accessor :slept
|
|
9
|
+
def sleep(secs)
|
|
10
|
+
@slept ||= []
|
|
11
|
+
@slept << secs
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class TestARSendmail < Test::Unit::TestCase
|
|
16
|
+
|
|
17
|
+
def setup
|
|
18
|
+
ActionMailer::Base.reset
|
|
19
|
+
Email.reset
|
|
20
|
+
Net::SMTP.reset
|
|
21
|
+
|
|
22
|
+
@sm = ActionMailer::ARSendmail.new
|
|
23
|
+
@sm.verbose = true
|
|
24
|
+
|
|
25
|
+
@include_c_e = ! $".grep(/config\/environment.rb/).empty?
|
|
26
|
+
$" << 'config/environment.rb' unless @include_c_e
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def teardown
|
|
30
|
+
$".delete 'config/environment.rb' unless @include_c_e
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_fake_time
|
|
34
|
+
assert_equal Time.now, Time.parse('Thu Aug 10 2006 11:41:05')
|
|
35
|
+
end
|
|
36
|
+
def test_class_create_migration
|
|
37
|
+
out, = util_capture do
|
|
38
|
+
ActionMailer::ARSendmail.create_migration 'Mail'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
expected = <<-EOF
|
|
42
|
+
class AddMail < ActiveRecord::Migration
|
|
43
|
+
def self.up
|
|
44
|
+
create_table :mail do |t|
|
|
45
|
+
t.column :from, :string
|
|
46
|
+
t.column :to, :string
|
|
47
|
+
t.column :last_send_attempt, :integer, :default => 0
|
|
48
|
+
t.column :deliver_after, :datetime
|
|
49
|
+
t.column :mail, :text
|
|
50
|
+
t.column :created_on, :datetime
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.down
|
|
55
|
+
drop_table :mail
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
EOF
|
|
59
|
+
|
|
60
|
+
assert_equal expected, out.string
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def test_class_create_model
|
|
64
|
+
out, = util_capture do
|
|
65
|
+
ActionMailer::ARSendmail.create_model 'Mail'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
expected = <<-EOF
|
|
69
|
+
class Mail < ActiveRecord::Base
|
|
70
|
+
end
|
|
71
|
+
EOF
|
|
72
|
+
|
|
73
|
+
assert_equal expected, out.string
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_class_mailq
|
|
77
|
+
Email.create :from => nobody, :to => 'recip@h1.example.com',
|
|
78
|
+
:mail => 'body0'
|
|
79
|
+
Email.create :from => nobody, :to => 'recip@h1.example.com',
|
|
80
|
+
:mail => 'body1'
|
|
81
|
+
last = Email.create :from => nobody, :to => 'recip@h2.example.com',
|
|
82
|
+
:mail => 'body2'
|
|
83
|
+
Email.create :from => nobody, :to => 'nextweek@h1.example.com',
|
|
84
|
+
:mail => 'body0', :deliver_after => Time.parse('Sun Sep 10 2006 11:19:48')
|
|
85
|
+
|
|
86
|
+
last.last_send_attempt = Time.parse('Thu Aug 10 2006 11:40:05').to_i
|
|
87
|
+
|
|
88
|
+
out, err = util_capture do
|
|
89
|
+
ActionMailer::ARSendmail.mailq 'Email'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
expected = <<-EOF
|
|
93
|
+
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
|
|
94
|
+
1 5 Thu Aug 10 11:19:49 nobody@example.com
|
|
95
|
+
recip@h1.example.com
|
|
96
|
+
|
|
97
|
+
2 5 Thu Aug 10 11:19:50 nobody@example.com
|
|
98
|
+
recip@h1.example.com
|
|
99
|
+
|
|
100
|
+
3 5 Thu Aug 10 11:19:51 nobody@example.com
|
|
101
|
+
Last send attempt: Thu Aug 10 11:40:05 +1000 2006
|
|
102
|
+
recip@h2.example.com
|
|
103
|
+
|
|
104
|
+
-- 0 Kbytes in 3 Requests.
|
|
105
|
+
EOF
|
|
106
|
+
|
|
107
|
+
assert_equal expected, out.string
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def test_class_mailq_empty
|
|
111
|
+
out, err = util_capture do
|
|
112
|
+
ActionMailer::ARSendmail.mailq 'Email'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
assert_equal "Mail queue is empty\n", out.string
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_class_new
|
|
119
|
+
@sm = ActionMailer::ARSendmail.new
|
|
120
|
+
|
|
121
|
+
assert_equal 60, @sm.delay
|
|
122
|
+
assert_equal Email, @sm.email_class
|
|
123
|
+
assert_equal nil, @sm.once
|
|
124
|
+
assert_equal nil, @sm.verbose
|
|
125
|
+
assert_equal nil, @sm.batch_size
|
|
126
|
+
|
|
127
|
+
@sm = ActionMailer::ARSendmail.new :Delay => 75, :Verbose => true,
|
|
128
|
+
:TableName => 'Object', :Once => true,
|
|
129
|
+
:BatchSize => 1000
|
|
130
|
+
|
|
131
|
+
assert_equal 75, @sm.delay
|
|
132
|
+
assert_equal Object, @sm.email_class
|
|
133
|
+
assert_equal true, @sm.once
|
|
134
|
+
assert_equal true, @sm.verbose
|
|
135
|
+
assert_equal 1000, @sm.batch_size
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_class_parse_args_batch_size
|
|
139
|
+
options = ActionMailer::ARSendmail.process_args %w[-b 500]
|
|
140
|
+
|
|
141
|
+
assert_equal 500, options[:BatchSize]
|
|
142
|
+
|
|
143
|
+
options = ActionMailer::ARSendmail.process_args %w[--batch-size 500]
|
|
144
|
+
|
|
145
|
+
assert_equal 500, options[:BatchSize]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def test_class_parse_args_chdir
|
|
149
|
+
argv = %w[-c /tmp]
|
|
150
|
+
|
|
151
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
152
|
+
|
|
153
|
+
assert_equal '/tmp', options[:Chdir]
|
|
154
|
+
|
|
155
|
+
argv = %w[--chdir /tmp]
|
|
156
|
+
|
|
157
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
158
|
+
|
|
159
|
+
assert_equal '/tmp', options[:Chdir]
|
|
160
|
+
|
|
161
|
+
argv = %w[-c /nonexistent]
|
|
162
|
+
|
|
163
|
+
out, err = util_capture do
|
|
164
|
+
assert_raises SystemExit do
|
|
165
|
+
ActionMailer::ARSendmail.process_args argv
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_class_parse_args_daemon
|
|
171
|
+
argv = %w[-d]
|
|
172
|
+
|
|
173
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
174
|
+
|
|
175
|
+
assert_equal true, options[:Daemon]
|
|
176
|
+
|
|
177
|
+
argv = %w[--daemon]
|
|
178
|
+
|
|
179
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
180
|
+
|
|
181
|
+
assert_equal true, options[:Daemon]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_class_parse_args_delay
|
|
185
|
+
argv = %w[--delay 75]
|
|
186
|
+
|
|
187
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
188
|
+
|
|
189
|
+
assert_equal 75, options[:Delay]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def test_class_parse_args_environment
|
|
193
|
+
assert_equal nil, ENV['RAILS_ENV']
|
|
194
|
+
|
|
195
|
+
argv = %w[-e production]
|
|
196
|
+
|
|
197
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
198
|
+
|
|
199
|
+
assert_equal 'production', options[:RailsEnv]
|
|
200
|
+
|
|
201
|
+
assert_equal 'production', ENV['RAILS_ENV']
|
|
202
|
+
|
|
203
|
+
argv = %w[--environment production]
|
|
204
|
+
|
|
205
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
206
|
+
|
|
207
|
+
assert_equal 'production', options[:RailsEnv]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def test_class_parse_args_mailq
|
|
211
|
+
options = ActionMailer::ARSendmail.process_args []
|
|
212
|
+
deny_includes :MailQ, options
|
|
213
|
+
|
|
214
|
+
argv = %w[--mailq]
|
|
215
|
+
|
|
216
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
217
|
+
|
|
218
|
+
assert_equal true, options[:MailQ]
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def test_class_parse_args_max_age
|
|
222
|
+
options = ActionMailer::ARSendmail.process_args []
|
|
223
|
+
assert_equal 86400 * 7, options[:MaxAge]
|
|
224
|
+
|
|
225
|
+
argv = %w[--max-age 86400]
|
|
226
|
+
|
|
227
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
228
|
+
|
|
229
|
+
assert_equal 86400, options[:MaxAge]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def test_class_parse_args_migration
|
|
233
|
+
options = ActionMailer::ARSendmail.process_args []
|
|
234
|
+
deny_includes :Migration, options
|
|
235
|
+
|
|
236
|
+
argv = %w[--create-migration]
|
|
237
|
+
|
|
238
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
239
|
+
|
|
240
|
+
assert_equal true, options[:Migrate]
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def test_class_parse_args_model
|
|
244
|
+
options = ActionMailer::ARSendmail.process_args []
|
|
245
|
+
deny_includes :Model, options
|
|
246
|
+
|
|
247
|
+
argv = %w[--create-model]
|
|
248
|
+
|
|
249
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
250
|
+
|
|
251
|
+
assert_equal true, options[:Model]
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def test_class_parse_args_no_config_environment
|
|
255
|
+
$".delete 'config/environment.rb'
|
|
256
|
+
|
|
257
|
+
out, err = util_capture do
|
|
258
|
+
assert_raise SystemExit do
|
|
259
|
+
ActionMailer::ARSendmail.process_args []
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
ensure
|
|
264
|
+
$" << 'config/environment.rb' if @include_c_e
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def test_class_parse_args_no_config_environment_migrate
|
|
268
|
+
$".delete 'config/environment.rb'
|
|
269
|
+
|
|
270
|
+
out, err = util_capture do
|
|
271
|
+
ActionMailer::ARSendmail.process_args %w[--create-migration]
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
assert true # count
|
|
275
|
+
|
|
276
|
+
ensure
|
|
277
|
+
$" << 'config/environment.rb' if @include_c_e
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def test_class_parse_args_no_config_environment_model
|
|
281
|
+
$".delete 'config/environment.rb'
|
|
282
|
+
|
|
283
|
+
out, err = util_capture do
|
|
284
|
+
ActionMailer::ARSendmail.process_args %w[--create-model]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
assert true # count
|
|
288
|
+
|
|
289
|
+
rescue SystemExit
|
|
290
|
+
flunk 'Should not exit'
|
|
291
|
+
|
|
292
|
+
ensure
|
|
293
|
+
$" << 'config/environment.rb' if @include_c_e
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def test_class_parse_args_once
|
|
297
|
+
argv = %w[-o]
|
|
298
|
+
|
|
299
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
300
|
+
|
|
301
|
+
assert_equal true, options[:Once]
|
|
302
|
+
|
|
303
|
+
argv = %w[--once]
|
|
304
|
+
|
|
305
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
306
|
+
|
|
307
|
+
assert_equal true, options[:Once]
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def test_class_parse_args_table_name
|
|
311
|
+
argv = %w[-t Email]
|
|
312
|
+
|
|
313
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
314
|
+
|
|
315
|
+
assert_equal 'Email', options[:TableName]
|
|
316
|
+
|
|
317
|
+
argv = %w[--table-name=Email]
|
|
318
|
+
|
|
319
|
+
options = ActionMailer::ARSendmail.process_args argv
|
|
320
|
+
|
|
321
|
+
assert_equal 'Email', options[:TableName]
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def test_class_usage
|
|
325
|
+
out, err = util_capture do
|
|
326
|
+
assert_raises SystemExit do
|
|
327
|
+
ActionMailer::ARSendmail.usage 'opts'
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
assert_equal '', out.string
|
|
332
|
+
assert_equal "opts\n", err.string
|
|
333
|
+
|
|
334
|
+
out, err = util_capture do
|
|
335
|
+
assert_raises SystemExit do
|
|
336
|
+
ActionMailer::ARSendmail.usage 'opts', 'hi'
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
assert_equal '', out.string
|
|
341
|
+
assert_equal "hi\n\nopts\n", err.string
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def test_cleanup
|
|
345
|
+
e1 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
346
|
+
e1.created_on = Time.now
|
|
347
|
+
e2 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
348
|
+
e3 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
349
|
+
e3.last_send_attempt = Time.now
|
|
350
|
+
|
|
351
|
+
out, err = util_capture do
|
|
352
|
+
@sm.cleanup
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
assert_equal '', out.string
|
|
356
|
+
assert_equal "expired 1 emails from the queue\n", err.string
|
|
357
|
+
assert_equal 2, Email.records.length
|
|
358
|
+
|
|
359
|
+
assert_equal [e1, e2], Email.records
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def test_cleanup_disabled
|
|
363
|
+
e1 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
364
|
+
e1.created_on = Time.now
|
|
365
|
+
e2 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
366
|
+
|
|
367
|
+
@sm.max_age = 0
|
|
368
|
+
|
|
369
|
+
out, err = util_capture do
|
|
370
|
+
@sm.cleanup
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
assert_equal '', out.string
|
|
374
|
+
assert_equal 2, Email.records.length
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def test_deliver
|
|
378
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
379
|
+
|
|
380
|
+
out, err = util_capture do
|
|
381
|
+
@sm.deliver [email]
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
assert_equal 1, Net::SMTP.deliveries.length
|
|
385
|
+
assert_equal ['body', 'from', 'to'], Net::SMTP.deliveries.first
|
|
386
|
+
assert_equal 0, Email.records.length
|
|
387
|
+
assert_equal 0, Net::SMTP.reset_called, 'Reset connection on SyntaxError'
|
|
388
|
+
|
|
389
|
+
assert_equal '', out.string
|
|
390
|
+
assert_equal "sent email 00000000001 from from to to: \"queued\"\n", err.string
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def test_deliver_auth_error
|
|
394
|
+
Net::SMTP.on_start do
|
|
395
|
+
e = Net::SMTPAuthenticationError.new 'try again'
|
|
396
|
+
e.set_backtrace %w[one two three]
|
|
397
|
+
raise e
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
now = Time.now.to_i
|
|
401
|
+
|
|
402
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
403
|
+
|
|
404
|
+
out, err = util_capture do
|
|
405
|
+
@sm.deliver [email]
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
assert_equal 0, Net::SMTP.deliveries.length
|
|
409
|
+
assert_equal 1, Email.records.length
|
|
410
|
+
assert_equal 0, Email.records.first.last_send_attempt
|
|
411
|
+
assert_equal 0, Net::SMTP.reset_called
|
|
412
|
+
assert_equal 1, @sm.failed_auth_count
|
|
413
|
+
assert_equal [60], @sm.slept
|
|
414
|
+
|
|
415
|
+
assert_equal '', out.string
|
|
416
|
+
assert_equal "authentication error, retrying: try again\n", err.string
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def test_deliver_auth_error_recover
|
|
420
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
421
|
+
@sm.failed_auth_count = 1
|
|
422
|
+
|
|
423
|
+
out, err = util_capture do @sm.deliver [email] end
|
|
424
|
+
|
|
425
|
+
assert_equal 0, @sm.failed_auth_count
|
|
426
|
+
assert_equal 1, Net::SMTP.deliveries.length
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def test_deliver_auth_error_twice
|
|
430
|
+
Net::SMTP.on_start do
|
|
431
|
+
e = Net::SMTPAuthenticationError.new 'try again'
|
|
432
|
+
e.set_backtrace %w[one two three]
|
|
433
|
+
raise e
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
@sm.failed_auth_count = 1
|
|
437
|
+
|
|
438
|
+
out, err = util_capture do
|
|
439
|
+
assert_raise Net::SMTPAuthenticationError do
|
|
440
|
+
@sm.deliver []
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
assert_equal 2, @sm.failed_auth_count
|
|
445
|
+
assert_equal "authentication error, giving up: try again\n", err.string
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def test_deliver_4xx_error
|
|
449
|
+
Net::SMTP.on_send_message do
|
|
450
|
+
e = Net::SMTPSyntaxError.new 'try again'
|
|
451
|
+
e.set_backtrace %w[one two three]
|
|
452
|
+
raise e
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
now = Time.now.to_i
|
|
456
|
+
|
|
457
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
458
|
+
|
|
459
|
+
out, err = util_capture do
|
|
460
|
+
@sm.deliver [email]
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
assert_equal 0, Net::SMTP.deliveries.length
|
|
464
|
+
assert_equal 1, Email.records.length
|
|
465
|
+
assert_operator now, :<=, Email.records.first.last_send_attempt
|
|
466
|
+
assert_equal 1, Net::SMTP.reset_called, 'Reset connection on SyntaxError'
|
|
467
|
+
|
|
468
|
+
assert_equal '', out.string
|
|
469
|
+
assert_equal "error sending email 1: \"try again\"(Net::SMTPSyntaxError):\n\tone\n\ttwo\n\tthree\n", err.string
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def test_deliver_5xx_error
|
|
473
|
+
Net::SMTP.on_send_message do
|
|
474
|
+
e = Net::SMTPFatalError.new 'unknown recipient'
|
|
475
|
+
e.set_backtrace %w[one two three]
|
|
476
|
+
raise e
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
now = Time.now.to_i
|
|
480
|
+
|
|
481
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
482
|
+
|
|
483
|
+
out, err = util_capture do
|
|
484
|
+
@sm.deliver [email]
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
assert_equal 0, Net::SMTP.deliveries.length
|
|
488
|
+
assert_equal 0, Email.records.length
|
|
489
|
+
assert_equal 1, Net::SMTP.reset_called, 'Reset connection on SyntaxError'
|
|
490
|
+
|
|
491
|
+
assert_equal '', out.string
|
|
492
|
+
assert_equal "5xx error sending email 1, removing from queue: \"unknown recipient\"(Net::SMTPFatalError):\n\tone\n\ttwo\n\tthree\n", err.string
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def test_deliver_errno_epipe
|
|
496
|
+
Net::SMTP.on_send_message do
|
|
497
|
+
raise Errno::EPIPE
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
now = Time.now.to_i
|
|
501
|
+
|
|
502
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
503
|
+
|
|
504
|
+
out, err = util_capture do
|
|
505
|
+
@sm.deliver [email]
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
assert_equal 0, Net::SMTP.deliveries.length
|
|
509
|
+
assert_equal 1, Email.records.length
|
|
510
|
+
assert_operator now, :>=, Email.records.first.last_send_attempt
|
|
511
|
+
assert_equal 0, Net::SMTP.reset_called, 'Reset connection on SyntaxError'
|
|
512
|
+
|
|
513
|
+
assert_equal '', out.string
|
|
514
|
+
assert_equal '', err.string
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
def test_deliver_server_busy
|
|
518
|
+
Net::SMTP.on_send_message do
|
|
519
|
+
e = Net::SMTPServerBusy.new 'try again'
|
|
520
|
+
e.set_backtrace %w[one two three]
|
|
521
|
+
raise e
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
now = Time.now.to_i
|
|
525
|
+
|
|
526
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
527
|
+
|
|
528
|
+
out, err = util_capture do
|
|
529
|
+
@sm.deliver [email]
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
assert_equal 0, Net::SMTP.deliveries.length
|
|
533
|
+
assert_equal 1, Email.records.length
|
|
534
|
+
assert_operator now, :>=, Email.records.first.last_send_attempt
|
|
535
|
+
assert_equal 0, Net::SMTP.reset_called, 'Reset connection on SyntaxError'
|
|
536
|
+
assert_equal [60], @sm.slept
|
|
537
|
+
|
|
538
|
+
assert_equal '', out.string
|
|
539
|
+
assert_equal "server too busy, sleeping 60 seconds\n", err.string
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
def test_deliver_syntax_error
|
|
543
|
+
Net::SMTP.on_send_message do
|
|
544
|
+
Net::SMTP.on_send_message # clear
|
|
545
|
+
e = Net::SMTPSyntaxError.new 'blah blah blah'
|
|
546
|
+
e.set_backtrace %w[one two three]
|
|
547
|
+
raise e
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
now = Time.now.to_i
|
|
551
|
+
|
|
552
|
+
email1 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
553
|
+
email2 = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
554
|
+
|
|
555
|
+
out, err = util_capture do
|
|
556
|
+
@sm.deliver [email1, email2]
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
assert_equal 1, Net::SMTP.deliveries.length, 'delivery count'
|
|
560
|
+
assert_equal 1, Email.records.length
|
|
561
|
+
assert_equal 1, Net::SMTP.reset_called, 'Reset connection on SyntaxError'
|
|
562
|
+
assert_operator now, :<=, Email.records.first.last_send_attempt
|
|
563
|
+
|
|
564
|
+
assert_equal '', out.string
|
|
565
|
+
assert_equal "error sending email 1: \"blah blah blah\"(Net::SMTPSyntaxError):\n\tone\n\ttwo\n\tthree\nsent email 00000000002 from from to to: \"queued\"\n", err.string
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def test_deliver_timeout
|
|
569
|
+
Net::SMTP.on_send_message do
|
|
570
|
+
e = Timeout::Error.new 'timed out'
|
|
571
|
+
e.set_backtrace %w[one two three]
|
|
572
|
+
raise e
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
now = Time.now.to_i
|
|
576
|
+
|
|
577
|
+
email = Email.create :mail => 'body', :to => 'to', :from => 'from'
|
|
578
|
+
|
|
579
|
+
out, err = util_capture do
|
|
580
|
+
@sm.deliver [email]
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
assert_equal 0, Net::SMTP.deliveries.length
|
|
584
|
+
assert_equal 1, Email.records.length
|
|
585
|
+
assert_operator now, :>=, Email.records.first.last_send_attempt
|
|
586
|
+
assert_equal 1, Net::SMTP.reset_called, 'Reset connection on Timeout'
|
|
587
|
+
|
|
588
|
+
assert_equal '', out.string
|
|
589
|
+
assert_equal "error sending email 1: \"timed out\"(Timeout::Error):\n\tone\n\ttwo\n\tthree\n", err.string
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def test_do_exit
|
|
593
|
+
out, err = util_capture do
|
|
594
|
+
assert_raise SystemExit do
|
|
595
|
+
@sm.do_exit
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
assert_equal '', out.string
|
|
600
|
+
assert_equal "caught signal, shutting down\n", err.string
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def test_log
|
|
604
|
+
out, err = util_capture do
|
|
605
|
+
@sm.log 'hi'
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
assert_equal "hi\n", err.string
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def test_find_emails
|
|
612
|
+
email_data = [
|
|
613
|
+
{ :mail => 'body0', :to => 'recip@h1.example.com', :from => nobody },
|
|
614
|
+
{ :mail => 'body1', :to => 'recip@h1.example.com', :from => nobody },
|
|
615
|
+
{ :mail => 'body2', :to => 'recip@h2.example.com', :from => nobody },
|
|
616
|
+
]
|
|
617
|
+
|
|
618
|
+
emails = email_data.map do |email_data| Email.create email_data end
|
|
619
|
+
|
|
620
|
+
tried = Email.create :mail => 'body3', :to => 'recip@h3.example.com',
|
|
621
|
+
:from => nobody
|
|
622
|
+
|
|
623
|
+
tried.last_send_attempt = Time.now.to_i - 258
|
|
624
|
+
|
|
625
|
+
found_emails = []
|
|
626
|
+
|
|
627
|
+
out, err = util_capture do
|
|
628
|
+
found_emails = @sm.find_emails
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
assert_equal emails, found_emails
|
|
632
|
+
|
|
633
|
+
assert_equal '', out.string
|
|
634
|
+
assert_equal "found 3 emails to send\n", err.string
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
def test_smtp_settings
|
|
638
|
+
ActionMailer::Base.server_settings[:address] = 'localhost'
|
|
639
|
+
|
|
640
|
+
assert_equal 'localhost', @sm.smtp_settings[:address]
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
def nobody
|
|
644
|
+
'nobody@example.com'
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
end
|
|
648
|
+
|