imap-backup 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/Gemfile +1 -1
  4. data/Rakefile +4 -4
  5. data/bin/imap-backup +23 -25
  6. data/imap-backup.gemspec +14 -14
  7. data/lib/email/mboxrd/message.rb +23 -5
  8. data/lib/email/provider.rb +4 -4
  9. data/lib/imap/backup.rb +18 -18
  10. data/lib/imap/backup/account/connection.rb +6 -6
  11. data/lib/imap/backup/account/folder.rb +4 -5
  12. data/lib/imap/backup/configuration/account.rb +20 -22
  13. data/lib/imap/backup/configuration/asker.rb +8 -10
  14. data/lib/imap/backup/configuration/connection_tester.rb +3 -5
  15. data/lib/imap/backup/configuration/folder_chooser.rb +10 -12
  16. data/lib/imap/backup/configuration/list.rb +1 -3
  17. data/lib/imap/backup/configuration/setup.rb +13 -14
  18. data/lib/imap/backup/configuration/store.rb +7 -8
  19. data/lib/imap/backup/downloader.rb +0 -2
  20. data/lib/imap/backup/serializer/base.rb +0 -2
  21. data/lib/imap/backup/serializer/directory.rb +3 -4
  22. data/lib/imap/backup/serializer/mbox.rb +11 -12
  23. data/lib/imap/backup/utils.rb +2 -3
  24. data/lib/imap/backup/version.rb +2 -2
  25. data/spec/spec_helper.rb +6 -6
  26. data/spec/support/higline_test_helpers.rb +1 -1
  27. data/spec/support/shared_examples/account_flagging.rb +6 -6
  28. data/spec/unit/account/connection_spec.rb +50 -51
  29. data/spec/unit/account/folder_spec.rb +18 -19
  30. data/spec/unit/configuration/account_spec.rb +96 -97
  31. data/spec/unit/configuration/asker_spec.rb +33 -34
  32. data/spec/unit/configuration/connection_tester_spec.rb +18 -19
  33. data/spec/unit/configuration/folder_chooser_spec.rb +34 -35
  34. data/spec/unit/configuration/list_spec.rb +13 -14
  35. data/spec/unit/configuration/setup_spec.rb +46 -47
  36. data/spec/unit/configuration/store_spec.rb +56 -57
  37. data/spec/unit/downloader_spec.rb +18 -19
  38. data/spec/unit/email/mboxrd/message_spec.rb +55 -11
  39. data/spec/unit/email/provider_spec.rb +12 -12
  40. data/spec/unit/serializer/base_spec.rb +7 -9
  41. data/spec/unit/serializer/directory_spec.rb +18 -19
  42. data/spec/unit/serializer/mbox_spec.rb +35 -37
  43. data/spec/unit/utils_spec.rb +26 -27
  44. metadata +17 -2
@@ -1,19 +1,18 @@
1
- # encoding: utf-8
2
- require 'spec_helper'
3
- require 'json'
1
+ require "spec_helper"
2
+ require "json"
4
3
 
5
4
  describe Imap::Backup::Configuration::Store do
6
- let(:directory) { '/base/path' }
7
- let(:file_path) { File.join(directory, '/config.json') }
5
+ let(:directory) { "/base/path" }
6
+ let(:file_path) { File.join(directory, "/config.json") }
8
7
  let(:file_exists) { true }
9
8
  let(:directory_exists) { true }
10
- let(:data) { {:debug => debug, :accounts => accounts} }
9
+ let(:data) { {debug: debug, accounts: accounts} }
11
10
  let(:debug) { true }
12
11
  let(:accounts) { [] }
13
12
  let(:configuration) { data.to_json }
14
13
 
15
14
  before do
16
- stub_const('Imap::Backup::Configuration::Store::CONFIGURATION_DIRECTORY', directory)
15
+ stub_const("Imap::Backup::Configuration::Store::CONFIGURATION_DIRECTORY", directory)
17
16
  allow(File).to receive(:directory?).with(directory).and_return(directory_exists)
18
17
  allow(File).to receive(:exist?).with(file_path).and_return(file_exists)
19
18
  allow(Imap::Backup::Utils).to receive(:stat).with(directory).and_return(0700)
@@ -22,9 +21,9 @@ describe Imap::Backup::Configuration::Store do
22
21
  allow(File).to receive(:read).with(file_path).and_return(configuration)
23
22
  end
24
23
 
25
- describe '.exist?' do
24
+ describe ".exist?" do
26
25
  [true, false].each do |exists|
27
- state = exists ? 'exists' : "doesn't exist"
26
+ state = exists ? "exists" : "doesn't exist"
28
27
  context "when the file #{state}" do
29
28
  let(:file_exists) { exists }
30
29
 
@@ -35,123 +34,123 @@ describe Imap::Backup::Configuration::Store do
35
34
  end
36
35
  end
37
36
 
38
- describe '#path' do
39
- it 'is the directory containing the configuration file' do
37
+ describe "#path" do
38
+ it "is the directory containing the configuration file" do
40
39
  expect(subject.path).to eq(directory)
41
40
  end
42
41
  end
43
42
 
44
- describe '#modified?' do
43
+ describe "#modified?" do
45
44
  context "'with accounts flagged 'modified'" do
46
- let(:accounts) { [{:name => 'foo', :modified => true}] }
45
+ let(:accounts) { [{name: "foo", modified: true}] }
47
46
 
48
- it 'is true' do
47
+ it "is true" do
49
48
  expect(subject.modified?).to be_truthy
50
49
  end
51
50
  end
52
51
 
53
52
  context "'with accounts flagged 'delete'" do
54
- let(:accounts) { [{:name => 'foo', :delete => true}] }
53
+ let(:accounts) { [{name: "foo", delete: true}] }
55
54
 
56
- it 'is true' do
55
+ it "is true" do
57
56
  expect(subject.modified?).to be_truthy
58
57
  end
59
58
  end
60
59
 
61
60
  context "without accounts flagged 'modified'" do
62
- let(:accounts) { [{:name => 'foo'}] }
61
+ let(:accounts) { [{name: "foo"}] }
63
62
 
64
- it 'is false' do
63
+ it "is false" do
65
64
  expect(subject.modified?).to be_falsey
66
65
  end
67
66
  end
68
67
  end
69
68
 
70
- describe '#debug?' do
71
- context 'when the debug flag is true' do
72
- it 'is true' do
69
+ describe "#debug?" do
70
+ context "when the debug flag is true" do
71
+ it "is true" do
73
72
  expect(subject.debug?).to be_truthy
74
73
  end
75
74
  end
76
75
 
77
- context 'when the debug flag is false' do
76
+ context "when the debug flag is false" do
78
77
  let(:debug) { false }
79
78
 
80
- it 'is false' do
79
+ it "is false" do
81
80
  expect(subject.debug?).to be_falsey
82
81
  end
83
82
  end
84
83
 
85
- context 'when the debug flag is missing' do
86
- let(:data) { {:accounts => accounts} }
84
+ context "when the debug flag is missing" do
85
+ let(:data) { {accounts: accounts} }
87
86
 
88
- it 'is false' do
87
+ it "is false" do
89
88
  expect(subject.debug?).to be_falsey
90
89
  end
91
90
  end
92
91
 
93
- context 'when the debug flag is neither true nor false' do
94
- let(:debug) { 'hi' }
92
+ context "when the debug flag is neither true nor false" do
93
+ let(:debug) { "hi" }
95
94
 
96
- it 'is false' do
95
+ it "is false" do
97
96
  expect(subject.debug?).to be_falsey
98
97
  end
99
98
  end
100
99
  end
101
100
 
102
- describe '#debug=' do
101
+ describe "#debug=" do
103
102
  before { subject.debug = debug }
104
103
 
105
- context 'when the supplied value is true' do
106
- it 'sets the flag to true' do
104
+ context "when the supplied value is true" do
105
+ it "sets the flag to true" do
107
106
  expect(subject.debug?).to be_truthy
108
107
  end
109
108
  end
110
109
 
111
- context 'when the supplied value is false' do
110
+ context "when the supplied value is false" do
112
111
  let(:debug) { false }
113
112
 
114
- it 'sets the flag to false' do
113
+ it "sets the flag to false" do
115
114
  expect(subject.debug?).to be_falsey
116
115
  end
117
116
  end
118
117
 
119
- context 'when the supplied value is neither true nor false' do
120
- let(:debug) { 'ciao' }
118
+ context "when the supplied value is neither true nor false" do
119
+ let(:debug) { "ciao" }
121
120
 
122
- it 'sets the flag to false' do
121
+ it "sets the flag to false" do
123
122
  expect(subject.debug?).to be_falsey
124
123
  end
125
124
  end
126
125
  end
127
126
 
128
- describe '#save' do
127
+ describe "#save" do
129
128
  let(:directory_exists) { false }
130
- let(:file) { double('File', :write => nil) }
129
+ let(:file) { double("File", write: nil) }
131
130
 
132
131
  before do
133
132
  allow(FileUtils).to receive(:mkdir)
134
133
  allow(FileUtils).to receive(:chmod)
135
- allow(File).to receive(:open).with(file_path, 'w') { |&b| b.call file }
136
- allow(JSON).to receive(:pretty_generate).and_return('JSON output')
134
+ allow(File).to receive(:open).with(file_path, "w") { |&b| b.call file }
135
+ allow(JSON).to receive(:pretty_generate).and_return("JSON output")
137
136
  end
138
137
 
139
138
  subject { described_class.new }
140
139
 
141
- it 'creates the config directory' do
140
+ it "creates the config directory" do
142
141
  subject.save
143
142
 
144
143
  expect(FileUtils).to have_received(:mkdir).with(directory)
145
144
  end
146
145
 
147
- it 'saves the configuration' do
146
+ it "saves the configuration" do
148
147
  subject.save
149
148
 
150
- expect(file).to have_received(:write).with('JSON output')
149
+ expect(file).to have_received(:write).with("JSON output")
151
150
  end
152
151
 
153
- context 'when accounts are modified' do
154
- let(:accounts) { [{:name => 'foo', :modified => true}] }
152
+ context "when accounts are modified" do
153
+ let(:accounts) { [{name: "foo", modified: true}] }
155
154
 
156
155
  before { subject.save }
157
156
 
@@ -163,17 +162,17 @@ describe Imap::Backup::Configuration::Store do
163
162
  end
164
163
  end
165
164
 
166
- context 'when accounts are to be deleted' do
165
+ context "when accounts are to be deleted" do
167
166
  let(:accounts) do
168
167
  [
169
- {:name => 'keep_me'},
170
- {:name => 'delete_me', :delete => true},
168
+ {name: "keep_me"},
169
+ {name: "delete_me", delete: true}
171
170
  ]
172
171
  end
173
172
 
174
173
  before { subject.save }
175
174
 
176
- it 'does not save them' do
175
+ it "does not save them" do
177
176
  expected = Marshal.load(Marshal.dump(data))
178
177
  expected[:accounts].pop
179
178
 
@@ -181,15 +180,15 @@ describe Imap::Backup::Configuration::Store do
181
180
  end
182
181
  end
183
182
 
184
- context 'when file permissions are too open' do
183
+ context "when file permissions are too open" do
185
184
  before { subject.save }
186
185
 
187
- it 'sets them to 0600' do
186
+ it "sets them to 0600" do
188
187
  expect(FileUtils).to have_received(:chmod).with(0600, file_path)
189
188
  end
190
189
  end
191
190
 
192
- context 'if the configuration file is missing' do
191
+ context "if the configuration file is missing" do
193
192
  let(:file_exists) { false }
194
193
 
195
194
  it "doesn't fail" do
@@ -199,17 +198,17 @@ describe Imap::Backup::Configuration::Store do
199
198
  end
200
199
  end
201
200
 
202
- context 'if the config file permissions are too lax' do
201
+ context "if the config file permissions are too lax" do
203
202
  let(:file_exists) { true }
204
203
 
205
204
  before do
206
- allow(Imap::Backup::Utils).to receive(:check_permissions).with(file_path, 0600).and_raise('Error')
205
+ allow(Imap::Backup::Utils).to receive(:check_permissions).with(file_path, 0600).and_raise("Error")
207
206
  end
208
207
 
209
- it 'fails' do
208
+ it "fails" do
210
209
  expect do
211
210
  subject.save
212
- end.to raise_error(RuntimeError, 'Error')
211
+ end.to raise_error(RuntimeError, "Error")
213
212
  end
214
213
  end
215
214
  end
@@ -1,39 +1,38 @@
1
- # encoding: utf-8
2
- require 'spec_helper'
1
+ require "spec_helper"
3
2
 
4
3
  describe Imap::Backup::Downloader do
5
- describe '#run' do
6
- let(:message) { {'RFC822' => 'blah'} }
7
- let(:folder) { double('Imap::Backup::Account::Folder', :fetch => message, :name => 'folder') }
8
- let(:folder_uids) { ['111', '222', '333'] }
9
- let(:serializer) { double('Imap::Backup::Serializer', :save => nil) }
10
- let(:serializer_uids) { ['222'] }
4
+ describe "#run" do
5
+ let(:message) { {"RFC822" => "blah"} }
6
+ let(:folder) { double("Imap::Backup::Account::Folder", fetch: message, name: "folder") }
7
+ let(:folder_uids) { ["111", "222", "333"] }
8
+ let(:serializer) { double("Imap::Backup::Serializer", save: nil) }
9
+ let(:serializer_uids) { ["222"] }
11
10
 
12
11
  subject { described_class.new(folder, serializer) }
13
12
 
14
13
  before do
15
14
  allow(folder).to receive(:uids).and_return(folder_uids)
16
15
  allow(serializer).to receive(:uids).and_return(serializer_uids)
17
- allow(folder).to receive(:fetch).with('333').and_return(nil)
16
+ allow(folder).to receive(:fetch).with("333").and_return(nil)
18
17
  subject.run
19
18
  end
20
19
 
21
- context '#run' do
22
- context 'fetched messages' do
23
- it 'are saved' do
24
- expect(serializer).to have_received(:save).with('111', message)
20
+ context "#run" do
21
+ context "fetched messages" do
22
+ it "are saved" do
23
+ expect(serializer).to have_received(:save).with("111", message)
25
24
  end
26
25
  end
27
26
 
28
- context 'messages which are already present' do
29
- specify 'are skipped' do
30
- expect(serializer).to_not have_received(:save).with('222', anything)
27
+ context "messages which are already present" do
28
+ specify "are skipped" do
29
+ expect(serializer).to_not have_received(:save).with("222", anything)
31
30
  end
32
31
  end
33
32
 
34
- context 'failed fetches' do
35
- specify 'are skipped' do
36
- expect(serializer).to_not have_received(:save).with('333', anything)
33
+ context "failed fetches" do
34
+ specify "are skipped" do
35
+ expect(serializer).to_not have_received(:save).with("333", anything)
37
36
  end
38
37
  end
39
38
  end
@@ -1,32 +1,47 @@
1
- # encoding: utf-8
1
+ require "spec_helper"
2
2
 
3
- require 'spec_helper'
3
+ msg_no_from = %Q|Delivered-To: you@example.com
4
+ From: example <www.example.com>
5
+ To: FirstName LastName <you@example.com>
6
+ Subject: Re: no subject|
7
+
8
+ msg_bad_from = %Q|Delivered-To: you@example.com
9
+ from: "FirstName LastName (TEXT)" <"TEXT*" <no-reply@example.com>>
10
+ To: FirstName LastName <you@example.com>
11
+ Subject: Re: no subject
12
+ Sender: FistName LastName <"TEXT*"no-reply=example.com@example.com>|
13
+
14
+ msg_no_from_but_return_path = %Q|Delivered-To: you@example.com
15
+ From: example <www.example.com>
16
+ To: FirstName LastName <you@example.com>
17
+ Return-Path: <me@example.com>
18
+ Subject: Re: no subject|
4
19
 
5
20
  describe Email::Mboxrd::Message do
6
- let(:from) { 'me@example.com' }
21
+ let(:from) { "me@example.com" }
7
22
  let(:date) { DateTime.new(2012, 12, 13, 18, 23, 45) }
8
23
  let(:message_body) do
9
- double('Body', :clone => cloned_message_body, :force_encoding => nil)
24
+ double("Body", clone: cloned_message_body, force_encoding: nil)
10
25
  end
11
26
  let(:cloned_message_body) { "Foo\nBar\nFrom at the beginning of the line\n>>From quoted" }
12
27
 
13
28
  subject { described_class.new(message_body) }
14
29
 
15
- context '#to_s' do
16
- let(:mail) { double('Mail', :from =>[from], :date => date) }
30
+ context "#to_s" do
31
+ let(:mail) { double("Mail", from: [from], date: date) }
17
32
 
18
33
  before do
19
34
  allow(Mail).to receive(:new).with(cloned_message_body).and_return(mail)
20
35
  end
21
36
 
22
- it 'does not modify the message' do
37
+ it "does not modify the message" do
23
38
  subject.to_s
24
39
 
25
- expect(message_body).to_not have_received(:force_encoding).with('binary')
40
+ expect(message_body).to_not have_received(:force_encoding).with("binary")
26
41
  end
27
42
 
28
43
  it "adds a 'From ' line at the start" do
29
- expect(subject.to_s).to start_with('From ' + from + ' ' + date.asctime + "\n")
44
+ expect(subject.to_s).to start_with("From " + from + " " + date.asctime + "\n")
30
45
  end
31
46
 
32
47
  it "replaces existing 'From ' with '>From '" do
@@ -37,12 +52,41 @@ describe Email::Mboxrd::Message do
37
52
  expect(subject.to_s).to include("\n>>>From quoted")
38
53
  end
39
54
 
40
- context 'when date is missing' do
55
+ context "when date is missing" do
41
56
  let(:date) { nil }
42
57
 
43
- it 'does no fail' do
58
+ it "does no fail" do
44
59
  expect { subject.to_s }.to_not raise_error
45
60
  end
46
61
  end
47
62
  end
63
+
64
+ context '#from' do
65
+ before do
66
+ # call original for these tests because we want to test the behaviour of
67
+ # class-under-test given different behaviour of the Mail parser
68
+ allow(Mail).to receive(:new).and_call_original
69
+ end
70
+
71
+ context "when original message 'from' is nil" do
72
+ let(:message_body) { msg_no_from }
73
+ it "'from' is empty string" do
74
+ expect(subject.to_s).to start_with("From \n")
75
+ end
76
+ end
77
+
78
+ context "when original message 'from' is a string but not an address" do
79
+ let(:message_body) { msg_bad_from }
80
+ it "'from' is empty string" do
81
+ expect(subject.to_s).to start_with("From \n")
82
+ end
83
+ end
84
+
85
+ context "when original message 'from' is nil and 'envelope from' is nil and 'return path' is available" do
86
+ let(:message_body) { msg_no_from_but_return_path }
87
+ it "'return path' is used as 'from'" do
88
+ expect(subject.to_s).to start_with("From " + from + " \n")
89
+ end
90
+ end
91
+ end
48
92
  end
@@ -1,9 +1,9 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe Email::Provider do
4
- describe '.for_address' do
5
- context 'known providers' do
6
- [['gmail.com', :gmail], ['fastmail.fm', :fastmail]].each do |domain, provider|
4
+ describe ".for_address" do
5
+ context "known providers" do
6
+ [["gmail.com", :gmail], ["fastmail.fm", :fastmail]].each do |domain, provider|
7
7
  it "recognizes #{provider}" do
8
8
  address = "foo@#{domain}"
9
9
  expect(described_class.for_address(address).provider).to eq(provider)
@@ -11,24 +11,24 @@ describe Email::Provider do
11
11
  end
12
12
  end
13
13
 
14
- context 'with unknown providers' do
15
- it 'returns a default provider' do
16
- expect(described_class.for_address('foo@unknown.com').provider).to eq(:default)
14
+ context "with unknown providers" do
15
+ it "returns a default provider" do
16
+ expect(described_class.for_address("foo@unknown.com").provider).to eq(:default)
17
17
  end
18
18
  end
19
19
  end
20
20
 
21
21
  subject { described_class.new(:gmail) }
22
22
 
23
- describe '#options' do
24
- it 'returns options' do
23
+ describe "#options" do
24
+ it "returns options" do
25
25
  expect(subject.options).to eq(port: 993, ssl: true)
26
26
  end
27
27
  end
28
28
 
29
- describe '#host' do
30
- it 'returns host' do
31
- expect(subject.host).to eq('imap.gmail.com')
29
+ describe "#host" do
30
+ it "returns host" do
31
+ expect(subject.host).to eq("imap.gmail.com")
32
32
  end
33
33
  end
34
34
  end