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
@@ -1,115 +1,44 @@
1
- require "forwardable"
2
-
3
- require "imap/backup/serializer/mbox_store"
4
-
5
1
  module Imap::Backup
6
2
  class Serializer::Mbox
7
- extend Forwardable
8
- def_delegators :store, :mbox_pathname
3
+ attr_reader :folder_path
9
4
 
10
- attr_reader :path
11
- attr_reader :folder
12
-
13
- def initialize(path, folder)
14
- @path = path
15
- @folder = folder
5
+ def initialize(folder_path)
6
+ @folder_path = folder_path
16
7
  end
17
8
 
18
- def apply_uid_validity(value)
19
- case
20
- when store.uid_validity.nil?
21
- store.uid_validity = value
22
- nil
23
- when store.uid_validity == value
24
- # NOOP
25
- nil
26
- else
27
- apply_new_uid_validity value
9
+ def append(message)
10
+ File.open(pathname, "ab") do |file|
11
+ file.write message
28
12
  end
29
13
  end
30
14
 
31
- def force_uid_validity(value)
32
- store.uid_validity = value
15
+ def exist?
16
+ File.exist?(pathname)
33
17
  end
34
18
 
35
- def uids
36
- store.uids
37
- end
38
-
39
- def load(uid)
40
- store.load(uid)
41
- end
42
-
43
- def each_message(uids)
44
- store.each_message(uids)
45
- end
46
-
47
- def save(uid, message)
48
- store.add(uid, message)
49
- end
50
-
51
- def rename(new_name)
52
- @folder = new_name
53
- store.rename new_name
54
- end
19
+ def length
20
+ return nil if !exist?
55
21
 
56
- def update_uid(old, new)
57
- store.update_uid old, new
22
+ File.stat(pathname).size
58
23
  end
59
24
 
60
- private
61
-
62
- def store
63
- @store ||=
64
- begin
65
- create_containing_directory
66
- Serializer::MboxStore.new(path, folder)
67
- end
25
+ def pathname
26
+ "#{folder_path}.mbox"
68
27
  end
69
28
 
70
- def apply_new_uid_validity(value)
71
- digit = 0
72
- new_name = nil
73
- loop do
74
- extra = digit.zero? ? "" : "-#{digit}"
75
- new_name = "#{folder}-#{store.uid_validity}#{extra}"
76
- test_store = Serializer::MboxStore.new(path, new_name)
77
- break if !test_store.exist?
78
-
79
- digit += 1
29
+ def rename(new_path)
30
+ if exist?
31
+ old_pathname = pathname
32
+ @folder_path = new_path
33
+ File.rename(old_pathname, pathname)
34
+ else
35
+ @folder_path = new_path
80
36
  end
81
- rename_store new_name, value
82
- end
83
-
84
- def rename_store(new_name, value)
85
- store.rename new_name
86
- @store = nil
87
- store.uid_validity = value
88
- new_name
89
- end
90
-
91
- def relative_path
92
- File.dirname(folder)
93
37
  end
94
38
 
95
- def containing_directory
96
- File.join(path, relative_path)
97
- end
98
-
99
- def full_path
100
- File.expand_path(containing_directory)
101
- end
102
-
103
- def create_containing_directory
104
- if !File.directory?(full_path)
105
- Utils.make_folder(
106
- path, relative_path, Serializer::DIRECTORY_PERMISSIONS
107
- )
108
- end
109
-
110
- if Utils.mode(full_path) !=
111
- Serializer::DIRECTORY_PERMISSIONS
112
- FileUtils.chmod Serializer::DIRECTORY_PERMISSIONS, full_path
39
+ def rewind(length)
40
+ File.open(pathname, File::RDWR | File::CREAT, 0o644) do |f|
41
+ f.truncate(length)
113
42
  end
114
43
  end
115
44
  end
@@ -1,4 +1,6 @@
1
1
  module Imap::Backup
2
+ class Serializer; end
3
+
2
4
  class Serializer::MboxEnumerator
3
5
  attr_reader :mbox_pathname
4
6
 
@@ -1,6 +1,183 @@
1
+ require "forwardable"
2
+
3
+ require "email/mboxrd/message"
4
+ require "imap/backup/serializer/imap"
5
+ require "imap/backup/serializer/mbox"
6
+ require "imap/backup/serializer/mbox_enumerator"
7
+
1
8
  module Imap::Backup
2
- module Serializer
3
- DIRECTORY_PERMISSIONS = 0o700
4
- FILE_PERMISSIONS = 0o600
9
+ class Serializer
10
+ extend Forwardable
11
+
12
+ def_delegator :mbox, :pathname, :mbox_pathname
13
+ def_delegators :imap, :uid_validity, :uids, :update_uid
14
+
15
+ attr_reader :folder
16
+ attr_reader :path
17
+
18
+ def initialize(path, folder)
19
+ @path = path
20
+ @folder = folder
21
+ end
22
+
23
+ def apply_uid_validity(value)
24
+ case
25
+ when uid_validity.nil?
26
+ imap.uid_validity = value
27
+ nil
28
+ when uid_validity == value
29
+ # NOOP
30
+ nil
31
+ else
32
+ apply_new_uid_validity value
33
+ end
34
+ end
35
+
36
+ def force_uid_validity(value)
37
+ imap.uid_validity = value
38
+ end
39
+
40
+ def append(uid, message)
41
+ raise "Can't add messages without uid_validity" if !imap.uid_validity
42
+
43
+ uid = uid.to_i
44
+ if imap.include?(uid)
45
+ Logger.logger.debug(
46
+ "[#{folder}] message #{uid} already downloaded - skipping"
47
+ )
48
+ return
49
+ end
50
+
51
+ do_append uid, message
52
+ end
53
+
54
+ def load(uid_maybe_string)
55
+ uid = uid_maybe_string.to_i
56
+ message_index = imap.index(uid)
57
+ return nil if message_index.nil?
58
+
59
+ load_nth(message_index)
60
+ end
61
+
62
+ def load_nth(index)
63
+ enumerator = Serializer::MboxEnumerator.new(mbox.pathname)
64
+ enumerator.each.with_index do |raw, i|
65
+ next if i != index
66
+
67
+ return Email::Mboxrd::Message.from_serialized(raw)
68
+ end
69
+ nil
70
+ end
71
+
72
+ def each_message(required_uids)
73
+ return enum_for(:each_message, required_uids) if !block_given?
74
+
75
+ indexes = required_uids.each.with_object({}) do |uid_maybe_string, acc|
76
+ uid = uid_maybe_string.to_i
77
+ index = imap.index(uid)
78
+ acc[index] = uid if index
79
+ end
80
+ enumerator = Serializer::MboxEnumerator.new(mbox.pathname)
81
+ enumerator.each.with_index do |raw, i|
82
+ uid = indexes[i]
83
+ next if !uid
84
+
85
+ yield uid, Email::Mboxrd::Message.from_serialized(raw)
86
+ end
87
+ end
88
+
89
+ def rename(new_name)
90
+ # Initialize so we get memoized instances with the correct folder_path
91
+ mbox
92
+ imap
93
+ @folder = new_name
94
+ ensure_containing_directory
95
+ mbox.rename folder_path
96
+ imap.rename folder_path
97
+ end
98
+
99
+ private
100
+
101
+ def do_append(uid, message)
102
+ mboxrd_message = Email::Mboxrd::Message.new(message)
103
+ initial = mbox.length || 0
104
+ mbox_appended = false
105
+ begin
106
+ mbox.append mboxrd_message.to_serialized
107
+ mbox_appended = true
108
+ imap.append uid
109
+ rescue StandardError => e
110
+ mbox.rewind(initial) if mbox_appended
111
+
112
+ message = <<-ERROR.gsub(/^\s*/m, "")
113
+ [#{folder}] failed to append message #{uid}:
114
+ #{message}. #{e}:
115
+ #{e.backtrace.join("\n")}"
116
+ ERROR
117
+ Logger.logger.warn message
118
+ end
119
+ end
120
+
121
+ def mbox
122
+ @mbox ||=
123
+ begin
124
+ ensure_containing_directory
125
+ Serializer::Mbox.new(folder_path)
126
+ end
127
+ end
128
+
129
+ def imap
130
+ @imap ||=
131
+ begin
132
+ ensure_containing_directory
133
+ Serializer::Imap.new(folder_path)
134
+ end
135
+ end
136
+
137
+ def folder_path
138
+ folder_path_for(path, folder)
139
+ end
140
+
141
+ def folder_path_for(path, folder)
142
+ relative = File.join(path, folder)
143
+ File.expand_path(relative)
144
+ end
145
+
146
+ def ensure_containing_directory
147
+ relative = File.dirname(folder)
148
+ directory = Serializer::Directory.new(path, relative)
149
+ directory.ensure_exists
150
+ end
151
+
152
+ def apply_new_uid_validity(value)
153
+ new_name = rename_existing_folder
154
+ # Clear memoization so we get empty data
155
+ @mbox = nil
156
+ @imap = nil
157
+ imap.uid_validity = value
158
+
159
+ new_name
160
+ end
161
+
162
+ def rename_existing_folder
163
+ digit = 0
164
+ new_name = nil
165
+ loop do
166
+ extra = digit.zero? ? "" : "-#{digit}"
167
+ new_name = "#{folder}-#{imap.uid_validity}#{extra}"
168
+ new_folder_path = folder_path_for(path, new_name)
169
+ test_mbox = Serializer::Mbox.new(new_folder_path)
170
+ test_imap = Serializer::Imap.new(new_folder_path)
171
+ break if !test_mbox.exist? && !test_imap.exist?
172
+
173
+ digit += 1
174
+ end
175
+
176
+ previous = folder
177
+ rename(new_name)
178
+ @folder = previous
179
+
180
+ new_name
181
+ end
5
182
  end
6
183
  end
@@ -26,6 +26,7 @@ module Imap::Backup
26
26
  modify_password menu
27
27
  modify_backup_path menu
28
28
  choose_folders menu
29
+ modify_multi_fetch_size menu
29
30
  modify_server menu
30
31
  modify_connection_options menu
31
32
  test_connection menu
@@ -37,21 +38,30 @@ module Imap::Backup
37
38
 
38
39
  def header(menu)
39
40
  modified = account.modified? ? "*" : ""
40
- connection_options =
41
- if account.connection_options
42
- escaped =
43
- JSON.generate(account.connection_options).
44
- gsub('"', '\"')
45
- "\n connection options #{escaped}"
46
- end
41
+
42
+ if account.multi_fetch_size > 1
43
+ multi_fetch_size = "\nmulti-fetch #{account.multi_fetch_size}"
44
+ end
45
+
46
+ if account.connection_options
47
+ escaped =
48
+ JSON.generate(account.connection_options)
49
+ connection_options =
50
+ "\nconnection options '#{escaped}'"
51
+ space = " " * 12
52
+ else
53
+ connection_options = nil
54
+ space = " " * 4
55
+ end
56
+
47
57
  menu.header = <<~HEADER.chomp
48
58
  #{helpers.title_prefix} Account#{modified}
49
59
 
50
- email #{account.username}
51
- password #{masked_password}
52
- path #{account.local_path}
53
- folders #{folders.map { |f| f[:name] }.join(', ')}
54
- server #{account.server}#{connection_options}
60
+ email #{space}#{account.username}
61
+ password#{space}#{masked_password}
62
+ path #{space}#{account.local_path}
63
+ folders #{space}#{folders.map { |f| f[:name] }.join(', ')}#{multi_fetch_size}
64
+ server #{space}#{account.server}#{connection_options}
55
65
 
56
66
  Choose an action
57
67
  HEADER
@@ -70,12 +80,10 @@ module Imap::Backup
70
80
  )
71
81
  else
72
82
  account.username = username
73
- # rubocop:disable Style/IfUnlessModifier
74
83
  default = default_server(username)
75
84
  if default && (account.server.nil? || (account.server == ""))
76
85
  account.server = default
77
86
  end
78
- # rubocop:enable Style/IfUnlessModifier
79
87
  end
80
88
  end
81
89
  end
@@ -88,20 +96,6 @@ module Imap::Backup
88
96
  end
89
97
  end
90
98
 
91
- def modify_server(menu)
92
- menu.choice("modify server") do
93
- server = highline.ask("server: ")
94
- account.server = server if !server.nil?
95
- end
96
- end
97
-
98
- def modify_connection_options(menu)
99
- menu.choice("modify connection options") do
100
- connection_options = highline.ask("connections options (as JSON): ")
101
- account.connection_options = connection_options if !connection_options.nil?
102
- end
103
- end
104
-
105
99
  def path_modification_validator(path)
106
100
  same = config.accounts.find do |a|
107
101
  a.username != account.username && a.local_path == path
@@ -130,6 +124,35 @@ module Imap::Backup
130
124
  end
131
125
  end
132
126
 
127
+ def modify_multi_fetch_size(menu)
128
+ menu.choice("modify multi-fetch size (number of emails to fetch at a time)") do
129
+ size = highline.ask("size: ")
130
+ int = size.to_i
131
+ account.multi_fetch_size = int if int.positive?
132
+ end
133
+ end
134
+
135
+ def modify_server(menu)
136
+ menu.choice("modify server") do
137
+ server = highline.ask("server: ")
138
+ account.server = server if !server.nil?
139
+ end
140
+ end
141
+
142
+ def modify_connection_options(menu)
143
+ menu.choice("modify connection options") do
144
+ connection_options = highline.ask("connections options (as JSON): ")
145
+ if !connection_options.nil?
146
+ begin
147
+ account.connection_options = connection_options
148
+ rescue JSON::ParserError
149
+ Kernel.puts "Malformed JSON, please try again"
150
+ highline.ask "Press a key "
151
+ end
152
+ end
153
+ end
154
+ end
155
+
133
156
  def test_connection(menu)
134
157
  menu.choice("test connection") do
135
158
  result = Setup::ConnectionTester.new(account).test
@@ -141,7 +164,7 @@ module Imap::Backup
141
164
  def delete_account(menu)
142
165
  menu.choice("delete") do
143
166
  if highline.agree("Are you sure? (y/n) ")
144
- account.mark_for_deletion!
167
+ account.mark_for_deletion
145
168
  throw :done
146
169
  end
147
170
  end
@@ -9,7 +9,7 @@ module Imap::Backup
9
9
  end
10
10
 
11
11
  def version
12
- Version::VERSION
12
+ VERSION
13
13
  end
14
14
  end
15
15
  end
@@ -19,7 +19,7 @@ module Imap::Backup
19
19
 
20
20
  def run
21
21
  local_folder_ok = local_folder.set_up
22
- return if !local_folder_ok
22
+ return false if !local_folder_ok
23
23
 
24
24
  if local_folder.msf_exists?
25
25
  if force
@@ -1,9 +1,9 @@
1
1
  module Imap; end
2
2
 
3
3
  module Imap::Backup
4
- MAJOR = 5
5
- MINOR = 2
4
+ MAJOR = 6
5
+ MINOR = 0
6
6
  REVISION = 0
7
- PRE = nil
7
+ PRE = "rc2".freeze
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
9
9
  end
data/lib/imap/backup.rb CHANGED
@@ -8,7 +8,6 @@ require "imap/backup/downloader"
8
8
  require "imap/backup/logger"
9
9
  require "imap/backup/uploader"
10
10
  require "imap/backup/serializer"
11
- require "imap/backup/serializer/mbox"
12
11
  require "imap/backup/setup"
13
12
  require "imap/backup/setup/account"
14
13
  require "imap/backup/setup/asker"
@@ -70,10 +70,18 @@ RSpec.describe "backup", type: :aruba, docker: true do
70
70
  expect(mbox_content(renamed_folder)).to eq(message_as_mbox_entry(msg3))
71
71
  end
72
72
 
73
+ it "renames the old metadata file" do
74
+ expect(imap_parsed(renamed_folder)).to be_a Hash
75
+ end
76
+
73
77
  it "downloads messages" do
74
78
  expect(mbox_content(folder)).to eq(messages_as_mbox)
75
79
  end
76
80
 
81
+ it "creates a metadata file" do
82
+ expect(imap_parsed(folder)).to be_a Hash
83
+ end
84
+
77
85
  context "when a renamed local backup exists" do
78
86
  let!(:pre) do
79
87
  super()
@@ -88,21 +96,5 @@ RSpec.describe "backup", type: :aruba, docker: true do
88
96
  end
89
97
  end
90
98
  end
91
-
92
- context "when an unversioned .imap file is found" do
93
- let!(:pre) do
94
- create_directory local_backup_path
95
- File.open(imap_path(folder), "w") { |f| f.write "old format imap" }
96
- File.open(mbox_path(folder), "w") { |f| f.write "old format emails" }
97
- end
98
-
99
- it "replaces the .imap file with a versioned JSON file" do
100
- expect(imap_metadata[:uids]).to eq(folder_uids)
101
- end
102
-
103
- it "does the download" do
104
- expect(mbox_content(folder)).to eq(messages_as_mbox)
105
- end
106
- end
107
99
  end
108
100
  end
@@ -1,6 +1,7 @@
1
1
  require "aruba/rspec"
2
2
 
3
3
  require_relative "backup_directory"
4
+ require "imap/backup/serializer/mbox"
4
5
 
5
6
  Aruba.configure do |config|
6
7
  config.home_directory = File.expand_path("./tmp/home")
@@ -36,10 +37,10 @@ module StoreHelpers
36
37
  account = config.accounts.find { |a| a.username == email }
37
38
  raise "Account not found" if !account
38
39
  FileUtils.mkdir_p account.local_path
39
- store = Imap::Backup::Serializer::MboxStore.new(account.local_path, folder)
40
- store.uid_validity = "42" if !store.uid_validity
40
+ serializer = Imap::Backup::Serializer.new(account.local_path, folder)
41
+ serializer.force_uid_validity("42") if !serializer.uid_validity
41
42
  serialized = to_serialized(from: from, subject: subject, body: body)
42
- store.add(uid, serialized)
43
+ serializer.append uid, serialized
43
44
  end
44
45
 
45
46
  def to_serialized(from:, subject:, body:)
@@ -24,21 +24,24 @@ describe Imap::Backup::Account::Connection do
24
24
  let(:account) do
25
25
  instance_double(
26
26
  Imap::Backup::Account,
27
- username: USERNAME,
27
+ username: username,
28
28
  password: PASSWORD,
29
29
  local_path: LOCAL_PATH,
30
30
  folders: config_folders,
31
+ multi_fetch_size: multi_fetch_size,
31
32
  server: server,
32
33
  connection_options: nil
33
34
  )
34
35
  end
36
+ let(:username) { USERNAME }
35
37
  let(:config_folders) { [FOLDER_CONFIG] }
38
+ let(:multi_fetch_size) { 1 }
36
39
  let(:root_info) do
37
40
  instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)
38
41
  end
39
42
  let(:serializer) do
40
43
  instance_double(
41
- Imap::Backup::Serializer::Mbox,
44
+ Imap::Backup::Serializer,
42
45
  folder: serialized_folder,
43
46
  force_uid_validity: nil,
44
47
  apply_uid_validity: new_uid_validity,
@@ -87,6 +90,23 @@ describe Imap::Backup::Account::Connection do
87
90
  end
88
91
  end
89
92
 
93
+ context "when the provider is Apple" do
94
+ let(:username) { "user@mac.com" }
95
+ let(:apple_client) do
96
+ instance_double(
97
+ Imap::Backup::Client::AppleMail, login: nil
98
+ )
99
+ end
100
+
101
+ before do
102
+ allow(Imap::Backup::Client::AppleMail).to receive(:new) { apple_client }
103
+ end
104
+
105
+ it "returns the Apple client" do
106
+ expect(result).to eq(apple_client)
107
+ end
108
+ end
109
+
90
110
  context "when run" do
91
111
  before { subject.client }
92
112
 
@@ -116,7 +136,7 @@ describe Imap::Backup::Account::Connection do
116
136
 
117
137
  before do
118
138
  allow(Imap::Backup::Account::Folder).to receive(:new) { folder }
119
- allow(Imap::Backup::Serializer::Mbox).to receive(:new) { serializer }
139
+ allow(Imap::Backup::Serializer).to receive(:new) { serializer }
120
140
  end
121
141
 
122
142
  it "creates the path" do
@@ -150,16 +170,24 @@ describe Imap::Backup::Account::Connection do
150
170
  let(:exists) { true }
151
171
  let(:uid_validity) { 123 }
152
172
  let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
173
+ let(:multi_fetch_size) { 10 }
153
174
 
154
175
  before do
155
176
  allow(Imap::Backup::Downloader).
156
177
  to receive(:new).with(folder, serializer, anything) { downloader }
157
178
  allow(Imap::Backup::Account::Folder).to receive(:new).
158
179
  with(subject, BACKUP_FOLDER) { folder }
159
- allow(Imap::Backup::Serializer::Mbox).to receive(:new).
180
+ allow(Imap::Backup::Serializer).to receive(:new).
160
181
  with(LOCAL_PATH, IMAP_FOLDER) { serializer }
161
182
  end
162
183
 
184
+ it "passes the multi_fetch_size" do
185
+ subject.run_backup
186
+
187
+ expect(Imap::Backup::Downloader).to have_received(:new).
188
+ with(anything, anything, {multi_fetch_size: 10})
189
+ end
190
+
163
191
  context "with supplied config_folders" do
164
192
  it "runs the downloader" do
165
193
  expect(downloader).to receive(:run)
@@ -184,7 +212,7 @@ describe Imap::Backup::Account::Connection do
184
212
  before do
185
213
  allow(Imap::Backup::Account::Folder).to receive(:new).
186
214
  with(subject, ROOT_NAME) { folder }
187
- allow(Imap::Backup::Serializer::Mbox).to receive(:new).
215
+ allow(Imap::Backup::Serializer).to receive(:new).
188
216
  with(LOCAL_PATH, ROOT_NAME) { serializer }
189
217
  end
190
218
 
@@ -273,18 +301,18 @@ describe Imap::Backup::Account::Connection do
273
301
  end
274
302
  let(:updated_serializer) do
275
303
  instance_double(
276
- Imap::Backup::Serializer::Mbox, force_uid_validity: nil
304
+ Imap::Backup::Serializer, force_uid_validity: nil
277
305
  )
278
306
  end
279
307
 
280
308
  before do
281
309
  allow(Imap::Backup::Account::Folder).to receive(:new).
282
310
  with(subject, FOLDER_NAME) { folder }
283
- allow(Imap::Backup::Serializer::Mbox).to receive(:new).
311
+ allow(Imap::Backup::Serializer).to receive(:new).
284
312
  with(anything, FOLDER_NAME) { serializer }
285
313
  allow(Imap::Backup::Account::Folder).to receive(:new).
286
314
  with(subject, "new name") { updated_folder }
287
- allow(Imap::Backup::Serializer::Mbox).to receive(:new).
315
+ allow(Imap::Backup::Serializer).to receive(:new).
288
316
  with(anything, "new name") { updated_serializer }
289
317
  allow(Imap::Backup::Uploader).to receive(:new).
290
318
  with(folder, serializer) { uploader }
@@ -64,6 +64,16 @@ describe Imap::Backup::Account::Folder do
64
64
  expect(subject.uids).to eq([])
65
65
  end
66
66
  end
67
+
68
+ context "when the UID search fails" do
69
+ before do
70
+ allow(client).to receive(:uid_search).and_raise(NoMethodError)
71
+ end
72
+
73
+ it "returns an empty array" do
74
+ expect(subject.uids).to eq([])
75
+ end
76
+ end
67
77
  end
68
78
 
69
79
  describe "#fetch_multi" do