imap-backup 2.0.0.rc1 → 2.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +3 -5
  3. data/docs/docker-imap.md +18 -0
  4. data/imap-backup.gemspec +1 -0
  5. data/lib/email/mboxrd/message.rb +2 -0
  6. data/lib/imap/backup/account/folder.rb +1 -1
  7. data/lib/imap/backup/configuration/store.rb +1 -1
  8. data/lib/imap/backup/version.rb +1 -1
  9. data/spec/fixtures/connection.yml +3 -2
  10. data/spec/unit/email/mboxrd/message_spec.rb +34 -2
  11. data/spec/unit/{account → imap/backup/account}/connection_spec.rb +122 -13
  12. data/spec/unit/imap/backup/account/folder_spec.rb +168 -0
  13. data/spec/unit/{configuration → imap/backup/configuration}/account_spec.rb +0 -0
  14. data/spec/unit/{configuration → imap/backup/configuration}/asker_spec.rb +0 -0
  15. data/spec/unit/{configuration → imap/backup/configuration}/connection_tester_spec.rb +0 -0
  16. data/spec/unit/{configuration → imap/backup/configuration}/folder_chooser_spec.rb +0 -0
  17. data/spec/unit/{configuration → imap/backup/configuration}/list_spec.rb +0 -0
  18. data/spec/unit/{configuration → imap/backup/configuration}/setup_spec.rb +0 -0
  19. data/spec/unit/{configuration → imap/backup/configuration}/store_spec.rb +1 -0
  20. data/spec/unit/{downloader_spec.rb → imap/backup/downloader_spec.rb} +0 -0
  21. data/spec/unit/imap/backup/serializer/mbox_spec.rb +164 -0
  22. data/spec/unit/{serializer → imap/backup/serializer}/mbox_store_spec.rb +0 -0
  23. data/spec/unit/imap/backup/uploader_spec.rb +40 -0
  24. data/spec/unit/{utils_spec.rb → imap/backup/utils_spec.rb} +0 -0
  25. metadata +33 -30
  26. data/spec/unit/account/folder_spec.rb +0 -75
  27. data/spec/unit/serializer/mbox_spec.rb +0 -82
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 3f897dc0bac78683c9cae100718a03cf934adc0348866a4688ce5d2196bbdcfa
4
- data.tar.gz: 164bc47c5acb9e448b3c61e6d58d18c3f48cd03fd6a2cf236682e5f5abd11232
2
+ SHA1:
3
+ metadata.gz: d5590ab1d097eb650e1af98d2b913fc6f2b95287
4
+ data.tar.gz: 7ff2e0eb4c6dde13300166e9e18b512c5e01c6fa
5
5
  SHA512:
6
- metadata.gz: 1ef7c3cea9a72c2fec7f87491c80eebc1aeeeca50c207fec4c713259a2de34c986a7a2457aed7cc20b36e4173f52fd9d30ac0340bca64600d23792d00433ac3f
7
- data.tar.gz: 787c00650b5dd72258e895f51457599e0c20bd4d5897c5ec8c812fd4a54ce96796538b8c88cc689a02365f8fed5122237724a566aae9849dd5b807804a3a644f
6
+ metadata.gz: f372713523f16c4e281c7443c3f8f5bca116c392b93e3bea2df1f2ea823675f1330ef44d5b5f50ba6f21a991d972a1acc39483a36784fb77f0608b484cd24979
7
+ data.tar.gz: 99bfc3b3a4c2366a76bf0f94e019a37b564b8e06b54213746fa520e51af8d89cb2795e3dcc2eafb69fae0b59d2c4060bf505859591507719bae553d66cf8f04f
@@ -1,10 +1,8 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.10
4
- - 2.2.10
5
- - 2.3.7
6
- - 2.4.4
7
- - 2.5.1
3
+ - 2.3
4
+ - 2.4
5
+ - 2.5
8
6
  branches:
9
7
  only:
10
8
  - master
@@ -0,0 +1,18 @@
1
+ # Access Docker imap server
2
+
3
+ ```ruby
4
+ require "net/imap"
5
+
6
+ imap = Net::IMAP.new("localhost", {port: 8993, ssl: {verify_mode: 0}})
7
+ username = "address@example.org"
8
+ imap.login(username, "pass")
9
+
10
+ message = "From: #{username}\nSubject: Some Subject\n\nHello!\n"
11
+ response = imap.append("INBOX", message, nil, nil)
12
+
13
+ imap.examine("INBOX")
14
+ uids = imap.uid_search(["ALL"]).sort
15
+
16
+ REQUESTED_ATTRIBUTES = ["RFC822", "FLAGS", "INTERNALDATE"].freeze
17
+ fetch_data_items = imap.uid_fetch(uids, REQUESTED_ATTRIBUTES)
18
+ ```
@@ -13,6 +13,7 @@ Gem::Specification.new do |gem|
13
13
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
14
  gem.test_files = gem.files.grep(%r{^spec/})
15
15
  gem.require_paths = ["lib"]
16
+ gem.required_ruby_version = [">= 2.2.0"]
16
17
  gem.version = Imap::Backup::VERSION
17
18
 
18
19
  gem.post_install_message = <<-MESSAGE.gsub(/^\s{4}/m, "")
@@ -27,6 +27,8 @@ module Email::Mboxrd
27
27
 
28
28
  def date
29
29
  parsed.date
30
+ rescue
31
+ nil
30
32
  end
31
33
 
32
34
  def imap_body
@@ -67,7 +67,7 @@ module Imap::Backup
67
67
 
68
68
  def append(message)
69
69
  body = message.imap_body
70
- date = message.date.to_time
70
+ date = message.date&.to_time
71
71
  response = imap.append(name, body, nil, date)
72
72
  extract_uid(response)
73
73
  end
@@ -76,7 +76,7 @@ module Imap::Backup
76
76
  if !File.directory?(path)
77
77
  FileUtils.mkdir path
78
78
  end
79
- if Utils::stat(path) != 0o700
79
+ if Utils::mode(path) != 0o700
80
80
  FileUtils.chmod 0o700, path
81
81
  end
82
82
  end
@@ -4,6 +4,6 @@ module Imap::Backup
4
4
  MAJOR = 2
5
5
  MINOR = 0
6
6
  REVISION = 0
7
- PRE = "rc1"
7
+ PRE = "rc2"
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
9
9
  end
@@ -2,5 +2,6 @@
2
2
  :username: 'address@example.org'
3
3
  :password: 'pass'
4
4
  :connection_options:
5
- :port: 8143
6
- :ssl: false
5
+ :port: 8993
6
+ :ssl:
7
+ :verify_mode: 0
@@ -30,6 +30,21 @@ describe Email::Mboxrd::Message do
30
30
  let(:cloned_message_body) do
31
31
  "Foo\nBar\nFrom at the beginning of the line\n>>From quoted"
32
32
  end
33
+ let(:msg_good) do
34
+ %Q|Delivered-To: you@example.com
35
+ From: Foo <foo@example.com>
36
+ To: FirstName LastName <you@example.com>
37
+ Date: #{date.rfc822}
38
+ Subject: Re: no subject|
39
+ end
40
+
41
+ let(:msg_bad_date) do
42
+ %Q|Delivered-To: you@example.com
43
+ From: Foo <foo@example.com>
44
+ To: FirstName LastName <you@example.com>
45
+ Date: Mon,5 May 2014 08:97:99 GMT
46
+ Subject: Re: no subject|
47
+ end
33
48
 
34
49
  subject { described_class.new(message_body) }
35
50
 
@@ -90,14 +105,15 @@ describe Email::Mboxrd::Message do
90
105
  allow(Mail).to receive(:new).and_call_original
91
106
  end
92
107
 
93
- context "when original message 'from' is nil" do
108
+ context "when original message 'from' is missing" do
94
109
  let(:message_body) { msg_no_from }
110
+
95
111
  it "'from' is empty string" do
96
112
  expect(subject.to_serialized).to start_with("From \n")
97
113
  end
98
114
  end
99
115
 
100
- context "when original message 'from' is a string but not an address" do
116
+ context "when original message 'from' is a string but not well-formed address" do
101
117
  let(:message_body) { msg_bad_from }
102
118
 
103
119
  it "'from' is empty string" do
@@ -122,4 +138,20 @@ describe Email::Mboxrd::Message do
122
138
  end
123
139
  end
124
140
  end
141
+
142
+ context "#date" do
143
+ let(:message_body) { msg_good }
144
+
145
+ it "returns the date" do
146
+ expect(subject.date).to eq(date)
147
+ end
148
+
149
+ context "with incorrect minutes and seconds" do
150
+ let(:message_body) { msg_bad_date }
151
+
152
+ it "returns nil" do
153
+ expect(subject.date).to be_nil
154
+ end
155
+ end
156
+ end
125
157
  end
@@ -28,9 +28,21 @@ describe Imap::Backup::Account::Connection do
28
28
  instance_double(Net::IMAP::MailboxList, name: root_name)
29
29
  end
30
30
  let(:root_name) { "foo" }
31
+ let(:serializer) do
32
+ instance_double(
33
+ Imap::Backup::Serializer::Mbox,
34
+ folder: serialized_folder,
35
+ force_uid_validity: nil,
36
+ set_uid_validity: new_uid_validity,
37
+ uids: [local_uid]
38
+ )
39
+ end
40
+ let(:serialized_folder) { nil }
41
+ let(:new_uid_validity) { nil }
42
+ let(:local_uid) { "local_uid" }
31
43
 
32
44
  before do
33
- allow(Net::IMAP).to receive(:new).and_return(imap)
45
+ allow(Net::IMAP).to receive(:new) { imap }
34
46
  allow(imap).to receive(:list).with("", "") { [root_info] }
35
47
  allow(imap).to receive(:list).with(root_name, "*") { imap_folders }
36
48
  allow(Imap::Backup::Utils).to receive(:make_folder)
@@ -89,10 +101,6 @@ describe Imap::Backup::Account::Connection do
89
101
  let(:folder) do
90
102
  instance_double(Imap::Backup::Account::Folder, uids: [remote_uid])
91
103
  end
92
- let(:local_uid) { "local_uid" }
93
- let(:serializer) do
94
- instance_double(Imap::Backup::Serializer::Mbox, uids: [local_uid])
95
- end
96
104
  let(:remote_uid) { "remote_uid" }
97
105
 
98
106
  before do
@@ -122,12 +130,6 @@ describe Imap::Backup::Account::Connection do
122
130
  )
123
131
  end
124
132
  let(:uid_validity) { 123 }
125
- let(:serializer) do
126
- instance_double(
127
- Imap::Backup::Serializer::Mbox,
128
- set_uid_validity: nil
129
- )
130
- end
131
133
  let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
132
134
 
133
135
  before do
@@ -193,13 +195,120 @@ describe Imap::Backup::Account::Connection do
193
195
  end
194
196
  end
195
197
 
198
+ context "#restore" do
199
+ let(:folder) do
200
+ instance_double(
201
+ Imap::Backup::Account::Folder,
202
+ create: nil,
203
+ exist?: exists,
204
+ name: "my_folder",
205
+ uid_validity: uid_validity
206
+ )
207
+ end
208
+ let(:exists) { true }
209
+ let(:uid_validity) { 123 }
210
+ let(:serialized_folder) { "old name" }
211
+ let(:uploader) do
212
+ instance_double(Imap::Backup::Uploader, run: false)
213
+ end
214
+ let(:updated_uploader) do
215
+ instance_double(Imap::Backup::Uploader, run: false)
216
+ end
217
+ let(:updated_folder) do
218
+ instance_double(
219
+ Imap::Backup::Account::Folder,
220
+ create: nil,
221
+ uid_validity: "new uid validity"
222
+ )
223
+ end
224
+ let(:updated_serializer) do
225
+ instance_double(
226
+ Imap::Backup::Serializer::Mbox, force_uid_validity: nil
227
+ )
228
+ end
229
+
230
+ before do
231
+ allow(Imap::Backup::Account::Folder).to receive(:new).
232
+ with(subject, "my_folder") { folder }
233
+ allow(Imap::Backup::Serializer::Mbox).to receive(:new).
234
+ with(anything, "my_folder") { serializer }
235
+ allow(Imap::Backup::Account::Folder).to receive(:new).
236
+ with(subject, "new name") { updated_folder }
237
+ allow(Imap::Backup::Serializer::Mbox).to receive(:new).
238
+ with(anything, "new name") { updated_serializer }
239
+ allow(Imap::Backup::Uploader).to receive(:new).
240
+ with(folder, serializer) { uploader }
241
+ allow(Imap::Backup::Uploader).to receive(:new).
242
+ with(updated_folder, updated_serializer) { updated_uploader }
243
+ allow(Pathname).to receive(:glob).
244
+ and_yield(Pathname.new(File.join(local_path, "my_folder.imap")))
245
+ subject.restore
246
+ end
247
+
248
+ it "sets local uid validity" do
249
+ expect(serializer).to have_received(:set_uid_validity).with(uid_validity)
250
+ end
251
+
252
+ context "when folders exist" do
253
+ context "when the local folder is renamed" do
254
+ let(:new_uid_validity) { "new name" }
255
+
256
+ it "creates the new folder" do
257
+ expect(updated_folder).to have_received(:create)
258
+ end
259
+
260
+ it "sets the renamed folder's uid validity" do
261
+ expect(updated_serializer).
262
+ to have_received(:force_uid_validity).with("new uid validity")
263
+ end
264
+
265
+ it "creates the uploader with updated folder and serializer" do
266
+ expect(updated_uploader).to have_received(:run)
267
+ end
268
+ end
269
+
270
+ context "when the local folder is not renamed" do
271
+ it "runs the uploader" do
272
+ expect(uploader).to have_received(:run)
273
+ end
274
+ end
275
+ end
276
+
277
+ context "when folders don't exist" do
278
+ let(:exists) { false }
279
+
280
+ it "creates the folder" do
281
+ expect(folder).to have_received(:create)
282
+ end
283
+
284
+ it "sets local uid validity" do
285
+ end
286
+
287
+ it "runs the uploader" do
288
+ expect(uploader).to have_received(:run)
289
+ end
290
+ end
291
+ end
292
+
293
+ context "#reconnect" do
294
+ before { subject.reconnect }
295
+
296
+ it "disconnects from the server" do
297
+ expect(imap).to have_received(:disconnect)
298
+ end
299
+
300
+ it "causes reconnection on future access" do
301
+ allow(Net::IMAP).to receive(:new) { imap }
302
+ subject.imap
303
+ expect(Net::IMAP).to have_received(:new).twice
304
+ end
305
+ end
306
+
196
307
  context "#disconnect" do
197
308
  before { subject.disconnect }
198
309
 
199
310
  it "disconnects from the server" do
200
311
  expect(imap).to have_received(:disconnect)
201
312
  end
202
-
203
- include_examples "connects to IMAP"
204
313
  end
205
314
  end
@@ -0,0 +1,168 @@
1
+ require "spec_helper"
2
+
3
+ describe Imap::Backup::Account::Folder do
4
+ let(:imap) do
5
+ instance_double(
6
+ Net::IMAP,
7
+ append: append_response,
8
+ create: nil,
9
+ examine: nil,
10
+ responses: responses
11
+ )
12
+ end
13
+ let(:connection) { double("Imap::Backup::Account::Connection", imap: imap) }
14
+ let(:missing_mailbox_data) do
15
+ double("Data", text: "Unknown Mailbox: my_folder")
16
+ end
17
+ let(:missing_mailbox_response) do
18
+ double("Response", data: missing_mailbox_data)
19
+ end
20
+ let(:missing_mailbox_error) do
21
+ Net::IMAP::NoResponseError.new(missing_mailbox_response)
22
+ end
23
+ let(:responses) { [] }
24
+ let(:append_response) { nil }
25
+
26
+ subject { described_class.new(connection, "my_folder") }
27
+
28
+ context "#uids" do
29
+ let(:uids) { [5678, 123] }
30
+
31
+ before { allow(imap).to receive(:uid_search).and_return(uids) }
32
+
33
+ it "lists available messages" do
34
+ expect(subject.uids).to eq(uids.reverse)
35
+ end
36
+
37
+ context "with missing mailboxes" do
38
+ before do
39
+ allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
40
+ end
41
+
42
+ it "returns an empty array" do
43
+ expect(subject.uids).to eq([])
44
+ end
45
+ end
46
+ end
47
+
48
+ context "#fetch" do
49
+ let(:message_body) { double("the body", force_encoding: nil) }
50
+ let(:attributes) { {"RFC822" => message_body, "other" => "xxx"} }
51
+ let(:fetch_data_item) do
52
+ instance_double(Net::IMAP::FetchData, attr: attributes)
53
+ end
54
+
55
+ before { allow(imap).to receive(:uid_fetch) { [fetch_data_item] } }
56
+
57
+ it "returns the message" do
58
+ expect(subject.fetch(123)).to eq(attributes)
59
+ end
60
+
61
+ context "if the server responds with nothing" do
62
+ before { allow(imap).to receive(:uid_fetch) { nil } }
63
+
64
+ it "is nil" do
65
+ expect(subject.fetch(123)).to be_nil
66
+ end
67
+ end
68
+
69
+ context "if the mailbox doesn't exist" do
70
+ before do
71
+ allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
72
+ end
73
+
74
+ it "is nil" do
75
+ expect(subject.fetch(123)).to be_nil
76
+ end
77
+ end
78
+
79
+ it "sets the encoding on the message" do
80
+ subject.fetch(123)
81
+
82
+ expect(message_body).to have_received(:force_encoding).with("utf-8")
83
+ end
84
+ end
85
+
86
+ context "#folder" do
87
+ it "is the name" do
88
+ expect(subject.folder).to eq("my_folder")
89
+ end
90
+ end
91
+
92
+ context "#exist?" do
93
+ context "when the folder exists" do
94
+ it "is true" do
95
+ expect(subject.exist?).to be_truthy
96
+ end
97
+ end
98
+
99
+ context "when the folder doesn't exist" do
100
+ before do
101
+ allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
102
+ end
103
+
104
+ it "is false" do
105
+ expect(subject.exist?).to be_falsey
106
+ end
107
+ end
108
+ end
109
+
110
+ context "#create" do
111
+ context "when the folder exists" do
112
+ before { subject.create }
113
+
114
+ it "is does not create the folder" do
115
+ expect(imap).to_not have_received(:create)
116
+ end
117
+ end
118
+
119
+ context "when the folder doesn't exist" do
120
+ before do
121
+ allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
122
+ subject.create
123
+ end
124
+
125
+ it "is does not create the folder" do
126
+ expect(imap).to have_received(:create)
127
+ end
128
+ end
129
+ end
130
+
131
+ context "#uid_validity" do
132
+ let(:responses) { {"UIDVALIDITY" => ["x", "uid validity"]} }
133
+
134
+ it "is returned" do
135
+ expect(subject.uid_validity).to eq("uid validity")
136
+ end
137
+ end
138
+
139
+ context "#append" do
140
+ let(:message) do
141
+ instance_double(
142
+ Email::Mboxrd::Message,
143
+ imap_body: "imap body",
144
+ date: Time.now
145
+ )
146
+ end
147
+ let(:append_response) { "response" }
148
+ let(:result) { subject.append(message) }
149
+
150
+ before do
151
+ allow(append_response).
152
+ to receive_message_chain("data.code.data") { "1 2" }
153
+ result
154
+ end
155
+
156
+ it "appends the message" do
157
+ expect(imap).to have_received(:append)
158
+ end
159
+
160
+ it "returns the new uid" do
161
+ expect(result).to eq(2)
162
+ end
163
+
164
+ it "set the new uid validity" do
165
+ expect(subject.uid_validity).to eq(1)
166
+ end
167
+ end
168
+ end
@@ -16,6 +16,7 @@ describe Imap::Backup::Configuration::Store do
16
16
  "Imap::Backup::Configuration::Store::CONFIGURATION_DIRECTORY", directory
17
17
  )
18
18
  allow(File).to receive(:directory?).with(directory) { directory_exists }
19
+ allow(File).to receive(:exist?).and_call_original
19
20
  allow(File).to receive(:exist?).with(file_path) { file_exists }
20
21
  allow(Imap::Backup::Utils).
21
22
  to receive(:stat).with(directory).and_return(0o700)
@@ -0,0 +1,164 @@
1
+ describe Imap::Backup::Serializer::Mbox do
2
+ let(:base_path) { "/base/path" }
3
+ let(:store) do
4
+ instance_double(
5
+ Imap::Backup::Serializer::MboxStore,
6
+ add: nil,
7
+ rename: nil,
8
+ uids: nil,
9
+ uid_validity: existing_uid_validity,
10
+ "uid_validity=": nil
11
+ )
12
+ end
13
+ let(:imap_folder) { "folder" }
14
+ let(:permissions) { 0o700 }
15
+ let(:dir_exists) { true }
16
+ let(:existing_uid_validity) { nil }
17
+
18
+ before do
19
+ allow(Imap::Backup::Utils).to receive(:make_folder)
20
+ allow(Imap::Backup::Utils).to receive(:mode) { permissions }
21
+ allow(Imap::Backup::Utils).to receive(:check_permissions) { true }
22
+ allow(File).to receive(:directory?) { dir_exists }
23
+ end
24
+
25
+ subject { described_class.new(base_path, imap_folder) }
26
+
27
+ before do
28
+ allow(FileUtils).to receive(:chmod)
29
+ allow(Imap::Backup::Serializer::MboxStore).to receive(:new) { store }
30
+ end
31
+
32
+ context "containing directory" do
33
+ before { subject.uids }
34
+
35
+ context "when the IMAP folder has multiple elements" do
36
+ let(:imap_folder) { "folder/path" }
37
+
38
+ context "when the containing directory is missing" do
39
+ let(:dir_exists) { false }
40
+
41
+ it "is created" do
42
+ expect(Imap::Backup::Utils).to have_received(:make_folder).
43
+ with(base_path, File.dirname(imap_folder), 0o700)
44
+ end
45
+ end
46
+ end
47
+
48
+ context "when the containing directory permissons are incorrect" do
49
+ let(:permissions) { 0o777 }
50
+
51
+ it "corrects them" do
52
+ path = File.expand_path(File.join(base_path, File.dirname(imap_folder)))
53
+ expect(FileUtils).to have_received(:chmod).with(0o700, path)
54
+ end
55
+ end
56
+
57
+ context "when the containing directory permissons are correct" do
58
+ it "does nothing" do
59
+ expect(FileUtils).to_not have_received(:chmod)
60
+ end
61
+ end
62
+
63
+ context "when the containing directory exists" do
64
+ it "is not created" do
65
+ expect(Imap::Backup::Utils).to_not have_received(:make_folder).
66
+ with(base_path, File.dirname(imap_folder), 0o700)
67
+ end
68
+ end
69
+ end
70
+
71
+ context "#uids" do
72
+ it "calls the store" do
73
+ subject.uids
74
+
75
+ expect(store).to have_received(:uids)
76
+ end
77
+ end
78
+
79
+ context "#save" do
80
+ it "calls the store" do
81
+ subject.save("foo", "bar")
82
+
83
+ expect(store).to have_received(:add)
84
+ end
85
+ end
86
+
87
+ context "#set_uid_validity" do
88
+ let(:result) { subject.set_uid_validity("aaa") }
89
+
90
+ context "when the existing uid validity is unset" do
91
+ let!(:result) { super() }
92
+
93
+ it "sets uid validity" do
94
+ expect(store).to have_received(:uid_validity=).with("aaa")
95
+ end
96
+
97
+ it "does not rename the store" do
98
+ expect(store).to_not have_received(:rename)
99
+ end
100
+
101
+ it "returns nil" do
102
+ expect(result).to be_nil
103
+ end
104
+ end
105
+
106
+ context "when the uid validity is unchanged" do
107
+ let!(:result) { super() }
108
+ let(:existing_uid_validity) { "aaa" }
109
+
110
+ it "does not set uid validity" do
111
+ expect(store).to_not have_received(:uid_validity=)
112
+ end
113
+
114
+ it "does not rename the store" do
115
+ expect(store).to_not have_received(:rename)
116
+ end
117
+
118
+ it "returns nil" do
119
+ expect(result).to be_nil
120
+ end
121
+ end
122
+
123
+ context "when the uid validity is changed" do
124
+ let(:existing_uid_validity) { "bbb" }
125
+ let(:existing_store) do
126
+ instance_double(Imap::Backup::Serializer::MboxStore)
127
+ end
128
+ let(:exists) { false }
129
+
130
+ before do
131
+ allow(Imap::Backup::Serializer::MboxStore).
132
+ to receive(:new).with(anything, /bbb/) { existing_store }
133
+ allow(existing_store).to receive(:exist?).and_return(exists, false)
134
+ result
135
+ end
136
+
137
+ it "sets uid validity" do
138
+ expect(store).to have_received(:uid_validity=).with("aaa")
139
+ end
140
+
141
+ context "when adding the uid validity does not cause a name clash" do
142
+ it "renames the store, adding the existing uid validity" do
143
+ expect(store).to have_received(:rename).with("folder.bbb")
144
+ end
145
+
146
+ it "returns the new name" do
147
+ expect(result).to eq("folder.bbb")
148
+ end
149
+ end
150
+
151
+ context "when adding the uid validity causes a name clash" do
152
+ let(:exists) { true }
153
+
154
+ it "renames the store, adding the existing uid validity and a digit" do
155
+ expect(store).to have_received(:rename).with("folder.bbb.1")
156
+ end
157
+
158
+ it "returns the new name" do
159
+ expect(result).to eq("folder.bbb.1")
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+
3
+ describe Imap::Backup::Uploader do
4
+ subject { described_class.new(folder, serializer) }
5
+
6
+ let(:folder) do
7
+ instance_double(Imap::Backup::Account::Folder, uids: [2, 3], append: 99)
8
+ end
9
+ let(:serializer) do
10
+ instance_double(
11
+ Imap::Backup::Serializer::Mbox,
12
+ uids: [1, 2],
13
+ update_uid: nil
14
+ )
15
+ end
16
+
17
+ describe "#run" do
18
+ before do
19
+ allow(serializer).to receive(:load).with(1) { "missing message" }
20
+ allow(serializer).to receive(:load).with(2) { "existing message" }
21
+ subject.run
22
+ end
23
+
24
+ context "with messages that are missing" do
25
+ it "restores them" do
26
+ expect(folder).to have_received(:append).with("missing message")
27
+ end
28
+
29
+ it "updates the local message id" do
30
+ expect(serializer).to have_received(:update_uid).with(1, 99)
31
+ end
32
+ end
33
+
34
+ context "with messages that are present on server" do
35
+ it "does nothing" do
36
+ expect(folder).to_not have_received(:append).with("existing message")
37
+ end
38
+ end
39
+ end
40
+ 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: 2.0.0.rc1
4
+ version: 2.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: 2018-08-29 00:00:00.000000000 Z
11
+ date: 2019-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -141,6 +141,7 @@ files:
141
141
  - Rakefile
142
142
  - bin/imap-backup
143
143
  - docker-compose.yml
144
+ - docs/docker-imap.md
144
145
  - imap-backup.gemspec
145
146
  - lib/email/mboxrd/message.rb
146
147
  - lib/email/provider.rb
@@ -175,21 +176,22 @@ files:
175
176
  - spec/support/higline_test_helpers.rb
176
177
  - spec/support/shared_examples/account_flagging.rb
177
178
  - spec/support/silence_logging.rb
178
- - spec/unit/account/connection_spec.rb
179
- - spec/unit/account/folder_spec.rb
180
- - spec/unit/configuration/account_spec.rb
181
- - spec/unit/configuration/asker_spec.rb
182
- - spec/unit/configuration/connection_tester_spec.rb
183
- - spec/unit/configuration/folder_chooser_spec.rb
184
- - spec/unit/configuration/list_spec.rb
185
- - spec/unit/configuration/setup_spec.rb
186
- - spec/unit/configuration/store_spec.rb
187
- - spec/unit/downloader_spec.rb
188
179
  - spec/unit/email/mboxrd/message_spec.rb
189
180
  - spec/unit/email/provider_spec.rb
190
- - spec/unit/serializer/mbox_spec.rb
191
- - spec/unit/serializer/mbox_store_spec.rb
192
- - spec/unit/utils_spec.rb
181
+ - spec/unit/imap/backup/account/connection_spec.rb
182
+ - spec/unit/imap/backup/account/folder_spec.rb
183
+ - spec/unit/imap/backup/configuration/account_spec.rb
184
+ - spec/unit/imap/backup/configuration/asker_spec.rb
185
+ - spec/unit/imap/backup/configuration/connection_tester_spec.rb
186
+ - spec/unit/imap/backup/configuration/folder_chooser_spec.rb
187
+ - spec/unit/imap/backup/configuration/list_spec.rb
188
+ - spec/unit/imap/backup/configuration/setup_spec.rb
189
+ - spec/unit/imap/backup/configuration/store_spec.rb
190
+ - spec/unit/imap/backup/downloader_spec.rb
191
+ - spec/unit/imap/backup/serializer/mbox_spec.rb
192
+ - spec/unit/imap/backup/serializer/mbox_store_spec.rb
193
+ - spec/unit/imap/backup/uploader_spec.rb
194
+ - spec/unit/imap/backup/utils_spec.rb
193
195
  - tmp/.gitkeep
194
196
  homepage: https://github.com/joeyates/imap-backup
195
197
  licenses: []
@@ -206,7 +208,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
206
208
  requirements:
207
209
  - - ">="
208
210
  - !ruby/object:Gem::Version
209
- version: '0'
211
+ version: 2.2.0
210
212
  required_rubygems_version: !ruby/object:Gem::Requirement
211
213
  requirements:
212
214
  - - ">"
@@ -214,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
216
  version: 1.3.1
215
217
  requirements: []
216
218
  rubyforge_project:
217
- rubygems_version: 2.7.6
219
+ rubygems_version: 2.5.2.3
218
220
  signing_key:
219
221
  specification_version: 4
220
222
  summary: Backup GMail (or other IMAP) accounts to disk
@@ -233,18 +235,19 @@ test_files:
233
235
  - spec/support/higline_test_helpers.rb
234
236
  - spec/support/shared_examples/account_flagging.rb
235
237
  - spec/support/silence_logging.rb
236
- - spec/unit/account/connection_spec.rb
237
- - spec/unit/account/folder_spec.rb
238
- - spec/unit/configuration/account_spec.rb
239
- - spec/unit/configuration/asker_spec.rb
240
- - spec/unit/configuration/connection_tester_spec.rb
241
- - spec/unit/configuration/folder_chooser_spec.rb
242
- - spec/unit/configuration/list_spec.rb
243
- - spec/unit/configuration/setup_spec.rb
244
- - spec/unit/configuration/store_spec.rb
245
- - spec/unit/downloader_spec.rb
246
238
  - spec/unit/email/mboxrd/message_spec.rb
247
239
  - spec/unit/email/provider_spec.rb
248
- - spec/unit/serializer/mbox_spec.rb
249
- - spec/unit/serializer/mbox_store_spec.rb
250
- - spec/unit/utils_spec.rb
240
+ - spec/unit/imap/backup/account/connection_spec.rb
241
+ - spec/unit/imap/backup/account/folder_spec.rb
242
+ - spec/unit/imap/backup/configuration/account_spec.rb
243
+ - spec/unit/imap/backup/configuration/asker_spec.rb
244
+ - spec/unit/imap/backup/configuration/connection_tester_spec.rb
245
+ - spec/unit/imap/backup/configuration/folder_chooser_spec.rb
246
+ - spec/unit/imap/backup/configuration/list_spec.rb
247
+ - spec/unit/imap/backup/configuration/setup_spec.rb
248
+ - spec/unit/imap/backup/configuration/store_spec.rb
249
+ - spec/unit/imap/backup/downloader_spec.rb
250
+ - spec/unit/imap/backup/serializer/mbox_spec.rb
251
+ - spec/unit/imap/backup/serializer/mbox_store_spec.rb
252
+ - spec/unit/imap/backup/uploader_spec.rb
253
+ - spec/unit/imap/backup/utils_spec.rb
@@ -1,75 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Imap::Backup::Account::Folder do
4
- let(:imap) { double("Net::IMAP", examine: nil) }
5
- let(:connection) { double("Imap::Backup::Account::Connection", imap: imap) }
6
- let(:missing_mailbox_data) do
7
- double("Data", text: "Unknown Mailbox: my_folder")
8
- end
9
- let(:missing_mailbox_response) do
10
- double("Response", data: missing_mailbox_data)
11
- end
12
- let(:missing_mailbox_error) do
13
- Net::IMAP::NoResponseError.new(missing_mailbox_response)
14
- end
15
-
16
- subject { described_class.new(connection, "my_folder") }
17
-
18
- context "#uids" do
19
- let(:uids) { [5678, 123] }
20
-
21
- before { allow(imap).to receive(:uid_search).and_return(uids) }
22
-
23
- it "lists available messages" do
24
- expect(subject.uids).to eq(uids.reverse)
25
- end
26
-
27
- context "with missing mailboxes" do
28
- before do
29
- allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
30
- end
31
-
32
- it "returns an empty array" do
33
- expect(subject.uids).to eq([])
34
- end
35
- end
36
- end
37
-
38
- context "#fetch" do
39
- let(:message_body) { double("the body", force_encoding: nil) }
40
- let(:attributes) { {"RFC822" => message_body, "other" => "xxx"} }
41
- let(:fetch_data_item) do
42
- instance_double(Net::IMAP::FetchData, attr: attributes)
43
- end
44
-
45
- before { allow(imap).to receive(:uid_fetch) { [fetch_data_item] } }
46
-
47
- it "returns the message" do
48
- expect(subject.fetch(123)).to eq(attributes)
49
- end
50
-
51
- context "if the server responds with nothing" do
52
- before { allow(imap).to receive(:uid_fetch) { nil } }
53
-
54
- it "is nil" do
55
- expect(subject.fetch(123)).to be_nil
56
- end
57
- end
58
-
59
- context "if the mailbox doesn't exist" do
60
- before do
61
- allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
62
- end
63
-
64
- it "is nil" do
65
- expect(subject.fetch(123)).to be_nil
66
- end
67
- end
68
-
69
- it "sets the encoding on the message" do
70
- subject.fetch(123)
71
-
72
- expect(message_body).to have_received(:force_encoding).with("utf-8")
73
- end
74
- end
75
- end
@@ -1,82 +0,0 @@
1
- describe Imap::Backup::Serializer::Mbox do
2
- let(:base_path) { "/base/path" }
3
- let(:store) do
4
- instance_double(
5
- Imap::Backup::Serializer::MboxStore,
6
- add: nil,
7
- uids: nil
8
- )
9
- end
10
- let(:imap_folder) { "folder" }
11
- let(:permissions) { 0o700 }
12
- let(:dir_exists) { true }
13
-
14
- before do
15
- allow(Imap::Backup::Utils).to receive(:make_folder)
16
- allow(Imap::Backup::Utils).to receive(:mode) { permissions }
17
- allow(Imap::Backup::Utils).to receive(:check_permissions) { true }
18
- allow(File).to receive(:directory?) { dir_exists }
19
- end
20
-
21
- subject { described_class.new(base_path, imap_folder) }
22
-
23
- before do
24
- allow(FileUtils).to receive(:chmod)
25
- allow(Imap::Backup::Serializer::MboxStore).to receive(:new) { store }
26
- end
27
-
28
- context "containing directory" do
29
- before { subject.uids }
30
-
31
- context "when the IMAP folder has multiple elements" do
32
- let(:imap_folder) { "folder/path" }
33
-
34
- context "when the containing directory is missing" do
35
- let(:dir_exists) { false }
36
-
37
- it "is created" do
38
- expect(Imap::Backup::Utils).to have_received(:make_folder).
39
- with(base_path, File.dirname(imap_folder), 0o700)
40
- end
41
- end
42
- end
43
-
44
- context "when the containing directory permissons are incorrect" do
45
- let(:permissions) { 0o777 }
46
-
47
- it "corrects them" do
48
- path = File.expand_path(File.join(base_path, File.dirname(imap_folder)))
49
- expect(FileUtils).to have_received(:chmod).with(0o700, path)
50
- end
51
- end
52
-
53
- context "when the containing directory permissons are correct" do
54
- it "does nothing" do
55
- expect(FileUtils).to_not have_received(:chmod)
56
- end
57
- end
58
-
59
- context "when the containing directory exists" do
60
- it "is not created" do
61
- expect(Imap::Backup::Utils).to_not have_received(:make_folder).
62
- with(base_path, File.dirname(imap_folder), 0o700)
63
- end
64
- end
65
- end
66
-
67
- context "#uids" do
68
- it "calls the store" do
69
- subject.uids
70
-
71
- expect(store).to have_received(:uids)
72
- end
73
- end
74
-
75
- context "#save" do
76
- it "calls the store" do
77
- subject.save("foo", "bar")
78
-
79
- expect(store).to have_received(:add)
80
- end
81
- end
82
- end