imap-backup 4.0.5 → 4.1.2

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/bin/imap-backup +5 -2
  3. data/lib/imap/backup/account/connection.rb +70 -58
  4. data/lib/imap/backup/account/folder.rb +23 -3
  5. data/lib/imap/backup/account.rb +6 -7
  6. data/lib/imap/backup/cli/accounts.rb +43 -0
  7. data/lib/imap/backup/cli/folders.rb +3 -1
  8. data/lib/imap/backup/cli/helpers.rb +8 -9
  9. data/lib/imap/backup/cli/local.rb +4 -2
  10. data/lib/imap/backup/cli/setup.rb +1 -1
  11. data/lib/imap/backup/cli/status.rb +1 -1
  12. data/lib/imap/backup/cli/utils.rb +3 -2
  13. data/lib/imap/backup/{configuration/store.rb → configuration.rb} +49 -14
  14. data/lib/imap/backup/downloader.rb +26 -12
  15. data/lib/imap/backup/logger.rb +42 -0
  16. data/lib/imap/backup/sanitizer.rb +42 -0
  17. data/lib/imap/backup/serializer/mbox_store.rb +2 -2
  18. data/lib/imap/backup/{configuration → setup}/account.rb +59 -41
  19. data/lib/imap/backup/{configuration → setup}/asker.rb +5 -5
  20. data/lib/imap/backup/setup/connection_tester.rb +26 -0
  21. data/lib/imap/backup/{configuration → setup}/folder_chooser.rb +25 -17
  22. data/lib/imap/backup/setup/helpers.rb +15 -0
  23. data/lib/imap/backup/{configuration/setup.rb → setup.rb} +33 -25
  24. data/lib/imap/backup/uploader.rb +2 -2
  25. data/lib/imap/backup/version.rb +2 -2
  26. data/lib/imap/backup.rb +7 -33
  27. data/lib/retry_on_error.rb +1 -1
  28. data/spec/features/backup_spec.rb +1 -0
  29. data/spec/features/status_spec.rb +43 -0
  30. data/spec/features/support/email_server.rb +5 -2
  31. data/spec/features/support/shared/connection_context.rb +7 -5
  32. data/spec/support/higline_test_helpers.rb +1 -1
  33. data/spec/support/silence_logging.rb +1 -1
  34. data/spec/unit/email/provider/base_spec.rb +1 -1
  35. data/spec/unit/email/provider_spec.rb +2 -2
  36. data/spec/unit/imap/backup/account/connection_spec.rb +22 -26
  37. data/spec/unit/imap/backup/cli/accounts_spec.rb +47 -0
  38. data/spec/unit/imap/backup/cli/local_spec.rb +15 -4
  39. data/spec/unit/imap/backup/cli/utils_spec.rb +54 -42
  40. data/spec/unit/imap/backup/{configuration/store_spec.rb → configuration_spec.rb} +23 -24
  41. data/spec/unit/imap/backup/downloader_spec.rb +1 -1
  42. data/spec/unit/imap/backup/logger_spec.rb +48 -0
  43. data/spec/unit/imap/backup/{configuration → setup}/account_spec.rb +78 -70
  44. data/spec/unit/imap/backup/{configuration → setup}/asker_spec.rb +2 -2
  45. data/spec/unit/imap/backup/{configuration → setup}/connection_tester_spec.rb +10 -10
  46. data/spec/unit/imap/backup/{configuration → setup}/folder_chooser_spec.rb +25 -26
  47. data/spec/unit/imap/backup/{configuration/setup_spec.rb → setup_spec.rb} +81 -52
  48. metadata +51 -48
  49. data/lib/imap/backup/configuration/connection_tester.rb +0 -14
  50. data/lib/imap/backup/configuration/list.rb +0 -53
  51. data/spec/support/shared_examples/account_flagging.rb +0 -23
  52. data/spec/unit/imap/backup/configuration/list_spec.rb +0 -89
  53. data/spec/unit/imap/backup_spec.rb +0 -28
@@ -1,17 +1,43 @@
1
- describe Imap::Backup::Configuration::Account do
1
+ describe Imap::Backup::Setup::Account do
2
2
  ACCOUNT = "account".freeze
3
3
  GMAIL_IMAP_SERVER = "imap.gmail.com".freeze
4
4
  HIGHLINE = "highline".freeze
5
- STORE = "store".freeze
6
-
7
- subject { described_class.new(store, account, highline) }
5
+ CONFIG = "config".freeze
6
+
7
+ subject { described_class.new(config, account, highline) }
8
+
9
+ let(:account) do
10
+ instance_double(
11
+ Imap::Backup::Account,
12
+ username: existing_email,
13
+ password: existing_password,
14
+ server: current_server,
15
+ connection_options: nil,
16
+ local_path: "/backup/path",
17
+ folders: [{name: "my_folder"}],
18
+ modified?: false
19
+ )
20
+ end
21
+ let(:account1) do
22
+ instance_double(
23
+ Imap::Backup::Account,
24
+ username: other_email,
25
+ local_path: other_existing_path
26
+ )
27
+ end
28
+ let(:accounts) { [account, account1] }
29
+ let(:existing_email) { "user@example.com" }
30
+ let(:new_email) { "foo@example.com" }
31
+ let(:current_server) { "imap.example.com" }
32
+ let(:existing_password) { "password" }
33
+ let(:other_email) { "other@example.com" }
34
+ let(:other_existing_path) { "/other/existing/path" }
8
35
 
9
- let(:account) { ACCOUNT }
10
36
  let(:highline) { HIGHLINE }
11
- let(:store) { STORE }
37
+ let(:config) { CONFIG }
12
38
 
13
39
  describe "#initialize" do
14
- [:store, :account, :highline].each do |param|
40
+ [:config, :account, :highline].each do |param|
15
41
  it "expects #{param}" do
16
42
  expect(subject.send(param)).to eq(send(param))
17
43
  end
@@ -40,31 +66,9 @@ describe Imap::Backup::Configuration::Account do
40
66
 
41
67
  let(:highline) { instance_double(HighLine) }
42
68
  let(:menu) { highline_menu_class.new }
43
- let(:store) do
44
- instance_double(Imap::Backup::Configuration::Store, accounts: accounts)
45
- end
46
- let(:accounts) { [account, account1] }
47
- let(:account) do
48
- {
49
- username: existing_email,
50
- server: current_server,
51
- local_path: "/backup/path",
52
- folders: [{name: "my_folder"}],
53
- password: existing_password
54
- }
69
+ let(:config) do
70
+ instance_double(Imap::Backup::Configuration, accounts: accounts)
55
71
  end
56
- let(:account1) do
57
- {
58
- username: other_email,
59
- local_path: other_existing_path
60
- }
61
- end
62
- let(:existing_email) { "user@example.com" }
63
- let(:new_email) { "foo@example.com" }
64
- let(:current_server) { "imap.example.com" }
65
- let(:existing_password) { "password" }
66
- let(:other_email) { "other@example.com" }
67
- let(:other_existing_path) { "/other/existing/path" }
68
72
 
69
73
  before do
70
74
  allow(Kernel).to receive(:system)
@@ -100,7 +104,7 @@ describe Imap::Backup::Configuration::Account do
100
104
  "choose backup folders",
101
105
  "test connection",
102
106
  "delete",
103
- "return to main menu",
107
+ "(q) return to main menu",
104
108
  "quit" # TODO: quit is hidden
105
109
  ].each do |item|
106
110
  before { subject.run }
@@ -113,11 +117,11 @@ describe Imap::Backup::Configuration::Account do
113
117
 
114
118
  describe "account details" do
115
119
  [
116
- ["email", /email:\s+user@example.com/],
117
- ["server", /server:\s+imap.example.com/],
118
- ["password", /password:\s+x+/],
119
- ["path", %r(path:\s+/backup/path)],
120
- ["folders", /folders:\s+my_folder/]
120
+ ["email", /email\s+user@example.com/],
121
+ ["password", /password\s+x+/],
122
+ ["path", %r(path\s+/backup/path)],
123
+ ["folders", /folders\s+my_folder/],
124
+ ["server", /server\s+imap.example.com/]
121
125
  ].each do |attribute, value|
122
126
  before { subject.run }
123
127
 
@@ -132,14 +136,16 @@ describe Imap::Backup::Configuration::Account do
132
136
  before { subject.run }
133
137
 
134
138
  it "indicates that a password is not set" do
135
- expect(menu.header).to include("password: (unset)")
139
+ expect(menu.header).to include("password (unset)")
136
140
  end
137
141
  end
138
142
  end
139
143
 
140
144
  describe "choosing 'modify email'" do
141
145
  before do
142
- allow(Imap::Backup::Configuration::Asker).
146
+ allow(account).to receive(:"username=")
147
+ allow(account).to receive(:"server=")
148
+ allow(Imap::Backup::Setup::Asker).
143
149
  to receive(:email) { new_email }
144
150
  subject.run
145
151
  menu.choices["modify email"].call
@@ -158,7 +164,7 @@ describe Imap::Backup::Configuration::Account do
158
164
  let(:current_server) { nil }
159
165
 
160
166
  it "sets a default server" do
161
- expect(account[:server]).to eq(expected)
167
+ expect(account).to have_received(:"server=").with(expected)
162
168
  end
163
169
  end
164
170
 
@@ -166,7 +172,7 @@ describe Imap::Backup::Configuration::Account do
166
172
  let(:current_server) { "" }
167
173
 
168
174
  it "sets a default server" do
169
- expect(account[:server]).to eq(expected)
175
+ expect(account).to have_received(:"server=").with(expected)
170
176
  end
171
177
  end
172
178
  end
@@ -183,17 +189,15 @@ describe Imap::Backup::Configuration::Account do
183
189
  end
184
190
 
185
191
  it "does not set a default server" do
186
- expect(account[:server]).to be_nil
192
+ expect(account).to_not have_received(:"server=")
187
193
  end
188
194
  end
189
195
  end
190
196
 
191
197
  context "when the email is new" do
192
198
  it "modifies the email address" do
193
- expect(account[:username]).to eq(new_email)
199
+ expect(account).to have_received(:"username=").with(new_email)
194
200
  end
195
-
196
- include_examples "it flags the account as modified"
197
201
  end
198
202
 
199
203
  context "when the email already exists" do
@@ -205,10 +209,8 @@ describe Imap::Backup::Configuration::Account do
205
209
  end
206
210
 
207
211
  it "doesn't set the email" do
208
- expect(account[:username]).to eq(existing_email)
212
+ expect(account.username).to eq(existing_email)
209
213
  end
210
-
211
- include_examples "it doesn't flag the account as modified"
212
214
  end
213
215
  end
214
216
 
@@ -216,7 +218,8 @@ describe Imap::Backup::Configuration::Account do
216
218
  let(:new_password) { "new_password" }
217
219
 
218
220
  before do
219
- allow(Imap::Backup::Configuration::Asker).
221
+ allow(account).to receive(:"password=")
222
+ allow(Imap::Backup::Setup::Asker).
220
223
  to receive(:password) { new_password }
221
224
  subject.run
222
225
  menu.choices["modify password"].call
@@ -224,20 +227,16 @@ describe Imap::Backup::Configuration::Account do
224
227
 
225
228
  context "when the user enters a password" do
226
229
  it "updates the password" do
227
- expect(account[:password]).to eq(new_password)
230
+ expect(account).to have_received(:"password=").with(new_password)
228
231
  end
229
-
230
- include_examples "it flags the account as modified"
231
232
  end
232
233
 
233
234
  context "when the user cancels" do
234
235
  let(:new_password) { nil }
235
236
 
236
237
  it "does nothing" do
237
- expect(account[:password]).to eq(existing_password)
238
+ expect(account.password).to eq(existing_password)
238
239
  end
239
-
240
- include_examples "it doesn't flag the account as modified"
241
240
  end
242
241
  end
243
242
 
@@ -245,6 +244,7 @@ describe Imap::Backup::Configuration::Account do
245
244
  let(:server) { "server" }
246
245
 
247
246
  before do
247
+ allow(account).to receive(:"server=")
248
248
  allow(highline).to receive(:ask).with("server: ") { server }
249
249
 
250
250
  subject.run
@@ -253,19 +253,18 @@ describe Imap::Backup::Configuration::Account do
253
253
  end
254
254
 
255
255
  it "updates the server" do
256
- expect(account[:server]).to eq(server)
256
+ expect(account).to have_received(:"server=").with(server)
257
257
  end
258
-
259
- include_examples "it flags the account as modified"
260
258
  end
261
259
 
262
260
  describe "choosing 'modify backup path'" do
263
261
  let(:new_backup_path) { "/new/path" }
264
262
 
265
263
  before do
264
+ allow(account).to receive(:"local_path=")
266
265
  @validator = nil
267
266
  allow(
268
- Imap::Backup::Configuration::Asker
267
+ Imap::Backup::Setup::Asker
269
268
  ).to receive(:backup_path) do |_path, validator|
270
269
  @validator = validator
271
270
  new_backup_path
@@ -275,7 +274,7 @@ describe Imap::Backup::Configuration::Account do
275
274
  end
276
275
 
277
276
  it "updates the path" do
278
- expect(account[:local_path]).to eq(new_backup_path)
277
+ expect(account).to have_received(:"local_path=").with(new_backup_path)
279
278
  end
280
279
 
281
280
  context "when the path is not used by other backups" do
@@ -293,17 +292,15 @@ describe Imap::Backup::Configuration::Account do
293
292
  # rubocop:enable RSpec/InstanceVariable
294
293
  end
295
294
  end
296
-
297
- include_examples "it flags the account as modified"
298
295
  end
299
296
 
300
297
  describe "choosing 'choose backup folders'" do
301
298
  let(:chooser) do
302
- instance_double(Imap::Backup::Configuration::FolderChooser, run: nil)
299
+ instance_double(Imap::Backup::Setup::FolderChooser, run: nil)
303
300
  end
304
301
 
305
302
  before do
306
- allow(Imap::Backup::Configuration::FolderChooser).
303
+ allow(Imap::Backup::Setup::FolderChooser).
307
304
  to receive(:new) { chooser }
308
305
  subject.run
309
306
  menu.choices["choose backup folders"].call
@@ -315,17 +312,23 @@ describe Imap::Backup::Configuration::Account do
315
312
  end
316
313
 
317
314
  describe "choosing 'test connection'" do
315
+ let(:connection_tester) do
316
+ instance_double(
317
+ Imap::Backup::Setup::ConnectionTester,
318
+ test: "All fine"
319
+ )
320
+ end
321
+
318
322
  before do
319
- allow(Imap::Backup::Configuration::ConnectionTester).
320
- to receive(:test) { "All fine" }
323
+ allow(Imap::Backup::Setup::ConnectionTester).
324
+ to receive(:new) { connection_tester }
321
325
  allow(highline).to receive(:ask)
322
326
  subject.run
323
327
  menu.choices["test connection"].call
324
328
  end
325
329
 
326
330
  it "tests the connection" do
327
- expect(Imap::Backup::Configuration::ConnectionTester).
328
- to have_received(:test).with(account)
331
+ expect(connection_tester).to have_received(:test)
329
332
  end
330
333
  end
331
334
 
@@ -333,6 +336,7 @@ describe Imap::Backup::Configuration::Account do
333
336
  let(:confirmed) { true }
334
337
 
335
338
  before do
339
+ allow(account).to receive(:mark_for_deletion!)
336
340
  allow(highline).to receive(:agree) { confirmed }
337
341
  subject.run
338
342
  catch :done do
@@ -345,13 +349,17 @@ describe Imap::Backup::Configuration::Account do
345
349
  end
346
350
 
347
351
  context "when the user confirms deletion" do
348
- include_examples "it flags the account to be deleted"
352
+ it "flags the account to be deleted" do
353
+ expect(account).to have_received(:mark_for_deletion!)
354
+ end
349
355
  end
350
356
 
351
357
  context "without confirmation" do
352
358
  let(:confirmed) { false }
353
359
 
354
- include_examples "it doesn't flag the account to be deleted"
360
+ it "doesn't flag the account to be deleted" do
361
+ expect(account).to_not have_received(:mark_for_deletion!)
362
+ end
355
363
  end
356
364
  end
357
365
  end
@@ -1,5 +1,5 @@
1
1
  module Imap::Backup
2
- describe Configuration::Asker do
2
+ describe Setup::Asker do
3
3
  subject { described_class.new(highline) }
4
4
 
5
5
  let(:highline) { double }
@@ -16,7 +16,7 @@ module Imap::Backup
16
16
  let(:answer) { "foo" }
17
17
 
18
18
  before do
19
- allow(Configuration::Setup).to receive(:highline) { highline }
19
+ allow(Setup).to receive(:highline) { highline }
20
20
  allow(highline).to receive(:ask) do |&b|
21
21
  b.call query
22
22
  answer
@@ -1,5 +1,7 @@
1
- describe Imap::Backup::Configuration::ConnectionTester do
2
- describe ".test" do
1
+ describe Imap::Backup::Setup::ConnectionTester do
2
+ describe "#test" do
3
+ subject { described_class.new("foo") }
4
+
3
5
  let(:connection) do
4
6
  instance_double(Imap::Backup::Account::Connection, client: nil)
5
7
  end
@@ -8,17 +10,15 @@ describe Imap::Backup::Configuration::ConnectionTester do
8
10
  allow(Imap::Backup::Account::Connection).to receive(:new) { connection }
9
11
  end
10
12
 
11
- describe "call" do
12
- it "tries to connect" do
13
- expect(connection).to receive(:client)
13
+ it "tries to connect" do
14
+ expect(connection).to receive(:client)
14
15
 
15
- subject.test("foo")
16
- end
16
+ subject.test
17
17
  end
18
18
 
19
19
  describe "success" do
20
20
  it "returns success" do
21
- expect(subject.test("foo")).to match(/successful/)
21
+ expect(subject.test).to match(/successful/)
22
22
  end
23
23
  end
24
24
 
@@ -35,7 +35,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
35
35
  end
36
36
 
37
37
  it "returns error" do
38
- expect(subject.test("foo")).to match(/no response/i)
38
+ expect(subject.test).to match(/no response/i)
39
39
  end
40
40
  end
41
41
 
@@ -43,7 +43,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
43
43
  let(:error) { "Error" }
44
44
 
45
45
  it "returns error" do
46
- expect(subject.test("foo")).to match(/unexpected error/i)
46
+ expect(subject.test).to match(/unexpected error/i)
47
47
  end
48
48
  end
49
49
  end
@@ -1,4 +1,4 @@
1
- describe Imap::Backup::Configuration::FolderChooser do
1
+ describe Imap::Backup::Setup::FolderChooser do
2
2
  include HighLineTestHelpers
3
3
 
4
4
  describe "#run" do
@@ -6,10 +6,17 @@ describe Imap::Backup::Configuration::FolderChooser do
6
6
 
7
7
  let(:connection) do
8
8
  instance_double(
9
- Imap::Backup::Account::Connection, folders: connection_folders
9
+ Imap::Backup::Account::Connection, folder_names: connection_folders
10
10
  )
11
11
  end
12
- let(:account) { {folders: []} }
12
+ let(:account) do
13
+ instance_double(
14
+ Imap::Backup::Account,
15
+ folders: account_folders,
16
+ "folders=": nil
17
+ )
18
+ end
19
+ let(:account_folders) { [] }
13
20
  let(:connection_folders) { [] }
14
21
  let!(:highline_streams) { prepare_highline }
15
22
  let(:input) { highline_streams[0] }
@@ -18,7 +25,7 @@ describe Imap::Backup::Configuration::FolderChooser do
18
25
  before do
19
26
  allow(Imap::Backup::Account::Connection).to receive(:new) { connection }
20
27
  allow(Kernel).to receive(:system)
21
- allow(Imap::Backup.logger).to receive(:warn)
28
+ allow(Imap::Backup::Logger.logger).to receive(:warn)
22
29
  end
23
30
 
24
31
  describe "display" do
@@ -36,7 +43,7 @@ describe Imap::Backup::Configuration::FolderChooser do
36
43
  end
37
44
 
38
45
  describe "folder listing" do
39
- let(:account) { {folders: [{name: "my_folder"}]} }
46
+ let(:account_folders) { [{name: "my_folder"}]}
40
47
  let(:connection_folders) do
41
48
  # N.B. my_folder is already backed up
42
49
  %w(my_folder another_folder)
@@ -62,10 +69,9 @@ describe Imap::Backup::Configuration::FolderChooser do
62
69
  end
63
70
 
64
71
  specify "are added to the account" do
65
- expect(account[:folders]).to include(name: "another_folder")
72
+ expect(account).to have_received(:"folders=").
73
+ with([{name: "my_folder"}, {name: "another_folder"}])
66
74
  end
67
-
68
- include_examples "it flags the account as modified"
69
75
  end
70
76
 
71
77
  context "when removing folders" do
@@ -76,22 +82,16 @@ describe Imap::Backup::Configuration::FolderChooser do
76
82
  end
77
83
 
78
84
  specify "are removed from the account" do
79
- expect(account[:folders]).to_not include(name: "my_folder")
85
+ expect(account).to have_received(:"folders=").with([])
80
86
  end
81
-
82
- include_examples "it flags the account as modified"
83
87
  end
84
88
  end
85
89
 
86
90
  context "with missing remote folders" do
87
- let(:account) do
88
- {folders: [{name: "on_server"}, {name: "not_on_server"}]}
89
- end
90
- let(:connection_folders) do
91
- [
92
- instance_double(Imap::Backup::Account::Folder, name: "on_server")
93
- ]
91
+ let(:account_folders) do
92
+ [{name: "on_server"}, {name: "not_on_server"}]
94
93
  end
94
+ let(:connection_folders) { ["on_server"] }
95
95
 
96
96
  before do
97
97
  allow(Kernel).to receive(:puts)
@@ -99,22 +99,21 @@ describe Imap::Backup::Configuration::FolderChooser do
99
99
  end
100
100
 
101
101
  specify "are removed from the account" do
102
- expect(account[:folders]).to_not include(name: "not_on_server")
102
+ expect(account).to have_received(:"folders=").
103
+ with([{name: "on_server"}])
103
104
  end
104
-
105
- include_examples "it flags the account as modified"
106
105
  end
107
106
 
108
107
  context "when folders are not available" do
109
108
  let(:connection_folders) { nil }
110
109
 
111
110
  before do
112
- allow(Imap::Backup::Configuration::Setup.highline).
111
+ allow(Imap::Backup::Setup.highline).
113
112
  to receive(:ask) { "q" }
114
113
  end
115
114
 
116
115
  it "asks to press a key" do
117
- expect(Imap::Backup::Configuration::Setup.highline).
116
+ expect(Imap::Backup::Setup.highline).
118
117
  to receive(:ask).with("Press a key ")
119
118
 
120
119
  subject.run
@@ -125,19 +124,19 @@ describe Imap::Backup::Configuration::FolderChooser do
125
124
  before do
126
125
  allow(Imap::Backup::Account::Connection).
127
126
  to receive(:new).with(account).and_raise("error")
128
- allow(Imap::Backup::Configuration::Setup.highline).
127
+ allow(Imap::Backup::Setup.highline).
129
128
  to receive(:ask) { "q" }
130
129
  end
131
130
 
132
131
  it "prints an error message" do
133
- expect(Imap::Backup.logger).
132
+ expect(Imap::Backup::Logger.logger).
134
133
  to receive(:warn).with("Connection failed")
135
134
 
136
135
  subject.run
137
136
  end
138
137
 
139
138
  it "asks to continue" do
140
- expect(Imap::Backup::Configuration::Setup.highline).
139
+ expect(Imap::Backup::Setup.highline).
141
140
  to receive(:ask).with("Press a key ")
142
141
 
143
142
  subject.run