imap-backup 5.2.0 → 6.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -2
- data/docs/development.md +10 -4
- data/lib/cli_coverage.rb +1 -1
- data/lib/imap/backup/account/connection.rb +7 -11
- data/lib/imap/backup/account.rb +31 -11
- data/lib/imap/backup/cli/folders.rb +3 -3
- data/lib/imap/backup/cli/migrate.rb +3 -3
- data/lib/imap/backup/cli/utils.rb +2 -2
- data/lib/imap/backup/configuration.rb +1 -11
- data/lib/imap/backup/downloader.rb +13 -9
- data/lib/imap/backup/serializer/directory.rb +37 -0
- data/lib/imap/backup/serializer/imap.rb +120 -0
- data/lib/imap/backup/serializer/mbox.rb +23 -94
- data/lib/imap/backup/serializer/mbox_enumerator.rb +2 -0
- data/lib/imap/backup/serializer.rb +180 -3
- data/lib/imap/backup/setup/account.rb +52 -29
- data/lib/imap/backup/setup/helpers.rb +1 -1
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +1 -1
- data/lib/imap/backup/version.rb +3 -3
- data/lib/imap/backup.rb +0 -1
- data/spec/features/backup_spec.rb +8 -16
- data/spec/features/support/aruba.rb +4 -3
- data/spec/unit/imap/backup/account/connection_spec.rb +36 -8
- data/spec/unit/imap/backup/account/folder_spec.rb +10 -0
- data/spec/unit/imap/backup/account_spec.rb +246 -0
- data/spec/unit/imap/backup/cli/accounts_spec.rb +12 -1
- data/spec/unit/imap/backup/cli/backup_spec.rb +19 -0
- data/spec/unit/imap/backup/cli/folders_spec.rb +39 -0
- data/spec/unit/imap/backup/cli/local_spec.rb +26 -7
- data/spec/unit/imap/backup/cli/migrate_spec.rb +80 -0
- data/spec/unit/imap/backup/cli/restore_spec.rb +67 -0
- data/spec/unit/imap/backup/cli/setup_spec.rb +17 -0
- data/spec/unit/imap/backup/cli/utils_spec.rb +68 -5
- data/spec/unit/imap/backup/cli_spec.rb +93 -0
- data/spec/unit/imap/backup/client/apple_mail_spec.rb +9 -0
- data/spec/unit/imap/backup/configuration_spec.rb +2 -2
- data/spec/unit/imap/backup/downloader_spec.rb +59 -7
- data/spec/unit/imap/backup/migrator_spec.rb +1 -1
- data/spec/unit/imap/backup/sanitizer_spec.rb +42 -0
- data/spec/unit/imap/backup/serializer/directory_spec.rb +37 -0
- data/spec/unit/imap/backup/serializer/imap_spec.rb +218 -0
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -183
- data/spec/unit/imap/backup/serializer_spec.rb +296 -0
- data/spec/unit/imap/backup/setup/account_spec.rb +120 -25
- data/spec/unit/imap/backup/setup/helpers_spec.rb +15 -0
- data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +116 -0
- data/spec/unit/imap/backup/uploader_spec.rb +1 -1
- data/spec/unit/retry_on_error_spec.rb +34 -0
- metadata +36 -7
- data/lib/imap/backup/serializer/mbox_store.rb +0 -217
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +0 -329
@@ -0,0 +1,246 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe Account do
|
3
|
+
subject { described_class.new(options) }
|
4
|
+
|
5
|
+
let(:options) { {username: "user", password: "pwd"} }
|
6
|
+
|
7
|
+
describe "#changes" do
|
8
|
+
it "lists changes" do
|
9
|
+
subject.username = "new"
|
10
|
+
|
11
|
+
expect(subject.changes).to eq(username: {from: "user", to: "new"})
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when more than one change is made" do
|
15
|
+
it "retains the last one" do
|
16
|
+
subject.username = "first"
|
17
|
+
subject.username = "second"
|
18
|
+
|
19
|
+
expect(subject.changes).to eq(username: {from: "user", to: "second"})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when a value is reset to its original value" do
|
24
|
+
it "removes the change" do
|
25
|
+
subject.username = "changed"
|
26
|
+
subject.username = "user"
|
27
|
+
|
28
|
+
expect(subject.changes).to eq({})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#connection" do
|
34
|
+
it "returns a Connection for the Account" do
|
35
|
+
result = subject.connection
|
36
|
+
|
37
|
+
expect(result).to be_a(Account::Connection)
|
38
|
+
expect(result.account).to be subject
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#valid?" do
|
43
|
+
context "with username and password" do
|
44
|
+
it "is true" do
|
45
|
+
expect(subject.valid?).to be true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "without a username" do
|
50
|
+
let(:options) { {password: "pwd"} }
|
51
|
+
|
52
|
+
it "is false" do
|
53
|
+
expect(subject.valid?).to be false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "without a password" do
|
58
|
+
let(:options) { {username: "user"} }
|
59
|
+
|
60
|
+
it "is false" do
|
61
|
+
expect(subject.valid?).to be false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#modified?" do
|
67
|
+
context "with changes" do
|
68
|
+
it "is true" do
|
69
|
+
subject.username = "new"
|
70
|
+
|
71
|
+
expect(subject.modified?).to be true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "without changes" do
|
76
|
+
it "is false" do
|
77
|
+
expect(subject.modified?).to be false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#clear_changes" do
|
83
|
+
it "clears changes" do
|
84
|
+
subject.username = "new"
|
85
|
+
subject.clear_changes
|
86
|
+
|
87
|
+
expect(subject.changes).to eq({})
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#mark_for_deletion" do
|
92
|
+
it "sets marked_for_deletion" do
|
93
|
+
subject.mark_for_deletion
|
94
|
+
|
95
|
+
expect(subject.marked_for_deletion?).to be true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#marked_for_deletion?" do
|
100
|
+
it "defaults to false" do
|
101
|
+
expect(subject.marked_for_deletion?).to be false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#to_h" do
|
106
|
+
it "returns a Hash representation" do
|
107
|
+
expect(subject.to_h).to eq({username: "user", password: "pwd"})
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when local_path is set" do
|
111
|
+
let(:options) { {username: "user", password: "pwd", local_path: "local_path"} }
|
112
|
+
|
113
|
+
it "includes local_path" do
|
114
|
+
expect(subject.to_h).to include({local_path: "local_path"})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when folders is set" do
|
119
|
+
let(:options) { {username: "user", password: "pwd", folders: ["folder"]} }
|
120
|
+
|
121
|
+
it "includes folders" do
|
122
|
+
expect(subject.to_h).to include({folders: ["folder"]})
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when multi_fetch_size is set" do
|
127
|
+
let(:options) { {username: "user", password: "pwd", multi_fetch_size: "3"} }
|
128
|
+
|
129
|
+
it "includes multi_fetch_size" do
|
130
|
+
expect(subject.to_h).to include({multi_fetch_size: 3})
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when server is set" do
|
135
|
+
let(:options) { {username: "user", password: "pwd", server: "server"} }
|
136
|
+
|
137
|
+
it "includes server" do
|
138
|
+
expect(subject.to_h).to include({server: "server"})
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "when connection_options is set" do
|
143
|
+
let(:options) { {username: "user", password: "pwd", connection_options: "foo"} }
|
144
|
+
|
145
|
+
it "includes connection_options" do
|
146
|
+
expect(subject.to_h).to include({connection_options: "foo"})
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#multi_fetch_size" do
|
152
|
+
let(:options) { {username: "user", password: "pwd", multi_fetch_size: multi_fetch_size} }
|
153
|
+
let(:multi_fetch_size) { 10 }
|
154
|
+
|
155
|
+
it "returns the initialized value" do
|
156
|
+
expect(subject.multi_fetch_size).to eq(10)
|
157
|
+
end
|
158
|
+
|
159
|
+
context "when the initialized value is a string representation of a positive number" do
|
160
|
+
let(:multi_fetch_size) { "10" }
|
161
|
+
|
162
|
+
it "returns one" do
|
163
|
+
expect(subject.multi_fetch_size).to eq(10)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "when the initialized value is not a number" do
|
168
|
+
let(:multi_fetch_size) { "ciao" }
|
169
|
+
|
170
|
+
it "returns one" do
|
171
|
+
expect(subject.multi_fetch_size).to eq(1)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when the initialized value is not a positive number" do
|
176
|
+
let(:multi_fetch_size) { "-99" }
|
177
|
+
|
178
|
+
it "returns one" do
|
179
|
+
expect(subject.multi_fetch_size).to eq(1)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
[
|
185
|
+
[:username, "username", "username"],
|
186
|
+
[:password, "password", "password"],
|
187
|
+
[:local_path, "local_path", "local_path"],
|
188
|
+
[:multi_fetch_size, "2", 2],
|
189
|
+
[:server, "server", "server"],
|
190
|
+
[:folders, ["folder"], ["folder"]],
|
191
|
+
[:connection_options, '{"some": "option"}', {"some" => "option"}]
|
192
|
+
].each do |attribute, value, expected|
|
193
|
+
describe "##{attribute}=" do
|
194
|
+
let(:options) { {} }
|
195
|
+
|
196
|
+
before { subject.send(:"#{attribute}=", value) }
|
197
|
+
|
198
|
+
it "sets the #{attribute}" do
|
199
|
+
expect(subject.send(attribute)).to eq(expected)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "adds a change" do
|
203
|
+
expect(subject.changes).to eq(attribute => {from: nil, to: expected})
|
204
|
+
end
|
205
|
+
|
206
|
+
if attribute == :folders
|
207
|
+
context "when the supplied value is not an Array" do
|
208
|
+
it "fails" do
|
209
|
+
expect do
|
210
|
+
subject.folders = "aaa"
|
211
|
+
end.to raise_error(RuntimeError, /must be an Array/)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if attribute == :multi_fetch_size
|
217
|
+
context "when the supplied value is not a number" do
|
218
|
+
before { subject.multi_fetch_size = "ciao" }
|
219
|
+
|
220
|
+
it "sets multi_fetch_size to one" do
|
221
|
+
expect(subject.multi_fetch_size).to eq(1)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "when the supplied value is not a positive number" do
|
226
|
+
before { subject.multi_fetch_size = "-1" }
|
227
|
+
|
228
|
+
it "sets multi_fetch_size to one" do
|
229
|
+
expect(subject.multi_fetch_size).to eq(1)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
if attribute == :connection_options
|
235
|
+
context "when the supplied value is not valid JSON" do
|
236
|
+
it "fails" do
|
237
|
+
expect do
|
238
|
+
subject.connection_options = "NOT JSON"
|
239
|
+
end.to raise_error(JSON::ParserError)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require "imap/backup/cli/accounts"
|
2
2
|
|
3
3
|
describe Imap::Backup::CLI::Accounts do
|
4
|
-
subject { described_class.new }
|
4
|
+
subject { described_class.new(required_accounts) }
|
5
5
|
|
6
|
+
let(:required_accounts) { [] }
|
6
7
|
let(:accounts) { [account1, account2] }
|
7
8
|
let(:account1) do
|
8
9
|
instance_double(
|
@@ -43,5 +44,15 @@ describe Imap::Backup::CLI::Accounts do
|
|
43
44
|
end.to raise_error(Imap::Backup::ConfigurationNotFound, /not found/)
|
44
45
|
end
|
45
46
|
end
|
47
|
+
|
48
|
+
context "when an account list is provided" do
|
49
|
+
let(:required_accounts) { %w(a2@example.com) }
|
50
|
+
|
51
|
+
specify "calls the block with each account" do
|
52
|
+
result = subject.map { |a| a }
|
53
|
+
|
54
|
+
expect(result).to eq([account2])
|
55
|
+
end
|
56
|
+
end
|
46
57
|
end
|
47
58
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe CLI::Backup do
|
3
|
+
subject { described_class.new({}) }
|
4
|
+
|
5
|
+
let(:connection) { instance_double(Account::Connection, run_backup: nil) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
# rubocop:disable RSpec/SubjectStub
|
9
|
+
allow(subject).to receive(:each_connection).with([]).and_yield(connection)
|
10
|
+
# rubocop:enable RSpec/SubjectStub
|
11
|
+
end
|
12
|
+
|
13
|
+
it "runs the backup for each connection" do
|
14
|
+
subject.run
|
15
|
+
|
16
|
+
expect(connection).to have_received(:run_backup)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe CLI::Folders do
|
3
|
+
subject { described_class.new({}) }
|
4
|
+
|
5
|
+
let(:connection) do
|
6
|
+
instance_double(
|
7
|
+
Account::Connection, account: account, folder_names: folder_names
|
8
|
+
)
|
9
|
+
end
|
10
|
+
let(:account) { instance_double(Account, username: "user") }
|
11
|
+
let(:folder_names) { ["my-folder"] }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(Kernel).to receive(:puts)
|
15
|
+
allow(Kernel).to receive(:warn)
|
16
|
+
# rubocop:disable RSpec/SubjectStub
|
17
|
+
allow(subject).to receive(:each_connection).with([]).and_yield(connection)
|
18
|
+
# rubocop:enable RSpec/SubjectStub
|
19
|
+
|
20
|
+
subject.run
|
21
|
+
end
|
22
|
+
|
23
|
+
it "lists folders" do
|
24
|
+
expect(Kernel).to have_received(:puts).with("\tmy-folder")
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when the folder list is not fetched" do
|
28
|
+
let(:folder_names) { nil }
|
29
|
+
|
30
|
+
it "warns" do
|
31
|
+
expect(Kernel).to have_received(:warn).with(/Unable to list/)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "doesn't fail" do
|
35
|
+
subject.run
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -23,20 +23,22 @@ describe Imap::Backup::CLI::Local do
|
|
23
23
|
let(:folder) { instance_double(Imap::Backup::Account::Folder, name: "bar") }
|
24
24
|
let(:serializer) do
|
25
25
|
instance_double(
|
26
|
-
Imap::Backup::Serializer
|
26
|
+
Imap::Backup::Serializer,
|
27
27
|
uids: uids,
|
28
|
-
each_message:
|
28
|
+
each_message: each_message
|
29
29
|
)
|
30
30
|
end
|
31
31
|
let(:uids) { ["123"] }
|
32
|
+
let(:each_message) { [[123, message]] }
|
32
33
|
let(:message) do
|
33
34
|
instance_double(
|
34
35
|
Email::Mboxrd::Message,
|
35
36
|
date: Date.today,
|
36
|
-
subject:
|
37
|
+
subject: message_subject,
|
37
38
|
supplied_body: "Supplied"
|
38
39
|
)
|
39
40
|
end
|
41
|
+
let(:message_subject) { "Ciao" }
|
40
42
|
let(:email) { "foo@example.com" }
|
41
43
|
|
42
44
|
before do
|
@@ -64,18 +66,35 @@ describe Imap::Backup::CLI::Local do
|
|
64
66
|
end
|
65
67
|
|
66
68
|
describe "list" do
|
67
|
-
|
68
|
-
subject.list(email, "bar")
|
69
|
+
before { subject.list(email, "bar") }
|
69
70
|
|
71
|
+
it "lists downloaded emails" do
|
70
72
|
expect(Kernel).to have_received(:puts).with(/Ciao/)
|
71
73
|
end
|
74
|
+
|
75
|
+
context "when the subject line is too long" do
|
76
|
+
let(:message_subject) { "A" * 70 }
|
77
|
+
|
78
|
+
it "is shortened" do
|
79
|
+
expect(Kernel).to have_received(:puts).with(/\sA{57}\.\.\./)
|
80
|
+
end
|
81
|
+
end
|
72
82
|
end
|
73
83
|
|
74
84
|
describe "show" do
|
75
|
-
|
76
|
-
subject.show(email, "bar", "123")
|
85
|
+
before { subject.show(email, "bar", uids.join(",")) }
|
77
86
|
|
87
|
+
it "prints a downloaded email" do
|
78
88
|
expect(Kernel).to have_received(:puts).with("Supplied")
|
79
89
|
end
|
90
|
+
|
91
|
+
context "when more than one email is requested" do
|
92
|
+
let(:uids) { %w(123 456) }
|
93
|
+
let(:each_message) { [[123, message], [456, message]] }
|
94
|
+
|
95
|
+
it "prints a header" do
|
96
|
+
expect(Kernel).to have_received(:puts).with(/\| UID: 123 /)
|
97
|
+
end
|
98
|
+
end
|
80
99
|
end
|
81
100
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe CLI::Migrate do
|
3
|
+
subject { described_class.new(source, destination, **options) }
|
4
|
+
|
5
|
+
let(:source) { "source" }
|
6
|
+
let(:destination) { "destination" }
|
7
|
+
let(:options) { {} }
|
8
|
+
let(:config) { instance_double(Configuration, accounts: [account1, account2]) }
|
9
|
+
let(:account1) { instance_double(Account, username: "source", local_path: "path") }
|
10
|
+
let(:account2) { instance_double(Account, username: "destination", connection: connection) }
|
11
|
+
let(:connection) { instance_double(Account::Connection) }
|
12
|
+
let(:migrator) { instance_double(Migrator, run: nil) }
|
13
|
+
let(:imap_pathname) { Pathname.new("path/foo.imap") }
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow(Configuration).to receive(:new) { config }
|
17
|
+
allow(Pathname).to receive(:glob).and_yield(imap_pathname)
|
18
|
+
allow(Migrator).to receive(:new) { migrator }
|
19
|
+
allow(Account::Folder).to receive(:new).and_call_original
|
20
|
+
end
|
21
|
+
|
22
|
+
it "migrates each folder" do
|
23
|
+
subject.run
|
24
|
+
|
25
|
+
expect(migrator).to have_received(:run)
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when source and destination emails are the same" do
|
29
|
+
let(:destination) { "source" }
|
30
|
+
|
31
|
+
it "fails" do
|
32
|
+
expect do
|
33
|
+
subject.run
|
34
|
+
end.to raise_error(RuntimeError, /cannot be the same/)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the source account is not found" do
|
39
|
+
let(:source) { "unknown" }
|
40
|
+
|
41
|
+
it "fails" do
|
42
|
+
expect do
|
43
|
+
subject.run
|
44
|
+
end.to raise_error(RuntimeError, /does not exist/)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the destination account is not found" do
|
49
|
+
let(:destination) { "unknown" }
|
50
|
+
|
51
|
+
it "fails" do
|
52
|
+
expect do
|
53
|
+
subject.run
|
54
|
+
end.to raise_error(RuntimeError, /does not exist/)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when source_prefix is supplied" do
|
59
|
+
let(:options) { {source_prefix: "src/"} }
|
60
|
+
let(:imap_pathname) { Pathname.new("path/src/foo.imap") }
|
61
|
+
|
62
|
+
it "removes the prefix" do
|
63
|
+
subject.run
|
64
|
+
|
65
|
+
expect(Account::Folder).to have_received(:new).with(anything, "foo")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when destination_prefix is supplied" do
|
70
|
+
let(:options) { {destination_prefix: "dest/"} }
|
71
|
+
let(:imap_pathname) { Pathname.new("path/foo.imap") }
|
72
|
+
|
73
|
+
it "removes the prefix" do
|
74
|
+
subject.run
|
75
|
+
|
76
|
+
expect(Account::Folder).to have_received(:new).with(anything, "dest/foo")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# rubocop:disable RSpec/SubjectStub
|
2
|
+
module Imap::Backup
|
3
|
+
describe CLI::Restore do
|
4
|
+
subject { described_class.new(email, options) }
|
5
|
+
|
6
|
+
let(:connection) { instance_double(Account::Connection, restore: nil) }
|
7
|
+
|
8
|
+
describe "#run" do
|
9
|
+
context "when an email is provided" do
|
10
|
+
let(:email) { "email" }
|
11
|
+
let(:options) { {} }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(subject).to receive(:connection).with(email) { connection }
|
15
|
+
|
16
|
+
subject.run
|
17
|
+
end
|
18
|
+
|
19
|
+
it "runs restore on the account" do
|
20
|
+
expect(connection).to have_received(:restore)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when neither an email nor a list of account names is provided" do
|
25
|
+
let(:email) { nil }
|
26
|
+
let(:options) { {} }
|
27
|
+
|
28
|
+
before do
|
29
|
+
allow(subject).to receive(:each_connection).with([]).and_yield(connection)
|
30
|
+
|
31
|
+
subject.run
|
32
|
+
end
|
33
|
+
|
34
|
+
it "runs restore on each account" do
|
35
|
+
expect(connection).to have_received(:restore)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when an email and a list of account names is provided" do
|
40
|
+
let(:email) { "email" }
|
41
|
+
let(:options) { {accounts: "email2"} }
|
42
|
+
|
43
|
+
it "fails" do
|
44
|
+
expect do
|
45
|
+
subject.run
|
46
|
+
end.to raise_error(RuntimeError, /Pass either an email or the --accounts option/)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when just a list of account names is provided" do
|
51
|
+
let(:email) { nil }
|
52
|
+
let(:options) { {accounts: "email2"} }
|
53
|
+
|
54
|
+
before do
|
55
|
+
allow(subject).to receive(:each_connection).with(["email2"]).and_yield(connection)
|
56
|
+
|
57
|
+
subject.run
|
58
|
+
end
|
59
|
+
|
60
|
+
it "runs restore on each account" do
|
61
|
+
expect(connection).to have_received(:restore)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# rubocop:enable RSpec/SubjectStub
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe CLI::Setup do
|
3
|
+
subject { described_class.new }
|
4
|
+
|
5
|
+
let(:setup) { instance_double(Setup, run: nil) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(Setup).to receive(:new) { setup }
|
9
|
+
end
|
10
|
+
|
11
|
+
it "reruns the setup process" do
|
12
|
+
subject.run
|
13
|
+
|
14
|
+
expect(setup).to have_received(:run)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -11,7 +11,8 @@ module Imap::Backup
|
|
11
11
|
instance_double(
|
12
12
|
Account::Connection,
|
13
13
|
account: account,
|
14
|
-
backup_folders: [folder]
|
14
|
+
backup_folders: [folder],
|
15
|
+
local_folders: ["folder"]
|
15
16
|
)
|
16
17
|
end
|
17
18
|
let(:account) do
|
@@ -31,18 +32,80 @@ module Imap::Backup
|
|
31
32
|
end
|
32
33
|
let(:serializer) do
|
33
34
|
instance_double(
|
34
|
-
Serializer
|
35
|
+
Serializer,
|
35
36
|
uids: %w(123 789),
|
36
37
|
apply_uid_validity: nil,
|
37
|
-
|
38
|
+
append: nil
|
38
39
|
)
|
39
40
|
end
|
41
|
+
let(:exporter) { instance_double(Thunderbird::MailboxExporter, run: nil) }
|
40
42
|
let(:email) { "foo@example.com" }
|
41
43
|
|
42
44
|
before do
|
43
45
|
allow(CLI::Accounts).to receive(:new) { accounts }
|
44
46
|
allow(Account::Connection).to receive(:new) { connection }
|
45
|
-
allow(Serializer
|
47
|
+
allow(Serializer).to receive(:new) { serializer }
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#export_to_thunderbird" do
|
51
|
+
let(:command) { subject.export_to_thunderbird(email) }
|
52
|
+
let(:options) { {} }
|
53
|
+
let(:profiles) { instance_double(Thunderbird::Profiles, installs: installs, profile: named_profile) }
|
54
|
+
let(:installs) { [install1] }
|
55
|
+
let(:install1) { instance_double(Thunderbird::Install, default: default_install) }
|
56
|
+
let(:default_install) { "default" }
|
57
|
+
let(:named_profile) { "named" }
|
58
|
+
|
59
|
+
before do
|
60
|
+
allow(Thunderbird::MailboxExporter).to receive(:new) { exporter }
|
61
|
+
allow(Thunderbird::Profiles).to receive(:new) { profiles }
|
62
|
+
# rubocop:disable RSpec/SubjectStub
|
63
|
+
allow(subject).to receive(:options) { options }
|
64
|
+
# rubocop:enable RSpec/SubjectStub
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when no profile_name is supplied" do
|
68
|
+
context "when no default Thunderbird profile is found" do
|
69
|
+
let(:default_install) { nil }
|
70
|
+
|
71
|
+
it "fails" do
|
72
|
+
expect do
|
73
|
+
command
|
74
|
+
end.to raise_error(RuntimeError, /Default .*? not found/)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when there is more than one install" do
|
79
|
+
let(:installs) { [install1, install2] }
|
80
|
+
let(:install2) { instance_double(Thunderbird::Install, default: default_install) }
|
81
|
+
|
82
|
+
it "fails" do
|
83
|
+
expect do
|
84
|
+
command
|
85
|
+
end.to raise_error(RuntimeError, /multiple installs.*?supply a profile name/m)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when a profile_name is supplied" do
|
91
|
+
let(:options) { {"profile" => "profile"} }
|
92
|
+
|
93
|
+
context "when the supplied profile_name is not found" do
|
94
|
+
let(:named_profile) { nil }
|
95
|
+
|
96
|
+
it "fails" do
|
97
|
+
expect do
|
98
|
+
command
|
99
|
+
end.to raise_error(RuntimeError, /profile 'profile' not found/)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "exports the profile" do
|
105
|
+
command
|
106
|
+
|
107
|
+
expect(exporter).to have_received(:run)
|
108
|
+
end
|
46
109
|
end
|
47
110
|
|
48
111
|
describe "ignore_history" do
|
@@ -55,7 +118,7 @@ module Imap::Backup
|
|
55
118
|
it "fills the local folder with fake emails" do
|
56
119
|
subject.ignore_history(email)
|
57
120
|
|
58
|
-
expect(serializer).to have_received(:
|
121
|
+
expect(serializer).to have_received(:append).with("456", /From: fake@email.com/)
|
59
122
|
end
|
60
123
|
end
|
61
124
|
end
|