gitlab-mail_room 0.0.10 → 0.0.19

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.
@@ -3,12 +3,23 @@ require 'spec_helper'
3
3
  describe MailRoom::Mailbox do
4
4
  let(:sample_message) { MailRoom::Message.new(uid: 123, body: 'a message') }
5
5
 
6
+ context 'with IMAP configuration' do
7
+ subject { build_mailbox }
8
+
9
+ describe '#imap?' do
10
+ it 'configured as an IMAP inbox' do
11
+ expect(subject.imap?).to be true
12
+ expect(subject.microsoft_graph?).to be false
13
+ end
14
+ end
15
+ end
16
+
6
17
  describe "#deliver" do
7
18
  context "with arbitration_method of noop" do
8
19
  it 'arbitrates with a Noop instance' do
9
- mailbox = build_mailbox({:arbitration_method => 'noop'})
20
+ mailbox = build_mailbox({arbitration_method: 'noop'})
10
21
  noop = stub(:deliver?)
11
- MailRoom::Arbitration['noop'].stubs(:new => noop)
22
+ MailRoom::Arbitration['noop'].stubs(new: noop)
12
23
 
13
24
  uid = 123
14
25
 
@@ -20,9 +31,9 @@ describe MailRoom::Mailbox do
20
31
 
21
32
  context "with arbitration_method of redis" do
22
33
  it 'arbitrates with a Redis instance' do
23
- mailbox = build_mailbox({:arbitration_method => 'redis'})
34
+ mailbox = build_mailbox({arbitration_method: 'redis'})
24
35
  redis = stub(:deliver?)
25
- MailRoom::Arbitration['redis'].stubs(:new => redis)
36
+ MailRoom::Arbitration['redis'].stubs(new: redis)
26
37
  uid = 123
27
38
  redis.expects(:deliver?).with(uid)
28
39
 
@@ -32,9 +43,9 @@ describe MailRoom::Mailbox do
32
43
 
33
44
  context "with delivery_method of noop" do
34
45
  it 'delivers with a Noop instance' do
35
- mailbox = build_mailbox({:delivery_method => 'noop'})
46
+ mailbox = build_mailbox({delivery_method: 'noop'})
36
47
  noop = stub(:deliver)
37
- MailRoom::Delivery['noop'].stubs(:new => noop)
48
+ MailRoom::Delivery['noop'].stubs(new: noop)
38
49
 
39
50
  noop.expects(:deliver).with(sample_message.body)
40
51
 
@@ -44,9 +55,9 @@ describe MailRoom::Mailbox do
44
55
 
45
56
  context "with delivery_method of logger" do
46
57
  it 'delivers with a Logger instance' do
47
- mailbox = build_mailbox({:delivery_method => 'logger'})
58
+ mailbox = build_mailbox({delivery_method: 'logger'})
48
59
  logger = stub(:deliver)
49
- MailRoom::Delivery['logger'].stubs(:new => logger)
60
+ MailRoom::Delivery['logger'].stubs(new: logger)
50
61
 
51
62
  logger.expects(:deliver).with(sample_message.body)
52
63
 
@@ -56,9 +67,9 @@ describe MailRoom::Mailbox do
56
67
 
57
68
  context "with delivery_method of postback" do
58
69
  it 'delivers with a Postback instance' do
59
- mailbox = build_mailbox({:delivery_method => 'postback'})
70
+ mailbox = build_mailbox({delivery_method: 'postback'})
60
71
  postback = stub(:deliver)
61
- MailRoom::Delivery['postback'].stubs(:new => postback)
72
+ MailRoom::Delivery['postback'].stubs(new: postback)
62
73
 
63
74
  postback.expects(:deliver).with(sample_message.body)
64
75
 
@@ -68,9 +79,9 @@ describe MailRoom::Mailbox do
68
79
 
69
80
  context "with delivery_method of letter_opener" do
70
81
  it 'delivers with a LetterOpener instance' do
71
- mailbox = build_mailbox({:delivery_method => 'letter_opener'})
82
+ mailbox = build_mailbox({delivery_method: 'letter_opener'})
72
83
  letter_opener = stub(:deliver)
73
- MailRoom::Delivery['letter_opener'].stubs(:new => letter_opener)
84
+ MailRoom::Delivery['letter_opener'].stubs(new: letter_opener)
74
85
 
75
86
  letter_opener.expects(:deliver).with(sample_message.body)
76
87
 
@@ -82,7 +93,7 @@ describe MailRoom::Mailbox do
82
93
  it "doesn't deliver the message" do
83
94
  mailbox = build_mailbox({ name: "magic mailbox", delivery_method: 'noop' })
84
95
  noop = stub(:deliver)
85
- MailRoom::Delivery['noop'].stubs(:new => noop)
96
+ MailRoom::Delivery['noop'].stubs(new: noop)
86
97
  noop.expects(:deliver).never
87
98
 
88
99
  mailbox.deliver(MailRoom::Message.new(uid: 1234, body: nil))
@@ -91,9 +102,9 @@ describe MailRoom::Mailbox do
91
102
 
92
103
  context "with ssl options hash" do
93
104
  it 'replaces verify mode with constant' do
94
- mailbox = build_mailbox({:ssl => {:verify_mode => :none}})
105
+ mailbox = build_mailbox({ssl: {verify_mode: :none}})
95
106
 
96
- expect(mailbox.ssl_options).to eq({:verify_mode => OpenSSL::SSL::VERIFY_NONE})
107
+ expect(mailbox.ssl_options).to eq({verify_mode: OpenSSL::SSL::VERIFY_NONE})
97
108
  end
98
109
  end
99
110
 
@@ -121,8 +132,45 @@ describe MailRoom::Mailbox do
121
132
  describe "#validate!" do
122
133
  context "with missing configuration" do
123
134
  it 'raises an error' do
124
- expect { build_mailbox({:name => nil}) }.to raise_error(MailRoom::ConfigurationError)
125
- expect { build_mailbox({:host => nil}) }.to raise_error(MailRoom::ConfigurationError)
135
+ expect { build_mailbox({name: nil}) }.to raise_error(MailRoom::ConfigurationError)
136
+ expect { build_mailbox({host: nil}) }.to raise_error(MailRoom::ConfigurationError)
137
+ end
138
+ end
139
+
140
+ context "with Microsoft Graph configuration" do
141
+ let(:options) do
142
+ {
143
+ arbitration_method: 'redis',
144
+ }.merge(REQUIRED_MICROSOFT_GRAPH_DEFAULTS)
145
+ end
146
+
147
+ subject { build_mailbox(options) }
148
+
149
+ def delete_inbox_option(key)
150
+ options[:inbox_options] = options[:inbox_options].dup.delete(key)
151
+ end
152
+
153
+ it 'allows password omission' do
154
+ expect { subject }.not_to raise_error
155
+ end
156
+
157
+ it 'configured as a Microsoft Graph inbox' do
158
+ expect(subject.imap?).to be false
159
+ expect(subject.microsoft_graph?).to be true
160
+ end
161
+
162
+ it 'raises an error when the inbox options are not present' do
163
+ options.delete(:inbox_options)
164
+
165
+ expect { subject }.to raise_error(MailRoom::ConfigurationError)
166
+ end
167
+
168
+ %i[tenant_id client_id client_secret].each do |item|
169
+ it "raises an error when the #{item} is not present" do
170
+ delete_inbox_option(item)
171
+
172
+ expect { subject }.to raise_error(MailRoom::ConfigurationError)
173
+ end
126
174
  end
127
175
  end
128
176
  end
@@ -1,61 +1,77 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe MailRoom::MailboxWatcher do
4
- let(:mailbox) {build_mailbox}
5
-
6
- describe '#running?' do
7
- it 'is false by default' do
8
- watcher = MailRoom::MailboxWatcher.new(mailbox)
9
- expect(watcher.running?).to eq(false)
4
+ context 'with IMAP configured' do
5
+ let(:mailbox) {build_mailbox}
6
+
7
+ describe '#running?' do
8
+ it 'is false by default' do
9
+ watcher = MailRoom::MailboxWatcher.new(mailbox)
10
+ expect(watcher.running?).to eq(false)
11
+ end
10
12
  end
11
- end
12
13
 
13
- describe '#run' do
14
- let(:imap) {stub(:login => true, :select => true)}
15
- let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
14
+ describe '#run' do
15
+ let(:imap) {stub(login: true, select: true)}
16
+ let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
16
17
 
17
- before :each do
18
- Net::IMAP.stubs(:new).returns(imap) # prevent connection
19
- end
18
+ before :each do
19
+ Net::IMAP.stubs(:new).returns(imap) # prevent connection
20
+ end
20
21
 
21
- it 'loops over wait while running' do
22
- connection = MailRoom::IMAP::Connection.new(mailbox)
22
+ it 'loops over wait while running' do
23
+ connection = MailRoom::IMAP::Connection.new(mailbox)
23
24
 
24
- MailRoom::IMAP::Connection.stubs(:new).returns(connection)
25
+ MailRoom::IMAP::Connection.stubs(:new).returns(connection)
25
26
 
26
- watcher.expects(:running?).twice.returns(true, false)
27
- connection.expects(:wait).once
28
- connection.expects(:on_new_message).once
27
+ watcher.expects(:running?).twice.returns(true, false)
28
+ connection.expects(:wait).once
29
+ connection.expects(:on_new_message).once
29
30
 
30
- watcher.run
31
- watcher.watching_thread.join # wait for finishing run
31
+ watcher.run
32
+ watcher.watching_thread.join # wait for finishing run
33
+ end
32
34
  end
33
- end
34
35
 
35
- describe '#quit' do
36
- let(:imap) {stub(:login => true, :select => true)}
37
- let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
36
+ describe '#quit' do
37
+ let(:imap) {stub(login: true, select: true)}
38
+ let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
38
39
 
39
- before :each do
40
- Net::IMAP.stubs(:new).returns(imap) # prevent connection
41
- end
40
+ before :each do
41
+ Net::IMAP.stubs(:new).returns(imap) # prevent connection
42
+ end
43
+
44
+ it 'closes and waits for the connection' do
45
+ connection = MailRoom::IMAP::Connection.new(mailbox)
46
+ connection.stubs(:wait)
47
+ connection.stubs(:quit)
42
48
 
43
- it 'closes and waits for the connection' do
44
- connection = MailRoom::IMAP::Connection.new(mailbox)
45
- connection.stubs(:wait)
46
- connection.stubs(:quit)
49
+ MailRoom::IMAP::Connection.stubs(:new).returns(connection)
47
50
 
48
- MailRoom::IMAP::Connection.stubs(:new).returns(connection)
51
+ watcher.run
52
+
53
+ expect(watcher.running?).to eq(true)
54
+
55
+ connection.expects(:quit)
56
+
57
+ watcher.quit
58
+
59
+ expect(watcher.running?).to eq(false)
60
+ end
61
+ end
62
+ end
49
63
 
50
- watcher.run
64
+ context 'with Microsoft Graph configured' do
65
+ let(:mailbox) { build_mailbox(REQUIRED_MICROSOFT_GRAPH_DEFAULTS) }
51
66
 
52
- expect(watcher.running?).to eq(true)
67
+ subject { described_class.new(mailbox) }
53
68
 
54
- connection.expects(:quit)
69
+ it 'initializes a Microsoft Graph connection' do
70
+ connection = stub(on_new_message: nil)
55
71
 
56
- watcher.quit
72
+ MailRoom::MicrosoftGraph::Connection.stubs(:new).returns(connection)
57
73
 
58
- expect(watcher.running?).to eq(false)
74
+ expect(subject.send(:connection)).to eq(connection)
59
75
  end
60
76
  end
61
77
  end
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require 'spec_helper'
5
+ require 'json'
6
+ require 'webmock/rspec'
7
+
8
+ describe MailRoom::MicrosoftGraph::Connection do
9
+ let(:tenant_id) { options[:inbox_options][:tenant_id] }
10
+ let(:options) do
11
+ {
12
+ delete_after_delivery: true,
13
+ expunge_deleted: true
14
+ }.merge(REQUIRED_MICROSOFT_GRAPH_DEFAULTS)
15
+ end
16
+ let(:mailbox) { build_mailbox(options) }
17
+ let(:base_url) { 'https://graph.microsoft.com/v1.0/users/user@example.com/mailFolders/inbox/messages' }
18
+ let(:message_base_url) { 'https://graph.microsoft.com/v1.0/users/user@example.com/messages' }
19
+
20
+ let(:connection) { described_class.new(mailbox) }
21
+ let(:uid) { 1 }
22
+ let(:access_token) { SecureRandom.hex }
23
+ let(:refresh_token) { SecureRandom.hex }
24
+ let(:expires_in) { Time.now + 3600 }
25
+ let(:unread_messages_body) { '' }
26
+ let(:status) { 200 }
27
+ let!(:stub_token) do
28
+ stub_request(:post, "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token").to_return(
29
+ body: { 'access_token' => access_token, 'refresh_token' => refresh_token, 'expires_in' => expires_in }.to_json,
30
+ headers: { 'Content-Type' => 'application/json' }
31
+ )
32
+ end
33
+ let!(:stub_unread_messages_request) do
34
+ stub_request(:get, "#{base_url}?$filter=isRead%20eq%20false").to_return(
35
+ status: status,
36
+ body: unread_messages_body.to_json,
37
+ headers: { 'Content-Type' => 'application/json' }
38
+ )
39
+ end
40
+
41
+ before do
42
+ WebMock.enable!
43
+ end
44
+
45
+ context '#quit' do
46
+ it 'returns false' do
47
+ expect(connection.stopped?).to be_falsey
48
+ end
49
+
50
+ it 'returns true' do
51
+ connection.quit
52
+
53
+ expect(connection.stopped?).to be_truthy
54
+ end
55
+
56
+ it 'does not attempt to process the mailbox' do
57
+ connection.quit
58
+
59
+ connection.expects(:process_mailbox).times(0)
60
+ connection.wait
61
+ end
62
+ end
63
+
64
+ context '#wait' do
65
+ before do
66
+ connection.stubs(:do_sleep)
67
+ end
68
+
69
+ describe 'poll interval' do
70
+ it 'defaults to 60 seconds' do
71
+ expect(connection.send(:poll_interval)).to eq(60)
72
+ end
73
+
74
+ it 'calls do_sleep 60 times' do
75
+ connection.expects(:do_sleep).with(1).times(60)
76
+
77
+ connection.wait
78
+ end
79
+
80
+ context 'interval set to 10' do
81
+ let(:options) do
82
+ {
83
+ inbox_method: :microsoft_graph,
84
+ inbox_options: {
85
+ tenant_id: '98776',
86
+ client_id: '12345',
87
+ client_secret: 'MY-SECRET',
88
+ poll_interval: '10'
89
+ }
90
+ }
91
+ end
92
+
93
+ it 'sets the poll interval to 10' do
94
+ expect(connection.send(:poll_interval)).to eq(10)
95
+ end
96
+
97
+ it 'calls do_sleep 10 times' do
98
+ connection.expects(:do_sleep).with(1).times(10)
99
+
100
+ connection.wait
101
+ end
102
+ end
103
+ end
104
+
105
+ context 'with a single message' do
106
+ let(:message_id) { SecureRandom.hex }
107
+ let(:unread_messages_body) { { value: ['id' => message_id] } }
108
+ let(:message_url) { "#{message_base_url}/#{message_id}" }
109
+ let(:message_body) { 'hello world' }
110
+
111
+ it 'requests message ID' do
112
+ stub_get = stub_request(:get, "#{message_url}/$value").to_return(
113
+ status: 200,
114
+ body: message_body
115
+ )
116
+ stub_patch = stub_request(:patch, message_url).with(body: { "isRead": true }.to_json)
117
+ stub_delete = stub_request(:delete, message_url)
118
+ message_count = 0
119
+
120
+ connection.on_new_message do |message|
121
+ message_count += 1
122
+ expect(message.uid).to eq(message_id)
123
+ expect(message.body).to eq(message_body)
124
+ end
125
+
126
+ connection.wait
127
+
128
+ assert_requested(stub_token)
129
+ assert_requested(stub_unread_messages_request)
130
+ assert_requested(stub_get)
131
+ assert_requested(stub_patch)
132
+ assert_requested(stub_delete)
133
+ expect(message_count).to eq(1)
134
+ end
135
+ end
136
+
137
+ context 'with multiple pages of messages' do
138
+ let(:message_ids) { [SecureRandom.hex, SecureRandom.hex] }
139
+ let(:next_page_url) { 'https://graph.microsoft.com/v1.0/nextPage' }
140
+ let(:unread_messages_body) { { value: ['id' => message_ids.first], '@odata.nextLink' => next_page_url } }
141
+ let(:message_body) { 'hello world' }
142
+
143
+ it 'requests message ID' do
144
+ stub_request(:get, next_page_url).to_return(
145
+ status: 200,
146
+ body: { value: ['id' => message_ids[1]] }.to_json
147
+ )
148
+
149
+ stubs = []
150
+ message_ids.each do |message_id|
151
+ rfc822_msg_url = "#{message_base_url}/#{message_id}/$value"
152
+ stubs << stub_request(:get, rfc822_msg_url).to_return(
153
+ status: 200,
154
+ body: message_body
155
+ )
156
+
157
+ msg_url = "#{message_base_url}/#{message_id}"
158
+ stubs << stub_request(:patch, msg_url).with(body: { "isRead": true }.to_json)
159
+ stubs << stub_request(:delete, msg_url)
160
+ end
161
+
162
+ message_count = 0
163
+
164
+ connection.on_new_message do |message|
165
+ expect(message.uid).to eq(message_ids[message_count])
166
+ expect(message.body).to eq(message_body)
167
+ message_count += 1
168
+ end
169
+
170
+ connection.wait
171
+
172
+ stubs.each { |stub| assert_requested(stub) }
173
+ expect(message_count).to eq(2)
174
+ end
175
+ end
176
+
177
+ shared_examples 'request backoff' do
178
+ it 'backs off' do
179
+ connection.expects(:backoff)
180
+
181
+ connection.on_new_message {}
182
+ connection.wait
183
+
184
+ expect(connection.throttled_count).to eq(1)
185
+ end
186
+ end
187
+
188
+ context 'too many requests' do
189
+ let(:status) { 429 }
190
+
191
+ it_behaves_like 'request backoff'
192
+ end
193
+
194
+ context 'too much bandwidth' do
195
+ let(:status) { 509 }
196
+
197
+ it_behaves_like 'request backoff'
198
+ end
199
+
200
+ context 'invalid JSON response' do
201
+ let(:body) { 'this is something' }
202
+
203
+ it 'ignores the message and logs a warning' do
204
+ mailbox.logger.expects(:warn)
205
+
206
+ connection.on_new_message {}
207
+ connection.wait
208
+ end
209
+ end
210
+
211
+ context '500 error' do
212
+ let(:status) { 500 }
213
+
214
+ it 'terminates due to error' do
215
+ connection.on_new_message {}
216
+
217
+ expect { connection.wait }.to raise_error(OAuth2::Error)
218
+ end
219
+ end
220
+ end
221
+ end
data/spec/spec_helper.rb CHANGED
@@ -22,11 +22,21 @@ RSpec.configure do |config|
22
22
  end
23
23
 
24
24
  REQUIRED_MAILBOX_DEFAULTS = {
25
- :name => "inbox",
26
- :email => "user@example.com",
27
- :password => "password123"
25
+ name: "inbox",
26
+ email: "user@example.com",
27
+ password: "password123"
28
28
  }
29
29
 
30
+ REQUIRED_MICROSOFT_GRAPH_DEFAULTS = {
31
+ password: nil,
32
+ inbox_method: :microsoft_graph,
33
+ inbox_options: {
34
+ tenant_id: '98776',
35
+ client_id: '12345',
36
+ client_secret: 'MY-SECRET',
37
+ }.freeze
38
+ }.freeze
39
+
30
40
  def build_mailbox(options = {})
31
41
  MailRoom::Mailbox.new(REQUIRED_MAILBOX_DEFAULTS.merge(options))
32
42
  end