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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rspec-all +2 -0
- data/.rubocop.yml +15 -2
- data/.rubocop_todo.yml +58 -0
- data/.travis.yml +15 -2
- data/README.md +14 -22
- data/Rakefile +6 -3
- data/bin/imap-backup +5 -11
- data/imap-backup.gemspec +10 -6
- data/lib/email/mboxrd/message.rb +16 -16
- data/lib/imap/backup/account/connection.rb +38 -22
- data/lib/imap/backup/account/folder.rb +23 -7
- data/lib/imap/backup/configuration/account.rb +25 -21
- data/lib/imap/backup/configuration/asker.rb +3 -2
- data/lib/imap/backup/configuration/connection_tester.rb +1 -1
- data/lib/imap/backup/configuration/folder_chooser.rb +32 -5
- data/lib/imap/backup/configuration/list.rb +2 -0
- data/lib/imap/backup/configuration/setup.rb +2 -1
- data/lib/imap/backup/configuration/store.rb +3 -6
- data/lib/imap/backup/downloader.rb +8 -7
- data/lib/imap/backup/serializer/mbox.rb +44 -25
- data/lib/imap/backup/serializer/mbox_enumerator.rb +31 -0
- data/lib/imap/backup/serializer/mbox_store.rb +35 -32
- data/lib/imap/backup/uploader.rb +11 -2
- data/lib/imap/backup/utils.rb +11 -9
- data/lib/imap/backup/version.rb +2 -2
- data/spec/features/backup_spec.rb +6 -5
- data/spec/features/helper.rb +1 -1
- data/spec/features/restore_spec.rb +75 -27
- data/spec/features/support/backup_directory.rb +7 -7
- data/spec/features/support/email_server.rb +15 -11
- data/spec/features/support/shared/connection_context.rb +2 -2
- data/spec/features/support/shared/message_fixtures.rb +8 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/fixtures.rb +2 -2
- data/spec/support/higline_test_helpers.rb +1 -1
- data/spec/unit/email/mboxrd/message_spec.rb +73 -53
- data/spec/unit/email/provider_spec.rb +3 -5
- data/spec/unit/imap/backup/account/connection_spec.rb +82 -59
- data/spec/unit/imap/backup/account/folder_spec.rb +75 -37
- data/spec/unit/imap/backup/configuration/account_spec.rb +95 -61
- data/spec/unit/imap/backup/configuration/asker_spec.rb +43 -45
- data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +21 -22
- data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +66 -33
- data/spec/unit/imap/backup/configuration/list_spec.rb +32 -11
- data/spec/unit/imap/backup/configuration/setup_spec.rb +97 -56
- data/spec/unit/imap/backup/configuration/store_spec.rb +30 -25
- data/spec/unit/imap/backup/downloader_spec.rb +28 -26
- data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +45 -0
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +109 -51
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +232 -20
- data/spec/unit/imap/backup/uploader_spec.rb +23 -9
- data/spec/unit/imap/backup/utils_spec.rb +14 -15
- data/spec/unit/imap/backup_spec.rb +28 -0
- 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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
:
|
13
|
-
|
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)
|
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,
|
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
|
-
|
33
|
+
expect(highline).to receive(:ask).with("#{prompt}: ")
|
36
34
|
|
37
|
-
|
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
|
-
|
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
|
-
|
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
|
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(
|
67
|
+
expect(subject.email).to eq(email)
|
72
68
|
end
|
73
69
|
end
|
74
70
|
|
75
|
-
|
71
|
+
describe "#password" do
|
76
72
|
let(:password1) { "password" }
|
77
73
|
let(:password2) { "password" }
|
78
|
-
let(:answers) { [
|
79
|
-
let(:answer1) { true }
|
80
|
-
let(:answer2) { false }
|
74
|
+
let(:answers) { [true, false] }
|
81
75
|
|
82
76
|
before do
|
83
|
-
|
84
|
-
allow(highline).to receive(:ask).
|
85
|
-
|
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[
|
90
|
-
|
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
|
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
|
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(
|
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
|
113
|
-
at_least(
|
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
|
-
|
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
|
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(
|
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
|
-
|
5
|
-
let(:connection)
|
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
|
-
|
12
|
-
before { @result = subject.test("foo") }
|
13
|
-
|
11
|
+
describe "call" do
|
14
12
|
it "tries to connect" do
|
15
|
-
expect(connection).to
|
13
|
+
expect(connection).to receive(:imap)
|
14
|
+
|
15
|
+
subject.test("foo")
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
before { @result = subject.test("foo") }
|
21
|
-
|
19
|
+
describe "success" do
|
22
20
|
it "returns success" do
|
23
|
-
expect(
|
21
|
+
expect(subject.test("foo")).to match(/successful/)
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
|
-
|
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 =
|
36
|
-
|
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
|
40
|
-
expect(
|
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
|
-
|
47
|
-
|
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
|
-
|
4
|
+
describe "#run" do
|
5
|
+
subject { described_class.new(account) }
|
6
|
+
|
7
7
|
let(:connection) do
|
8
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
24
|
-
before { subject.run }
|
25
|
-
|
24
|
+
describe "display" do
|
26
25
|
it "clears the screen" do
|
27
|
-
expect(
|
26
|
+
expect(Kernel).to receive(:system).with("clear")
|
27
|
+
|
28
|
+
subject.run
|
28
29
|
end
|
29
30
|
|
30
|
-
it "
|
31
|
-
|
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
|
-
|
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 =
|
40
|
-
|
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
|
-
|
51
|
+
describe "display" do
|
45
52
|
before { subject.run }
|
46
53
|
|
47
54
|
it "shows folders which are being backed up" do
|
48
|
-
expect(
|
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(
|
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(
|
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(
|
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)
|
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
|
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)
|
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
|
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
|
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
|
-
|
11
|
+
instance_double(Imap::Backup::Configuration::Store, accounts: accounts)
|
12
12
|
end
|
13
13
|
let(:exists) { true }
|
14
14
|
let(:connection1) do
|
15
|
-
|
15
|
+
instance_double(Imap::Backup::Account::Connection, disconnect: nil)
|
16
16
|
end
|
17
17
|
let(:connection2) do
|
18
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 "
|
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
|
-
|
84
|
+
end.to raise_error(Imap::Backup::ConfigurationNotFound, /not found/)
|
64
85
|
end
|
65
86
|
end
|
66
87
|
end
|