imap-backup 2.1.1 → 2.2.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/README.md +6 -2
  4. data/bin/imap-backup +1 -1
  5. data/lib/email/mboxrd/message.rb +0 -1
  6. data/lib/imap/backup/account/connection.rb +28 -21
  7. data/lib/imap/backup/account/folder.rb +0 -1
  8. data/lib/imap/backup/serializer/mbox_enumerator.rb +1 -1
  9. data/lib/imap/backup/uploader.rb +10 -1
  10. data/lib/imap/backup/version.rb +2 -2
  11. data/spec/features/restore_spec.rb +75 -27
  12. data/spec/features/support/email_server.rb +1 -3
  13. data/spec/features/support/shared/message_fixtures.rb +8 -0
  14. data/spec/unit/email/mboxrd/message_spec.rb +0 -6
  15. data/spec/unit/imap/backup/account/connection_spec.rb +58 -43
  16. data/spec/unit/imap/backup/account/folder_spec.rb +16 -20
  17. data/spec/unit/imap/backup/configuration/account_spec.rb +31 -25
  18. data/spec/unit/imap/backup/configuration/asker_spec.rb +20 -17
  19. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +6 -10
  20. data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +16 -10
  21. data/spec/unit/imap/backup/configuration/list_spec.rb +6 -3
  22. data/spec/unit/imap/backup/configuration/setup_spec.rb +40 -25
  23. data/spec/unit/imap/backup/configuration/store_spec.rb +18 -16
  24. data/spec/unit/imap/backup/downloader_spec.rb +14 -14
  25. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +6 -1
  26. data/spec/unit/imap/backup/serializer/mbox_spec.rb +54 -40
  27. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +49 -31
  28. data/spec/unit/imap/backup/uploader_spec.rb +20 -7
  29. data/spec/unit/imap/backup/utils_spec.rb +8 -9
  30. metadata +2 -2
@@ -20,11 +20,11 @@ describe Imap::Backup::Configuration::Store do
20
20
  allow(File).to receive(:exist?).and_call_original
21
21
  allow(File).to receive(:exist?).with(file_path) { file_exists }
22
22
  allow(Imap::Backup::Utils).
23
- to receive(:stat).with(directory).and_return(0o700)
23
+ to receive(:stat).with(directory) { 0o700 }
24
24
  allow(Imap::Backup::Utils).
25
- to receive(:stat).with(file_path).and_return(0o600)
26
- allow(Imap::Backup::Utils).to receive(:check_permissions).and_return(nil)
27
- allow(File).to receive(:read).with(file_path).and_return(configuration)
25
+ to receive(:stat).with(file_path) { 0o600 }
26
+ allow(Imap::Backup::Utils).to receive(:check_permissions) { nil }
27
+ allow(File).to receive(:read).with(file_path) { configuration }
28
28
  end
29
29
 
30
30
  describe ".exist?" do
@@ -140,19 +140,19 @@ describe Imap::Backup::Configuration::Store do
140
140
  allow(FileUtils).to receive(:mkdir)
141
141
  allow(FileUtils).to receive(:chmod)
142
142
  allow(File).to receive(:open).with(file_path, "w").and_yield(file)
143
- allow(JSON).to receive(:pretty_generate).and_return("JSON output")
143
+ allow(JSON).to receive(:pretty_generate) { "JSON output" }
144
144
  end
145
145
 
146
146
  it "creates the config directory" do
147
- subject.save
147
+ expect(FileUtils).to receive(:mkdir).with(directory)
148
148
 
149
- expect(FileUtils).to have_received(:mkdir).with(directory)
149
+ subject.save
150
150
  end
151
151
 
152
152
  it "saves the configuration" do
153
- subject.save
153
+ expect(file).to receive(:write).with("JSON output")
154
154
 
155
- expect(file).to have_received(:write).with("JSON output")
155
+ subject.save
156
156
  end
157
157
 
158
158
  context "when accounts are modified" do
@@ -164,7 +164,9 @@ describe Imap::Backup::Configuration::Store do
164
164
  expected = Marshal.load(Marshal.dump(data))
165
165
  expected[:accounts][0].delete(:modified)
166
166
 
167
- expect(JSON).to have_received(:pretty_generate).with(expected)
167
+ expect(JSON).to receive(:pretty_generate).with(expected)
168
+
169
+ subject.save
168
170
  end
169
171
  end
170
172
 
@@ -176,21 +178,21 @@ describe Imap::Backup::Configuration::Store do
176
178
  ]
177
179
  end
178
180
 
179
- before { subject.save }
180
-
181
181
  it "does not save them" do
182
182
  expected = Marshal.load(Marshal.dump(data))
183
183
  expected[:accounts].pop
184
184
 
185
- expect(JSON).to have_received(:pretty_generate).with(expected)
185
+ expect(JSON).to receive(:pretty_generate).with(expected)
186
+
187
+ subject.save
186
188
  end
187
189
  end
188
190
 
189
191
  context "when file permissions are too open" do
190
- before { subject.save }
191
-
192
192
  it "sets them to 0600" do
193
- expect(FileUtils).to have_received(:chmod).with(0o600, file_path)
193
+ expect(FileUtils).to receive(:chmod).with(0o600, file_path)
194
+
195
+ subject.save
194
196
  end
195
197
  end
196
198
 
@@ -7,37 +7,37 @@ describe Imap::Backup::Downloader do
7
7
  instance_double(
8
8
  Imap::Backup::Account::Folder,
9
9
  fetch: message,
10
- name: "folder"
10
+ name: "folder",
11
+ uids: folder_uids
11
12
  )
12
13
  end
13
14
  let(:folder_uids) { %w(111 222 333) }
14
15
  let(:serializer) do
15
- instance_double(Imap::Backup::Serializer::Mbox, save: nil)
16
- end
17
- let(:serializer_uids) { ["222"] }
18
-
19
- before do
20
- allow(folder).to receive(:uids).and_return(folder_uids)
21
- allow(serializer).to receive(:uids).and_return(serializer_uids)
22
- allow(folder).to receive(:fetch).with("333").and_return(nil)
23
- subject.run
16
+ instance_double(Imap::Backup::Serializer::Mbox, save: nil, uids: ["222"])
24
17
  end
25
18
 
26
19
  context "with fetched messages" do
27
- it "are saved" do
28
- expect(serializer).to have_received(:save).with("111", message)
20
+ specify "are saved" do
21
+ expect(serializer).to receive(:save).with("111", message)
22
+
23
+ subject.run
29
24
  end
30
25
  end
31
26
 
32
27
  context "with messages which are already present" do
33
28
  specify "are skipped" do
34
- expect(serializer).to_not have_received(:save).with("222", anything)
29
+ expect(serializer).to_not receive(:save).with("222", anything)
30
+
31
+ subject.run
35
32
  end
36
33
  end
37
34
 
38
35
  context "with failed fetches" do
39
36
  specify "are skipped" do
40
- expect(serializer).to_not have_received(:save).with("333", anything)
37
+ allow(folder).to receive(:fetch).with("333") { nil }
38
+ expect(serializer).to_not receive(:save).with("333", anything)
39
+
40
+ subject.run
41
41
  end
42
42
  end
43
43
  end
@@ -21,11 +21,16 @@ describe Imap::Backup::Serializer::MboxEnumerator do
21
21
 
22
22
  before do
23
23
  allow(File).to receive(:open).and_call_original
24
- allow(File).to receive(:open).with(mbox_pathname).and_yield(mbox_file)
24
+ allow(File).to receive(:open).with(mbox_pathname, "rb").and_yield(mbox_file)
25
25
  allow(mbox_file).to receive(:gets).and_return(*lines)
26
26
  end
27
27
 
28
28
  describe "#each" do
29
+ it "reads files as binary" do
30
+ expect(File).to receive(:open).with(mbox_pathname, "rb")
31
+ subject.each {}
32
+ end
33
+
29
34
  it "yields messages" do
30
35
  expect { |b| subject.each(&b) }.
31
36
  to yield_successive_args(message1.join, message2.join)
@@ -28,8 +28,6 @@ describe Imap::Backup::Serializer::Mbox do
28
28
  end
29
29
 
30
30
  describe "folder path" do
31
- before { subject.uids }
32
-
33
31
  context "when it has multiple elements" do
34
32
  let(:imap_folder) { "folder/path" }
35
33
 
@@ -37,8 +35,10 @@ describe Imap::Backup::Serializer::Mbox do
37
35
  let(:dir_exists) { false }
38
36
 
39
37
  it "is created" do
40
- expect(Imap::Backup::Utils).to have_received(:make_folder).
38
+ expect(Imap::Backup::Utils).to receive(:make_folder).
41
39
  with(base_path, File.dirname(imap_folder), 0o700)
40
+
41
+ subject.uids
42
42
  end
43
43
  end
44
44
  end
@@ -48,57 +48,66 @@ describe Imap::Backup::Serializer::Mbox do
48
48
 
49
49
  it "corrects them" do
50
50
  path = File.expand_path(File.join(base_path, File.dirname(imap_folder)))
51
- expect(FileUtils).to have_received(:chmod).with(0o700, path)
51
+ expect(FileUtils).to receive(:chmod).with(0o700, path)
52
+
53
+ subject.uids
52
54
  end
53
55
  end
54
56
 
55
57
  context "when permissons are correct" do
56
58
  it "does nothing" do
57
- expect(FileUtils).to_not have_received(:chmod)
59
+ expect(FileUtils).to_not receive(:chmod)
60
+
61
+ subject.uids
58
62
  end
59
63
  end
60
64
 
61
65
  context "when it exists" do
62
66
  it "is not created" do
63
- expect(Imap::Backup::Utils).to_not have_received(:make_folder).
67
+ expect(Imap::Backup::Utils).to_not receive(:make_folder).
64
68
  with(base_path, File.dirname(imap_folder), 0o700)
69
+
70
+ subject.uids
65
71
  end
66
72
  end
67
73
  end
68
74
 
69
75
  describe "#apply_uid_validity" do
70
- let(:result) { subject.apply_uid_validity("aaa") }
71
-
72
76
  context "when the existing uid validity is unset" do
73
- let!(:result) { super() }
74
-
75
77
  it "sets uid validity" do
76
- expect(store).to have_received(:uid_validity=).with("aaa")
78
+ expect(store).to receive(:uid_validity=).with("aaa")
79
+
80
+ subject.apply_uid_validity("aaa")
77
81
  end
78
82
 
79
83
  it "does not rename the store" do
80
- expect(store).to_not have_received(:rename)
84
+ expect(store).to_not receive(:rename)
85
+
86
+ subject.apply_uid_validity("aaa")
81
87
  end
82
88
 
83
89
  it "returns nil" do
84
- expect(result).to be_nil
90
+ expect(subject.apply_uid_validity("aaa")).to be_nil
85
91
  end
86
92
  end
87
93
 
88
94
  context "when the uid validity is unchanged" do
89
- let!(:result) { super() }
90
95
  let(:existing_uid_validity) { "aaa" }
91
96
 
92
97
  it "does not set uid validity" do
93
- expect(store).to_not have_received(:uid_validity=)
98
+ expect(store).to_not receive(:uid_validity=)
99
+
100
+ subject.apply_uid_validity("aaa")
94
101
  end
95
102
 
96
103
  it "does not rename the store" do
97
- expect(store).to_not have_received(:rename)
104
+ expect(store).to_not receive(:rename)
105
+
106
+ subject.apply_uid_validity("aaa")
98
107
  end
99
108
 
100
109
  it "returns nil" do
101
- expect(result).to be_nil
110
+ expect(subject.apply_uid_validity("aaa")).to be_nil
102
111
  end
103
112
  end
104
113
 
@@ -113,20 +122,23 @@ describe Imap::Backup::Serializer::Mbox do
113
122
  allow(Imap::Backup::Serializer::MboxStore).
114
123
  to receive(:new).with(anything, /bbb/) { existing_store }
115
124
  allow(existing_store).to receive(:exist?).and_return(exists, false)
116
- result
117
125
  end
118
126
 
119
127
  it "sets uid validity" do
120
- expect(store).to have_received(:uid_validity=).with("aaa")
128
+ expect(store).to receive(:uid_validity=).with("aaa")
129
+
130
+ subject.apply_uid_validity("aaa")
121
131
  end
122
132
 
123
133
  context "when adding the uid validity does not cause a name clash" do
124
134
  it "renames the store, adding the existing uid validity" do
125
- expect(store).to have_received(:rename).with("folder.bbb")
135
+ expect(store).to receive(:rename).with("folder.bbb")
136
+
137
+ subject.apply_uid_validity("aaa")
126
138
  end
127
139
 
128
140
  it "returns the new name" do
129
- expect(result).to eq("folder.bbb")
141
+ expect(subject.apply_uid_validity("aaa")).to eq("folder.bbb")
130
142
  end
131
143
  end
132
144
 
@@ -134,67 +146,69 @@ describe Imap::Backup::Serializer::Mbox do
134
146
  let(:exists) { true }
135
147
 
136
148
  it "renames the store, adding the existing uid validity and a digit" do
137
- expect(store).to have_received(:rename).with("folder.bbb.1")
149
+ expect(store).to receive(:rename).with("folder.bbb.1")
150
+
151
+ subject.apply_uid_validity("aaa")
138
152
  end
139
153
 
140
154
  it "returns the new name" do
141
- expect(result).to eq("folder.bbb.1")
155
+ expect(subject.apply_uid_validity("aaa")).to eq("folder.bbb.1")
142
156
  end
143
157
  end
144
158
  end
145
159
  end
146
160
 
147
161
  describe "#force_uid_validity" do
148
- before { subject.force_uid_validity("66") }
149
-
150
162
  it "sets the uid_validity" do
151
- expect(store).to have_received(:uid_validity=).with("66")
163
+ expect(store).to receive(:uid_validity=).with("66")
164
+
165
+ subject.force_uid_validity("66")
152
166
  end
153
167
  end
154
168
 
155
169
  describe "#uids" do
156
170
  it "calls the store" do
157
- subject.uids
171
+ expect(store).to receive(:uids)
158
172
 
159
- expect(store).to have_received(:uids)
173
+ subject.uids
160
174
  end
161
175
  end
162
176
 
163
177
  describe "#load" do
164
- let(:result) { subject.load("66") }
165
-
166
178
  before { allow(store).to receive(:load).with("66") { "xxx" } }
167
179
 
168
180
  it "returns the value loaded by the store" do
169
- expect(result).to eq("xxx")
181
+ expect(subject.load("66")).to eq("xxx")
170
182
  end
171
183
  end
172
184
 
173
185
  describe "#save" do
174
- before { subject.save("foo", "bar") }
175
-
176
186
  it "calls the store" do
177
- expect(store).to have_received(:add).with("foo", "bar")
187
+ expect(store).to receive(:add).with("foo", "bar")
188
+
189
+ subject.save("foo", "bar")
178
190
  end
179
191
  end
180
192
 
181
193
  describe "#rename" do
182
- before { subject.rename("foo") }
183
-
184
194
  it "calls the store" do
185
- expect(store).to have_received(:rename).with("foo")
195
+ expect(store).to receive(:rename).with("foo")
196
+
197
+ subject.rename("foo")
186
198
  end
187
199
 
188
200
  it "updates the folder name" do
201
+ subject.rename("foo")
202
+
189
203
  expect(subject.folder).to eq("foo")
190
204
  end
191
205
  end
192
206
 
193
207
  describe "#update_uid" do
194
- before { subject.update_uid("foo", "bar") }
195
-
196
208
  it "calls the store" do
197
- expect(store).to have_received(:update_uid).with("foo", "bar")
209
+ expect(store).to receive(:update_uid).with("foo", "bar")
210
+
211
+ subject.update_uid("foo", "bar")
198
212
  end
199
213
  end
200
214
  end
@@ -51,14 +51,16 @@ describe Imap::Backup::Serializer::MboxStore do
51
51
  }.to_json
52
52
  end
53
53
 
54
- before { subject.uid_validity = new_uid_validity }
55
-
56
54
  it "sets uid_validity" do
55
+ subject.uid_validity = new_uid_validity
56
+
57
57
  expect(subject.uid_validity).to eq(new_uid_validity)
58
58
  end
59
59
 
60
60
  it "writes the imap file" do
61
- expect(imap_file).to have_received(:write).with(updated_imap_content)
61
+ expect(imap_file).to receive(:write).with(updated_imap_content)
62
+
63
+ subject.uid_validity = new_uid_validity
62
64
  end
63
65
  end
64
66
 
@@ -80,22 +82,26 @@ describe Imap::Backup::Serializer::MboxStore do
80
82
  allow(JSON).to receive(:parse).and_raise(JSON::ParserError)
81
83
  end
82
84
 
83
- let!(:result) { subject.uids }
84
-
85
85
  it "returns an empty Array" do
86
- expect(result).to eq([])
86
+ expect(subject.uids).to eq([])
87
87
  end
88
88
 
89
89
  it "deletes the imap file" do
90
- expect(File).to have_received(:unlink).with(imap_pathname)
90
+ expect(File).to receive(:unlink).with(imap_pathname)
91
+
92
+ subject.uids
91
93
  end
92
94
 
93
95
  it "deletes the mbox file" do
94
- expect(File).to have_received(:unlink).with(mbox_pathname)
96
+ expect(File).to receive(:unlink).with(mbox_pathname)
97
+
98
+ subject.uids
95
99
  end
96
100
 
97
101
  it "writes a blank mbox file" do
98
- expect(mbox_file).to have_received(:write).with("")
102
+ expect(mbox_file).to receive(:write).with("")
103
+
104
+ subject.uids
99
105
  end
100
106
  end
101
107
 
@@ -126,28 +132,29 @@ describe Imap::Backup::Serializer::MboxStore do
126
132
  end
127
133
 
128
134
  before do
129
- allow(Email::Mboxrd::Message).to receive(:new).and_return(message)
135
+ allow(Email::Mboxrd::Message).to receive(:new) { message }
130
136
  allow(File).to receive(:open).with(mbox_pathname, "ab") { mbox_file }
131
137
  end
132
138
 
133
139
  it "saves the message to the mbox" do
134
- subject.add(message_uid, "The\nemail\n")
140
+ expect(mbox_file).to receive(:write).with(mbox_formatted_message)
135
141
 
136
- expect(mbox_file).to have_received(:write).with(mbox_formatted_message)
142
+ subject.add(message_uid, "The\nemail\n")
137
143
  end
138
144
 
139
145
  it "saves the uid to the imap file" do
140
- subject.add(message_uid, "The\nemail\n")
146
+ expect(imap_file).to receive(:write).with(updated_imap_content)
141
147
 
142
- expect(imap_file).to have_received(:write).with(updated_imap_content)
148
+ subject.add(message_uid, "The\nemail\n")
143
149
  end
144
150
 
145
151
  context "when the message is already downloaded" do
146
152
  let(:uids) { [999] }
147
153
 
148
154
  it "skips the message" do
155
+ expect(mbox_file).to_not receive(:write)
156
+
149
157
  subject.add(message_uid, "The\nemail\n")
150
- expect(mbox_file).to_not have_received(:write)
151
158
  end
152
159
  end
153
160
 
@@ -157,8 +164,9 @@ describe Imap::Backup::Serializer::MboxStore do
157
164
  end
158
165
 
159
166
  it "skips the message" do
167
+ expect(mbox_file).to_not receive(:write)
168
+
160
169
  subject.add(message_uid, "The\nemail\n")
161
- expect(mbox_file).to_not have_received(:write)
162
170
  end
163
171
 
164
172
  it "does not fail" do
@@ -171,7 +179,6 @@ describe Imap::Backup::Serializer::MboxStore do
171
179
 
172
180
  describe "#load" do
173
181
  let(:uid) { "1" }
174
- let(:result) { subject.load(uid) }
175
182
  let(:enumerator) do
176
183
  instance_double(Imap::Backup::Serializer::MboxEnumerator)
177
184
  end
@@ -189,14 +196,14 @@ describe Imap::Backup::Serializer::MboxStore do
189
196
  end
190
197
 
191
198
  it "returns the message" do
192
- expect(result.supplied_body).to eq("ciao")
199
+ expect(subject.load(uid).supplied_body).to eq("ciao")
193
200
  end
194
201
 
195
202
  context "when the UID is unknown" do
196
203
  let(:uid) { "99" }
197
204
 
198
205
  it "returns nil" do
199
- expect(result).to be_nil
206
+ expect(subject.load(uid)).to be_nil
200
207
  end
201
208
  end
202
209
  end
@@ -211,34 +218,40 @@ describe Imap::Backup::Serializer::MboxStore do
211
218
  }.to_json
212
219
  end
213
220
 
214
- before { subject.update_uid(old_uid, "999") }
215
-
216
221
  it "updates the stored UID" do
217
- expect(imap_file).to have_received(:write).with(updated_imap_content)
222
+ expect(imap_file).to receive(:write).with(updated_imap_content)
223
+
224
+ subject.update_uid(old_uid, "999")
218
225
  end
219
226
 
220
227
  context "when the UID is unknown" do
221
228
  let(:old_uid) { "42" }
222
229
 
223
230
  it "does nothing" do
224
- expect(imap_file).to_not have_received(:write)
231
+ expect(imap_file).to_not receive(:write)
232
+
233
+ subject.update_uid(old_uid, "999")
225
234
  end
226
235
  end
227
236
  end
228
237
 
229
238
  describe "#reset" do
230
- before { subject.reset }
231
-
232
239
  it "deletes the imap file" do
233
- expect(File).to have_received(:unlink).with(imap_pathname)
240
+ expect(File).to receive(:unlink).with(imap_pathname)
241
+
242
+ subject.reset
234
243
  end
235
244
 
236
245
  it "deletes the mbox file" do
237
- expect(File).to have_received(:unlink).with(mbox_pathname)
246
+ expect(File).to receive(:unlink).with(mbox_pathname)
247
+
248
+ subject.reset
238
249
  end
239
250
 
240
251
  it "writes a blank mbox file" do
241
- expect(mbox_file).to have_received(:write).with("")
252
+ expect(mbox_file).to receive(:write).with("")
253
+
254
+ subject.reset
242
255
  end
243
256
  end
244
257
 
@@ -252,18 +265,23 @@ describe Imap::Backup::Serializer::MboxStore do
252
265
  allow(File).to receive(:rename).and_call_original
253
266
  allow(File).to receive(:rename).with(imap_pathname, new_imap_name)
254
267
  allow(File).to receive(:rename).with(mbox_pathname, new_mbox_name)
255
- subject.rename(new_name)
256
268
  end
257
269
 
258
270
  it "renames the imap file" do
259
- expect(File).to have_received(:rename).with(imap_pathname, new_imap_name)
271
+ expect(File).to receive(:rename).with(imap_pathname, new_imap_name)
272
+
273
+ subject.rename(new_name)
260
274
  end
261
275
 
262
276
  it "renames the mbox file" do
263
- expect(File).to have_received(:rename).with(mbox_pathname, new_mbox_name)
277
+ expect(File).to receive(:rename).with(mbox_pathname, new_mbox_name)
278
+
279
+ subject.rename(new_name)
264
280
  end
265
281
 
266
282
  it "updates the folder name" do
283
+ subject.rename(new_name)
284
+
267
285
  expect(subject.folder).to eq(new_name)
268
286
  end
269
287
  end