mailcannon 0.0.6 → 0.0.8.pre.1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -2
- data/Rakefile +1 -1
- data/Readme.md +44 -5
- data/env.sample.sh +6 -4
- data/lib/mailcannon/adapters/sendgrid_web.rb +49 -13
- data/lib/mailcannon/envelope.rb +26 -12
- data/lib/mailcannon/envelope_bag.rb +9 -0
- data/lib/mailcannon/envelope_bag_map_reduce.rb +106 -0
- data/lib/mailcannon/envelope_bag_statistic.rb +5 -0
- data/lib/mailcannon/reduces/envelope_bag_map.js +3 -0
- data/lib/mailcannon/reduces/envelope_bag_reduce.js +116 -0
- data/lib/mailcannon/sendgrid_event.rb +18 -0
- data/lib/mailcannon/version.rb +3 -2
- data/lib/mailcannon/workers/barrel.rb +7 -2
- data/lib/mailcannon/workers/envelope_bag_reduce_job.rb +11 -0
- data/lib/mailcannon.rb +5 -4
- data/mailcannon.gemspec +0 -1
- data/spec/integration/1k_spec.rb +16 -2
- data/spec/integration/full_stack_stats_spec.rb +118 -0
- data/spec/integration/xsmtpapi_spec.rb +5 -6
- data/spec/mailcannon/adapters/sendgrid_spec.rb +17 -4
- data/spec/mailcannon/envelope_bag_map_reduce_spec.rb +211 -0
- data/spec/mailcannon/envelope_bag_statistic_spec.rb +41 -0
- data/spec/mailcannon/envelope_spec.rb +5 -0
- data/spec/mailcannon/workers/barrel_spec.rb +0 -8
- data/spec/mailcannon/workers/envelope_bag_reduce_job_spec.rb +17 -0
- data/spec/spec_helper.rb +4 -1
- data/templates/config/mailcannon.yml +10 -1
- metadata +18 -4
data/spec/integration/1k_spec.rb
CHANGED
@@ -1,10 +1,24 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require 'benchmark'
|
3
|
+
|
2
4
|
|
3
5
|
describe "shoot 1k emails!" do
|
4
|
-
let(:
|
6
|
+
let!(:envelope_a) { build(:envelope_multi_1k) }
|
5
7
|
it "sends http request for Sendgrid web API" do
|
6
8
|
VCR.use_cassette('mailcannon_integration_1k') do
|
7
|
-
|
9
|
+
Sidekiq::Testing.inline! do
|
10
|
+
bm = Benchmark.measure do
|
11
|
+
envelope_a.post!
|
12
|
+
end
|
13
|
+
puts "1k test real time: #{bm.real}"
|
14
|
+
expect(envelope_a.reload.processed?).to be_true
|
15
|
+
|
16
|
+
# Travis has been showing unstable performance, not feasible to include performance tests.
|
17
|
+
# The performance varies from machine to machine, specially when using dedicated servers for each service.
|
18
|
+
if ENV['PERFORMANCE_TEST']
|
19
|
+
expect(bm.real<0.2).to be_true
|
20
|
+
end
|
21
|
+
end
|
8
22
|
end
|
9
23
|
end
|
10
24
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe 'full stack test' do
|
4
|
+
describe "should send 2 envelopes, receive correct statistics and map/reduce data correctly" do
|
5
|
+
|
6
|
+
let(:expected_hash_a){
|
7
|
+
{
|
8
|
+
"posted"=>{"count"=>1.0, "targets"=>["1"]},
|
9
|
+
"processed"=>{"count"=>1.0, "targets"=>["2"]},
|
10
|
+
"delivered"=>{"count"=>1.0, "targets"=>["3"]},
|
11
|
+
"open" => {"count"=>1.0, "targets"=>["4"]},
|
12
|
+
"click"=>{"count"=>1.0, "targets"=>["5"]},
|
13
|
+
"deferred"=>{"count"=>1.0, "targets"=>["6"]},
|
14
|
+
"spam_report"=>{"count"=>1.0, "targets"=>["7"]},
|
15
|
+
"spam"=>{"count"=>1.0, "targets"=>["8"]},
|
16
|
+
"unsubscribe"=>{"count"=>1.0, "targets"=>["9"]},
|
17
|
+
"drop"=>{"count"=>1.0, "targets"=>["10"]},
|
18
|
+
"hard_bounce" => {"count"=>1.0, "targets"=>["11"]},
|
19
|
+
"soft_bounce"=>{"count"=>1.0, "targets"=>["12"]},
|
20
|
+
"unknown"=>{"count"=>1.0, "targets"=>["13"]}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
let(:expected_hash_b){
|
25
|
+
{
|
26
|
+
"posted"=>{"count"=>2.0, "targets"=>["1", "1" ]},
|
27
|
+
"processed"=>{"count"=>2.0, "targets"=>["2", "2"]},
|
28
|
+
"delivered"=>{"count"=>2.0, "targets"=>["3", "3"]},
|
29
|
+
"open" => {"count"=>2.0, "targets"=>["4", "4"]},
|
30
|
+
"click"=>{"count"=>2.0, "targets"=>["5","5"]},
|
31
|
+
"deferred"=>{"count"=>2.0, "targets"=>["6", "6"]},
|
32
|
+
"spam_report"=>{"count"=>2.0, "targets"=>["7", "7"]},
|
33
|
+
"spam"=>{"count"=>2.0, "targets"=>["8", "8"]},
|
34
|
+
"unsubscribe"=>{"count"=>2.0, "targets"=>["9", "9"]},
|
35
|
+
"drop"=>{"count"=>2.0, "targets"=>["10", "10"]},
|
36
|
+
"hard_bounce" => {"count"=>2.0, "targets"=>["11", "11"]},
|
37
|
+
"soft_bounce"=>{"count"=>2.0, "targets"=>["12", "12"]},
|
38
|
+
"unknown"=>{"count"=>2.0, "targets"=>["13", "13"]}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
context "send emails and map reduce" do
|
43
|
+
let(:envelope_bag) { build(:empty_envelope_bag)}
|
44
|
+
let!(:envelope_a) { build(:envelope_multi) }
|
45
|
+
let!(:envelope_b) { build(:envelope_multi) }
|
46
|
+
|
47
|
+
it "sends http request for Sendgrid web API" do
|
48
|
+
envelope_bag.save
|
49
|
+
envelope_bag.envelopes << envelope_a
|
50
|
+
envelope_bag.envelopes << envelope_b
|
51
|
+
VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
|
52
|
+
Sidekiq::Testing.inline! do
|
53
|
+
bm = Benchmark.measure do
|
54
|
+
envelope_a.send_bulk!
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
envelope_a_hash = [
|
60
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'posted', target_id: '1'},
|
61
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'processed', target_id: '2'},
|
62
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'delivered', target_id: '3'},
|
63
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'open', target_id: '4'},
|
64
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'click', type: 'bounce', target_id: '5'},
|
65
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'deferred', type: 'expected', target_id: '6'},
|
66
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'spam_report', target_id: '7'},
|
67
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'spam', target_id: '8'},
|
68
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'unsubscribe', target_id: '9'},
|
69
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'drop', target_id: '10'},
|
70
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'bounce', type: 'bounce', target_id: '11'},
|
71
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'bounce', type: 'expected', target_id: '12'},
|
72
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'gfyigad', target_id: '13'},
|
73
|
+
]
|
74
|
+
MailCannon::SendgridEvent.insert_bulk(envelope_a_hash)
|
75
|
+
|
76
|
+
Sidekiq::Testing.inline! do
|
77
|
+
MailCannon::EnvelopeBagReduceJob.perform_async([envelope_bag.id])
|
78
|
+
end
|
79
|
+
|
80
|
+
expect(envelope_a.reload.sendgrid_events.where(processed: true).count).to eq(13)
|
81
|
+
expect(envelope_bag.stats).to eq(expected_hash_a)
|
82
|
+
|
83
|
+
VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
|
84
|
+
Sidekiq::Testing.inline! do
|
85
|
+
bm = Benchmark.measure do
|
86
|
+
envelope_b.send_bulk!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
envelope_b_hash = [
|
92
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'posted', target_id: '1'},
|
93
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'processed', target_id: '2'},
|
94
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'delivered', target_id: '3'},
|
95
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'open', target_id: '4'},
|
96
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'click', type: 'bounce', target_id: '5'},
|
97
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'deferred', type: 'expected', target_id: '6'},
|
98
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'spam_report', target_id: '7'},
|
99
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'spam', target_id: '8'},
|
100
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'unsubscribe', target_id: '9'},
|
101
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'drop', target_id: '10'},
|
102
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'bounce', type: 'bounce', target_id: '11'},
|
103
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'bounce', type: 'expected', target_id: '12'},
|
104
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'gfyigad', target_id: '13'},
|
105
|
+
]
|
106
|
+
MailCannon::SendgridEvent.insert_bulk(envelope_b_hash)
|
107
|
+
|
108
|
+
Sidekiq::Testing.inline! do
|
109
|
+
MailCannon::EnvelopeBagReduceJob.perform_async([envelope_bag.id])
|
110
|
+
end
|
111
|
+
|
112
|
+
expect(envelope_b.reload.sendgrid_events.where(processed: true).count).to eq(13)
|
113
|
+
expect(envelope_bag.stats).to eq(expected_hash_b)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -5,9 +5,13 @@ describe 'X-SMTPAPI compatibility' do
|
|
5
5
|
# This should guarantee the expected behavior for the following api:
|
6
6
|
# http://sendgrid.com/docs/API_Reference/SMTP_API/unique_arguments.html
|
7
7
|
context "generates expected xsmtpapi for #post!" do
|
8
|
+
let(:envelope_bag) { build(:empty_envelope_bag)}
|
8
9
|
let(:envelope) { build(:envelope_multi, xsmtpapi: { "sub" => { "-email-id-" => ["314159","271828"] }, "unique_args" => { "email_id" => "-email-id-"} }) }
|
10
|
+
let(:expectated_hash) { {"sub"=>{"-email-id-"=>["314159", "271828"], "*|NAME|*"=>["Mail Cannon", "Lucas Martins", "Contact"], "*|EMAIL|*"=>["mailcannon@railsnapraia.com", "lucasmartins@railsnapraia.com", "contact@railsonthebeach.com"]}, "unique_args"=>{"email_id"=>"-email-id-", "envelope_id"=>envelope.id, "envelope_bag_id"=>envelope_bag.id}, "to"=>["mailcannon@railsnapraia.com", "lucasmartins@railsnapraia.com", "contact@railsonthebeach.com"]} }
|
9
11
|
|
10
12
|
it "returns true" do
|
13
|
+
envelope_bag.save
|
14
|
+
envelope_bag.envelopes << envelope
|
11
15
|
VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
|
12
16
|
Sidekiq::Testing.inline! do
|
13
17
|
envelope.post!
|
@@ -15,12 +19,7 @@ describe 'X-SMTPAPI compatibility' do
|
|
15
19
|
end
|
16
20
|
envelope.save
|
17
21
|
envelope.reload # content is changed inside the Adapter module
|
18
|
-
expect(envelope.xsmtpapi).to eq(
|
19
|
-
"sub"=>{"*|NAME|*"=>["Mail Cannon", "Lucas Martins", "Contact"]},
|
20
|
-
"unique_args"=>{"email_id"=>"-email-id-"},
|
21
|
-
"to"=>["mailcannon@railsnapraia.com",
|
22
|
-
"lucasmartins@railsnapraia.com",
|
23
|
-
"contact@railsonthebeach.com"]})
|
22
|
+
expect(envelope.xsmtpapi).to eq(expectated_hash)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -21,13 +21,19 @@ describe MailCannon::Adapter::SendgridWeb do
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
describe "#send!" do
|
24
|
+
let(:envelope_bag) { build(:empty_envelope_bag) }
|
24
25
|
let(:envelope) { build(:envelope) }
|
26
|
+
|
25
27
|
it "sends http request for Sendgrid web API" do
|
28
|
+
envelope_bag.save
|
29
|
+
envelope_bag.envelopes << envelope
|
26
30
|
VCR.use_cassette('mailcannon_adapter_sendgrid_send') do
|
27
31
|
expect(envelope.send!).to be_true
|
28
32
|
end
|
29
33
|
end
|
30
34
|
it "calls after_sent callback" do
|
35
|
+
envelope_bag.save
|
36
|
+
envelope_bag.envelopes << envelope
|
31
37
|
VCR.use_cassette('mailcannon_adapter_sendgrid_send') do
|
32
38
|
envelope.should_receive(:after_sent)
|
33
39
|
envelope.send!
|
@@ -36,8 +42,11 @@ describe MailCannon::Adapter::SendgridWeb do
|
|
36
42
|
end
|
37
43
|
|
38
44
|
describe "#send_bulk!" do
|
45
|
+
let(:envelope_bag) { build(:empty_envelope_bag) }
|
39
46
|
let(:envelope) { build(:envelope_multi) }
|
40
47
|
it "sends http request for Sendgrid web API" do
|
48
|
+
envelope_bag.save
|
49
|
+
envelope_bag.envelopes << envelope
|
41
50
|
VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
|
42
51
|
expect(envelope.send_bulk!).to be_true
|
43
52
|
end
|
@@ -46,12 +55,16 @@ describe MailCannon::Adapter::SendgridWeb do
|
|
46
55
|
|
47
56
|
context "grab auth exception" do
|
48
57
|
describe "#send!" do
|
58
|
+
let(:envelope_bag) { build(:empty_envelope_bag) }
|
49
59
|
let(:envelope) { build(:envelope_wrong_auth) }
|
50
|
-
|
51
|
-
|
52
|
-
|
60
|
+
it "sends http request for Sendgrid web API with a wrong user/passwd combination" do
|
61
|
+
envelope_bag.save
|
62
|
+
envelope_bag.envelopes << envelope
|
63
|
+
Sidekiq::Testing.inline! do
|
64
|
+
expect{envelope.send!}.to raise_error(MailCannon::Adapter::AuthException)
|
65
|
+
end
|
53
66
|
end
|
54
67
|
end
|
55
68
|
end
|
56
|
-
|
69
|
+
|
57
70
|
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe MailCannon::EnvelopeBagMapReduce do
|
4
|
+
|
5
|
+
describe "#find_gem_root_path" do
|
6
|
+
|
7
|
+
after(:all) do
|
8
|
+
Gem::Specification.any_instance.unstub(:gem_dir)
|
9
|
+
Gem::Specification.unstub(:find_by_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "when gem is found" do
|
13
|
+
it "returns the path" do
|
14
|
+
mocked_gem_path = "/app/vendor/bundle/ruby/2.0.0/bundler/gems/mailcannon-2c266138c2eb"
|
15
|
+
Gem::Specification.stub(:find_by_name).and_return(Gem::Specification.new)
|
16
|
+
Gem::Specification.any_instance.stub(:gem_dir).and_return(mocked_gem_path)
|
17
|
+
expect(MailCannon::EnvelopeBag.find_gem_root_path).to eq(mocked_gem_path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when gem is not found" do
|
22
|
+
it "returns empty string" do
|
23
|
+
Gem::Specification.stub(:find_by_name) do
|
24
|
+
raise LoadError.new(message: /mailcannon/)
|
25
|
+
end
|
26
|
+
expect(MailCannon::EnvelopeBag.find_gem_root_path).to eq("")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when dependence is not found" do
|
31
|
+
it "raises error" do
|
32
|
+
Gem::Specification.stub(:find_by_name) do
|
33
|
+
raise LoadError.new(message: /some_other_gem/)
|
34
|
+
end
|
35
|
+
expect{ MailCannon::EnvelopeBag.find_gem_root_path }.to raise_error
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "something else goes wrong" do
|
40
|
+
it "raises exception" do
|
41
|
+
Gem::Specification.stub(:find_by_name).and_raise(Exception.new)
|
42
|
+
expect{ MailCannon::EnvelopeBag.find_gem_root_path }.to raise_exception
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#js_map" do
|
48
|
+
|
49
|
+
before(:each) do
|
50
|
+
File.stub(:read) { |path| path }
|
51
|
+
MailCannon::EnvelopeBag.instance_variable_set(:@js_map, nil)
|
52
|
+
end
|
53
|
+
|
54
|
+
after(:each) do
|
55
|
+
File.unstub(:read)
|
56
|
+
MailCannon::EnvelopeBag.unstub(:find_gem_root_path)
|
57
|
+
MailCannon::EnvelopeBag.instance_variable_set(:@js_map, nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
context "when gem is found" do
|
62
|
+
it "returns full gem file path" do
|
63
|
+
MailCannon::EnvelopeBag.stub(:find_gem_root_path).and_return("path/to/gem")
|
64
|
+
expect(MailCannon::EnvelopeBag.js_map).to eq("path/to/gem/lib/mailcannon/reduces/envelope_bag_map.js")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when gem is not found" do
|
69
|
+
it "returns file path relative to current location" do
|
70
|
+
MailCannon::EnvelopeBag.stub(:find_gem_root_path).and_return("")
|
71
|
+
expect(MailCannon::EnvelopeBag.js_map).to eq("lib/mailcannon/reduces/envelope_bag_map.js")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#js_reduce" do
|
77
|
+
|
78
|
+
before(:each) do
|
79
|
+
File.stub(:read) { |path| path }
|
80
|
+
MailCannon::EnvelopeBag.instance_variable_set(:@js_reduce, nil)
|
81
|
+
end
|
82
|
+
|
83
|
+
after(:each) do
|
84
|
+
File.unstub(:read)
|
85
|
+
MailCannon::EnvelopeBag.unstub(:find_gem_root_path)
|
86
|
+
MailCannon::EnvelopeBag.instance_variable_set(:@js_reduce, nil)
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when gem is found" do
|
90
|
+
it "returns full gem file path" do
|
91
|
+
MailCannon::EnvelopeBag.stub(:find_gem_root_path).and_return("path/to/gem")
|
92
|
+
expect(MailCannon::EnvelopeBag.js_reduce).to eq("path/to/gem/lib/mailcannon/reduces/envelope_bag_reduce.js")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "when gem is not found" do
|
97
|
+
it "returns file path relative to current location" do
|
98
|
+
MailCannon::EnvelopeBag.stub(:find_gem_root_path).and_return("")
|
99
|
+
expect(MailCannon::EnvelopeBag.js_reduce).to eq("lib/mailcannon/reduces/envelope_bag_reduce.js")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
let!(:envelope_bag) { build(:empty_envelope_bag) }
|
106
|
+
let(:envelope_a) { build(:envelope, envelope_bag_id: envelope_bag.id) }
|
107
|
+
let(:envelope_b) { build(:envelope, envelope_bag_id: envelope_bag.id) }
|
108
|
+
let(:envelope_c) { build(:envelope, envelope_bag_id: envelope_bag.id) }
|
109
|
+
|
110
|
+
def insert_sample_events
|
111
|
+
test_hash = [
|
112
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'delivered', target_id: '1'},
|
113
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'open', target_id: '2'},
|
114
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'bounce', type: 'bounce', target_id: '3'},
|
115
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'delivered', target_id: '1'},
|
116
|
+
{envelope_id: envelope_b.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'click', target_id: '2'},
|
117
|
+
{envelope_id: envelope_c.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'click', target_id: '1'}
|
118
|
+
]
|
119
|
+
MailCannon::SendgridEvent.insert_bulk(test_hash)
|
120
|
+
end
|
121
|
+
|
122
|
+
before(:each) do
|
123
|
+
envelope_a.save
|
124
|
+
envelope_b.save
|
125
|
+
envelope_c.save
|
126
|
+
insert_sample_events
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "#change_events_status_for_envelope" do
|
130
|
+
it "sets events status (processed) to :lock(false)" do
|
131
|
+
MailCannon::EnvelopeBag.change_events_status_for_envelope_bag(envelope_bag.id, nil, :lock)
|
132
|
+
expect(envelope_a.reload.sendgrid_events.where(processed: false).count).to eq(3)
|
133
|
+
expect(envelope_b.reload.sendgrid_events.where(processed: false).count).to eq(2)
|
134
|
+
expect(envelope_c.reload.sendgrid_events.where(processed: false).count).to eq(1)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "sets events status (processed) to :processed(true)" do
|
138
|
+
MailCannon::EnvelopeBag.change_events_status_for_envelope_bag(envelope_bag.id, nil, :processed)
|
139
|
+
expect(envelope_a.reload.sendgrid_events.where(processed: true).count).to eq(3)
|
140
|
+
expect(envelope_b.reload.sendgrid_events.where(processed: true).count).to eq(2)
|
141
|
+
expect(envelope_c.reload.sendgrid_events.where(processed: true).count).to eq(1)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe ".reduce_statistics_for_envelope_bag" do
|
146
|
+
let(:expected_hash_a){
|
147
|
+
{
|
148
|
+
"posted"=>{"count"=>0.0, "targets"=>[]},
|
149
|
+
"processed"=>{"count"=>0.0, "targets"=>[]},
|
150
|
+
"delivered"=>{"count"=>2.0, "targets"=>["1", "1"]},
|
151
|
+
"open"=>{"count"=>1.0, "targets"=>["2"]},
|
152
|
+
"click"=>{"count"=>2.0, "targets"=>["2", "1"]},
|
153
|
+
"deferred"=>{"count"=>0.0, "targets"=>[]},
|
154
|
+
"spam_report"=>{"count"=>0.0, "targets"=>[]},
|
155
|
+
"spam"=>{"count"=>0.0, "targets"=>[]},
|
156
|
+
"unsubscribe"=>{"count"=>0.0, "targets"=>[]},
|
157
|
+
"drop"=>{"count"=>0.0, "targets"=>[]},
|
158
|
+
"hard_bounce"=>{"count"=>1.0, "targets"=>["3"]},
|
159
|
+
"soft_bounce"=>{"count"=>0.0, "targets"=>[]},
|
160
|
+
"unknown"=>{"count"=>0.0, "targets"=>[]}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
let(:expected_hash_b){
|
165
|
+
{
|
166
|
+
"posted"=>{"count"=>0.0, "targets"=>[]},
|
167
|
+
"processed"=>{"count"=>0.0, "targets"=>[]},
|
168
|
+
"delivered"=>{"count"=>4.0, "targets"=>["1", "1","1", "1"]},
|
169
|
+
"open"=>{"count"=>2.0, "targets"=>["2", "2"]},
|
170
|
+
"click"=>{"count"=>4.0, "targets"=>["2", "1","2", "1"]},
|
171
|
+
"deferred"=>{"count"=>0.0, "targets"=>[]},
|
172
|
+
"spam_report"=>{"count"=>0.0, "targets"=>[]},
|
173
|
+
"spam"=>{"count"=>0.0, "targets"=>[]},
|
174
|
+
"unsubscribe"=>{"count"=>0.0, "targets"=>[]},
|
175
|
+
"drop"=>{"count"=>0.0, "targets"=>[]},
|
176
|
+
"hard_bounce"=>{"count"=>2.0, "targets"=>["3","3"]},
|
177
|
+
"soft_bounce"=>{"count"=>0.0, "targets"=>[]},
|
178
|
+
"unknown"=>{"count"=>0.0, "targets"=>[]}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
it "measure reduce output availability" do
|
183
|
+
envelope_bag.reduce_statistics
|
184
|
+
start_time = Time.now
|
185
|
+
while MailCannon::EnvelopeBagStatistic.count==0
|
186
|
+
sleep 1
|
187
|
+
end
|
188
|
+
end_time = Time.now
|
189
|
+
diff = end_time-start_time
|
190
|
+
expect(diff<1.0).to be_true
|
191
|
+
end
|
192
|
+
|
193
|
+
it "creates an EnvelopeStatistic entry" do
|
194
|
+
expect{ MailCannon::EnvelopeBag.reduce_statistics_for_envelope_bag(envelope_bag.id) }.to change{ MailCannon::EnvelopeBagStatistic.count }.from(0).to(1)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "returns statistics hash/json" do
|
198
|
+
envelope_bag.reduce_statistics
|
199
|
+
expect(envelope_bag.stats).to eq(expected_hash_a)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "merges recurring reduces" do
|
203
|
+
envelope_bag.reduce_statistics
|
204
|
+
expect(envelope_bag.stats).to eq(expected_hash_a)
|
205
|
+
insert_sample_events
|
206
|
+
envelope_bag.reduce_statistics
|
207
|
+
expect(envelope_bag.stats).to eq(expected_hash_b)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe MailCannon::EnvelopeBagStatistic do
|
4
|
+
|
5
|
+
let!(:envelope_bag) { build(:empty_envelope_bag) }
|
6
|
+
let!(:envelope_a) { build(:envelope, envelope_bag_id: envelope_bag.id) }
|
7
|
+
|
8
|
+
def insert_sample_events
|
9
|
+
test_hash = [
|
10
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo1@bar.com', timestamp: 1322000092, unique_arg: 'my unique arg', event: 'delivered', target_id: '1'},
|
11
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo2@bar.com', timestamp: 1322000093, unique_arg: 'my unique arg', event: 'open', target_id: '2'},
|
12
|
+
{envelope_id: envelope_a.id, envelope_bag_id: envelope_bag.id, email: 'foo3@bar.com', timestamp: 1322000094, unique_arg: 'my unique arg', event: 'bounce', target_id: '3'},
|
13
|
+
]
|
14
|
+
MailCannon::SendgridEvent.insert_bulk(test_hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
envelope_bag.save
|
19
|
+
envelope_a.save
|
20
|
+
insert_sample_events
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "stats" do
|
24
|
+
it "has expected keys" do
|
25
|
+
envelope_bag.reduce_statistics
|
26
|
+
expect(envelope_bag.stats).to have_key("posted")
|
27
|
+
expect(envelope_bag.stats).to have_key("processed")
|
28
|
+
expect(envelope_bag.stats).to have_key("delivered")
|
29
|
+
expect(envelope_bag.stats).to have_key("open")
|
30
|
+
expect(envelope_bag.stats).to have_key("click")
|
31
|
+
expect(envelope_bag.stats).to have_key("deferred")
|
32
|
+
expect(envelope_bag.stats).to have_key("spam_report")
|
33
|
+
expect(envelope_bag.stats).to have_key("spam")
|
34
|
+
expect(envelope_bag.stats).to have_key("unsubscribe")
|
35
|
+
expect(envelope_bag.stats).to have_key("drop")
|
36
|
+
expect(envelope_bag.stats).to have_key("soft_bounce")
|
37
|
+
expect(envelope_bag.stats).to have_key("hard_bounce")
|
38
|
+
expect(envelope_bag.stats).to have_key("unknown")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -73,10 +73,13 @@ describe MailCannon::Envelope do
|
|
73
73
|
|
74
74
|
describe "xsmtpapi" do
|
75
75
|
context "keep xsmtpapi arguments after #post!" do
|
76
|
+
let(:envelope_bag) { build(:empty_envelope_bag)}
|
76
77
|
let(:envelope) { build(:envelope_multi, xsmtpapi: { "unique_args" => { "userid" => "1123", "template" => "welcome" }}) }
|
77
78
|
let(:name_placeholder) { MailCannon.config['default_name_placeholder'].to_s }
|
78
79
|
|
79
80
|
it "returns true" do
|
81
|
+
envelope_bag.save
|
82
|
+
envelope_bag.envelopes << envelope
|
80
83
|
VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
|
81
84
|
Sidekiq::Testing.inline! do
|
82
85
|
envelope.post!
|
@@ -84,6 +87,8 @@ describe MailCannon::Envelope do
|
|
84
87
|
end
|
85
88
|
envelope.reload # content is changed inside the Adapter module
|
86
89
|
expect(envelope.xsmtpapi).to have_key("unique_args")
|
90
|
+
expect(envelope.xsmtpapi["unique_args"]).to have_key("envelope_id") if MailCannon.config['add_envelope_id_to_unique_args']
|
91
|
+
expect(envelope.xsmtpapi["unique_args"]).to have_key("envelope_bag_id") if MailCannon.config['add_envelope_bag_id_to_unique_args']
|
87
92
|
expect(envelope.xsmtpapi).to have_key("to")
|
88
93
|
expect(envelope.xsmtpapi).to have_key("sub")
|
89
94
|
expect(envelope.xsmtpapi['sub']).to have_key("*|NAME|*")
|
@@ -23,14 +23,6 @@ describe MailCannon::Barrel do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
pending "calls Envelope#send!" do
|
27
|
-
Sidekiq::Testing.inline! do
|
28
|
-
MailCannon::Adapter::SendgridWeb.any_instance.should_receive('send!')
|
29
|
-
VCR.use_cassette('mailcannon_barrel_envelope_post') do
|
30
|
-
envelope.post!
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
26
|
context "check for expected adapter behavior" do
|
35
27
|
it "implements send! behavior" do
|
36
28
|
expect(envelope.respond_to?(:send!)).to be_true
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe MailCannon::EnvelopeBagReduceJob do
|
4
|
+
describe "perform" do
|
5
|
+
|
6
|
+
let(:bag_1) { create(:filled_envelope_bag) }
|
7
|
+
let(:bag_2) { create(:filled_envelope_bag) }
|
8
|
+
|
9
|
+
it "calls the reduce trigger for each envelope" do
|
10
|
+
Sidekiq::Testing.inline! do
|
11
|
+
MailCannon::EnvelopeBag.should_receive(:reduce_statistics_for_envelope_bag).with(bag_2.id.to_s).and_return(nil)
|
12
|
+
MailCannon::EnvelopeBag.should_receive(:reduce_statistics_for_envelope_bag).with(bag_1.id.to_s).and_return(nil)
|
13
|
+
MailCannon::EnvelopeBagReduceJob.perform_async([bag_2.id, bag_1.id])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,4 +11,13 @@ waiting_time: 0
|
|
11
11
|
auto_destroy: true
|
12
12
|
|
13
13
|
# MailCannon builds the 'name' substitution Array for convenience, using the name from Envelope.to: [{email: 'foo', name: 'bar'}]
|
14
|
-
default_name_placeholder: "*|NAME|*"
|
14
|
+
default_name_placeholder: "*|NAME|*"
|
15
|
+
|
16
|
+
# MailCannon builds the 'email' substitution Array for convenience, using the email from Envelope.to: [{email: 'foo', name: 'bar'}]
|
17
|
+
default_email_placeholder: "*|EMAIL|*"
|
18
|
+
|
19
|
+
# Pretty self explanatory. This is intended to be used by your event consuming service.
|
20
|
+
add_envelope_id_to_unique_args: true
|
21
|
+
|
22
|
+
# Pretty self explanatory. This is intended to be used by your event consuming service.
|
23
|
+
add_envelope_bag_id_to_unique_args: true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mailcannon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8.pre.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lucas Martins
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -280,12 +280,18 @@ files:
|
|
280
280
|
- lib/mailcannon/adapters/sendgrid_web.rb
|
281
281
|
- lib/mailcannon/envelope.rb
|
282
282
|
- lib/mailcannon/envelope_bag.rb
|
283
|
+
- lib/mailcannon/envelope_bag_map_reduce.rb
|
284
|
+
- lib/mailcannon/envelope_bag_statistic.rb
|
283
285
|
- lib/mailcannon/event.rb
|
284
286
|
- lib/mailcannon/hash.rb
|
285
287
|
- lib/mailcannon/mail.rb
|
288
|
+
- lib/mailcannon/reduces/envelope_bag_map.js
|
289
|
+
- lib/mailcannon/reduces/envelope_bag_reduce.js
|
290
|
+
- lib/mailcannon/sendgrid_event.rb
|
286
291
|
- lib/mailcannon/stamp.rb
|
287
292
|
- lib/mailcannon/version.rb
|
288
293
|
- lib/mailcannon/workers/barrel.rb
|
294
|
+
- lib/mailcannon/workers/envelope_bag_reduce_job.rb
|
289
295
|
- mailcannon.gemspec
|
290
296
|
- spec/factories/envelope.rb
|
291
297
|
- spec/factories/envelope_bag.rb
|
@@ -294,12 +300,16 @@ files:
|
|
294
300
|
- spec/fixtures/cassettes/mailcannon_adapter_sendgrid_send_bulk.yml
|
295
301
|
- spec/fixtures/cassettes/mailcannon_integration_1k.yml
|
296
302
|
- spec/integration/1k_spec.rb
|
303
|
+
- spec/integration/full_stack_stats_spec.rb
|
297
304
|
- spec/integration/xsmtpapi_spec.rb
|
298
305
|
- spec/mailcannon/adapters/sendgrid_spec.rb
|
306
|
+
- spec/mailcannon/envelope_bag_map_reduce_spec.rb
|
299
307
|
- spec/mailcannon/envelope_bag_spec.rb
|
308
|
+
- spec/mailcannon/envelope_bag_statistic_spec.rb
|
300
309
|
- spec/mailcannon/envelope_spec.rb
|
301
310
|
- spec/mailcannon/stamp_spec.rb
|
302
311
|
- spec/mailcannon/workers/barrel_spec.rb
|
312
|
+
- spec/mailcannon/workers/envelope_bag_reduce_job_spec.rb
|
303
313
|
- spec/mailcannon_spec.rb
|
304
314
|
- spec/spec_helper.rb
|
305
315
|
- spec/support/mongoid.yml
|
@@ -320,9 +330,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
320
330
|
version: 1.9.3
|
321
331
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
322
332
|
requirements:
|
323
|
-
- - '
|
333
|
+
- - '>'
|
324
334
|
- !ruby/object:Gem::Version
|
325
|
-
version:
|
335
|
+
version: 1.3.1
|
326
336
|
requirements: []
|
327
337
|
rubyforge_project:
|
328
338
|
rubygems_version: 2.0.14
|
@@ -337,12 +347,16 @@ test_files:
|
|
337
347
|
- spec/fixtures/cassettes/mailcannon_adapter_sendgrid_send_bulk.yml
|
338
348
|
- spec/fixtures/cassettes/mailcannon_integration_1k.yml
|
339
349
|
- spec/integration/1k_spec.rb
|
350
|
+
- spec/integration/full_stack_stats_spec.rb
|
340
351
|
- spec/integration/xsmtpapi_spec.rb
|
341
352
|
- spec/mailcannon/adapters/sendgrid_spec.rb
|
353
|
+
- spec/mailcannon/envelope_bag_map_reduce_spec.rb
|
342
354
|
- spec/mailcannon/envelope_bag_spec.rb
|
355
|
+
- spec/mailcannon/envelope_bag_statistic_spec.rb
|
343
356
|
- spec/mailcannon/envelope_spec.rb
|
344
357
|
- spec/mailcannon/stamp_spec.rb
|
345
358
|
- spec/mailcannon/workers/barrel_spec.rb
|
359
|
+
- spec/mailcannon/workers/envelope_bag_reduce_job_spec.rb
|
346
360
|
- spec/mailcannon_spec.rb
|
347
361
|
- spec/spec_helper.rb
|
348
362
|
- spec/support/mongoid.yml
|