imap-backup 2.0.0 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
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