imap-backup 4.2.0 → 5.1.0

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/imap-backup.gemspec +2 -0
  3. data/lib/imap/backup/account/connection.rb +4 -4
  4. data/lib/imap/backup/account/folder.rb +8 -0
  5. data/lib/imap/backup/cli/folders.rb +1 -1
  6. data/lib/imap/backup/cli/helpers.rb +4 -1
  7. data/lib/imap/backup/cli/local.rb +1 -1
  8. data/lib/imap/backup/cli/migrate.rb +114 -0
  9. data/lib/imap/backup/cli/remote.rb +15 -0
  10. data/lib/imap/backup/cli/restore.rb +20 -4
  11. data/lib/imap/backup/cli.rb +55 -10
  12. data/lib/imap/backup/client/default.rb +2 -2
  13. data/lib/imap/backup/downloader.rb +23 -9
  14. data/lib/imap/backup/migrator.rb +51 -0
  15. data/lib/imap/backup/sanitizer.rb +7 -4
  16. data/lib/imap/backup/version.rb +2 -2
  17. data/spec/features/backup_spec.rb +13 -12
  18. data/spec/features/configuration/minimal_configuration.rb +15 -0
  19. data/spec/features/configuration/missing_configuration.rb +14 -0
  20. data/spec/features/folders_spec.rb +36 -0
  21. data/spec/features/local/list_accounts_spec.rb +12 -0
  22. data/spec/features/local/list_emails_spec.rb +21 -0
  23. data/spec/features/local/list_folders_spec.rb +21 -0
  24. data/spec/features/local/show_an_email_spec.rb +34 -0
  25. data/spec/features/migrate_spec.rb +35 -0
  26. data/spec/features/remote/list_account_folders_spec.rb +16 -0
  27. data/spec/features/restore_spec.rb +7 -5
  28. data/spec/features/support/aruba.rb +73 -0
  29. data/spec/features/support/backup_directory.rb +0 -4
  30. data/spec/features/support/email_server.rb +1 -1
  31. data/spec/spec_helper.rb +2 -3
  32. data/spec/unit/imap/backup/account/connection_spec.rb +6 -8
  33. data/spec/unit/imap/backup/account/folder_spec.rb +26 -5
  34. data/spec/unit/imap/backup/cli/helpers_spec.rb +87 -0
  35. data/spec/unit/imap/backup/migrator_spec.rb +58 -0
  36. metadata +57 -11
  37. data/lib/thunderbird/install.rb +0 -16
  38. data/lib/thunderbird/local_folder.rb +0 -65
  39. data/lib/thunderbird/profile.rb +0 -30
  40. data/lib/thunderbird/profiles.rb +0 -71
  41. data/lib/thunderbird/subdirectory.rb +0 -93
  42. data/lib/thunderbird/subdirectory_placeholder.rb +0 -21
  43. data/lib/thunderbird.rb +0 -14
  44. data/spec/gather_rspec_coverage.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0cd29188ed972abfb2b2ce21d237ce3b7aed5b434717ba64fa2e3475da38ded
4
- data.tar.gz: 597b58cdee1ab1b5881ea29111ac85e7fdedfbaa370824d0d24ba1b19c869dcb
3
+ metadata.gz: 59babb556b2f47a07590debff15dcd5456f1e61454c3aba2e504610ce4322647
4
+ data.tar.gz: 89909077444746e44fe3955070df6b590942da0d44da58c9b08b282e694000d5
5
5
  SHA512:
6
- metadata.gz: a3adc1984eea76a5ef993a40e89520a01997e6d01357e0a1e41cd4b58a4efc56801fc3d4f8801b95e828ec3a6a1e7884be974aaabf40f921a3faccdc99cf9b98
7
- data.tar.gz: 55c0c91fdfb4829ba77be5292b4e475c6ea31e9e6e9ea5abf9702121f8a12c20b9b0105da5e1b97bcb2482105807c614530dea9e058d64af2cde96f7fd19348b
6
+ metadata.gz: ea00bb55bb853cef73ec12171a4bdde9b0b5ef88a914065bbe307f48712db3329e8c8f1712cfa48b4a49102d20fb38cc465dc463c2d3ea9e0caba000a184766b
7
+ data.tar.gz: 24d368c209c11db4ae7f05a93b9cf4e0435b65eef0cffd7e58f8c669e634ca735aa3187b6a6506b7b10ba3bec961d3aa9d7387df40d4880f1bed73df7f24bbb9
data/imap-backup.gemspec CHANGED
@@ -27,7 +27,9 @@ Gem::Specification.new do |gem|
27
27
  gem.add_runtime_dependency "os"
28
28
  gem.add_runtime_dependency "rake"
29
29
  gem.add_runtime_dependency "thor", "~> 1.1"
30
+ gem.add_runtime_dependency "thunderbird", ">= 0.0.0"
30
31
 
32
+ gem.add_development_dependency "aruba", ">= 0.0.0"
31
33
  gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
32
34
  if RUBY_ENGINE == "jruby"
33
35
  gem.add_development_dependency "pry-debugger-jruby"
@@ -16,11 +16,8 @@ module Imap::Backup
16
16
  def initialize(account)
17
17
  @account = account
18
18
  reset
19
- create_account_folder
20
19
  end
21
20
 
22
- # TODO: Make this private once the 'folders' command
23
- # has been removed.
24
21
  def folder_names
25
22
  @folder_names ||=
26
23
  begin
@@ -53,6 +50,7 @@ module Imap::Backup
53
50
  end
54
51
 
55
52
  def status
53
+ ensure_account_folder
56
54
  backup_folders.map do |folder|
57
55
  s = Serializer::Mbox.new(account.local_path, folder.name)
58
56
  {name: folder.name, local: s.uids, remote: folder.uids}
@@ -63,6 +61,7 @@ module Imap::Backup
63
61
  Imap::Backup::Logger.logger.debug "Running backup of account: #{account.username}"
64
62
  # start the connection so we get logging messages in the right order
65
63
  client
64
+ ensure_account_folder
66
65
  each_folder do |folder, serializer|
67
66
  next if !folder.exist?
68
67
 
@@ -82,6 +81,7 @@ module Imap::Backup
82
81
  def local_folders
83
82
  return enum_for(:local_folders) if !block_given?
84
83
 
84
+ ensure_account_folder
85
85
  glob = File.join(account.local_path, "**", "*.imap")
86
86
  base = Pathname.new(account.local_path)
87
87
  Pathname.glob(glob) do |path|
@@ -176,7 +176,7 @@ module Imap::Backup
176
176
  end
177
177
  end
178
178
 
179
- def create_account_folder
179
+ def ensure_account_folder
180
180
  Utils.make_folder(
181
181
  File.dirname(account.local_path),
182
182
  File.basename(account.local_path),
@@ -111,6 +111,14 @@ module Imap::Backup
111
111
  extract_uid(response)
112
112
  end
113
113
 
114
+ def clear
115
+ existing = uids
116
+ # Use read-write access, via `select`
117
+ client.select(utf7_encoded_name)
118
+ client.uid_store(existing, "+FLAGS", [:Deleted])
119
+ client.expunge
120
+ end
121
+
114
122
  private
115
123
 
116
124
  def examine
@@ -13,7 +13,7 @@ module Imap::Backup
13
13
  no_commands do
14
14
  def run
15
15
  each_connection(account_names) do |connection|
16
- puts connection.username
16
+ puts connection.account.username
17
17
  # TODO: Make folder_names private once this command
18
18
  # has been removed.
19
19
  folders = connection.folder_names
@@ -3,7 +3,10 @@ require "imap/backup/cli/accounts"
3
3
 
4
4
  module Imap::Backup::CLI::Helpers
5
5
  def symbolized(options)
6
- options.each.with_object({}) { |(k, v), acc| acc[k.intern] = v }
6
+ options.each.with_object({}) do |(k, v), acc|
7
+ key = k.gsub("-", "_").intern
8
+ acc[key] = v
9
+ end
7
10
  end
8
11
 
9
12
  def account(email)
@@ -11,7 +11,7 @@ module Imap::Backup
11
11
  accounts.each { |a| Kernel.puts a.username }
12
12
  end
13
13
 
14
- desc "folders EMAIL", "List account folders"
14
+ desc "folders EMAIL", "List backed up folders"
15
15
  def folders(email)
16
16
  connection = connection(email)
17
17
 
@@ -0,0 +1,114 @@
1
+ require "imap/backup/migrator"
2
+
3
+ module Imap::Backup
4
+ class CLI::Migrate < Thor
5
+ include Thor::Actions
6
+
7
+ attr_reader :destination_email
8
+ attr_reader :destination_prefix
9
+ attr_reader :reset
10
+ attr_reader :source_email
11
+ attr_reader :source_prefix
12
+
13
+ def initialize(
14
+ source_email,
15
+ destination_email,
16
+ destination_prefix: "",
17
+ reset: false,
18
+ source_prefix: ""
19
+ )
20
+ super([])
21
+ @destination_email = destination_email
22
+ @destination_prefix = destination_prefix
23
+ @reset = reset
24
+ @source_email = source_email
25
+ @source_prefix = source_prefix
26
+ end
27
+
28
+ no_commands do
29
+ def run
30
+ check_accounts!
31
+ folders.each do |serializer, folder|
32
+ Migrator.new(serializer, folder, reset: reset).run
33
+ end
34
+ end
35
+
36
+ def check_accounts!
37
+ if destination_email == source_email
38
+ raise "Source and destination accounts cannot be the same!"
39
+ end
40
+
41
+ if !destination_account
42
+ raise "Account #{destination_email} does not exist"
43
+ end
44
+
45
+ if !source_account
46
+ raise "Account #{source_email} does not exist"
47
+ end
48
+ end
49
+
50
+ def config
51
+ Configuration.new
52
+ end
53
+
54
+ def destination_account
55
+ config.accounts.find { |a| a.username == destination_email }
56
+ end
57
+
58
+ def folders
59
+ return enum_for(:folders) if !block_given?
60
+
61
+ glob = File.join(source_local_path, "**", "*.imap")
62
+ Pathname.glob(glob) do |path|
63
+ name = source_folder_name(path)
64
+ serializer = Serializer::Mbox.new(source_local_path, name)
65
+ folder = folder_for(name)
66
+ yield serializer, folder
67
+ end
68
+ end
69
+
70
+ def folder_for(source_folder)
71
+ no_source_prefix =
72
+ if source_prefix != "" &&
73
+ source_folder.start_with?(source_prefix)
74
+ source_folder.delete_prefix(source_prefix)
75
+ else
76
+ source_folder.to_s
77
+ end
78
+
79
+ with_destination_prefix =
80
+ if destination_prefix &&
81
+ destination_prefix != ""
82
+ destination_prefix + no_source_prefix
83
+ else
84
+ no_source_prefix
85
+ end
86
+
87
+ Account::Folder.new(
88
+ destination_account.connection,
89
+ with_destination_prefix
90
+ )
91
+ end
92
+
93
+ def source_local_path
94
+ source_account.local_path
95
+ end
96
+
97
+ def source_account
98
+ config.accounts.find { |a| a.username == source_email }
99
+ end
100
+
101
+ def source_folder_name(imap_pathname)
102
+ base = Pathname.new(source_local_path)
103
+ imap_name = imap_pathname.relative_path_from(base).to_s
104
+ dir = File.dirname(imap_name)
105
+ stripped = File.basename(imap_name, ".imap")
106
+ if dir == "."
107
+ stripped
108
+ else
109
+ File.join(dir, stripped)
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,15 @@
1
+ module Imap::Backup
2
+ class CLI::Remote < Thor
3
+ include Thor::Actions
4
+ include CLI::Helpers
5
+
6
+ desc "folders EMAIL", "List account folders"
7
+ def folders(email)
8
+ connection = connection(email)
9
+
10
+ connection.folder_names.each do |name|
11
+ Kernel.puts %("#{name}")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,16 +3,32 @@ module Imap::Backup
3
3
  include Thor::Actions
4
4
  include CLI::Helpers
5
5
 
6
+ attr_reader :email
6
7
  attr_reader :account_names
7
8
 
8
- def initialize(options)
9
- super([])
10
- @account_names = (options[:accounts] || "").split(",")
9
+ def initialize(email = nil, options)
10
+ @email = email
11
+ @account_names =
12
+ if options.key?(:accounts)
13
+ options[:accounts].split(",")
14
+ end
11
15
  end
12
16
 
13
17
  no_commands do
14
18
  def run
15
- each_connection(account_names) { |connection| connection.restore }
19
+ case
20
+ when email && !account_names
21
+ connection = connection(email)
22
+ connection.restore
23
+ when !email && !account_names
24
+ Logger.logger.info "Calling restore without an EMAIL parameter is deprecated"
25
+ each_connection([]) { |connection| connection.restore }
26
+ when email && account_names.any?
27
+ raise "Pass either an email or the --accounts option, not both"
28
+ when account_names.any?
29
+ Logger.logger.info "Calling restore with the --account option is deprected, please pass a single EMAIL argument"
30
+ each_connection(account_names) { |connection| connection.restore }
31
+ end
16
32
  end
17
33
  end
18
34
  end
@@ -9,6 +9,7 @@ module Imap::Backup
9
9
  autoload :Backup, "imap/backup/cli/backup"
10
10
  autoload :Folders, "imap/backup/cli/folders"
11
11
  autoload :Local, "imap/backup/cli/local"
12
+ autoload :Migrate, "imap/backup/cli/migrate"
12
13
  autoload :Remote, "imap/backup/cli/remote"
13
14
  autoload :Restore, "imap/backup/cli/restore"
14
15
  autoload :Setup, "imap/backup/cli/setup"
@@ -27,7 +28,7 @@ module Imap::Backup
27
28
  method_option(
28
29
  "accounts",
29
30
  type: :string,
30
- banner: "a comma-separated list of accounts (defaults to all configured accounts)",
31
+ desc: "a comma-separated list of accounts (defaults to all configured accounts)",
31
32
  aliases: ["-a"]
32
33
  )
33
34
  end
@@ -57,15 +58,62 @@ module Imap::Backup
57
58
  Folders.new(symbolized(options)).run
58
59
  end
59
60
 
60
- desc "restore [OPTIONS]", "This command is deprecated, use `imap-backup restore ACCOUNT`"
61
+ desc "local SUBCOMMAND [OPTIONS]", "View local info"
62
+ subcommand "local", Local
63
+
64
+ desc "migrate SOURCE_EMAIL DESTINATION_EMAIL [OPTIONS]",
65
+ "[Experimental] Uploads backed-up emails from account SOURCE_EMAIL to account DESTINATION_EMAIL"
61
66
  long_desc <<~DESC
62
- By default, restores all local emails to their respective servers.
63
- This command is deprecated.
64
- Instead, use `imap-backup restore ACCOUNT` to restore a single account.
67
+ All emails which have been backed up for the "source account" (SOURCE_EMAIL) are
68
+ uploaded to the "destination account" (DESTINATION_EMAIL).
69
+
70
+ When one or other account has namespaces (i.e. prefixes like "INBOX."),
71
+ use the `--source-prefix=` and/or `--destination-prefix=` options.
72
+
73
+ Usually, you should migrate to an account with empty folders.
74
+
75
+ Before migrating each folder, `imap-backup` checks if the destination
76
+ folder is empty.
77
+
78
+ If it finds a non-empty destination folder, it halts with an error.
79
+
80
+ If you are sure that these destination emails can be deleted,
81
+ use the `--reset` option. In this case, all existing emails are
82
+ deleted before uploading the migrated emails.
83
+ DESC
84
+ method_option(
85
+ "destination-prefix",
86
+ type: :string,
87
+ desc: "the prefix (namespace) to add to destination folder names",
88
+ aliases: ["-d"]
89
+ )
90
+ method_option(
91
+ "reset",
92
+ type: :boolean,
93
+ desc: "DANGER! This option deletes all messages from destination folders before uploading",
94
+ aliases: ["-r"]
95
+ )
96
+ method_option(
97
+ "source-prefix",
98
+ type: :string,
99
+ desc: "the prefix (namespace) to strip from source folder names",
100
+ aliases: ["-s"]
101
+ )
102
+ def migrate(source_email, destination_email)
103
+ Migrate.new(source_email, destination_email, symbolized(options)).run
104
+ end
105
+
106
+ desc "remote SUBCOMMAND [OPTIONS]", "View info about online accounts"
107
+ subcommand "remote", Remote
108
+
109
+ desc "restore EMAIL", "Restores a single account"
110
+ long_desc <<~DESC
111
+ Restores all backed-up emails for the supplied account to
112
+ their original server.
65
113
  DESC
66
114
  accounts_option
67
- def restore
68
- Restore.new(symbolized(options)).run
115
+ def restore(email = nil)
116
+ Restore.new(email, symbolized(options)).run
69
117
  end
70
118
 
71
119
  desc "setup", "Configure imap-backup"
@@ -86,9 +134,6 @@ module Imap::Backup
86
134
  Status.new(symbolized(options)).run
87
135
  end
88
136
 
89
- desc "local SUBCOMMAND [OPTIONS]", "View local info"
90
- subcommand "local", Local
91
-
92
137
  desc "utils SUBCOMMAND [OPTIONS]", "Various utilities"
93
138
  subcommand "utils", Utils
94
139
  end
@@ -7,8 +7,8 @@ module Imap::Backup
7
7
  class Client::Default
8
8
  extend Forwardable
9
9
  def_delegators :imap, *%i(
10
- append authenticate create disconnect examine
11
- login responses uid_fetch uid_search
10
+ append authenticate create disconnect examine expunge
11
+ login responses select uid_fetch uid_search uid_store
12
12
  )
13
13
 
14
14
  attr_reader :args
@@ -13,31 +13,45 @@ module Imap::Backup
13
13
  def run
14
14
  uids = folder.uids - serializer.uids
15
15
  count = uids.count
16
- Imap::Backup::Logger.logger.debug "[#{folder.name}] #{count} new messages"
16
+ debug "#{count} new messages"
17
17
  uids.each_slice(block_size).with_index do |block, i|
18
- offset = i * block_size + 1
19
18
  uids_and_bodies = folder.fetch_multi(block)
20
19
  if uids_and_bodies.nil?
21
20
  if block_size > 1
22
- Imap::Backup::Logger.logger.debug("[#{folder.name}] Multi fetch failed for UIDs #{block.join(", ")}, switching to single fetches")
21
+ debug("Multi fetch failed for UIDs #{block.join(", ")}, switching to single fetches")
23
22
  @block_size = 1
24
23
  redo
25
24
  else
26
- Imap::Backup::Logger.logger.debug("[#{folder.name}] Fetch failed for UID #{block[0]} - skipping")
25
+ debug("Fetch failed for UID #{block[0]} - skipping")
27
26
  next
28
27
  end
29
28
  end
30
29
 
30
+ offset = i * block_size + 1
31
31
  uids_and_bodies.each.with_index do |uid_and_body, j|
32
32
  uid = uid_and_body[:uid]
33
33
  body = uid_and_body[:body]
34
- Imap::Backup::Logger.logger.debug(
35
- "[#{folder.name}] uid: #{uid} (#{offset +j}/#{count}) - " \
36
- "#{body.size} bytes"
37
- )
38
- serializer.save(uid, body)
34
+ case
35
+ when !body
36
+ info("Fetch returned empty body - skipping")
37
+ when !uid
38
+ info("Fetch returned empty UID - skipping")
39
+ else
40
+ debug("uid: #{uid} (#{offset + j}/#{count}) - #{body.size} bytes")
41
+ serializer.save(uid, body)
42
+ end
39
43
  end
40
44
  end
41
45
  end
46
+
47
+ private
48
+
49
+ def info(message)
50
+ Imap::Backup::Logger.logger.info("[#{folder.name}] #{message}")
51
+ end
52
+
53
+ def debug(message)
54
+ Imap::Backup::Logger.logger.debug("[#{folder.name}] #{message}")
55
+ end
42
56
  end
43
57
  end
@@ -0,0 +1,51 @@
1
+ module Imap::Backup
2
+ class Migrator
3
+ attr_reader :folder
4
+ attr_reader :reset
5
+ attr_reader :serializer
6
+
7
+ def initialize(serializer, folder, reset: false)
8
+ @folder = folder
9
+ @reset = reset
10
+ @serializer = serializer
11
+ end
12
+
13
+ def run
14
+ count = serializer.uids.count
15
+ folder.create
16
+ ensure_destination_empty!
17
+
18
+ Imap::Backup::Logger.logger.debug "[#{folder.name}] #{count} to migrate"
19
+ serializer.each_message(serializer.uids).with_index do |(uid, message), i|
20
+ next if message.nil?
21
+
22
+ log_prefix = "[#{folder.name}] uid: #{uid} (#{i + 1}/#{count}) -"
23
+ Imap::Backup::Logger.logger.debug(
24
+ "#{log_prefix} #{message.supplied_body.size} bytes"
25
+ )
26
+
27
+ folder.append(message)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def ensure_destination_empty!
34
+ if reset
35
+ folder.clear
36
+ else
37
+ fail_if_destination_not_empty!
38
+ end
39
+ end
40
+
41
+ def fail_if_destination_not_empty!
42
+ return if folder.uids.empty?
43
+
44
+ raise <<~ERROR
45
+ The destination folder '#{folder.name}' is not empty.
46
+ Pass the --reset flag if you want to clear existing emails from destination
47
+ folders before uploading.
48
+ ERROR
49
+ end
50
+ end
51
+ end
@@ -1,16 +1,19 @@
1
+ require "forwardable"
2
+
1
3
  module Imap::Backup
2
4
  class Sanitizer
5
+ extend Forwardable
6
+
3
7
  attr_reader :output
4
8
 
9
+ delegate puts: :output
10
+ delegate write: :output
11
+
5
12
  def initialize(output)
6
13
  @output = output
7
14
  @current = ""
8
15
  end
9
16
 
10
- def write(*args)
11
- output.write(*args)
12
- end
13
-
14
17
  def print(*args)
15
18
  @current << args.join
16
19
  loop do
@@ -1,8 +1,8 @@
1
1
  module Imap; end
2
2
 
3
3
  module Imap::Backup
4
- MAJOR = 4
5
- MINOR = 2
4
+ MAJOR = 5
5
+ MINOR = 1
6
6
  REVISION = 0
7
7
  PRE = nil
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
@@ -1,28 +1,28 @@
1
1
  require "features/helper"
2
2
 
3
- RSpec.describe "backup", type: :feature, docker: true do
3
+ RSpec.describe "backup", type: :aruba, docker: true do
4
4
  include_context "imap-backup connection"
5
5
  include_context "message-fixtures"
6
6
 
7
+ let(:local_backup_path) { File.expand_path("~/backup") }
8
+ let(:backup_folders) { [{name: folder}] }
9
+ let(:folder) { "my-stuff" }
7
10
  let(:messages_as_mbox) do
8
11
  message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
9
12
  end
10
- let(:folder) { "my-stuff" }
11
- let(:email1) { send_email folder, msg1 }
12
- let(:email2) { send_email folder, msg2 }
13
+
13
14
  let!(:pre) {}
14
15
  let!(:setup) do
15
16
  server_create_folder folder
16
- email1
17
- email2
18
- connection.run_backup
17
+ send_email folder, msg1
18
+ send_email folder, msg2
19
+ create_config(accounts: [account.to_h])
20
+
21
+ run_command_and_stop("imap-backup backup")
19
22
  end
20
23
 
21
24
  after do
22
- FileUtils.rm_rf local_backup_path
23
- delete_emails folder
24
25
  server_delete_folder folder
25
- connection.disconnect
26
26
  end
27
27
 
28
28
  it "downloads messages" do
@@ -51,11 +51,10 @@ RSpec.describe "backup", type: :feature, docker: true do
51
51
 
52
52
  context "when uid_validity does not match" do
53
53
  let(:new_name) { "NEWNAME" }
54
- let(:email3) { send_email folder, msg3 }
55
54
  let(:original_folder_uid_validity) { server_uid_validity(folder) }
56
55
  let!(:pre) do
57
56
  server_create_folder folder
58
- email3
57
+ send_email folder, msg3
59
58
  original_folder_uid_validity
60
59
  connection.run_backup
61
60
  connection.disconnect
@@ -78,6 +77,7 @@ RSpec.describe "backup", type: :feature, docker: true do
78
77
  context "when a renamed local backup exists" do
79
78
  let!(:pre) do
80
79
  super()
80
+ create_directory local_backup_path
81
81
  File.write(imap_path(renamed_folder), "existing imap")
82
82
  File.write(mbox_path(renamed_folder), "existing mbox")
83
83
  end
@@ -91,6 +91,7 @@ RSpec.describe "backup", type: :feature, docker: true do
91
91
 
92
92
  context "when an unversioned .imap file is found" do
93
93
  let!(:pre) do
94
+ create_directory local_backup_path
94
95
  File.open(imap_path(folder), "w") { |f| f.write "old format imap" }
95
96
  File.open(mbox_path(folder), "w") { |f| f.write "old format emails" }
96
97
  end
@@ -0,0 +1,15 @@
1
+ require "features/helper"
2
+
3
+ RSpec.describe "Running commands", type: :aruba do
4
+ before do
5
+ create_config(accounts: [])
6
+ run_command("imap-backup local accounts")
7
+ last_command_started.stop
8
+ end
9
+
10
+ context "when no accounts are configured" do
11
+ it "succeeds" do
12
+ expect(last_command_started).to have_exit_status(0)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require "features/helper"
2
+
3
+ RSpec.describe "Running commands", type: :aruba do
4
+ before do
5
+ run_command("imap-backup")
6
+ last_command_started.stop
7
+ end
8
+
9
+ context "when the configuration file is missing" do
10
+ it "fails" do
11
+ expect(last_command_started).not_to have_exit_status(0)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ require "features/helper"
2
+ require "imap/backup/cli/folders"
3
+
4
+ RSpec.describe "folders", type: :feature, docker: true do
5
+ include_context "imap-backup connection"
6
+
7
+ let(:options) do
8
+ {accounts: "address@example.org"}
9
+ end
10
+ let(:folder) { "my-stuff" }
11
+ let(:output) { StringIO.new }
12
+
13
+ before do
14
+ allow(Imap::Backup::CLI::Accounts).to receive(:new) { [account] }
15
+ server_create_folder folder
16
+ end
17
+
18
+ around do |example|
19
+ stdout = $stdout
20
+ $stdout = output
21
+ example.run
22
+ $stdout = stdout
23
+ end
24
+
25
+ after do
26
+ FileUtils.rm_rf local_backup_path
27
+ server_delete_folder folder
28
+ connection.disconnect
29
+ end
30
+
31
+ it "lists account folders" do
32
+ Imap::Backup::CLI::Folders.new(options).run
33
+
34
+ expect(output.string).to match(/^\tmy-stuff\n/)
35
+ end
36
+ end