gitlab-mail_room 0.0.4 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab/issue_templates/Default.md +9 -0
  3. data/.gitlab/issue_templates/Release.md +8 -0
  4. data/.gitlab-ci.yml +18 -16
  5. data/.rubocop.yml +5 -0
  6. data/.rubocop_todo.yml +494 -0
  7. data/.ruby-version +1 -1
  8. data/.travis.yml +12 -5
  9. data/CHANGELOG.md +4 -0
  10. data/CONTRIBUTING.md +40 -0
  11. data/README.md +136 -10
  12. data/Rakefile +1 -1
  13. data/lib/mail_room/arbitration/redis.rb +1 -1
  14. data/lib/mail_room/cli.rb +2 -2
  15. data/lib/mail_room/configuration.rb +11 -1
  16. data/lib/mail_room/connection.rb +10 -177
  17. data/lib/mail_room/coordinator.rb +8 -4
  18. data/lib/mail_room/crash_handler.rb +8 -12
  19. data/lib/mail_room/delivery/letter_opener.rb +1 -1
  20. data/lib/mail_room/delivery/postback.rb +36 -4
  21. data/lib/mail_room/delivery/sidekiq.rb +4 -3
  22. data/lib/mail_room/health_check.rb +60 -0
  23. data/lib/mail_room/imap/connection.rb +200 -0
  24. data/lib/mail_room/imap/message.rb +19 -0
  25. data/lib/mail_room/imap.rb +8 -0
  26. data/lib/mail_room/jwt.rb +39 -0
  27. data/lib/mail_room/logger/structured.rb +15 -1
  28. data/lib/mail_room/mailbox.rb +62 -20
  29. data/lib/mail_room/mailbox_watcher.rb +15 -2
  30. data/lib/mail_room/message.rb +16 -0
  31. data/lib/mail_room/microsoft_graph/connection.rb +243 -0
  32. data/lib/mail_room/microsoft_graph.rb +7 -0
  33. data/lib/mail_room/version.rb +2 -2
  34. data/lib/mail_room.rb +2 -0
  35. data/mail_room.gemspec +13 -4
  36. data/spec/fixtures/jwt_secret +1 -0
  37. data/spec/fixtures/test_config.yml +3 -0
  38. data/spec/lib/arbitration/redis_spec.rb +9 -7
  39. data/spec/lib/cli_spec.rb +32 -17
  40. data/spec/lib/configuration_spec.rb +10 -3
  41. data/spec/lib/coordinator_spec.rb +27 -11
  42. data/spec/lib/crash_handler_spec.rb +10 -9
  43. data/spec/lib/delivery/letter_opener_spec.rb +10 -6
  44. data/spec/lib/delivery/logger_spec.rb +8 -10
  45. data/spec/lib/delivery/postback_spec.rb +73 -41
  46. data/spec/lib/delivery/que_spec.rb +5 -8
  47. data/spec/lib/delivery/sidekiq_spec.rb +33 -11
  48. data/spec/lib/health_check_spec.rb +57 -0
  49. data/spec/lib/{connection_spec.rb → imap/connection_spec.rb} +13 -17
  50. data/spec/lib/imap/message_spec.rb +36 -0
  51. data/spec/lib/jwt_spec.rb +80 -0
  52. data/spec/lib/logger/structured_spec.rb +34 -2
  53. data/spec/lib/mailbox_spec.rb +79 -34
  54. data/spec/lib/mailbox_watcher_spec.rb +54 -41
  55. data/spec/lib/message_spec.rb +35 -0
  56. data/spec/lib/microsoft_graph/connection_spec.rb +252 -0
  57. data/spec/spec_helper.rb +14 -4
  58. metadata +130 -21
@@ -1,82 +1,91 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe MailRoom::Mailbox do
4
- let(:sample_message) { {'RFC822' => 'a message', 'UID' => 123} }
4
+ let(:sample_message) { MailRoom::Message.new(uid: 123, body: 'a message') }
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
5
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
 
15
- mailbox.deliver?(uid)
26
+ noop.expects(:deliver?).with(uid)
16
27
 
17
- expect(noop).to have_received(:deliver?).with(uid)
28
+ mailbox.deliver?(uid)
18
29
  end
19
30
  end
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)
26
-
36
+ MailRoom::Arbitration['redis'].stubs(new: redis)
27
37
  uid = 123
38
+ redis.expects(:deliver?).with(uid)
28
39
 
29
40
  mailbox.deliver?(uid)
30
-
31
- expect(redis).to have_received(:deliver?).with(uid)
32
41
  end
33
42
  end
34
43
 
35
44
  context "with delivery_method of noop" do
36
45
  it 'delivers with a Noop instance' do
37
- mailbox = build_mailbox({:delivery_method => 'noop'})
46
+ mailbox = build_mailbox({delivery_method: 'noop'})
38
47
  noop = stub(:deliver)
39
- MailRoom::Delivery['noop'].stubs(:new => noop)
48
+ MailRoom::Delivery['noop'].stubs(new: noop)
40
49
 
41
- mailbox.deliver(stub(:attr => sample_message))
50
+ noop.expects(:deliver).with(sample_message.body)
42
51
 
43
- expect(noop).to have_received(:deliver).with('a message')
52
+ mailbox.deliver(sample_message)
44
53
  end
45
54
  end
46
55
 
47
56
  context "with delivery_method of logger" do
48
57
  it 'delivers with a Logger instance' do
49
- mailbox = build_mailbox({:delivery_method => 'logger'})
58
+ mailbox = build_mailbox({delivery_method: 'logger'})
50
59
  logger = stub(:deliver)
51
- MailRoom::Delivery['logger'].stubs(:new => logger)
60
+ MailRoom::Delivery['logger'].stubs(new: logger)
52
61
 
53
- mailbox.deliver(stub(:attr => sample_message))
62
+ logger.expects(:deliver).with(sample_message.body)
54
63
 
55
- expect(logger).to have_received(:deliver).with('a message')
64
+ mailbox.deliver(sample_message)
56
65
  end
57
66
  end
58
67
 
59
68
  context "with delivery_method of postback" do
60
69
  it 'delivers with a Postback instance' do
61
- mailbox = build_mailbox({:delivery_method => 'postback'})
70
+ mailbox = build_mailbox({delivery_method: 'postback'})
62
71
  postback = stub(:deliver)
63
- MailRoom::Delivery['postback'].stubs(:new => postback)
72
+ MailRoom::Delivery['postback'].stubs(new: postback)
64
73
 
65
- mailbox.deliver(stub(:attr => sample_message))
74
+ postback.expects(:deliver).with(sample_message.body)
66
75
 
67
- expect(postback).to have_received(:deliver).with('a message')
76
+ mailbox.deliver(sample_message)
68
77
  end
69
78
  end
70
79
 
71
80
  context "with delivery_method of letter_opener" do
72
81
  it 'delivers with a LetterOpener instance' do
73
- mailbox = build_mailbox({:delivery_method => 'letter_opener'})
82
+ mailbox = build_mailbox({delivery_method: 'letter_opener'})
74
83
  letter_opener = stub(:deliver)
75
- MailRoom::Delivery['letter_opener'].stubs(:new => letter_opener)
84
+ MailRoom::Delivery['letter_opener'].stubs(new: letter_opener)
76
85
 
77
- mailbox.deliver(stub(:attr => sample_message))
86
+ letter_opener.expects(:deliver).with(sample_message.body)
78
87
 
79
- expect(letter_opener).to have_received(:deliver).with('a message')
88
+ mailbox.deliver(sample_message)
80
89
  end
81
90
  end
82
91
 
@@ -84,19 +93,18 @@ describe MailRoom::Mailbox do
84
93
  it "doesn't deliver the message" do
85
94
  mailbox = build_mailbox({ name: "magic mailbox", delivery_method: 'noop' })
86
95
  noop = stub(:deliver)
87
- MailRoom::Delivery['noop'].stubs(:new => noop)
88
-
89
- mailbox.deliver(stub(:attr => {'FLAGS' => [:Seen, :Recent]}))
96
+ MailRoom::Delivery['noop'].stubs(new: noop)
97
+ noop.expects(:deliver).never
90
98
 
91
- expect(noop).to have_received(:deliver).never
99
+ mailbox.deliver(MailRoom::Message.new(uid: 1234, body: nil))
92
100
  end
93
101
  end
94
102
 
95
103
  context "with ssl options hash" do
96
104
  it 'replaces verify mode with constant' do
97
- mailbox = build_mailbox({:ssl => {:verify_mode => :none}})
105
+ mailbox = build_mailbox({ssl: {verify_mode: :none}})
98
106
 
99
- 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})
100
108
  end
101
109
  end
102
110
 
@@ -124,8 +132,45 @@ describe MailRoom::Mailbox do
124
132
  describe "#validate!" do
125
133
  context "with missing configuration" do
126
134
  it 'raises an error' do
127
- expect { build_mailbox({:name => nil}) }.to raise_error(MailRoom::ConfigurationError)
128
- 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
129
174
  end
130
175
  end
131
176
  end
@@ -1,64 +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)}
17
+
18
+ before :each do
19
+ Net::IMAP.stubs(:new).returns(imap) # prevent connection
20
+ end
21
+
22
+ it 'loops over wait while running' do
23
+ connection = MailRoom::IMAP::Connection.new(mailbox)
24
+
25
+ MailRoom::IMAP::Connection.stubs(:new).returns(connection)
26
+
27
+ watcher.expects(:running?).twice.returns(true, false)
28
+ connection.expects(:wait).once
29
+ connection.expects(:on_new_message).once
16
30
 
17
- before :each do
18
- Net::IMAP.stubs(:new).returns(imap) # prevent connection
31
+ watcher.run
32
+ watcher.watching_thread.join # wait for finishing run
33
+ end
19
34
  end
20
35
 
21
- it 'loops over wait while running' do
22
- connection = MailRoom::Connection.new(mailbox)
23
- connection.stubs(:on_new_message)
24
- connection.stubs(:wait)
36
+ describe '#quit' do
37
+ let(:imap) {stub(login: true, select: true)}
38
+ let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
25
39
 
26
- MailRoom::Connection.stubs(:new).returns(connection)
40
+ before :each do
41
+ Net::IMAP.stubs(:new).returns(imap) # prevent connection
42
+ end
27
43
 
28
- watcher.stubs(:running?).returns(true).then.returns(false)
44
+ it 'closes and waits for the connection' do
45
+ connection = MailRoom::IMAP::Connection.new(mailbox)
46
+ connection.stubs(:wait)
47
+ connection.stubs(:quit)
29
48
 
30
- watcher.run
31
- watcher.watching_thread.join # wait for finishing run
49
+ MailRoom::IMAP::Connection.stubs(:new).returns(connection)
32
50
 
33
- expect(watcher).to have_received(:running?).times(2)
34
- expect(connection).to have_received(:wait).once
35
- expect(connection).to have_received(:on_new_message).once
36
- end
37
- end
51
+ watcher.run
38
52
 
39
- describe '#quit' do
40
- let(:imap) {stub(:login => true, :select => true)}
41
- let(:watcher) {MailRoom::MailboxWatcher.new(mailbox)}
53
+ expect(watcher.running?).to eq(true)
42
54
 
43
- before :each do
44
- Net::IMAP.stubs(:new).returns(imap) # prevent connection
45
- end
55
+ connection.expects(:quit)
46
56
 
47
- it 'closes and waits for the connection' do
48
- connection = MailRoom::Connection.new(mailbox)
49
- connection.stubs(:wait)
50
- connection.stubs(:quit)
57
+ watcher.quit
58
+
59
+ expect(watcher.running?).to eq(false)
60
+ end
61
+ end
62
+ end
51
63
 
52
- MailRoom::Connection.stubs(:new).returns(connection)
64
+ context 'with Microsoft Graph configured' do
65
+ let(:mailbox) { build_mailbox(REQUIRED_MICROSOFT_GRAPH_DEFAULTS) }
53
66
 
54
- watcher.run
67
+ subject { described_class.new(mailbox) }
55
68
 
56
- expect(watcher.running?).to eq(true)
69
+ it 'initializes a Microsoft Graph connection' do
70
+ connection = stub(on_new_message: nil)
57
71
 
58
- watcher.quit
72
+ MailRoom::MicrosoftGraph::Connection.stubs(:new).returns(connection)
59
73
 
60
- expect(connection).to have_received(:quit)
61
- expect(watcher.running?).to eq(false)
74
+ expect(subject.send(:connection)).to eq(connection)
62
75
  end
63
76
  end
64
77
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal:true
2
+
3
+ require 'spec_helper'
4
+ require 'securerandom'
5
+
6
+ describe MailRoom::Message do
7
+ let(:uid) { SecureRandom.hex }
8
+ let(:body) { 'hello world' }
9
+
10
+ subject { described_class.new(uid: uid, body: body) }
11
+
12
+ describe '#initalize' do
13
+ it 'initializes with required parameters' do
14
+ subject
15
+
16
+ expect(subject.uid).to eq(uid)
17
+ expect(subject.body).to eq(body)
18
+ end
19
+ end
20
+
21
+ describe '#==' do
22
+ let(:dup) { described_class.new(uid: uid, body: body) }
23
+
24
+ it 'matches an equivalent message' do
25
+ expect(dup == subject).to be true
26
+ end
27
+
28
+ it 'does not match a message with a different UID' do
29
+ msg = described_class.new(uid: '12345', body: body)
30
+
31
+ expect(subject == msg).to be false
32
+ expect(msg == subject).to be false
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,252 @@
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(:graph_endpoint) { 'https://graph.microsoft.com' }
18
+ let(:azure_ad_endpoint) { 'https://login.microsoftonline.com' }
19
+ let(:base_url) { "#{graph_endpoint}/v1.0/users/user@example.com/mailFolders/inbox/messages" }
20
+ let(:message_base_url) { "#{graph_endpoint}/v1.0/users/user@example.com/messages" }
21
+
22
+ let(:connection) { described_class.new(mailbox) }
23
+ let(:uid) { 1 }
24
+ let(:access_token) { SecureRandom.hex }
25
+ let(:refresh_token) { SecureRandom.hex }
26
+ let(:expires_in) { Time.now + 3600 }
27
+ let(:unread_messages_body) { '' }
28
+ let(:status) { 200 }
29
+ let!(:stub_token) do
30
+ stub_request(:post, "#{azure_ad_endpoint}/#{tenant_id}/oauth2/v2.0/token").to_return(
31
+ body: { 'access_token' => access_token, 'refresh_token' => refresh_token, 'expires_in' => expires_in }.to_json,
32
+ headers: { 'Content-Type' => 'application/json' }
33
+ )
34
+ end
35
+ let!(:stub_unread_messages_request) do
36
+ stub_request(:get, "#{base_url}?$filter=isRead%20eq%20false").to_return(
37
+ status: status,
38
+ body: unread_messages_body.to_json,
39
+ headers: { 'Content-Type' => 'application/json' }
40
+ )
41
+ end
42
+
43
+ before do
44
+ WebMock.enable!
45
+ end
46
+
47
+ context '#quit' do
48
+ it 'returns false' do
49
+ expect(connection.stopped?).to be_falsey
50
+ end
51
+
52
+ it 'returns true' do
53
+ connection.quit
54
+
55
+ expect(connection.stopped?).to be_truthy
56
+ end
57
+
58
+ it 'does not attempt to process the mailbox' do
59
+ connection.quit
60
+
61
+ connection.expects(:process_mailbox).times(0)
62
+ connection.wait
63
+ end
64
+ end
65
+
66
+ context '#wait' do
67
+ before do
68
+ connection.stubs(:do_sleep)
69
+ end
70
+
71
+ describe 'poll interval' do
72
+ it 'defaults to 60 seconds' do
73
+ expect(connection.send(:poll_interval)).to eq(60)
74
+ end
75
+
76
+ it 'calls do_sleep 60 times' do
77
+ connection.expects(:do_sleep).with(1).times(60)
78
+
79
+ connection.wait
80
+ end
81
+
82
+ context 'interval set to 10' do
83
+ let(:options) do
84
+ {
85
+ inbox_method: :microsoft_graph,
86
+ inbox_options: {
87
+ tenant_id: '98776',
88
+ client_id: '12345',
89
+ client_secret: 'MY-SECRET',
90
+ poll_interval: '10'
91
+ }
92
+ }
93
+ end
94
+
95
+ it 'sets the poll interval to 10' do
96
+ expect(connection.send(:poll_interval)).to eq(10)
97
+ end
98
+
99
+ it 'calls do_sleep 10 times' do
100
+ connection.expects(:do_sleep).with(1).times(10)
101
+
102
+ connection.wait
103
+ end
104
+ end
105
+ end
106
+
107
+ shared_examples 'with a single message' do
108
+ let(:message_id) { SecureRandom.hex }
109
+ let(:unread_messages_body) { { value: ['id' => message_id] } }
110
+ let(:message_url) { "#{message_base_url}/#{message_id}" }
111
+ let(:message_body) { 'hello world' }
112
+
113
+ it 'requests message ID' do
114
+ stub_get = stub_request(:get, "#{message_url}/$value").to_return(
115
+ status: 200,
116
+ body: message_body
117
+ )
118
+ stub_patch = stub_request(:patch, message_url).with(body: { "isRead": true }.to_json)
119
+ stub_delete = stub_request(:delete, message_url)
120
+ message_count = 0
121
+
122
+ connection.on_new_message do |message|
123
+ message_count += 1
124
+ expect(message.uid).to eq(message_id)
125
+ expect(message.body).to eq(message_body)
126
+ end
127
+
128
+ connection.wait
129
+
130
+ assert_requested(stub_token)
131
+ assert_requested(stub_unread_messages_request)
132
+ assert_requested(stub_get)
133
+ assert_requested(stub_patch)
134
+ assert_requested(stub_delete)
135
+ expect(message_count).to eq(1)
136
+ end
137
+ end
138
+
139
+ context 'with default Azure settings' do
140
+ before do
141
+ puts options
142
+ end
143
+ it_behaves_like 'with a single message'
144
+ end
145
+
146
+ # https://docs.microsoft.com/en-us/graph/deployments
147
+ context 'with an alternative Azure deployment' do
148
+ let(:graph_endpoint) { 'https://graph.microsoft.us' }
149
+ let(:azure_ad_endpoint) { 'https://login.microsoftonline.us' }
150
+ let(:options) do
151
+ {
152
+ inbox_method: :microsoft_graph,
153
+ delete_after_delivery: true,
154
+ expunge_deleted: true,
155
+ inbox_options: {
156
+ tenant_id: '98776',
157
+ client_id: '12345',
158
+ client_secret: 'MY-SECRET',
159
+ graph_endpoint: 'https://graph.microsoft.us',
160
+ azure_ad_endpoint: 'https://login.microsoftonline.us'
161
+ }
162
+ }
163
+ end
164
+
165
+ it_behaves_like 'with a single message'
166
+ end
167
+
168
+ context 'with multiple pages of messages' do
169
+ let(:message_ids) { [SecureRandom.hex, SecureRandom.hex] }
170
+ let(:next_page_url) { "#{graph_endpoint}/v1.0/nextPage" }
171
+ let(:unread_messages_body) { { value: ['id' => message_ids.first], '@odata.nextLink' => next_page_url } }
172
+ let(:message_body) { 'hello world' }
173
+
174
+ it 'requests message ID' do
175
+ stub_request(:get, next_page_url).to_return(
176
+ status: 200,
177
+ body: { value: ['id' => message_ids[1]] }.to_json
178
+ )
179
+
180
+ stubs = []
181
+ message_ids.each do |message_id|
182
+ rfc822_msg_url = "#{message_base_url}/#{message_id}/$value"
183
+ stubs << stub_request(:get, rfc822_msg_url).to_return(
184
+ status: 200,
185
+ body: message_body
186
+ )
187
+
188
+ msg_url = "#{message_base_url}/#{message_id}"
189
+ stubs << stub_request(:patch, msg_url).with(body: { "isRead": true }.to_json)
190
+ stubs << stub_request(:delete, msg_url)
191
+ end
192
+
193
+ message_count = 0
194
+
195
+ connection.on_new_message do |message|
196
+ expect(message.uid).to eq(message_ids[message_count])
197
+ expect(message.body).to eq(message_body)
198
+ message_count += 1
199
+ end
200
+
201
+ connection.wait
202
+
203
+ stubs.each { |stub| assert_requested(stub) }
204
+ expect(message_count).to eq(2)
205
+ end
206
+ end
207
+
208
+ shared_examples 'request backoff' do
209
+ it 'backs off' do
210
+ connection.expects(:backoff)
211
+
212
+ connection.on_new_message {}
213
+ connection.wait
214
+
215
+ expect(connection.throttled_count).to eq(1)
216
+ end
217
+ end
218
+
219
+ context 'too many requests' do
220
+ let(:status) { 429 }
221
+
222
+ it_behaves_like 'request backoff'
223
+ end
224
+
225
+ context 'too much bandwidth' do
226
+ let(:status) { 509 }
227
+
228
+ it_behaves_like 'request backoff'
229
+ end
230
+
231
+ context 'invalid JSON response' do
232
+ let(:body) { 'this is something' }
233
+
234
+ it 'ignores the message and logs a warning' do
235
+ mailbox.logger.expects(:warn)
236
+
237
+ connection.on_new_message {}
238
+ connection.wait
239
+ end
240
+ end
241
+
242
+ context '500 error' do
243
+ let(:status) { 500 }
244
+
245
+ it 'terminates due to error' do
246
+ connection.on_new_message {}
247
+
248
+ expect { connection.wait }.to raise_error(OAuth2::Error)
249
+ end
250
+ end
251
+ end
252
+ end
data/spec/spec_helper.rb CHANGED
@@ -2,10 +2,10 @@ require 'simplecov'
2
2
  SimpleCov.start
3
3
 
4
4
  require 'bundler/setup'
5
+ require 'date'
5
6
 
6
7
  require 'rspec'
7
8
  require 'mocha/api'
8
- require 'bourne'
9
9
 
10
10
  require File.expand_path('../../lib/mail_room', __FILE__)
11
11
 
@@ -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