imap-backup 2.0.0.rc1 → 2.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.
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