mailcannon 0.0.6 → 0.0.8.pre.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,24 @@
1
1
  require "spec_helper"
2
+ require 'benchmark'
3
+
2
4
 
3
5
  describe "shoot 1k emails!" do
4
- let(:envelope) { build(:envelope_multi_1k) }
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
- expect(envelope.send_bulk!).to be_true
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
- it "sends http request for Sendgrid web API with a wrong user/passwd combination" do
51
- Sidekiq::Testing.inline! do
52
- expect{envelope.send!}.to raise_error(MailCannon::Adapter::AuthException)
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
- end
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
@@ -56,4 +56,7 @@ VCR.configure do |c|
56
56
  end
57
57
 
58
58
  Mongoid.load!("spec/support/mongoid.yml", 'test')
59
- MailCannon.config('templates')
59
+ MailCannon.config('templates')
60
+
61
+ ENV['AIRBRAKE_TOKEN']=nil
62
+ ENV['LIBRATO_TOKEN']=nil
@@ -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.6
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-01-21 00:00:00.000000000 Z
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: '0'
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