imap-backup 2.0.0 → 2.2.2

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rspec-all +2 -0
  4. data/.rubocop.yml +15 -2
  5. data/.rubocop_todo.yml +58 -0
  6. data/.travis.yml +15 -2
  7. data/README.md +14 -22
  8. data/Rakefile +6 -3
  9. data/bin/imap-backup +5 -11
  10. data/imap-backup.gemspec +10 -6
  11. data/lib/email/mboxrd/message.rb +16 -16
  12. data/lib/imap/backup/account/connection.rb +38 -22
  13. data/lib/imap/backup/account/folder.rb +23 -7
  14. data/lib/imap/backup/configuration/account.rb +25 -21
  15. data/lib/imap/backup/configuration/asker.rb +3 -2
  16. data/lib/imap/backup/configuration/connection_tester.rb +1 -1
  17. data/lib/imap/backup/configuration/folder_chooser.rb +32 -5
  18. data/lib/imap/backup/configuration/list.rb +2 -0
  19. data/lib/imap/backup/configuration/setup.rb +2 -1
  20. data/lib/imap/backup/configuration/store.rb +3 -6
  21. data/lib/imap/backup/downloader.rb +8 -7
  22. data/lib/imap/backup/serializer/mbox.rb +44 -25
  23. data/lib/imap/backup/serializer/mbox_enumerator.rb +31 -0
  24. data/lib/imap/backup/serializer/mbox_store.rb +35 -32
  25. data/lib/imap/backup/uploader.rb +11 -2
  26. data/lib/imap/backup/utils.rb +11 -9
  27. data/lib/imap/backup/version.rb +2 -2
  28. data/spec/features/backup_spec.rb +6 -5
  29. data/spec/features/helper.rb +1 -1
  30. data/spec/features/restore_spec.rb +75 -27
  31. data/spec/features/support/backup_directory.rb +7 -7
  32. data/spec/features/support/email_server.rb +15 -11
  33. data/spec/features/support/shared/connection_context.rb +2 -2
  34. data/spec/features/support/shared/message_fixtures.rb +8 -0
  35. data/spec/spec_helper.rb +1 -1
  36. data/spec/support/fixtures.rb +2 -2
  37. data/spec/support/higline_test_helpers.rb +1 -1
  38. data/spec/unit/email/mboxrd/message_spec.rb +73 -53
  39. data/spec/unit/email/provider_spec.rb +3 -5
  40. data/spec/unit/imap/backup/account/connection_spec.rb +82 -59
  41. data/spec/unit/imap/backup/account/folder_spec.rb +75 -37
  42. data/spec/unit/imap/backup/configuration/account_spec.rb +95 -61
  43. data/spec/unit/imap/backup/configuration/asker_spec.rb +43 -45
  44. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +21 -22
  45. data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +66 -33
  46. data/spec/unit/imap/backup/configuration/list_spec.rb +32 -11
  47. data/spec/unit/imap/backup/configuration/setup_spec.rb +97 -56
  48. data/spec/unit/imap/backup/configuration/store_spec.rb +30 -25
  49. data/spec/unit/imap/backup/downloader_spec.rb +28 -26
  50. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +45 -0
  51. data/spec/unit/imap/backup/serializer/mbox_spec.rb +109 -51
  52. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +232 -20
  53. data/spec/unit/imap/backup/uploader_spec.rb +23 -9
  54. data/spec/unit/imap/backup/utils_spec.rb +14 -15
  55. data/spec/unit/imap/backup_spec.rb +28 -0
  56. metadata +13 -7
@@ -1,40 +1,38 @@
1
- require "spec_helper"
2
-
3
1
  module Imap::Backup
4
2
  describe Configuration::Asker do
3
+ subject { described_class.new(highline) }
4
+
5
5
  let(:highline) { double }
6
6
  let(:query) do
7
- double(
8
- "Query",
9
- :default= => nil,
10
- :readline= => nil,
11
- :validate= => nil,
12
- :responses => {},
13
- :echo= => nil
7
+ instance_double(
8
+ HighLine::Question,
9
+ "default=": nil,
10
+ "readline=": nil,
11
+ "validate=": nil,
12
+ "responses": {},
13
+ "echo=": nil
14
14
  )
15
15
  end
16
16
  let(:answer) { "foo" }
17
17
 
18
18
  before do
19
- allow(Configuration::Setup).to receive(:highline).and_return(highline)
19
+ allow(Configuration::Setup).to receive(:highline) { highline }
20
20
  allow(highline).to receive(:ask) do |&b|
21
21
  b.call query
22
22
  answer
23
23
  end
24
24
  end
25
25
 
26
- subject { described_class.new(highline) }
27
-
28
26
  [
29
27
  [:email, [], "email address"],
30
28
  [:password, [], "password"],
31
- [:backup_path, ["x", "y"], "backup directory"]
29
+ [:backup_path, %w(x y), "backup directory"]
32
30
  ].each do |method, params, prompt|
33
31
  context ".#{method}" do
34
32
  it "asks for input" do
35
- described_class.send(method, *params)
33
+ expect(highline).to receive(:ask).with("#{prompt}: ")
36
34
 
37
- expect(highline).to have_received(:ask).with("#{prompt}: ")
35
+ described_class.send(method, *params)
38
36
  end
39
37
 
40
38
  it "returns the answer" do
@@ -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,67 +53,66 @@ 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
 
62
- before do
63
- @result = subject.email
64
- end
65
-
66
60
  it "asks for an email" do
67
- expect(highline).to have_received(:ask).with(/email/)
61
+ expect(highline).to receive(:ask).with(/email/)
62
+
63
+ subject.email
68
64
  end
69
65
 
70
66
  it "returns the address" do
71
- expect(@result).to eq(email)
67
+ expect(subject.email).to eq(email)
72
68
  end
73
69
  end
74
70
 
75
- context "#password" do
71
+ describe "#password" do
76
72
  let(:password1) { "password" }
77
73
  let(:password2) { "password" }
78
- let(:answers) { [answer1, answer2] }
79
- let(:answer1) { true }
80
- let(:answer2) { false }
74
+ let(:answers) { [true, false] }
81
75
 
82
76
  before do
83
- @i = 0
84
- allow(highline).to receive(:ask).
85
- with("password: ").and_return(password1)
86
- allow(highline).to receive(:ask).
87
- with("repeat password: ").and_return(password2)
77
+ i = 0
78
+ allow(highline).to receive(:ask).with("password: ") { password1 }
79
+ allow(highline).to receive(:ask).with("repeat password: ") { password2 }
88
80
  allow(highline).to receive(:agree) do
89
- answer = answers[@i]
90
- @i += 1
81
+ answer = answers[i]
82
+ i += 1
91
83
  answer
92
84
  end
93
- @result = subject.password
94
85
  end
95
86
 
96
87
  it "asks for a password" do
97
- expect(highline).to have_received(:ask).with("password: ")
88
+ expect(highline).to receive(:ask).with("password: ")
89
+
90
+ subject.password
98
91
  end
99
92
 
100
93
  it "asks for confirmation" do
101
- expect(highline).to have_received(:ask).with("repeat password: ")
94
+ expect(highline).to receive(:ask).with("repeat password: ")
95
+
96
+ subject.password
102
97
  end
103
98
 
104
99
  it "returns the password" do
105
- expect(@result).to eq(password1)
100
+ expect(subject.password).to eq(password1)
106
101
  end
107
102
 
108
- context "different answers" do
103
+ context "with different answers" do
109
104
  let(:password2) { "secret" }
110
105
 
111
106
  it "asks to continue" do
112
- expect(highline).to have_received(:agree).
113
- at_least(1).times.with(/Continue\?/)
107
+ expect(highline).to receive(:agree).
108
+ at_least(:once).with(/Continue\?/)
109
+
110
+ subject.password
114
111
  end
115
112
  end
116
113
  end
117
114
 
118
- context "#backup_path" do
115
+ describe "#backup_path" do
119
116
  let(:path) { "/path" }
120
117
  let(:answer) { path }
121
118
 
@@ -124,15 +121,16 @@ module Imap::Backup
124
121
  b.call query
125
122
  path
126
123
  end
127
- @result = subject.backup_path("", //)
128
124
  end
129
125
 
130
126
  it "asks for a directory" do
131
- expect(highline).to have_received(:ask).with(/directory/)
127
+ expect(highline).to receive(:ask).with(/directory/)
128
+
129
+ subject.backup_path("", //)
132
130
  end
133
131
 
134
132
  it "returns the path" do
135
- expect(@result).to eq(path)
133
+ expect(subject.backup_path("", //)).to eq(path)
136
134
  end
137
135
  end
138
136
  end
@@ -1,50 +1,49 @@
1
- require "spec_helper"
2
-
3
1
  describe Imap::Backup::Configuration::ConnectionTester do
4
- context ".test" do
5
- let(:connection) { double("Imap::Backup::Account::Connection", imap: nil) }
2
+ describe ".test" do
3
+ let(:connection) do
4
+ instance_double(Imap::Backup::Account::Connection, imap: nil)
5
+ end
6
6
 
7
7
  before do
8
8
  allow(Imap::Backup::Account::Connection).to receive(:new) { connection }
9
9
  end
10
10
 
11
- context "call" do
12
- before { @result = subject.test("foo") }
13
-
11
+ describe "call" do
14
12
  it "tries to connect" do
15
- expect(connection).to have_received(:imap)
13
+ expect(connection).to receive(:imap)
14
+
15
+ subject.test("foo")
16
16
  end
17
17
  end
18
18
 
19
- context "success" do
20
- before { @result = subject.test("foo") }
21
-
19
+ describe "success" do
22
20
  it "returns success" do
23
- expect(@result).to match(/successful/)
21
+ expect(subject.test("foo")).to match(/successful/)
24
22
  end
25
23
  end
26
24
 
27
- context "failure" do
25
+ describe "failure" do
28
26
  before do
29
27
  allow(connection).to receive(:imap).and_raise(error)
30
- @result = subject.test("foo")
31
28
  end
32
29
 
33
- context "no connection" do
30
+ context "with no connection" do
34
31
  let(:error) do
35
- data = double("foo", text: "bar")
36
- Net::IMAP::NoResponseError.new(double("o", data: data))
32
+ data = OpenStruct.new(text: "bar")
33
+ response = OpenStruct.new(data: data)
34
+ Net::IMAP::NoResponseError.new(response)
37
35
  end
38
36
 
39
- it "returns success" do
40
- expect(@result).to match(/no response/i)
37
+ it "returns error" do
38
+ expect(subject.test("foo")).to match(/no response/i)
41
39
  end
42
40
  end
43
41
 
44
- context "other" do
42
+ context "when caused by other errors" do
45
43
  let(:error) { "Error" }
46
- it "returns success" do
47
- expect(@result).to match(/unexpected error/i)
44
+
45
+ it "returns error" do
46
+ expect(subject.test("foo")).to match(/unexpected error/i)
48
47
  end
49
48
  end
50
49
  end
@@ -1,61 +1,68 @@
1
- require "spec_helper"
2
-
3
1
  describe Imap::Backup::Configuration::FolderChooser do
4
2
  include HighLineTestHelpers
5
3
 
6
- context "#run" do
4
+ describe "#run" do
5
+ subject { described_class.new(account) }
6
+
7
7
  let(:connection) do
8
- double("Imap::Backup::Account::Connection", folders: remote_folders)
8
+ instance_double(
9
+ Imap::Backup::Account::Connection, folders: remote_folders
10
+ )
9
11
  end
10
12
  let(:account) { {folders: []} }
11
13
  let(:remote_folders) { [] }
12
-
13
- subject { described_class.new(account) }
14
+ let!(:highline_streams) { prepare_highline }
15
+ let(:input) { highline_streams[0] }
16
+ let(:output) { highline_streams[1] }
14
17
 
15
18
  before do
16
- allow(Imap::Backup::Account::Connection).
17
- to receive(:new).with(account) { connection }
18
- @input, @output = prepare_highline
19
- allow(subject).to receive(:system)
19
+ allow(Imap::Backup::Account::Connection).to receive(:new) { connection }
20
+ allow(Kernel).to receive(:system)
20
21
  allow(Imap::Backup.logger).to receive(:warn)
21
22
  end
22
23
 
23
- context "display" do
24
- before { subject.run }
25
-
24
+ describe "display" do
26
25
  it "clears the screen" do
27
- expect(subject).to have_received(:system).with("clear")
26
+ expect(Kernel).to receive(:system).with("clear")
27
+
28
+ subject.run
28
29
  end
29
30
 
30
- it "should show the menu" do
31
- expect(@output.string).to match %r{Add/remove folders}
31
+ it "shows the menu" do
32
+ subject.run
33
+
34
+ expect(output.string).to match %r{Add/remove folders}
32
35
  end
33
36
  end
34
37
 
35
- context "folder listing" do
38
+ describe "folder listing" do
36
39
  let(:account) { {folders: [{name: "my_folder"}]} }
37
40
  let(:remote_folders) do
38
41
  # this one is already backed up:
39
- folder1 = double("folder", name: "my_folder")
40
- folder2 = double("folder", name: "another_folder")
42
+ folder1 = instance_double(
43
+ Imap::Backup::Account::Folder, name: "my_folder"
44
+ )
45
+ folder2 = instance_double(
46
+ Imap::Backup::Account::Folder, name: "another_folder"
47
+ )
41
48
  [folder1, folder2]
42
49
  end
43
50
 
44
- context "display" do
51
+ describe "display" do
45
52
  before { subject.run }
46
53
 
47
54
  it "shows folders which are being backed up" do
48
- expect(@output.string).to include("+ my_folder")
55
+ expect(output.string).to include("+ my_folder")
49
56
  end
50
57
 
51
58
  it "shows folders which are not being backed up" do
52
- expect(@output.string).to include("- another_folder")
59
+ expect(output.string).to include("- another_folder")
53
60
  end
54
61
  end
55
62
 
56
- context "adding folders" do
63
+ context "when adding folders" do
57
64
  before do
58
- allow(@input).to receive(:gets).and_return("2\n", "q\n")
65
+ allow(input).to receive(:gets).and_return("2\n", "q\n")
59
66
 
60
67
  subject.run
61
68
  end
@@ -67,9 +74,9 @@ describe Imap::Backup::Configuration::FolderChooser do
67
74
  include_examples "it flags the account as modified"
68
75
  end
69
76
 
70
- context "removing folders" do
77
+ context "when removing folders" do
71
78
  before do
72
- allow(@input).to receive(:gets).and_return("1\n", "q\n")
79
+ allow(input).to receive(:gets).and_return("1\n", "q\n")
73
80
 
74
81
  subject.run
75
82
  end
@@ -82,18 +89,41 @@ describe Imap::Backup::Configuration::FolderChooser do
82
89
  end
83
90
  end
84
91
 
92
+ context "with missing remote folders" do
93
+ let(:account) do
94
+ {folders: [{name: "on_server"}, {name: "not_on_server"}]}
95
+ end
96
+ let(:remote_folders) do
97
+ [
98
+ instance_double(Imap::Backup::Account::Folder, name: "on_server")
99
+ ]
100
+ end
101
+
102
+ before do
103
+ allow(Kernel).to receive(:puts)
104
+ subject.run
105
+ end
106
+
107
+ specify "are removed from the account" do
108
+ expect(account[:folders]).to_not include(name: "not_on_server")
109
+ end
110
+
111
+ include_examples "it flags the account as modified"
112
+ end
113
+
85
114
  context "when folders are not available" do
86
115
  let(:remote_folders) { nil }
87
116
 
88
117
  before do
89
118
  allow(Imap::Backup::Configuration::Setup.highline).
90
- to receive(:ask).and_return("q")
91
- subject.run
119
+ to receive(:ask) { "q" }
92
120
  end
93
121
 
94
122
  it "asks to press a key" do
95
123
  expect(Imap::Backup::Configuration::Setup.highline).
96
- to have_received(:ask).with("Press a key ")
124
+ to receive(:ask).with("Press a key ")
125
+
126
+ subject.run
97
127
  end
98
128
  end
99
129
 
@@ -102,18 +132,21 @@ describe Imap::Backup::Configuration::FolderChooser do
102
132
  allow(Imap::Backup::Account::Connection).
103
133
  to receive(:new).with(account).and_raise("error")
104
134
  allow(Imap::Backup::Configuration::Setup.highline).
105
- to receive(:ask).and_return("q")
106
- subject.run
135
+ to receive(:ask) { "q" }
107
136
  end
108
137
 
109
138
  it "prints an error message" do
110
139
  expect(Imap::Backup.logger).
111
- to have_received(:warn).with("Connection failed")
140
+ to receive(:warn).with("Connection failed")
141
+
142
+ subject.run
112
143
  end
113
144
 
114
145
  it "asks to continue" do
115
146
  expect(Imap::Backup::Configuration::Setup.highline).
116
- to have_received(:ask).with("Press a key ")
147
+ to receive(:ask).with("Press a key ")
148
+
149
+ subject.run
117
150
  end
118
151
  end
119
152
  end
@@ -1,6 +1,6 @@
1
- require "spec_helper"
2
-
3
1
  describe Imap::Backup::Configuration::List do
2
+ subject { described_class.new }
3
+
4
4
  let(:accounts) do
5
5
  [
6
6
  {username: "a1@example.com"},
@@ -8,14 +8,14 @@ describe Imap::Backup::Configuration::List do
8
8
  ]
9
9
  end
10
10
  let(:store) do
11
- double("Imap::Backup::Configuration::Store", accounts: accounts)
11
+ instance_double(Imap::Backup::Configuration::Store, accounts: accounts)
12
12
  end
13
13
  let(:exists) { true }
14
14
  let(:connection1) do
15
- double("Imap::Backup::Account::Connection", disconnect: nil)
15
+ instance_double(Imap::Backup::Account::Connection, disconnect: nil)
16
16
  end
17
17
  let(:connection2) do
18
- double("Imap::Backup::Account::Connection", disconnect: nil)
18
+ instance_double(Imap::Backup::Account::Connection, disconnect: nil)
19
19
  end
20
20
 
21
21
  before do
@@ -28,12 +28,33 @@ describe Imap::Backup::Configuration::List do
28
28
  to receive(:new).with(accounts[1]) { connection2 }
29
29
  end
30
30
 
31
- subject { described_class.new }
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
+ end
39
+
40
+ it "sets global logging level" do
41
+ expect(Imap::Backup).to receive(:setup_logging).with(store)
32
42
 
33
- context "#initialize" do
43
+ subject.setup_logging
44
+ end
45
+
46
+ context "without a config" do
47
+ let(:config_exists) { false }
48
+
49
+ it "does nothing" do
50
+ expect(Imap::Backup).to_not receive(:setup_logging).with(store)
51
+
52
+ subject.setup_logging
53
+ end
54
+ end
34
55
  end
35
56
 
36
- context "#each_connection" do
57
+ describe "#each_connection" do
37
58
  specify "calls the block with each account's connection" do
38
59
  connections = []
39
60
 
@@ -45,7 +66,7 @@ describe Imap::Backup::Configuration::List do
45
66
  context "with account parameter" do
46
67
  subject { described_class.new(["a2@example.com"]) }
47
68
 
48
- it "should only create requested accounts" do
69
+ it "only creates requested accounts" do
49
70
  connections = []
50
71
 
51
72
  subject.each_connection { |a| connections << a }
@@ -58,9 +79,9 @@ describe Imap::Backup::Configuration::List do
58
79
  let(:exists) { false }
59
80
 
60
81
  it "fails" do
61
- expect {
82
+ expect do
62
83
  subject.each_connection {}
63
- }.to raise_error(Imap::Backup::ConfigurationNotFound, /not found/)
84
+ end.to raise_error(Imap::Backup::ConfigurationNotFound, /not found/)
64
85
  end
65
86
  end
66
87
  end