mail_daemon 0.1.0 → 1.0.0

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.
@@ -0,0 +1,205 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe MailDaemon::ImapWatcher do
4
+ let(:options) { {} }
5
+
6
+ before do
7
+ allow(ENV).to receive(:[]).with('REDIS_URL').and_return('redis://localhost:6379')
8
+ allow(ENV).to receive(:[]).with('MYSQL_HOST').and_return('localhost')
9
+ allow(ENV).to receive(:[]).with('MYSQL_DATABASE').and_return('test_db')
10
+ allow(ENV).to receive(:[]).with('MYSQL_USERNAME').and_return('test_user')
11
+ allow(ENV).to receive(:[]).with('MYSQL_PASSWORD').and_return('test_pass')
12
+ allow(ENV).to receive(:[]).with('MYSQL_PORT').and_return('3306')
13
+ allow(Redis).to receive(:new).and_return(instance_double(Redis))
14
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client))
15
+ end
16
+
17
+ describe '#initialize' do
18
+ it 'raises error when REDIS_URL is missing' do
19
+ allow(ENV).to receive(:[]).with('REDIS_URL').and_return(nil)
20
+ expect { described_class.new(options) }.to raise_error(/REDIS_URL environment variable is required/)
21
+ end
22
+
23
+ it 'raises error when MYSQL_HOST is missing' do
24
+ allow(ENV).to receive(:[]).with('MYSQL_HOST').and_return(nil)
25
+ expect { described_class.new(options) }.to raise_error(/MYSQL_HOST environment variable is required/)
26
+ end
27
+
28
+ it 'raises error when MYSQL_DATABASE is missing' do
29
+ allow(ENV).to receive(:[]).with('MYSQL_DATABASE').and_return(nil)
30
+ expect { described_class.new(options) }.to raise_error(/MYSQL_DATABASE environment variable is required/)
31
+ end
32
+
33
+ it 'raises error when MYSQL_USERNAME is missing' do
34
+ allow(ENV).to receive(:[]).with('MYSQL_USERNAME').and_return(nil)
35
+ expect { described_class.new(options) }.to raise_error(/MYSQL_USERNAME environment variable is required/)
36
+ end
37
+
38
+ it 'raises error when MYSQL_PASSWORD is missing' do
39
+ allow(ENV).to receive(:[]).with('MYSQL_PASSWORD').and_return(nil)
40
+ expect { described_class.new(options) }.to raise_error(/MYSQL_PASSWORD environment variable is required/)
41
+ end
42
+
43
+ it 'allows empty string for MYSQL_PASSWORD' do
44
+ allow(ENV).to receive(:[]).with('REDIS_URL').and_return('redis://localhost:6379')
45
+ allow(ENV).to receive(:[]).with('MYSQL_HOST').and_return('localhost')
46
+ allow(ENV).to receive(:[]).with('MYSQL_DATABASE').and_return('test_db')
47
+ allow(ENV).to receive(:[]).with('MYSQL_USERNAME').and_return('test_user')
48
+ # MYSQL_PASSWORD is set to empty string (which is truthy in Ruby, but falsy for the check)
49
+ allow(ENV).to receive(:[]).with('MYSQL_PASSWORD').and_return('')
50
+ allow(ENV).to receive(:[]).with('MYSQL_PORT').and_return('3306')
51
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
52
+ allow(Redis).to receive(:new)
53
+ allow(Signal).to receive(:trap)
54
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
55
+
56
+ # The code sets MYSQL_PORT
57
+ expect(ENV).to receive(:[]=).with('MYSQL_PORT', '3306')
58
+ expect { described_class.new(options) }.not_to raise_error
59
+ end
60
+
61
+ it 'sets MYSQL_PORT to 3306' do
62
+ expect(ENV).to receive(:[]=).with('MYSQL_PORT', '3306')
63
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
64
+ allow(Redis).to receive(:new)
65
+ allow(Signal).to receive(:trap)
66
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
67
+
68
+ described_class.new(options)
69
+ end
70
+
71
+ it 'creates Redis connection' do
72
+ redis_url = instance_double(URI::Generic, host: 'localhost', port: 6379)
73
+ allow(URI).to receive(:parse).with('redis://localhost:6379').and_return(redis_url)
74
+ allow(Signal).to receive(:trap)
75
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
76
+
77
+ expect(Redis).to receive(:new).with(host: 'localhost', port: 6379)
78
+ described_class.new(options)
79
+ end
80
+
81
+ it 'sets up signal traps for INT' do
82
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
83
+ allow(Redis).to receive(:new)
84
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
85
+
86
+ allow(Signal).to receive(:trap).and_call_original
87
+ expect(Signal).to receive(:trap).with('INT').and_call_original
88
+ expect(Signal).to receive(:trap).with('TERM').and_call_original
89
+ described_class.new(options)
90
+ end
91
+
92
+ it 'sets up signal traps for TERM' do
93
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
94
+ allow(Redis).to receive(:new)
95
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
96
+
97
+ allow(Signal).to receive(:trap).and_call_original
98
+ expect(Signal).to receive(:trap).with('INT').and_call_original
99
+ expect(Signal).to receive(:trap).with('TERM').and_call_original
100
+ described_class.new(options)
101
+ end
102
+ end
103
+
104
+ describe '#setup_watchers' do
105
+ let(:mysql_client) { instance_double(Mysql2::Client) }
106
+ let(:statement) { instance_double(Mysql2::Statement) }
107
+ let(:result) { [] }
108
+
109
+ before do
110
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
111
+ allow(Redis).to receive(:new)
112
+ allow(Signal).to receive(:trap)
113
+ allow(Mysql2::Client).to receive(:new).and_return(mysql_client)
114
+ allow(mysql_client).to receive(:prepare).and_return(statement)
115
+ allow(statement).to receive(:execute).and_return(result)
116
+ allow(mysql_client).to receive(:close)
117
+ end
118
+
119
+ it 'queries database for email accounts' do
120
+ watcher = described_class.new(options)
121
+ expect(mysql_client).to receive(:prepare).with(
122
+ 'select ea.*, a.nickname from case_blocks_email_accounts ea join case_blocks_accounts a on a.id = ea.account_id where ea.imap_enabled=1'
123
+ )
124
+ watcher.setup_watchers
125
+ end
126
+
127
+ it 'creates watchers for each enabled account' do
128
+ row = {
129
+ 'imap_username' => 'user@example.com',
130
+ 'imap_host' => 'imap.example.com',
131
+ 'imap_port' => 993,
132
+ 'imap_encrypted_password' => 'encrypted',
133
+ 'imap_ssl' => 1,
134
+ 'imap_start_tls' => 0,
135
+ 'imap_folder_name' => 'inbox',
136
+ 'imap_search_command' => 'UNSEEN',
137
+ 'imap_messages_processed' => 0,
138
+ 'imap_last_delivered_at' => nil,
139
+ 'nickname' => 'Test Account'
140
+ }
141
+
142
+ allow(statement).to receive(:execute).and_return([row])
143
+ encryption_instance = instance_double(MailDaemon::Encryption, decrypt: 'decrypted_password')
144
+ allow(MailDaemon::Encryption).to receive(:new).and_return(encryption_instance)
145
+ connection_instance = instance_double(MailDaemon::Imap::Connection, wait_for_messages: nil, disconnect: nil)
146
+ allow(MailDaemon::Imap::Connection).to receive(:new).and_return(connection_instance)
147
+
148
+ watcher = described_class.new(options)
149
+ watcher.setup_watchers
150
+
151
+ expect(watcher.instance_variable_get(:@watchers)).to include(connection_instance)
152
+ end
153
+ end
154
+
155
+ describe '#stop' do
156
+ it 'stops all watchers' do
157
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
158
+ allow(Redis).to receive(:new)
159
+ allow(Signal).to receive(:trap)
160
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
161
+
162
+ watcher1 = instance_double(MailDaemon::Imap::Connection, disconnect: true)
163
+ watcher2 = instance_double(MailDaemon::Imap::Connection, disconnect: true)
164
+
165
+ watcher = described_class.new(options)
166
+ watcher.instance_variable_set(:@watchers, [watcher1, watcher2])
167
+
168
+ expect(watcher1).to receive(:disconnect)
169
+ expect(watcher2).to receive(:disconnect)
170
+ watcher.stop
171
+ end
172
+ end
173
+
174
+ describe '#running?' do
175
+ it 'returns true when any watcher is running' do
176
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
177
+ allow(Redis).to receive(:new)
178
+ allow(Signal).to receive(:trap)
179
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
180
+
181
+ watcher1 = instance_double(MailDaemon::Imap::Connection, running?: true)
182
+ watcher2 = instance_double(MailDaemon::Imap::Connection, running?: false)
183
+
184
+ watcher = described_class.new(options)
185
+ watcher.instance_variable_set(:@watchers, [watcher1, watcher2])
186
+
187
+ expect(watcher.running?).to be true
188
+ end
189
+
190
+ it 'returns false when no watchers are running' do
191
+ allow(URI).to receive(:parse).and_return(instance_double(URI::Generic, host: 'localhost', port: 6379))
192
+ allow(Redis).to receive(:new)
193
+ allow(Signal).to receive(:trap)
194
+ allow(Mysql2::Client).to receive(:new).and_return(instance_double(Mysql2::Client, prepare: instance_double(Mysql2::Statement, execute: []), close: nil))
195
+
196
+ watcher1 = instance_double(MailDaemon::Imap::Connection, running?: false)
197
+ watcher2 = instance_double(MailDaemon::Imap::Connection, running?: false)
198
+
199
+ watcher = described_class.new(options)
200
+ watcher.instance_variable_set(:@watchers, [watcher1, watcher2])
201
+
202
+ expect(watcher.running?).to be false
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe MailDaemon::VERSION do
4
+ it 'is defined' do
5
+ expect(MailDaemon::VERSION).to be_a(String)
6
+ end
7
+
8
+ it 'has a version number' do
9
+ expect(MailDaemon::VERSION).not_to be_empty
10
+ end
11
+
12
+ it 'follows semantic versioning format' do
13
+ expect(MailDaemon::VERSION).to match(/\d+\.\d+\.\d+/)
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'mail_daemon'
5
+ require 'rspec'
6
+ require 'rspec/mocks'
7
+ require 'json'
8
+ require 'base64'
9
+ require 'openssl'
10
+ require 'uri'
11
+ require 'stringio'
12
+ require 'fileutils'
13
+
14
+ RSpec.configure do |config|
15
+ config.expect_with :rspec do |expectations|
16
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
17
+ end
18
+
19
+ config.mock_with :rspec do |mocks|
20
+ mocks.verify_partial_doubles = true
21
+ end
22
+
23
+ config.shared_context_metadata_behavior = :apply_to_host_groups
24
+ config.filter_run_when_matching :focus
25
+ config.example_status_persistence_file_path = "spec/examples.txt"
26
+ config.disable_monkey_patching!
27
+ config.warnings = true
28
+
29
+ if config.files_to_run.one?
30
+ config.default_formatter = "doc"
31
+ end
32
+
33
+ config.profile_examples = 10
34
+ config.order = :random
35
+ Kernel.srand config.seed
36
+ end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_daemon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - EmergeAdapt
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2018-06-08 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '1.6'
18
+ version: '2.0'
20
19
  type: :development
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '1.6'
25
+ version: '2.0'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +51,118 @@ dependencies:
52
51
  - - ">="
53
52
  - !ruby/object:Gem::Version
54
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.12'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.12'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rspec-mocks
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.12'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.12'
82
+ - !ruby/object:Gem::Dependency
83
+ name: mail
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: sinatra
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: nokogiri
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: sanitize
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: redis
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: mysql2
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :runtime
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
55
166
  description: "."
56
167
  email:
57
168
  - development@emergeadapt.com
@@ -61,6 +172,7 @@ extensions: []
61
172
  extra_rdoc_files: []
62
173
  files:
63
174
  - ".gitignore"
175
+ - ".rspec"
64
176
  - ".ruby-gemset"
65
177
  - ".ruby-version"
66
178
  - Gemfile
@@ -86,6 +198,18 @@ files:
86
198
  - lib/views/settings.erb
87
199
  - lib/views/stats.erb
88
200
  - mail_daemon.gemspec
201
+ - spec/examples.txt
202
+ - spec/mail_daemon/email_body_parser_spec.rb
203
+ - spec/mail_daemon/email_handler_spec.rb
204
+ - spec/mail_daemon/encryption_spec.rb
205
+ - spec/mail_daemon/handler_spec.rb
206
+ - spec/mail_daemon/helpers_spec.rb
207
+ - spec/mail_daemon/imap/connection_spec.rb
208
+ - spec/mail_daemon/imap/statuses_spec.rb
209
+ - spec/mail_daemon/imap/watcher_spec.rb
210
+ - spec/mail_daemon/imap_watcher_spec.rb
211
+ - spec/mail_daemon/version_spec.rb
212
+ - spec/spec_helper.rb
89
213
  - test/lib/order2cb/order_test.rb
90
214
  - test/lib/order2cb/version_test.rb
91
215
  - test/lib/order2cb_test.rb
@@ -94,7 +218,6 @@ homepage: ''
94
218
  licenses:
95
219
  - MIT
96
220
  metadata: {}
97
- post_install_message:
98
221
  rdoc_options: []
99
222
  require_paths:
100
223
  - lib
@@ -109,13 +232,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
232
  - !ruby/object:Gem::Version
110
233
  version: '0'
111
234
  requirements: []
112
- rubyforge_project:
113
- rubygems_version: 2.4.5.1
114
- signing_key:
235
+ rubygems_version: 3.7.2
115
236
  specification_version: 4
116
237
  summary: IMAP account daemon to watch for new mail and to execute a ruby block with
117
238
  data from message.
118
239
  test_files:
240
+ - spec/examples.txt
241
+ - spec/mail_daemon/email_body_parser_spec.rb
242
+ - spec/mail_daemon/email_handler_spec.rb
243
+ - spec/mail_daemon/encryption_spec.rb
244
+ - spec/mail_daemon/handler_spec.rb
245
+ - spec/mail_daemon/helpers_spec.rb
246
+ - spec/mail_daemon/imap/connection_spec.rb
247
+ - spec/mail_daemon/imap/statuses_spec.rb
248
+ - spec/mail_daemon/imap/watcher_spec.rb
249
+ - spec/mail_daemon/imap_watcher_spec.rb
250
+ - spec/mail_daemon/version_spec.rb
251
+ - spec/spec_helper.rb
119
252
  - test/lib/order2cb/order_test.rb
120
253
  - test/lib/order2cb/version_test.rb
121
254
  - test/lib/order2cb_test.rb