imap-backup 6.0.0.rc2 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/imap-backup.gemspec +5 -1
  3. data/lib/cli_coverage.rb +11 -11
  4. data/lib/email/provider/base.rb +2 -0
  5. data/lib/email/provider/unknown.rb +2 -0
  6. data/lib/email/provider.rb +2 -0
  7. data/lib/imap/backup/account/connection/backup_folders.rb +27 -0
  8. data/lib/imap/backup/account/connection/client_factory.rb +54 -0
  9. data/lib/imap/backup/account/connection/folder_names.rb +26 -0
  10. data/lib/imap/backup/account/connection.rb +11 -95
  11. data/lib/imap/backup/account/folder.rb +9 -6
  12. data/lib/imap/backup/account.rb +3 -5
  13. data/lib/imap/backup/cli/backup.rb +1 -3
  14. data/lib/imap/backup/cli/helpers.rb +24 -22
  15. data/lib/imap/backup/cli/local.rb +20 -13
  16. data/lib/imap/backup/cli/migrate.rb +4 -10
  17. data/lib/imap/backup/cli/restore.rb +8 -7
  18. data/lib/imap/backup/cli/setup.rb +10 -8
  19. data/lib/imap/backup/cli/stats.rb +78 -0
  20. data/lib/imap/backup/cli/status.rb +2 -2
  21. data/lib/imap/backup/cli/utils.rb +4 -6
  22. data/lib/imap/backup/cli.rb +24 -3
  23. data/lib/imap/backup/configuration.rb +9 -11
  24. data/lib/imap/backup/downloader.rb +48 -30
  25. data/lib/imap/backup/migrator.rb +5 -5
  26. data/lib/imap/backup/sanitizer.rb +3 -2
  27. data/lib/imap/backup/serializer/appender.rb +49 -0
  28. data/lib/imap/backup/serializer/message_enumerator.rb +29 -0
  29. data/lib/imap/backup/serializer/unused_name_finder.rb +27 -0
  30. data/lib/imap/backup/serializer.rb +24 -78
  31. data/lib/imap/backup/setup/account/header.rb +75 -0
  32. data/lib/imap/backup/setup/account.rb +14 -91
  33. data/lib/imap/backup/setup/asker.rb +4 -15
  34. data/lib/imap/backup/setup/backup_path.rb +41 -0
  35. data/lib/imap/backup/setup/email.rb +45 -0
  36. data/lib/imap/backup/setup/folder_chooser.rb +3 -3
  37. data/lib/imap/backup/setup/helpers.rb +1 -1
  38. data/lib/imap/backup/setup.rb +5 -4
  39. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +39 -20
  40. data/lib/imap/backup/uploader.rb +46 -8
  41. data/lib/imap/backup/utils.rb +1 -1
  42. data/lib/imap/backup/version.rb +1 -1
  43. data/lib/imap/backup.rb +0 -1
  44. metadata +31 -134
  45. data/spec/features/backup_spec.rb +0 -100
  46. data/spec/features/configuration/minimal_configuration.rb +0 -15
  47. data/spec/features/configuration/missing_configuration.rb +0 -14
  48. data/spec/features/folders_spec.rb +0 -36
  49. data/spec/features/helper.rb +0 -2
  50. data/spec/features/local/list_accounts_spec.rb +0 -12
  51. data/spec/features/local/list_emails_spec.rb +0 -21
  52. data/spec/features/local/list_folders_spec.rb +0 -21
  53. data/spec/features/local/show_an_email_spec.rb +0 -34
  54. data/spec/features/migrate_spec.rb +0 -35
  55. data/spec/features/remote/list_account_folders_spec.rb +0 -16
  56. data/spec/features/restore_spec.rb +0 -162
  57. data/spec/features/status_spec.rb +0 -43
  58. data/spec/features/support/aruba.rb +0 -78
  59. data/spec/features/support/backup_directory.rb +0 -43
  60. data/spec/features/support/email_server.rb +0 -110
  61. data/spec/features/support/shared/connection_context.rb +0 -14
  62. data/spec/features/support/shared/message_fixtures.rb +0 -16
  63. data/spec/fixtures/connection.yml +0 -7
  64. data/spec/spec_helper.rb +0 -15
  65. data/spec/support/fixtures.rb +0 -11
  66. data/spec/support/higline_test_helpers.rb +0 -8
  67. data/spec/support/silence_logging.rb +0 -7
  68. data/spec/unit/email/mboxrd/message_spec.rb +0 -177
  69. data/spec/unit/email/provider/apple_mail_spec.rb +0 -7
  70. data/spec/unit/email/provider/base_spec.rb +0 -11
  71. data/spec/unit/email/provider/fastmail_spec.rb +0 -7
  72. data/spec/unit/email/provider/gmail_spec.rb +0 -7
  73. data/spec/unit/email/provider_spec.rb +0 -27
  74. data/spec/unit/imap/backup/account/connection_spec.rb +0 -433
  75. data/spec/unit/imap/backup/account/folder_spec.rb +0 -261
  76. data/spec/unit/imap/backup/account_spec.rb +0 -246
  77. data/spec/unit/imap/backup/cli/accounts_spec.rb +0 -58
  78. data/spec/unit/imap/backup/cli/backup_spec.rb +0 -19
  79. data/spec/unit/imap/backup/cli/folders_spec.rb +0 -39
  80. data/spec/unit/imap/backup/cli/helpers_spec.rb +0 -87
  81. data/spec/unit/imap/backup/cli/local_spec.rb +0 -100
  82. data/spec/unit/imap/backup/cli/migrate_spec.rb +0 -80
  83. data/spec/unit/imap/backup/cli/restore_spec.rb +0 -67
  84. data/spec/unit/imap/backup/cli/setup_spec.rb +0 -17
  85. data/spec/unit/imap/backup/cli/utils_spec.rb +0 -125
  86. data/spec/unit/imap/backup/cli_spec.rb +0 -93
  87. data/spec/unit/imap/backup/client/apple_mail_spec.rb +0 -9
  88. data/spec/unit/imap/backup/client/default_spec.rb +0 -22
  89. data/spec/unit/imap/backup/configuration_spec.rb +0 -238
  90. data/spec/unit/imap/backup/downloader_spec.rb +0 -96
  91. data/spec/unit/imap/backup/logger_spec.rb +0 -48
  92. data/spec/unit/imap/backup/migrator_spec.rb +0 -58
  93. data/spec/unit/imap/backup/sanitizer_spec.rb +0 -42
  94. data/spec/unit/imap/backup/serializer/directory_spec.rb +0 -37
  95. data/spec/unit/imap/backup/serializer/imap_spec.rb +0 -218
  96. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +0 -45
  97. data/spec/unit/imap/backup/serializer/mbox_spec.rb +0 -101
  98. data/spec/unit/imap/backup/serializer_spec.rb +0 -296
  99. data/spec/unit/imap/backup/setup/account_spec.rb +0 -461
  100. data/spec/unit/imap/backup/setup/asker_spec.rb +0 -137
  101. data/spec/unit/imap/backup/setup/connection_tester_spec.rb +0 -51
  102. data/spec/unit/imap/backup/setup/folder_chooser_spec.rb +0 -146
  103. data/spec/unit/imap/backup/setup/helpers_spec.rb +0 -15
  104. data/spec/unit/imap/backup/setup_spec.rb +0 -301
  105. data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +0 -116
  106. data/spec/unit/imap/backup/uploader_spec.rb +0 -54
  107. data/spec/unit/imap/backup/utils_spec.rb +0 -92
  108. data/spec/unit/retry_on_error_spec.rb +0 -34
@@ -1,7 +0,0 @@
1
- describe Email::Provider::AppleMail do
2
- describe "#host" do
3
- it "returns host" do
4
- expect(subject.host).to eq("imap.mail.me.com")
5
- end
6
- end
7
- end
@@ -1,11 +0,0 @@
1
- describe Email::Provider::Base do
2
- describe "#options" do
3
- it "returns options" do
4
- expect(subject.options).to be_a(Hash)
5
- end
6
-
7
- it "forces TLSv1_2" do
8
- expect(subject.options[:ssl][:ssl_version]).to eq(:TLSv1_2)
9
- end
10
- end
11
- end
@@ -1,7 +0,0 @@
1
- describe Email::Provider::Fastmail do
2
- describe "#host" do
3
- it "returns host" do
4
- expect(subject.host).to eq("imap.fastmail.com")
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- describe Email::Provider::GMail do
2
- describe "#host" do
3
- it "returns host" do
4
- expect(subject.host).to eq("imap.gmail.com")
5
- end
6
- end
7
- end
@@ -1,27 +0,0 @@
1
- describe Email::Provider do
2
- describe ".for_address" do
3
- context "with known providers" do
4
- [
5
- ["fastmail.com", "Fastmail .com", Email::Provider::Fastmail],
6
- ["fastmail.fm", "Fastmail .fm", Email::Provider::Fastmail],
7
- ["gmail.com", "GMail", Email::Provider::GMail],
8
- ["icloud.com", "Apple Mail icloud.com", Email::Provider::AppleMail],
9
- ["mac.com", "Apple Mail mac.com", Email::Provider::AppleMail],
10
- ["me.com", "Apple Mail me.com", Email::Provider::AppleMail]
11
- ].each do |domain, name, klass|
12
- it "recognizes #{name} addresses" do
13
- address = "foo@#{domain}"
14
- expect(described_class.for_address(address)).to be_a(klass)
15
- end
16
- end
17
- end
18
-
19
- context "with unknown providers" do
20
- it "returns the Unknown provider" do
21
- result = described_class.for_address("foo@unknown.com")
22
-
23
- expect(result).to be_a(Email::Provider::Unknown)
24
- end
25
- end
26
- end
27
- end
@@ -1,433 +0,0 @@
1
- require "ostruct"
2
-
3
- describe Imap::Backup::Account::Connection do
4
- BACKUP_FOLDER = "backup_folder".freeze
5
- FOLDER_CONFIG = {name: BACKUP_FOLDER}.freeze
6
- FOLDER_NAME = "my_folder".freeze
7
- GMAIL_IMAP_SERVER = "imap.gmail.com".freeze
8
- IMAP_FOLDER = "imap_folder".freeze
9
- LOCAL_PATH = "local_path".freeze
10
- LOCAL_UID = "local_uid".freeze
11
- PASSWORD = "secret".freeze
12
- ROOT_NAME = "foo".freeze
13
- SERVER = "imap.example.com".freeze
14
- USERNAME = "username@example.com".freeze
15
-
16
- subject { described_class.new(account) }
17
-
18
- let(:client) do
19
- instance_double(
20
- Imap::Backup::Client::Default, authenticate: nil, login: nil, disconnect: nil
21
- )
22
- end
23
- let(:imap_folders) { [] }
24
- let(:account) do
25
- instance_double(
26
- Imap::Backup::Account,
27
- username: username,
28
- password: PASSWORD,
29
- local_path: LOCAL_PATH,
30
- folders: config_folders,
31
- multi_fetch_size: multi_fetch_size,
32
- server: server,
33
- connection_options: nil
34
- )
35
- end
36
- let(:username) { USERNAME }
37
- let(:config_folders) { [FOLDER_CONFIG] }
38
- let(:multi_fetch_size) { 1 }
39
- let(:root_info) do
40
- instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)
41
- end
42
- let(:serializer) do
43
- instance_double(
44
- Imap::Backup::Serializer,
45
- folder: serialized_folder,
46
- force_uid_validity: nil,
47
- apply_uid_validity: new_uid_validity,
48
- uids: [LOCAL_UID]
49
- )
50
- end
51
- let(:serialized_folder) { nil }
52
- let(:server) { SERVER }
53
- let(:new_uid_validity) { nil }
54
-
55
- before do
56
- allow(Imap::Backup::Client::Default).to receive(:new) { client }
57
- allow(client).to receive(:list) { imap_folders }
58
- allow(Imap::Backup::Utils).to receive(:make_folder)
59
- end
60
-
61
- shared_examples "connects to IMAP" do
62
- it "logs in to the imap server" do
63
- expect(client).to have_received(:login)
64
- end
65
- end
66
-
67
- describe "#client" do
68
- let(:result) { subject.client }
69
-
70
- it "returns the IMAP connection" do
71
- expect(result).to eq(client)
72
- end
73
-
74
- it "uses the password" do
75
- result
76
-
77
- expect(client).to have_received(:login).with(USERNAME, PASSWORD)
78
- end
79
-
80
- context "when the first login attempt fails" do
81
- before do
82
- outcomes = [-> { raise EOFError }, -> { true }]
83
- allow(client).to receive(:login) { outcomes.shift.call }
84
- end
85
-
86
- it "retries" do
87
- subject.client
88
-
89
- expect(client).to have_received(:login).twice
90
- end
91
- end
92
-
93
- context "when the provider is Apple" do
94
- let(:username) { "user@mac.com" }
95
- let(:apple_client) do
96
- instance_double(
97
- Imap::Backup::Client::AppleMail, login: nil
98
- )
99
- end
100
-
101
- before do
102
- allow(Imap::Backup::Client::AppleMail).to receive(:new) { apple_client }
103
- end
104
-
105
- it "returns the Apple client" do
106
- expect(result).to eq(apple_client)
107
- end
108
- end
109
-
110
- context "when run" do
111
- before { subject.client }
112
-
113
- include_examples "connects to IMAP"
114
- end
115
- end
116
-
117
- describe "#folder_names" do
118
- let(:imap_folders) do
119
- [IMAP_FOLDER]
120
- end
121
-
122
- it "returns the list of folders" do
123
- expect(subject.folder_names).to eq([IMAP_FOLDER])
124
- end
125
- end
126
-
127
- describe "#status" do
128
- let(:folder) do
129
- instance_double(
130
- Imap::Backup::Account::Folder,
131
- uids: [remote_uid],
132
- name: IMAP_FOLDER
133
- )
134
- end
135
- let(:remote_uid) { "remote_uid" }
136
-
137
- before do
138
- allow(Imap::Backup::Account::Folder).to receive(:new) { folder }
139
- allow(Imap::Backup::Serializer).to receive(:new) { serializer }
140
- end
141
-
142
- it "creates the path" do
143
- expect(Imap::Backup::Utils).to receive(:make_folder)
144
-
145
- subject.status
146
- end
147
-
148
- it "returns the names of folders" do
149
- expect(subject.status[0][:name]).to eq(IMAP_FOLDER)
150
- end
151
-
152
- it "returns local message uids" do
153
- expect(subject.status[0][:local]).to eq([LOCAL_UID])
154
- end
155
-
156
- it "retrieves the available uids" do
157
- expect(subject.status[0][:remote]).to eq([remote_uid])
158
- end
159
- end
160
-
161
- describe "#run_backup" do
162
- let(:folder) do
163
- instance_double(
164
- Imap::Backup::Account::Folder,
165
- name: IMAP_FOLDER,
166
- exist?: exists,
167
- uid_validity: uid_validity
168
- )
169
- end
170
- let(:exists) { true }
171
- let(:uid_validity) { 123 }
172
- let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
173
- let(:multi_fetch_size) { 10 }
174
-
175
- before do
176
- allow(Imap::Backup::Downloader).
177
- to receive(:new).with(folder, serializer, anything) { downloader }
178
- allow(Imap::Backup::Account::Folder).to receive(:new).
179
- with(subject, BACKUP_FOLDER) { folder }
180
- allow(Imap::Backup::Serializer).to receive(:new).
181
- with(LOCAL_PATH, IMAP_FOLDER) { serializer }
182
- end
183
-
184
- it "passes the multi_fetch_size" do
185
- subject.run_backup
186
-
187
- expect(Imap::Backup::Downloader).to have_received(:new).
188
- with(anything, anything, {multi_fetch_size: 10})
189
- end
190
-
191
- context "with supplied config_folders" do
192
- it "runs the downloader" do
193
- expect(downloader).to receive(:run)
194
-
195
- subject.run_backup
196
- end
197
-
198
- context "when a folder does not exist" do
199
- let(:exists) { false }
200
-
201
- it "does not run the downloader" do
202
- expect(downloader).to_not receive(:run)
203
-
204
- subject.run_backup
205
- end
206
- end
207
- end
208
-
209
- context "without supplied config_folders" do
210
- let(:imap_folders) { [ROOT_NAME] }
211
-
212
- before do
213
- allow(Imap::Backup::Account::Folder).to receive(:new).
214
- with(subject, ROOT_NAME) { folder }
215
- allow(Imap::Backup::Serializer).to receive(:new).
216
- with(LOCAL_PATH, ROOT_NAME) { serializer }
217
- end
218
-
219
- context "when supplied config_folders is nil" do
220
- let(:config_folders) { nil }
221
-
222
- it "runs the downloader for each folder" do
223
- expect(downloader).to receive(:run).exactly(:once)
224
-
225
- subject.run_backup
226
- end
227
- end
228
-
229
- context "when supplied config_folders is an empty list" do
230
- let(:config_folders) { [] }
231
-
232
- it "runs the downloader for each folder" do
233
- expect(downloader).to receive(:run).exactly(:once)
234
-
235
- subject.run_backup
236
- end
237
- end
238
-
239
- context "when the imap server doesn't return folders" do
240
- let(:config_folders) { nil }
241
- let(:imap_folders) { [] }
242
-
243
- it "fails" do
244
- expect do
245
- subject.run_backup
246
- end.to raise_error(RuntimeError, /Unable to get folder list/)
247
- end
248
- end
249
- end
250
-
251
- context "when the IMAP session expires" do
252
- before do
253
- data = OpenStruct.new(data: "Session expired")
254
- response = OpenStruct.new(data: data)
255
- outcomes = [
256
- -> { raise Net::IMAP::ByeResponseError, response },
257
- -> { nil }
258
- ]
259
- allow(downloader).to receive(:run) { outcomes.shift.call }
260
- end
261
-
262
- it "reconnects" do
263
- expect(downloader).to receive(:run).exactly(:twice)
264
-
265
- subject.run_backup
266
- end
267
- end
268
-
269
- context "when run" do
270
- before { subject.run_backup }
271
-
272
- include_examples "connects to IMAP"
273
- end
274
- end
275
-
276
- describe "#restore" do
277
- let(:folder) do
278
- instance_double(
279
- Imap::Backup::Account::Folder,
280
- create: nil,
281
- uids: uids,
282
- name: IMAP_FOLDER,
283
- uid_validity: uid_validity
284
- )
285
- end
286
- let(:uids) { [99] }
287
- let(:uid_validity) { 123 }
288
- let(:serialized_folder) { "old name" }
289
- let(:uploader) do
290
- instance_double(Imap::Backup::Uploader, run: false)
291
- end
292
- let(:updated_uploader) do
293
- instance_double(Imap::Backup::Uploader, run: false)
294
- end
295
- let(:updated_folder) do
296
- instance_double(
297
- Imap::Backup::Account::Folder,
298
- create: nil,
299
- uid_validity: "new uid validity"
300
- )
301
- end
302
- let(:updated_serializer) do
303
- instance_double(
304
- Imap::Backup::Serializer, force_uid_validity: nil
305
- )
306
- end
307
-
308
- before do
309
- allow(Imap::Backup::Account::Folder).to receive(:new).
310
- with(subject, FOLDER_NAME) { folder }
311
- allow(Imap::Backup::Serializer).to receive(:new).
312
- with(anything, FOLDER_NAME) { serializer }
313
- allow(Imap::Backup::Account::Folder).to receive(:new).
314
- with(subject, "new name") { updated_folder }
315
- allow(Imap::Backup::Serializer).to receive(:new).
316
- with(anything, "new name") { updated_serializer }
317
- allow(Imap::Backup::Uploader).to receive(:new).
318
- with(folder, serializer) { uploader }
319
- allow(Imap::Backup::Uploader).to receive(:new).
320
- with(updated_folder, updated_serializer) { updated_uploader }
321
- allow(Pathname).to receive(:glob).
322
- and_yield(Pathname.new(File.join(LOCAL_PATH, "#{FOLDER_NAME}.imap")))
323
- end
324
-
325
- it "sets local uid validity" do
326
- expect(serializer).to receive(:apply_uid_validity).with(uid_validity)
327
-
328
- subject.restore
329
- end
330
-
331
- context "when folders exist with contents" do
332
- context "when the local folder is renamed" do
333
- let(:new_uid_validity) { "new name" }
334
-
335
- it "creates the new folder" do
336
- expect(updated_folder).to receive(:create)
337
-
338
- subject.restore
339
- end
340
-
341
- it "sets the renamed folder's uid validity" do
342
- expect(updated_serializer).
343
- to receive(:force_uid_validity).with("new uid validity")
344
-
345
- subject.restore
346
- end
347
-
348
- it "creates the uploader with updated folder and serializer" do
349
- expect(updated_uploader).to receive(:run)
350
-
351
- subject.restore
352
- end
353
- end
354
-
355
- context "when the local folder is not renamed" do
356
- it "runs the uploader" do
357
- expect(uploader).to receive(:run)
358
-
359
- subject.restore
360
- end
361
- end
362
- end
363
-
364
- context "when folders don't exist or are empty" do
365
- let(:uids) { [] }
366
-
367
- it "creates the folder" do
368
- expect(folder).to receive(:create)
369
-
370
- subject.restore
371
- end
372
-
373
- it "forces local uid validity" do
374
- expect(serializer).to receive(:force_uid_validity).with(uid_validity)
375
-
376
- subject.restore
377
- end
378
-
379
- it "runs the uploader" do
380
- expect(uploader).to receive(:run)
381
-
382
- subject.restore
383
- end
384
- end
385
- end
386
-
387
- describe "#reconnect" do
388
- context "when the IMAP connection has been used" do
389
- before { subject.client }
390
-
391
- it "disconnects from the server" do
392
- expect(client).to receive(:disconnect)
393
-
394
- subject.reconnect
395
- end
396
- end
397
-
398
- context "when the IMAP connection has not been used" do
399
- it "does not disconnect from the server" do
400
- expect(client).to_not receive(:disconnect)
401
-
402
- subject.reconnect
403
- end
404
- end
405
-
406
- it "causes reconnection on future access" do
407
- expect(Imap::Backup::Client::Default).to receive(:new)
408
-
409
- subject.reconnect
410
- subject.client
411
- end
412
- end
413
-
414
- describe "#disconnect" do
415
- context "when the IMAP connection has been used" do
416
- it "disconnects from the server" do
417
- subject.client
418
-
419
- expect(client).to receive(:disconnect)
420
-
421
- subject.disconnect
422
- end
423
- end
424
-
425
- context "when the IMAP connection has not been used" do
426
- it "does not disconnect from the server" do
427
- expect(client).to_not receive(:disconnect)
428
-
429
- subject.disconnect
430
- end
431
- end
432
- end
433
- end