imap-backup 5.2.0 → 6.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -2
  3. data/docs/development.md +10 -4
  4. data/lib/cli_coverage.rb +1 -1
  5. data/lib/imap/backup/account/connection.rb +7 -11
  6. data/lib/imap/backup/account.rb +31 -11
  7. data/lib/imap/backup/cli/folders.rb +3 -3
  8. data/lib/imap/backup/cli/migrate.rb +3 -3
  9. data/lib/imap/backup/cli/utils.rb +2 -2
  10. data/lib/imap/backup/configuration.rb +1 -11
  11. data/lib/imap/backup/downloader.rb +13 -9
  12. data/lib/imap/backup/serializer/directory.rb +37 -0
  13. data/lib/imap/backup/serializer/imap.rb +120 -0
  14. data/lib/imap/backup/serializer/mbox.rb +23 -94
  15. data/lib/imap/backup/serializer/mbox_enumerator.rb +2 -0
  16. data/lib/imap/backup/serializer.rb +180 -3
  17. data/lib/imap/backup/setup/account.rb +52 -29
  18. data/lib/imap/backup/setup/helpers.rb +1 -1
  19. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +1 -1
  20. data/lib/imap/backup/version.rb +3 -3
  21. data/lib/imap/backup.rb +0 -1
  22. data/spec/features/backup_spec.rb +8 -16
  23. data/spec/features/support/aruba.rb +4 -3
  24. data/spec/unit/imap/backup/account/connection_spec.rb +36 -8
  25. data/spec/unit/imap/backup/account/folder_spec.rb +10 -0
  26. data/spec/unit/imap/backup/account_spec.rb +246 -0
  27. data/spec/unit/imap/backup/cli/accounts_spec.rb +12 -1
  28. data/spec/unit/imap/backup/cli/backup_spec.rb +19 -0
  29. data/spec/unit/imap/backup/cli/folders_spec.rb +39 -0
  30. data/spec/unit/imap/backup/cli/local_spec.rb +26 -7
  31. data/spec/unit/imap/backup/cli/migrate_spec.rb +80 -0
  32. data/spec/unit/imap/backup/cli/restore_spec.rb +67 -0
  33. data/spec/unit/imap/backup/cli/setup_spec.rb +17 -0
  34. data/spec/unit/imap/backup/cli/utils_spec.rb +68 -5
  35. data/spec/unit/imap/backup/cli_spec.rb +93 -0
  36. data/spec/unit/imap/backup/client/apple_mail_spec.rb +9 -0
  37. data/spec/unit/imap/backup/configuration_spec.rb +2 -2
  38. data/spec/unit/imap/backup/downloader_spec.rb +59 -7
  39. data/spec/unit/imap/backup/migrator_spec.rb +1 -1
  40. data/spec/unit/imap/backup/sanitizer_spec.rb +42 -0
  41. data/spec/unit/imap/backup/serializer/directory_spec.rb +37 -0
  42. data/spec/unit/imap/backup/serializer/imap_spec.rb +218 -0
  43. data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -183
  44. data/spec/unit/imap/backup/serializer_spec.rb +296 -0
  45. data/spec/unit/imap/backup/setup/account_spec.rb +120 -25
  46. data/spec/unit/imap/backup/setup/helpers_spec.rb +15 -0
  47. data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +116 -0
  48. data/spec/unit/imap/backup/uploader_spec.rb +1 -1
  49. data/spec/unit/retry_on_error_spec.rb +34 -0
  50. metadata +36 -7
  51. data/lib/imap/backup/serializer/mbox_store.rb +0 -217
  52. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +0 -329
@@ -11,10 +11,11 @@ describe Imap::Backup::Setup::Account do
11
11
  Imap::Backup::Account,
12
12
  username: existing_email,
13
13
  password: existing_password,
14
- server: current_server,
15
- connection_options: nil,
16
14
  local_path: "/backup/path",
17
15
  folders: [{name: "my_folder"}],
16
+ multi_fetch_size: multi_fetch_size,
17
+ server: current_server,
18
+ connection_options: connection_options,
18
19
  modified?: false
19
20
  )
20
21
  end
@@ -28,10 +29,12 @@ describe Imap::Backup::Setup::Account do
28
29
  let(:accounts) { [account, account1] }
29
30
  let(:existing_email) { "user@example.com" }
30
31
  let(:new_email) { "foo@example.com" }
31
- let(:current_server) { "imap.example.com" }
32
32
  let(:existing_password) { "password" }
33
33
  let(:other_email) { "other@example.com" }
34
34
  let(:other_existing_path) { "/other/existing/path" }
35
+ let(:multi_fetch_size) { 1 }
36
+ let(:current_server) { "imap.example.com" }
37
+ let(:connection_options) { nil }
35
38
 
36
39
  let(:highline) { HIGHLINE }
37
40
  let(:config) { CONFIG }
@@ -99,9 +102,11 @@ describe Imap::Backup::Setup::Account do
99
102
  [
100
103
  "modify email",
101
104
  "modify password",
102
- "modify server",
103
105
  "modify backup path",
104
106
  "choose backup folders",
107
+ "modify multi-fetch size (number of emails to fetch at a time)",
108
+ "modify server",
109
+ "modify connection options",
105
110
  "test connection",
106
111
  "delete",
107
112
  "(q) return to main menu",
@@ -136,7 +141,23 @@ describe Imap::Backup::Setup::Account do
136
141
  before { subject.run }
137
142
 
138
143
  it "indicates that a password is not set" do
139
- expect(menu.header).to include("password (unset)")
144
+ expect(menu.header).to match(/^password\s+\(unset\)/)
145
+ end
146
+ end
147
+
148
+ context "with multi_fetch_size" do
149
+ let(:multi_fetch_size) { 4 }
150
+
151
+ it "shows the size" do
152
+ expect(menu.header).to match(/^multi-fetch\s+4/)
153
+ end
154
+ end
155
+
156
+ context "with connection_options" do
157
+ let(:connection_options) { {some: "option"} }
158
+
159
+ it "shows the options" do
160
+ expect(menu.header).to match(/^connection options\s+'{"some":"option"}'/)
140
161
  end
141
162
  end
142
163
  end
@@ -240,23 +261,6 @@ describe Imap::Backup::Setup::Account do
240
261
  end
241
262
  end
242
263
 
243
- describe "choosing 'modify server'" do
244
- let(:server) { "server" }
245
-
246
- before do
247
- allow(account).to receive(:"server=")
248
- allow(highline).to receive(:ask).with("server: ") { server }
249
-
250
- subject.run
251
-
252
- menu.choices["modify server"].call
253
- end
254
-
255
- it "updates the server" do
256
- expect(account).to have_received(:"server=").with(server)
257
- end
258
- end
259
-
260
264
  describe "choosing 'modify backup path'" do
261
265
  let(:new_backup_path) { "/new/path" }
262
266
 
@@ -311,6 +315,97 @@ describe Imap::Backup::Setup::Account do
311
315
  end
312
316
  end
313
317
 
318
+ describe "choosing 'modify multi-fetch size'" do
319
+ let(:supplied) { "10" }
320
+
321
+ before do
322
+ allow(account).to receive(:multi_fetch_size=)
323
+ allow(highline).to receive(:ask).with("size: ") { supplied }
324
+
325
+ subject.run
326
+ menu.choices[
327
+ "modify multi-fetch size (number of emails to fetch at a time)"
328
+ ].call
329
+ end
330
+
331
+ it "sets the multi-fetch size" do
332
+ expect(account).to have_received(:multi_fetch_size=).with(10)
333
+ end
334
+
335
+ context "when the supplied value is not a number" do
336
+ let(:supplied) { "wrong!" }
337
+
338
+ it "does nothing" do
339
+ expect(account).to_not have_received(:multi_fetch_size=)
340
+ end
341
+ end
342
+
343
+ context "when the supplied value is not a positive number" do
344
+ let(:supplied) { "0" }
345
+
346
+ it "does nothing" do
347
+ expect(account).to_not have_received(:multi_fetch_size=)
348
+ end
349
+ end
350
+ end
351
+
352
+ describe "choosing 'modify server'" do
353
+ let(:server) { "server" }
354
+
355
+ before do
356
+ allow(account).to receive(:"server=")
357
+ allow(highline).to receive(:ask).with("server: ") { server }
358
+
359
+ subject.run
360
+
361
+ menu.choices["modify server"].call
362
+ end
363
+
364
+ it "updates the server" do
365
+ expect(account).to have_received(:"server=").with(server)
366
+ end
367
+ end
368
+
369
+ describe "choosing 'modify connection options'" do
370
+ context "when the JSON is well formed" do
371
+ let(:json) { "{}" }
372
+
373
+ before do
374
+ allow(highline).to receive(:ask).with("connections options (as JSON): ") { json }
375
+ allow(account).to receive(:"connection_options=")
376
+
377
+ subject.run
378
+
379
+ menu.choices["modify connection options"].call
380
+ end
381
+
382
+ it "updates the connection options" do
383
+ expect(account).to have_received(:"connection_options=").with(json)
384
+ end
385
+ end
386
+
387
+ context "when the JSON is malformed" do
388
+ before do
389
+ allow(highline).to receive(:ask).with("connections options (as JSON): ") { "xx" }
390
+ allow(account).to receive(:"connection_options=").and_raise(JSON::ParserError)
391
+ allow(highline).to receive(:ask).with("Press a key ")
392
+
393
+ subject.run
394
+
395
+ menu.choices["modify connection options"].call
396
+ end
397
+
398
+ it "does not fail" do
399
+ subject.run
400
+ end
401
+
402
+ it "reports the problem" do
403
+ expect(Kernel).to have_received(:puts).
404
+ with(/Malformed/)
405
+ end
406
+ end
407
+ end
408
+
314
409
  describe "choosing 'test connection'" do
315
410
  let(:connection_tester) do
316
411
  instance_double(
@@ -336,7 +431,7 @@ describe Imap::Backup::Setup::Account do
336
431
  let(:confirmed) { true }
337
432
 
338
433
  before do
339
- allow(account).to receive(:mark_for_deletion!)
434
+ allow(account).to receive(:mark_for_deletion)
340
435
  allow(highline).to receive(:agree) { confirmed }
341
436
  subject.run
342
437
  catch :done do
@@ -350,7 +445,7 @@ describe Imap::Backup::Setup::Account do
350
445
 
351
446
  context "when the user confirms deletion" do
352
447
  it "flags the account to be deleted" do
353
- expect(account).to have_received(:mark_for_deletion!)
448
+ expect(account).to have_received(:mark_for_deletion)
354
449
  end
355
450
  end
356
451
 
@@ -358,7 +453,7 @@ describe Imap::Backup::Setup::Account do
358
453
  let(:confirmed) { false }
359
454
 
360
455
  it "doesn't flag the account to be deleted" do
361
- expect(account).to_not have_received(:mark_for_deletion!)
456
+ expect(account).to_not have_received(:mark_for_deletion)
362
457
  end
363
458
  end
364
459
  end
@@ -0,0 +1,15 @@
1
+ module Imap::Backup
2
+ RSpec.describe Setup::Helpers do
3
+ describe "#title_prefix" do
4
+ it "is a string" do
5
+ expect(subject.title_prefix).to eq("imap-backup –")
6
+ end
7
+ end
8
+
9
+ describe "#version" do
10
+ it "is a version string" do
11
+ expect(subject.version).to match(/\A\d+\.\d+\.\d+(\.\w+)?\z/)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,116 @@
1
+ module Imap::Backup
2
+ describe Thunderbird::MailboxExporter do
3
+ subject { described_class.new("email", serializer, "profile", **args) }
4
+
5
+ let(:args) { {} }
6
+ let(:serializer) do
7
+ instance_double(
8
+ Serializer,
9
+ folder: "folder",
10
+ mbox_pathname: "mbox_pathname"
11
+ )
12
+ end
13
+ let(:local_folder) do
14
+ instance_double(
15
+ Thunderbird::LocalFolder,
16
+ exists?: local_folder_exists,
17
+ full_path: "full_path",
18
+ msf_exists?: msf_exists,
19
+ msf_path: "msf_path",
20
+ path: "path",
21
+ set_up: set_up_result
22
+ )
23
+ end
24
+ let(:local_folder_exists) { false }
25
+ let(:msf_exists) { false }
26
+ let(:set_up_result) { true }
27
+ let(:file) { instance_double(File, write: nil) }
28
+ let(:enumerator) { instance_double(Serializer::MboxEnumerator) }
29
+
30
+ before do
31
+ allow(File).to receive(:open).with("full_path", "w").and_yield(file)
32
+ allow(File).to receive(:unlink)
33
+ allow(Thunderbird::LocalFolder).to receive(:new) { local_folder }
34
+ allow(Serializer::MboxEnumerator).to receive(:new) { enumerator }
35
+ allow(enumerator).to receive(:each) { ["message"].enum_for(:each) }
36
+ allow(Email::Mboxrd::Message).to receive(:clean_serialized) { "cleaned" }
37
+ allow(Kernel).to receive(:puts)
38
+ end
39
+
40
+ describe "#run" do
41
+ let!(:result) { subject.run }
42
+
43
+ context "when the destination folder cannot be set up" do
44
+ let(:set_up_result) { false }
45
+
46
+ it "doesn't copy the mailbox" do
47
+ expect(file).to_not have_received(:write)
48
+ end
49
+
50
+ it "returns false" do
51
+ expect(result).to be false
52
+ end
53
+ end
54
+
55
+ context "when the .msf file exists" do
56
+ let(:msf_exists) { true }
57
+
58
+ context "when 'force' is set" do
59
+ let(:args) { {force: true} }
60
+
61
+ it "deletes the file" do
62
+ expect(File).to have_received(:unlink).with("msf_path")
63
+ end
64
+ end
65
+
66
+ context "when 'force' isn't set" do
67
+ it "doesn't copy the mailbox" do
68
+ expect(file).to_not have_received(:write)
69
+ end
70
+
71
+ it "returns false" do
72
+ expect(result).to be false
73
+ end
74
+ end
75
+ end
76
+
77
+ context "when the destination mailbox exists" do
78
+ let(:local_folder_exists) { true }
79
+
80
+ context "when 'force' is set" do
81
+ let(:args) { {force: true} }
82
+
83
+ it "writes the message" do
84
+ expect(file).to have_received(:write)
85
+ end
86
+
87
+ it "returns true" do
88
+ expect(result).to be true
89
+ end
90
+ end
91
+
92
+ context "when 'force' isn't set" do
93
+ it "doesn't copy the mailbox" do
94
+ expect(file).to_not have_received(:write)
95
+ end
96
+
97
+ it "returns false" do
98
+ expect(result).to be false
99
+ end
100
+ end
101
+ end
102
+
103
+ it "adds a 'From' line" do
104
+ expect(file).to have_received(:write).with(/From - \w+ \w+ \d+ \d+:\d+:\d+/)
105
+ end
106
+
107
+ it "writes the cleaned message" do
108
+ expect(file).to have_received(:write).with(/cleaned/)
109
+ end
110
+
111
+ it "returns true" do
112
+ expect(result).to be true
113
+ end
114
+ end
115
+ end
116
+ end
@@ -8,7 +8,7 @@ describe Imap::Backup::Uploader do
8
8
  end
9
9
  let(:serializer) do
10
10
  instance_double(
11
- Imap::Backup::Serializer::Mbox,
11
+ Imap::Backup::Serializer,
12
12
  uids: [1, 2],
13
13
  update_uid: nil
14
14
  )
@@ -0,0 +1,34 @@
1
+ class Retrier
2
+ class FooError < StandardError; end
3
+
4
+ include RetryOnError
5
+
6
+ def do_stuff(errors:, limit:)
7
+ calls = 0
8
+
9
+ retry_on_error(errors: errors, limit: limit) do
10
+ calls += 1
11
+ raise FooError if calls < 3
12
+
13
+ 42
14
+ end
15
+ end
16
+ end
17
+
18
+ RSpec.describe RetryOnError do
19
+ describe "#retry_on_error" do
20
+ subject { Retrier.new }
21
+
22
+ it "retries" do
23
+ expect(subject.do_stuff(errors: [Retrier::FooError], limit: 3)).to eq(42)
24
+ end
25
+
26
+ context "when the block fails more than the limit" do
27
+ it "fails" do
28
+ expect do
29
+ subject.do_stuff(errors: [Retrier::FooError], limit: 1)
30
+ end.to raise_error(Retrier::FooError)
31
+ end
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 6.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-24 00:00:00.000000000 Z
11
+ date: 2022-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -211,9 +211,10 @@ files:
211
211
  - lib/imap/backup/migrator.rb
212
212
  - lib/imap/backup/sanitizer.rb
213
213
  - lib/imap/backup/serializer.rb
214
+ - lib/imap/backup/serializer/directory.rb
215
+ - lib/imap/backup/serializer/imap.rb
214
216
  - lib/imap/backup/serializer/mbox.rb
215
217
  - lib/imap/backup/serializer/mbox_enumerator.rb
216
- - lib/imap/backup/serializer/mbox_store.rb
217
218
  - lib/imap/backup/setup.rb
218
219
  - lib/imap/backup/setup/account.rb
219
220
  - lib/imap/backup/setup/asker.rb
@@ -256,25 +257,39 @@ files:
256
257
  - spec/unit/email/provider_spec.rb
257
258
  - spec/unit/imap/backup/account/connection_spec.rb
258
259
  - spec/unit/imap/backup/account/folder_spec.rb
260
+ - spec/unit/imap/backup/account_spec.rb
259
261
  - spec/unit/imap/backup/cli/accounts_spec.rb
262
+ - spec/unit/imap/backup/cli/backup_spec.rb
263
+ - spec/unit/imap/backup/cli/folders_spec.rb
260
264
  - spec/unit/imap/backup/cli/helpers_spec.rb
261
265
  - spec/unit/imap/backup/cli/local_spec.rb
266
+ - spec/unit/imap/backup/cli/migrate_spec.rb
267
+ - spec/unit/imap/backup/cli/restore_spec.rb
268
+ - spec/unit/imap/backup/cli/setup_spec.rb
262
269
  - spec/unit/imap/backup/cli/utils_spec.rb
270
+ - spec/unit/imap/backup/cli_spec.rb
271
+ - spec/unit/imap/backup/client/apple_mail_spec.rb
263
272
  - spec/unit/imap/backup/client/default_spec.rb
264
273
  - spec/unit/imap/backup/configuration_spec.rb
265
274
  - spec/unit/imap/backup/downloader_spec.rb
266
275
  - spec/unit/imap/backup/logger_spec.rb
267
276
  - spec/unit/imap/backup/migrator_spec.rb
277
+ - spec/unit/imap/backup/sanitizer_spec.rb
278
+ - spec/unit/imap/backup/serializer/directory_spec.rb
279
+ - spec/unit/imap/backup/serializer/imap_spec.rb
268
280
  - spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb
269
281
  - spec/unit/imap/backup/serializer/mbox_spec.rb
270
- - spec/unit/imap/backup/serializer/mbox_store_spec.rb
282
+ - spec/unit/imap/backup/serializer_spec.rb
271
283
  - spec/unit/imap/backup/setup/account_spec.rb
272
284
  - spec/unit/imap/backup/setup/asker_spec.rb
273
285
  - spec/unit/imap/backup/setup/connection_tester_spec.rb
274
286
  - spec/unit/imap/backup/setup/folder_chooser_spec.rb
287
+ - spec/unit/imap/backup/setup/helpers_spec.rb
275
288
  - spec/unit/imap/backup/setup_spec.rb
289
+ - spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb
276
290
  - spec/unit/imap/backup/uploader_spec.rb
277
291
  - spec/unit/imap/backup/utils_spec.rb
292
+ - spec/unit/retry_on_error_spec.rb
278
293
  homepage: https://github.com/joeyates/imap-backup
279
294
  licenses:
280
295
  - MIT
@@ -290,9 +305,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
290
305
  version: '2.5'
291
306
  required_rubygems_version: !ruby/object:Gem::Requirement
292
307
  requirements:
293
- - - ">="
308
+ - - ">"
294
309
  - !ruby/object:Gem::Version
295
- version: '0'
310
+ version: 1.3.1
296
311
  requirements: []
297
312
  rubygems_version: 3.1.6
298
313
  signing_key:
@@ -300,18 +315,27 @@ specification_version: 4
300
315
  summary: Backup GMail (or other IMAP) accounts to disk
301
316
  test_files:
302
317
  - spec/spec_helper.rb
318
+ - spec/unit/imap/backup/account_spec.rb
319
+ - spec/unit/imap/backup/serializer/imap_spec.rb
320
+ - spec/unit/imap/backup/serializer/directory_spec.rb
303
321
  - spec/unit/imap/backup/serializer/mbox_spec.rb
304
- - spec/unit/imap/backup/serializer/mbox_store_spec.rb
305
322
  - spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb
323
+ - spec/unit/imap/backup/cli_spec.rb
306
324
  - spec/unit/imap/backup/setup_spec.rb
307
325
  - spec/unit/imap/backup/logger_spec.rb
308
326
  - spec/unit/imap/backup/configuration_spec.rb
309
327
  - spec/unit/imap/backup/setup/account_spec.rb
310
328
  - spec/unit/imap/backup/setup/connection_tester_spec.rb
311
329
  - spec/unit/imap/backup/setup/asker_spec.rb
330
+ - spec/unit/imap/backup/setup/helpers_spec.rb
312
331
  - spec/unit/imap/backup/setup/folder_chooser_spec.rb
332
+ - spec/unit/imap/backup/cli/folders_spec.rb
333
+ - spec/unit/imap/backup/cli/setup_spec.rb
334
+ - spec/unit/imap/backup/cli/backup_spec.rb
335
+ - spec/unit/imap/backup/cli/restore_spec.rb
313
336
  - spec/unit/imap/backup/cli/helpers_spec.rb
314
337
  - spec/unit/imap/backup/cli/accounts_spec.rb
338
+ - spec/unit/imap/backup/cli/migrate_spec.rb
315
339
  - spec/unit/imap/backup/cli/utils_spec.rb
316
340
  - spec/unit/imap/backup/cli/local_spec.rb
317
341
  - spec/unit/imap/backup/downloader_spec.rb
@@ -320,7 +344,12 @@ test_files:
320
344
  - spec/unit/imap/backup/account/folder_spec.rb
321
345
  - spec/unit/imap/backup/account/connection_spec.rb
322
346
  - spec/unit/imap/backup/utils_spec.rb
347
+ - spec/unit/imap/backup/serializer_spec.rb
323
348
  - spec/unit/imap/backup/client/default_spec.rb
349
+ - spec/unit/imap/backup/client/apple_mail_spec.rb
350
+ - spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb
351
+ - spec/unit/imap/backup/sanitizer_spec.rb
352
+ - spec/unit/retry_on_error_spec.rb
324
353
  - spec/unit/email/mboxrd/message_spec.rb
325
354
  - spec/unit/email/provider/gmail_spec.rb
326
355
  - spec/unit/email/provider/apple_mail_spec.rb