imap-backup 5.0.0 → 6.0.0.rc2
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.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/bin/imap-backup +4 -0
- data/docs/development.md +10 -4
- data/imap-backup.gemspec +2 -7
- data/lib/cli_coverage.rb +18 -0
- data/lib/imap/backup/account/connection.rb +7 -11
- data/lib/imap/backup/account/folder.rb +0 -16
- data/lib/imap/backup/account.rb +31 -11
- data/lib/imap/backup/cli/folders.rb +3 -3
- data/lib/imap/backup/cli/migrate.rb +3 -3
- data/lib/imap/backup/cli/restore.rb +20 -4
- data/lib/imap/backup/cli/utils.rb +2 -2
- data/lib/imap/backup/cli.rb +6 -7
- data/lib/imap/backup/configuration.rb +1 -11
- data/lib/imap/backup/downloader.rb +13 -9
- data/lib/imap/backup/serializer/directory.rb +37 -0
- data/lib/imap/backup/serializer/imap.rb +120 -0
- data/lib/imap/backup/serializer/mbox.rb +23 -94
- data/lib/imap/backup/serializer/mbox_enumerator.rb +2 -0
- data/lib/imap/backup/serializer.rb +180 -3
- data/lib/imap/backup/setup/account.rb +52 -29
- data/lib/imap/backup/setup/helpers.rb +1 -1
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +1 -1
- data/lib/imap/backup/version.rb +2 -2
- data/lib/imap/backup.rb +0 -1
- data/spec/features/backup_spec.rb +22 -29
- data/spec/features/restore_spec.rb +8 -6
- data/spec/features/support/aruba.rb +12 -3
- data/spec/features/support/backup_directory.rb +0 -4
- data/spec/features/support/email_server.rb +0 -1
- data/spec/spec_helper.rb +4 -9
- data/spec/unit/imap/backup/account/connection_spec.rb +36 -8
- data/spec/unit/imap/backup/account/folder_spec.rb +18 -16
- data/spec/unit/imap/backup/account_spec.rb +246 -0
- data/spec/unit/imap/backup/cli/accounts_spec.rb +12 -1
- data/spec/unit/imap/backup/cli/backup_spec.rb +19 -0
- data/spec/unit/imap/backup/cli/folders_spec.rb +39 -0
- data/spec/unit/imap/backup/cli/local_spec.rb +26 -7
- data/spec/unit/imap/backup/cli/migrate_spec.rb +80 -0
- data/spec/unit/imap/backup/cli/restore_spec.rb +67 -0
- data/spec/unit/imap/backup/cli/setup_spec.rb +17 -0
- data/spec/unit/imap/backup/cli/utils_spec.rb +68 -5
- data/spec/unit/imap/backup/cli_spec.rb +93 -0
- data/spec/unit/imap/backup/client/apple_mail_spec.rb +9 -0
- data/spec/unit/imap/backup/configuration_spec.rb +2 -2
- data/spec/unit/imap/backup/downloader_spec.rb +60 -8
- data/spec/unit/imap/backup/logger_spec.rb +1 -1
- data/spec/unit/imap/backup/migrator_spec.rb +1 -1
- data/spec/unit/imap/backup/sanitizer_spec.rb +42 -0
- data/spec/unit/imap/backup/serializer/directory_spec.rb +37 -0
- data/spec/unit/imap/backup/serializer/imap_spec.rb +218 -0
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -183
- data/spec/unit/imap/backup/serializer_spec.rb +296 -0
- data/spec/unit/imap/backup/setup/account_spec.rb +120 -25
- data/spec/unit/imap/backup/setup/helpers_spec.rb +15 -0
- data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +116 -0
- data/spec/unit/imap/backup/uploader_spec.rb +1 -1
- data/spec/unit/retry_on_error_spec.rb +34 -0
- metadata +44 -37
- data/lib/imap/backup/serializer/mbox_store.rb +0 -217
- data/lib/thunderbird/install.rb +0 -16
- data/lib/thunderbird/local_folder.rb +0 -65
- data/lib/thunderbird/profile.rb +0 -30
- data/lib/thunderbird/profiles.rb +0 -71
- data/lib/thunderbird/subdirectory.rb +0 -93
- data/lib/thunderbird/subdirectory_placeholder.rb +0 -21
- data/lib/thunderbird.rb +0 -14
- data/spec/gather_rspec_coverage.rb +0 -1
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +0 -329
@@ -0,0 +1,296 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe Serializer do
|
3
|
+
subject { described_class.new("path", "folder/sub") }
|
4
|
+
|
5
|
+
let(:directory) { instance_double(Serializer::Directory, ensure_exists: nil) }
|
6
|
+
let(:imap) do
|
7
|
+
instance_double(
|
8
|
+
Serializer::Imap,
|
9
|
+
exist?: true,
|
10
|
+
rename: nil,
|
11
|
+
uid_validity: existing_uid_validity,
|
12
|
+
"uid_validity=": nil
|
13
|
+
)
|
14
|
+
end
|
15
|
+
let(:mbox) do
|
16
|
+
instance_double(
|
17
|
+
Serializer::Mbox,
|
18
|
+
append: nil,
|
19
|
+
exist?: false,
|
20
|
+
length: 1,
|
21
|
+
pathname: "aaa",
|
22
|
+
rename: nil,
|
23
|
+
rewind: nil
|
24
|
+
)
|
25
|
+
end
|
26
|
+
let(:folder_path) { File.expand_path(File.join("path", "folder/sub")) }
|
27
|
+
let(:existing_uid_validity) { nil }
|
28
|
+
let(:enumerator) { instance_double(Serializer::MboxEnumerator) }
|
29
|
+
|
30
|
+
before do
|
31
|
+
allow(Serializer::Directory).to receive(:new) { directory }
|
32
|
+
allow(Serializer::Imap).to receive(:new).with(folder_path) { imap }
|
33
|
+
allow(Serializer::Mbox).to receive(:new) { mbox }
|
34
|
+
allow(Serializer::MboxEnumerator).to receive(:new) { enumerator }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#apply_uid_validity" do
|
38
|
+
let(:imap_test) { instance_double(Serializer::Imap, exist?: imap_test_exists) }
|
39
|
+
let(:imap_test_exists) { false }
|
40
|
+
let(:test_folder_path) do
|
41
|
+
File.expand_path(File.join("path", "folder/sub-#{existing_uid_validity}"))
|
42
|
+
end
|
43
|
+
let(:result) { subject.apply_uid_validity("new") }
|
44
|
+
|
45
|
+
before do
|
46
|
+
allow(Serializer::Imap).to receive(:new).with(test_folder_path) { imap_test }
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when there is no existing uid_validity" do
|
50
|
+
it "sets the metadata file's uid_validity" do
|
51
|
+
result
|
52
|
+
|
53
|
+
expect(imap).to have_received(:"uid_validity=").with("new")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when the new value is the same as the old value" do
|
58
|
+
let(:existing_uid_validity) { "new" }
|
59
|
+
|
60
|
+
it "does nothing" do
|
61
|
+
result
|
62
|
+
|
63
|
+
expect(imap).to_not have_received(:"uid_validity=")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when the new value is different from the old value" do
|
68
|
+
let(:existing_uid_validity) { "existing" }
|
69
|
+
|
70
|
+
it "renames the existing mailbox" do
|
71
|
+
result
|
72
|
+
|
73
|
+
expect(mbox).to have_received(:rename).with(test_folder_path)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "renames the existing metadata file" do
|
77
|
+
result
|
78
|
+
|
79
|
+
expect(imap).to have_received(:rename).with(test_folder_path)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns the new name for the old folder" do
|
83
|
+
expect(result).to eq("folder/sub-existing")
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when the default rename is not possible" do
|
87
|
+
let(:imap_test_exists) { true }
|
88
|
+
let(:imap_test1) { instance_double(Serializer::Imap, exist?: false) }
|
89
|
+
let(:test_folder_path1) do
|
90
|
+
File.expand_path(File.join("path", "folder/sub-#{existing_uid_validity}-1"))
|
91
|
+
end
|
92
|
+
|
93
|
+
before do
|
94
|
+
allow(Serializer::Imap).to receive(:new).with(test_folder_path1) { imap_test1 }
|
95
|
+
end
|
96
|
+
|
97
|
+
it "renames the mailbox, appending a numeral" do
|
98
|
+
result
|
99
|
+
|
100
|
+
expect(mbox).to have_received(:rename).with(test_folder_path1)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "renames the metadata file, appending a numeral" do
|
104
|
+
result
|
105
|
+
|
106
|
+
expect(imap).to have_received(:rename).with(test_folder_path1)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#force_uid_validity" do
|
113
|
+
it "sets the metadata file's uid_validity" do
|
114
|
+
subject.force_uid_validity("new")
|
115
|
+
|
116
|
+
expect(imap).to have_received(:"uid_validity=").with("new")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#append" do
|
121
|
+
let(:existing_uid_validity) { "42" }
|
122
|
+
let(:mboxrd_message) do
|
123
|
+
instance_double(Email::Mboxrd::Message, to_serialized: "serialized")
|
124
|
+
end
|
125
|
+
let(:uid_found) { false }
|
126
|
+
let(:command) { subject.append(99, "Hi") }
|
127
|
+
|
128
|
+
before do
|
129
|
+
allow(imap).to receive(:include?) { uid_found }
|
130
|
+
allow(imap).to receive(:append)
|
131
|
+
allow(Email::Mboxrd::Message).to receive(:new) { mboxrd_message }
|
132
|
+
end
|
133
|
+
|
134
|
+
it "appends the message to the mailbox" do
|
135
|
+
command
|
136
|
+
|
137
|
+
expect(mbox).to have_received(:append).with("serialized")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "appends the UID to the metadata" do
|
141
|
+
command
|
142
|
+
|
143
|
+
expect(imap).to have_received(:append).with(99)
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when appending to the mailbox causes an error" do
|
147
|
+
before do
|
148
|
+
allow(mbox).to receive(:append).and_throw(RuntimeError, "Boom")
|
149
|
+
end
|
150
|
+
|
151
|
+
it "does not fail" do
|
152
|
+
command
|
153
|
+
end
|
154
|
+
|
155
|
+
it "leaves the metadata file unchanged" do
|
156
|
+
command
|
157
|
+
|
158
|
+
expect(imap).to_not have_received(:append)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "when appending to the metadata file causes an error" do
|
163
|
+
before do
|
164
|
+
allow(imap).to receive(:append).and_throw(RuntimeError, "Boom")
|
165
|
+
end
|
166
|
+
|
167
|
+
it "does not fail" do
|
168
|
+
command
|
169
|
+
end
|
170
|
+
|
171
|
+
it "reset the mailbox to the previous position" do
|
172
|
+
command
|
173
|
+
|
174
|
+
expect(mbox).to have_received(:rewind)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when the metadata uid_validity has not been set" do
|
179
|
+
let(:existing_uid_validity) { nil }
|
180
|
+
|
181
|
+
it "fails" do
|
182
|
+
expect { command }.to raise_error(RuntimeError, /without uid_validity/)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context "when the message has already been backed up" do
|
187
|
+
let(:uid_found) { true }
|
188
|
+
|
189
|
+
it "doesn't append to the mailbox file" do
|
190
|
+
command
|
191
|
+
|
192
|
+
expect(mbox).to_not have_received(:append)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "doesn't append to the metadata file" do
|
196
|
+
command
|
197
|
+
|
198
|
+
expect(imap).to_not have_received(:append)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#load" do
|
204
|
+
let(:uid) { 999 }
|
205
|
+
let(:imap_index) { 0 }
|
206
|
+
let(:result) { subject.load(uid) }
|
207
|
+
|
208
|
+
before do
|
209
|
+
allow(imap).to receive(:index).with(999) { imap_index }
|
210
|
+
allow(enumerator).to receive(:each) { ["message"].enum_for(:each) }
|
211
|
+
end
|
212
|
+
|
213
|
+
it "returns an Email::Mboxrd::Message" do
|
214
|
+
expect(result).to be_a(Email::Mboxrd::Message)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "returns the message" do
|
218
|
+
expect(result.supplied_body).to eq("message")
|
219
|
+
end
|
220
|
+
|
221
|
+
context "when the message is not found" do
|
222
|
+
let(:imap_index) { nil }
|
223
|
+
|
224
|
+
it "returns nil" do
|
225
|
+
expect(result).to be nil
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context "when the supplied UID is a string" do
|
230
|
+
let(:uid) { "999" }
|
231
|
+
|
232
|
+
it "works" do
|
233
|
+
expect(result).to be_a(Email::Mboxrd::Message)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
describe "#load_nth" do
|
239
|
+
let(:imap_index) { 0 }
|
240
|
+
let(:result) { subject.load_nth(imap_index) }
|
241
|
+
|
242
|
+
before do
|
243
|
+
allow(enumerator).to receive(:each) { ["message"].enum_for(:each) }
|
244
|
+
end
|
245
|
+
|
246
|
+
it "returns an Email::Mboxrd::Message" do
|
247
|
+
expect(result).to be_a(Email::Mboxrd::Message)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "returns the message" do
|
251
|
+
expect(result.supplied_body).to eq("message")
|
252
|
+
end
|
253
|
+
|
254
|
+
context "when the message is not found" do
|
255
|
+
let(:imap_index) { 1 }
|
256
|
+
|
257
|
+
it "returns nil" do
|
258
|
+
expect(result).to be nil
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe "#each_message" do
|
264
|
+
let(:good_uid) { 999 }
|
265
|
+
|
266
|
+
before do
|
267
|
+
allow(imap).to receive(:index) { nil }
|
268
|
+
allow(imap).to receive(:index).with(good_uid) { 0 }
|
269
|
+
allow(enumerator).to receive(:each) { ["message"].enum_for(:each) }
|
270
|
+
end
|
271
|
+
|
272
|
+
it "yields matching UIDs" do
|
273
|
+
expect { |b| subject.each_message([good_uid], &b) }.
|
274
|
+
to yield_successive_args([good_uid, anything])
|
275
|
+
end
|
276
|
+
|
277
|
+
it "yields matching messages" do
|
278
|
+
messages = subject.each_message([good_uid]).map { |_uid, message| message }
|
279
|
+
expect(messages[0].supplied_body).to eq("message")
|
280
|
+
end
|
281
|
+
|
282
|
+
context "with UIDs that are not present" do
|
283
|
+
it "skips them" do
|
284
|
+
expect { |b| subject.each_message([good_uid, 1234], &b) }.
|
285
|
+
to yield_successive_args([good_uid, anything])
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "when called without a block" do
|
290
|
+
it "returns an Enumerator" do
|
291
|
+
expect(subject.each_message([])).to be_a(Enumerator)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
@@ -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
|
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
|
@@ -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
|