imap-backup 2.1.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +5 -2
  4. data/.rubocop_todo.yml +24 -0
  5. data/.travis.yml +13 -1
  6. data/README.md +9 -21
  7. data/Rakefile +6 -2
  8. data/imap-backup.gemspec +5 -1
  9. data/lib/email/mboxrd/message.rb +13 -12
  10. data/lib/imap/backup/account/connection.rb +3 -2
  11. data/lib/imap/backup/account/folder.rb +2 -0
  12. data/lib/imap/backup/configuration/account.rb +20 -16
  13. data/lib/imap/backup/configuration/asker.rb +1 -1
  14. data/lib/imap/backup/serializer/mbox.rb +39 -25
  15. data/lib/imap/backup/serializer/mbox_enumerator.rb +31 -0
  16. data/lib/imap/backup/serializer/mbox_store.rb +6 -27
  17. data/lib/imap/backup/version.rb +1 -1
  18. data/spec/features/support/email_server.rb +3 -0
  19. data/spec/support/fixtures.rb +1 -1
  20. data/spec/unit/email/mboxrd/message_spec.rb +20 -3
  21. data/spec/unit/email/provider_spec.rb +1 -1
  22. data/spec/unit/imap/backup/account/connection_spec.rb +10 -9
  23. data/spec/unit/imap/backup/account/folder_spec.rb +24 -10
  24. data/spec/unit/imap/backup/configuration/account_spec.rb +47 -22
  25. data/spec/unit/imap/backup/configuration/asker_spec.rb +5 -9
  26. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +6 -6
  27. data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +6 -6
  28. data/spec/unit/imap/backup/configuration/list_spec.rb +24 -1
  29. data/spec/unit/imap/backup/configuration/setup_spec.rb +29 -9
  30. data/spec/unit/imap/backup/configuration/store_spec.rb +5 -5
  31. data/spec/unit/imap/backup/downloader_spec.rb +11 -13
  32. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +40 -0
  33. data/spec/unit/imap/backup/serializer/mbox_spec.rb +63 -24
  34. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +162 -9
  35. data/spec/unit/imap/backup/utils_spec.rb +3 -3
  36. data/spec/unit/imap/backup_spec.rb +28 -0
  37. metadata +8 -2
@@ -1,5 +1,3 @@
1
- # rubocop:disable Metrics/ModuleLength
2
-
3
1
  module Imap::Backup
4
2
  describe Configuration::Asker do
5
3
  subject { described_class.new(highline) }
@@ -43,7 +41,7 @@ module Imap::Backup
43
41
  end
44
42
  end
45
43
 
46
- context "#initialize" do
44
+ describe "#initialize" do
47
45
  it "requires 1 parameter" do
48
46
  expect do
49
47
  described_class.new
@@ -55,7 +53,7 @@ module Imap::Backup
55
53
  end
56
54
  end
57
55
 
58
- context "#email" do
56
+ describe "#email" do
59
57
  let(:email) { "email@example.com" }
60
58
  let(:answer) { email }
61
59
  let(:result) { subject.email }
@@ -71,7 +69,7 @@ module Imap::Backup
71
69
  end
72
70
  end
73
71
 
74
- context "#password" do
72
+ describe "#password" do
75
73
  let(:password1) { "password" }
76
74
  let(:password2) { "password" }
77
75
  let(:answers) { [true, false] }
@@ -101,7 +99,7 @@ module Imap::Backup
101
99
  expect(result).to eq(password1)
102
100
  end
103
101
 
104
- context "different answers" do
102
+ context "with different answers" do
105
103
  let(:password2) { "secret" }
106
104
 
107
105
  it "asks to continue" do
@@ -111,7 +109,7 @@ module Imap::Backup
111
109
  end
112
110
  end
113
111
 
114
- context "#backup_path" do
112
+ describe "#backup_path" do
115
113
  let(:path) { "/path" }
116
114
  let(:answer) { path }
117
115
  let(:result) { subject.backup_path("", //) }
@@ -134,5 +132,3 @@ module Imap::Backup
134
132
  end
135
133
  end
136
134
  end
137
-
138
- # rubocop:enable Metrics/ModuleLength
@@ -1,5 +1,5 @@
1
1
  describe Imap::Backup::Configuration::ConnectionTester do
2
- context ".test" do
2
+ describe ".test" do
3
3
  let(:connection) do
4
4
  instance_double(Imap::Backup::Account::Connection, imap: nil)
5
5
  end
@@ -9,7 +9,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
9
9
  allow(Imap::Backup::Account::Connection).to receive(:new) { connection }
10
10
  end
11
11
 
12
- context "call" do
12
+ describe "call" do
13
13
  before { result }
14
14
 
15
15
  it "tries to connect" do
@@ -17,7 +17,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
17
17
  end
18
18
  end
19
19
 
20
- context "success" do
20
+ describe "success" do
21
21
  before { result }
22
22
 
23
23
  it "returns success" do
@@ -25,13 +25,13 @@ describe Imap::Backup::Configuration::ConnectionTester do
25
25
  end
26
26
  end
27
27
 
28
- context "failure" do
28
+ describe "failure" do
29
29
  before do
30
30
  allow(connection).to receive(:imap).and_raise(error)
31
31
  result
32
32
  end
33
33
 
34
- context "no connection" do
34
+ context "with no connection" do
35
35
  let(:error) do
36
36
  data = OpenStruct.new(text: "bar")
37
37
  response = OpenStruct.new(data: data)
@@ -43,7 +43,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
43
43
  end
44
44
  end
45
45
 
46
- context "other" do
46
+ context "when caused by other errors" do
47
47
  let(:error) { "Error" }
48
48
 
49
49
  it "returns error" do
@@ -1,7 +1,7 @@
1
1
  describe Imap::Backup::Configuration::FolderChooser do
2
2
  include HighLineTestHelpers
3
3
 
4
- context "#run" do
4
+ describe "#run" do
5
5
  subject { described_class.new(account) }
6
6
 
7
7
  let(:connection) do
@@ -21,7 +21,7 @@ describe Imap::Backup::Configuration::FolderChooser do
21
21
  allow(Imap::Backup.logger).to receive(:warn)
22
22
  end
23
23
 
24
- context "display" do
24
+ describe "display" do
25
25
  before { subject.run }
26
26
 
27
27
  it "clears the screen" do
@@ -33,7 +33,7 @@ describe Imap::Backup::Configuration::FolderChooser do
33
33
  end
34
34
  end
35
35
 
36
- context "folder listing" do
36
+ describe "folder listing" do
37
37
  let(:account) { {folders: [{name: "my_folder"}]} }
38
38
  let(:remote_folders) do
39
39
  # this one is already backed up:
@@ -46,7 +46,7 @@ describe Imap::Backup::Configuration::FolderChooser do
46
46
  [folder1, folder2]
47
47
  end
48
48
 
49
- context "display" do
49
+ describe "display" do
50
50
  before { subject.run }
51
51
 
52
52
  it "shows folders which are being backed up" do
@@ -58,7 +58,7 @@ describe Imap::Backup::Configuration::FolderChooser do
58
58
  end
59
59
  end
60
60
 
61
- context "adding folders" do
61
+ context "when adding folders" do
62
62
  before do
63
63
  allow(input).to receive(:gets).and_return("2\n", "q\n")
64
64
 
@@ -72,7 +72,7 @@ describe Imap::Backup::Configuration::FolderChooser do
72
72
  include_examples "it flags the account as modified"
73
73
  end
74
74
 
75
- context "removing folders" do
75
+ context "when removing folders" do
76
76
  before do
77
77
  allow(input).to receive(:gets).and_return("1\n", "q\n")
78
78
 
@@ -28,7 +28,30 @@ describe Imap::Backup::Configuration::List do
28
28
  to receive(:new).with(accounts[1]) { connection2 }
29
29
  end
30
30
 
31
- context "#each_connection" do
31
+ describe "#setup_logging" do
32
+ let(:config_exists) { true }
33
+
34
+ before do
35
+ allow(Imap::Backup::Configuration::Store).
36
+ to receive(:exist?) { config_exists }
37
+ allow(Imap::Backup).to receive(:setup_logging)
38
+ subject.setup_logging
39
+ end
40
+
41
+ it "sets global logging level" do
42
+ expect(Imap::Backup).to have_received(:setup_logging).with(store)
43
+ end
44
+
45
+ context "without a config" do
46
+ let(:config_exists) { false }
47
+
48
+ it "does nothing" do
49
+ expect(Imap::Backup).to_not have_received(:setup_logging).with(store)
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#each_connection" do
32
55
  specify "calls the block with each account's connection" do
33
56
  connections = []
34
57
 
@@ -3,7 +3,7 @@
3
3
  describe Imap::Backup::Configuration::Setup do
4
4
  include HighLineTestHelpers
5
5
 
6
- context "#initialize" do
6
+ describe "#initialize" do
7
7
  context "without a config file" do
8
8
  it "works" do
9
9
  described_class.new
@@ -11,7 +11,7 @@ describe Imap::Backup::Configuration::Setup do
11
11
  end
12
12
  end
13
13
 
14
- context "#run" do
14
+ describe "#run" do
15
15
  subject { described_class.new }
16
16
 
17
17
  let(:normal) { {username: "account@example.com"} }
@@ -41,7 +41,7 @@ describe Imap::Backup::Configuration::Setup do
41
41
  allow(Kernel).to receive(:system)
42
42
  end
43
43
 
44
- context "main menu" do
44
+ describe "main menu" do
45
45
  before { subject.run }
46
46
 
47
47
  %w(add\ account save\ and\ exit exit\ without\ saving).each do |choice|
@@ -63,33 +63,53 @@ describe Imap::Backup::Configuration::Setup do
63
63
  expect(Imap::Backup).to have_received(:setup_logging)
64
64
  end
65
65
 
66
- context "listing" do
66
+ describe "listing" do
67
67
  let(:accounts) { [normal, modified, deleted] }
68
68
  let(:modified) { {username: "modified@example.com", modified: true} }
69
69
  let(:deleted) { {username: "deleted@example.com", delete: true} }
70
70
 
71
71
  before { subject.run }
72
72
 
73
- context "normal accounts" do
73
+ describe "normal accounts" do
74
74
  it "are listed" do
75
75
  expect(output.string).to match(/account@example.com/)
76
76
  end
77
77
  end
78
78
 
79
- context "modified accounts" do
79
+ describe "modified accounts" do
80
80
  it "are flagged" do
81
81
  expect(output.string).to match(/modified@example.com \*/)
82
82
  end
83
83
  end
84
84
 
85
- context "deleted accounts" do
85
+ describe "deleted accounts" do
86
86
  it "are hidden" do
87
87
  expect(output.string).to_not match(/delete@example.com/)
88
88
  end
89
89
  end
90
90
  end
91
91
 
92
- context "adding accounts" do
92
+ context "when editing accounts" do
93
+ let(:account) do
94
+ instance_double(Imap::Backup::Configuration::Account, run: nil)
95
+ end
96
+
97
+ before do
98
+ allow(input).to receive(:gets).and_return("1\n", "exit\n")
99
+ allow(Imap::Backup::Configuration::Asker).to receive(:email).
100
+ with(no_args).and_return("new@example.com")
101
+ allow(Imap::Backup::Configuration::Account).to receive(:new).
102
+ with(store, normal, anything).and_return(account)
103
+
104
+ subject.run
105
+ end
106
+
107
+ it "edits the account" do
108
+ expect(account).to have_received(:run)
109
+ end
110
+ end
111
+
112
+ context "when adding accounts" do
93
113
  let(:blank_account) do
94
114
  {
95
115
  username: "new@example.com",
@@ -121,7 +141,7 @@ describe Imap::Backup::Configuration::Setup do
121
141
  end
122
142
  end
123
143
 
124
- context "logging" do
144
+ describe "logging" do
125
145
  context "when debug logging is disabled" do
126
146
  before do
127
147
  allow(input).to receive(:gets).and_return("start\n", "exit\n")
@@ -47,7 +47,7 @@ describe Imap::Backup::Configuration::Store do
47
47
  end
48
48
 
49
49
  describe "#modified?" do
50
- context "'with accounts flagged 'modified'" do
50
+ context "with accounts flagged 'modified'" do
51
51
  let(:accounts) { [{name: "foo", modified: true}] }
52
52
 
53
53
  it "is true" do
@@ -55,7 +55,7 @@ describe Imap::Backup::Configuration::Store do
55
55
  end
56
56
  end
57
57
 
58
- context "'with accounts flagged 'delete'" do
58
+ context "with accounts flagged 'delete'" do
59
59
  let(:accounts) { [{name: "foo", delete: true}] }
60
60
 
61
61
  it "is true" do
@@ -139,7 +139,7 @@ describe Imap::Backup::Configuration::Store do
139
139
  before do
140
140
  allow(FileUtils).to receive(:mkdir)
141
141
  allow(FileUtils).to receive(:chmod)
142
- allow(File).to receive(:open).with(file_path, "w") { |&b| b.call file }
142
+ allow(File).to receive(:open).with(file_path, "w").and_yield(file)
143
143
  allow(JSON).to receive(:pretty_generate).and_return("JSON output")
144
144
  end
145
145
 
@@ -194,7 +194,7 @@ describe Imap::Backup::Configuration::Store do
194
194
  end
195
195
  end
196
196
 
197
- context "if the configuration file is missing" do
197
+ context "when the configuration file is missing" do
198
198
  let(:file_exists) { false }
199
199
 
200
200
  it "doesn't fail" do
@@ -204,7 +204,7 @@ describe Imap::Backup::Configuration::Store do
204
204
  end
205
205
  end
206
206
 
207
- context "if the config file permissions are too lax" do
207
+ context "when the config file permissions are too lax" do
208
208
  let(:file_exists) { true }
209
209
 
210
210
  before do
@@ -23,23 +23,21 @@ describe Imap::Backup::Downloader do
23
23
  subject.run
24
24
  end
25
25
 
26
- context "#run" do
27
- context "fetched messages" do
28
- it "are saved" do
29
- expect(serializer).to have_received(:save).with("111", message)
30
- end
26
+ context "with fetched messages" do
27
+ it "are saved" do
28
+ expect(serializer).to have_received(:save).with("111", message)
31
29
  end
30
+ end
32
31
 
33
- context "messages which are already present" do
34
- specify "are skipped" do
35
- expect(serializer).to_not have_received(:save).with("222", anything)
36
- end
32
+ context "with messages which are already present" do
33
+ specify "are skipped" do
34
+ expect(serializer).to_not have_received(:save).with("222", anything)
37
35
  end
36
+ end
38
37
 
39
- context "failed fetches" do
40
- specify "are skipped" do
41
- expect(serializer).to_not have_received(:save).with("333", anything)
42
- end
38
+ context "with failed fetches" do
39
+ specify "are skipped" do
40
+ expect(serializer).to_not have_received(:save).with("333", anything)
43
41
  end
44
42
  end
45
43
  end
@@ -0,0 +1,40 @@
1
+ require "imap/backup/serializer/mbox_enumerator"
2
+
3
+ describe Imap::Backup::Serializer::MboxEnumerator do
4
+ subject { described_class.new(mbox_pathname) }
5
+
6
+ let(:mbox_pathname) { "/mbox/pathname" }
7
+ let(:mbox_file) { instance_double(File) }
8
+ let(:lines) { message1 + message2 + [nil] }
9
+ let(:message1) do
10
+ [
11
+ "From Frida\r\n",
12
+ "Hello\r\n"
13
+ ]
14
+ end
15
+ let(:message2) do
16
+ [
17
+ "From John\r\n",
18
+ "Hi\r\n"
19
+ ]
20
+ end
21
+
22
+ before do
23
+ allow(File).to receive(:open).and_call_original
24
+ allow(File).to receive(:open).with(mbox_pathname).and_yield(mbox_file)
25
+ allow(mbox_file).to receive(:gets).and_return(*lines)
26
+ end
27
+
28
+ describe "#each" do
29
+ it "yields messages" do
30
+ expect { |b| subject.each(&b) }.
31
+ to yield_successive_args(message1.join, message2.join)
32
+ end
33
+
34
+ context "without a block" do
35
+ it "returns an Enumerator" do
36
+ expect(subject.each).to be_a(Enumerator)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -9,7 +9,8 @@ describe Imap::Backup::Serializer::Mbox do
9
9
  rename: nil,
10
10
  uids: nil,
11
11
  uid_validity: existing_uid_validity,
12
- "uid_validity=": nil
12
+ "uid_validity=": nil,
13
+ update_uid: nil
13
14
  )
14
15
  end
15
16
  let(:imap_folder) { "folder" }
@@ -26,10 +27,10 @@ describe Imap::Backup::Serializer::Mbox do
26
27
  allow(Imap::Backup::Serializer::MboxStore).to receive(:new) { store }
27
28
  end
28
29
 
29
- context "containing directory" do
30
+ describe "folder path" do
30
31
  before { subject.uids }
31
32
 
32
- context "when the IMAP folder has multiple elements" do
33
+ context "when it has multiple elements" do
33
34
  let(:imap_folder) { "folder/path" }
34
35
 
35
36
  context "when the containing directory is missing" do
@@ -42,7 +43,7 @@ describe Imap::Backup::Serializer::Mbox do
42
43
  end
43
44
  end
44
45
 
45
- context "when the containing directory permissons are incorrect" do
46
+ context "when permissions are incorrect" do
46
47
  let(:permissions) { 0o777 }
47
48
 
48
49
  it "corrects them" do
@@ -51,13 +52,13 @@ describe Imap::Backup::Serializer::Mbox do
51
52
  end
52
53
  end
53
54
 
54
- context "when the containing directory permissons are correct" do
55
+ context "when permissons are correct" do
55
56
  it "does nothing" do
56
57
  expect(FileUtils).to_not have_received(:chmod)
57
58
  end
58
59
  end
59
60
 
60
- context "when the containing directory exists" do
61
+ context "when it exists" do
61
62
  it "is not created" do
62
63
  expect(Imap::Backup::Utils).to_not have_received(:make_folder).
63
64
  with(base_path, File.dirname(imap_folder), 0o700)
@@ -65,24 +66,8 @@ describe Imap::Backup::Serializer::Mbox do
65
66
  end
66
67
  end
67
68
 
68
- context "#uids" do
69
- it "calls the store" do
70
- subject.uids
71
-
72
- expect(store).to have_received(:uids)
73
- end
74
- end
75
-
76
- context "#save" do
77
- it "calls the store" do
78
- subject.save("foo", "bar")
79
-
80
- expect(store).to have_received(:add)
81
- end
82
- end
83
-
84
- context "#set_uid_validity" do
85
- let(:result) { subject.set_uid_validity("aaa") }
69
+ describe "#apply_uid_validity" do
70
+ let(:result) { subject.apply_uid_validity("aaa") }
86
71
 
87
72
  context "when the existing uid validity is unset" do
88
73
  let!(:result) { super() }
@@ -158,4 +143,58 @@ describe Imap::Backup::Serializer::Mbox do
158
143
  end
159
144
  end
160
145
  end
146
+
147
+ describe "#force_uid_validity" do
148
+ before { subject.force_uid_validity("66") }
149
+
150
+ it "sets the uid_validity" do
151
+ expect(store).to have_received(:uid_validity=).with("66")
152
+ end
153
+ end
154
+
155
+ describe "#uids" do
156
+ it "calls the store" do
157
+ subject.uids
158
+
159
+ expect(store).to have_received(:uids)
160
+ end
161
+ end
162
+
163
+ describe "#load" do
164
+ let(:result) { subject.load("66") }
165
+
166
+ before { allow(store).to receive(:load).with("66") { "xxx" } }
167
+
168
+ it "returns the value loaded by the store" do
169
+ expect(result).to eq("xxx")
170
+ end
171
+ end
172
+
173
+ describe "#save" do
174
+ before { subject.save("foo", "bar") }
175
+
176
+ it "calls the store" do
177
+ expect(store).to have_received(:add).with("foo", "bar")
178
+ end
179
+ end
180
+
181
+ describe "#rename" do
182
+ before { subject.rename("foo") }
183
+
184
+ it "calls the store" do
185
+ expect(store).to have_received(:rename).with("foo")
186
+ end
187
+
188
+ it "updates the folder name" do
189
+ expect(subject.folder).to eq("foo")
190
+ end
191
+ end
192
+
193
+ describe "#update_uid" do
194
+ before { subject.update_uid("foo", "bar") }
195
+
196
+ it "calls the store" do
197
+ expect(store).to have_received(:update_uid).with("foo", "bar")
198
+ end
199
+ end
161
200
  end