gitlab-mail_room 0.0.4 → 0.0.20

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.
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