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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -2
  3. data/docs/development.md +10 -4
  4. data/lib/cli_coverage.rb +1 -1
  5. data/lib/imap/backup/account/connection.rb +7 -11
  6. data/lib/imap/backup/account.rb +31 -11
  7. data/lib/imap/backup/cli/folders.rb +3 -3
  8. data/lib/imap/backup/cli/migrate.rb +3 -3
  9. data/lib/imap/backup/cli/utils.rb +2 -2
  10. data/lib/imap/backup/configuration.rb +1 -11
  11. data/lib/imap/backup/downloader.rb +13 -9
  12. data/lib/imap/backup/serializer/directory.rb +37 -0
  13. data/lib/imap/backup/serializer/imap.rb +120 -0
  14. data/lib/imap/backup/serializer/mbox.rb +23 -94
  15. data/lib/imap/backup/serializer/mbox_enumerator.rb +2 -0
  16. data/lib/imap/backup/serializer.rb +180 -3
  17. data/lib/imap/backup/setup/account.rb +52 -29
  18. data/lib/imap/backup/setup/helpers.rb +1 -1
  19. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +1 -1
  20. data/lib/imap/backup/version.rb +3 -3
  21. data/lib/imap/backup.rb +0 -1
  22. data/spec/features/backup_spec.rb +8 -16
  23. data/spec/features/support/aruba.rb +4 -3
  24. data/spec/unit/imap/backup/account/connection_spec.rb +36 -8
  25. data/spec/unit/imap/backup/account/folder_spec.rb +10 -0
  26. data/spec/unit/imap/backup/account_spec.rb +246 -0
  27. data/spec/unit/imap/backup/cli/accounts_spec.rb +12 -1
  28. data/spec/unit/imap/backup/cli/backup_spec.rb +19 -0
  29. data/spec/unit/imap/backup/cli/folders_spec.rb +39 -0
  30. data/spec/unit/imap/backup/cli/local_spec.rb +26 -7
  31. data/spec/unit/imap/backup/cli/migrate_spec.rb +80 -0
  32. data/spec/unit/imap/backup/cli/restore_spec.rb +67 -0
  33. data/spec/unit/imap/backup/cli/setup_spec.rb +17 -0
  34. data/spec/unit/imap/backup/cli/utils_spec.rb +68 -5
  35. data/spec/unit/imap/backup/cli_spec.rb +93 -0
  36. data/spec/unit/imap/backup/client/apple_mail_spec.rb +9 -0
  37. data/spec/unit/imap/backup/configuration_spec.rb +2 -2
  38. data/spec/unit/imap/backup/downloader_spec.rb +59 -7
  39. data/spec/unit/imap/backup/migrator_spec.rb +1 -1
  40. data/spec/unit/imap/backup/sanitizer_spec.rb +42 -0
  41. data/spec/unit/imap/backup/serializer/directory_spec.rb +37 -0
  42. data/spec/unit/imap/backup/serializer/imap_spec.rb +218 -0
  43. data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -183
  44. data/spec/unit/imap/backup/serializer_spec.rb +296 -0
  45. data/spec/unit/imap/backup/setup/account_spec.rb +120 -25
  46. data/spec/unit/imap/backup/setup/helpers_spec.rb +15 -0
  47. data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +116 -0
  48. data/spec/unit/imap/backup/uploader_spec.rb +1 -1
  49. data/spec/unit/retry_on_error_spec.rb +34 -0
  50. metadata +36 -7
  51. data/lib/imap/backup/serializer/mbox_store.rb +0 -217
  52. 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::Mbox,
26
+ Imap::Backup::Serializer,
27
27
  uids: uids,
28
- each_message: [[123, 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: "Ciao",
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
- it "lists downloaded emails" do
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
- it "prints a downloaded email" do
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::Mbox,
35
+ Serializer,
35
36
  uids: %w(123 789),
36
37
  apply_uid_validity: nil,
37
- save: nil
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::Mbox).to receive(:new) { 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(:save).with("456", /From: fake@email.com/)
121
+ expect(serializer).to have_received(:append).with("456", /From: fake@email.com/)
59
122
  end
60
123
  end
61
124
  end