imap-backup 5.2.0 → 6.0.0.rc2
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/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
|