imap-backup 5.2.0 → 6.0.1
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/imap-backup.gemspec +5 -1
- data/lib/cli_coverage.rb +11 -11
- data/lib/email/provider/base.rb +2 -0
- data/lib/email/provider/unknown.rb +2 -0
- data/lib/email/provider.rb +2 -0
- data/lib/imap/backup/account/connection/backup_folders.rb +27 -0
- data/lib/imap/backup/account/connection/client_factory.rb +54 -0
- data/lib/imap/backup/account/connection/folder_names.rb +26 -0
- data/lib/imap/backup/account/connection.rb +17 -105
- data/lib/imap/backup/account/folder.rb +9 -6
- data/lib/imap/backup/account.rb +36 -16
- data/lib/imap/backup/cli/backup.rb +1 -3
- data/lib/imap/backup/cli/folders.rb +3 -3
- data/lib/imap/backup/cli/helpers.rb +24 -22
- data/lib/imap/backup/cli/local.rb +20 -13
- data/lib/imap/backup/cli/migrate.rb +5 -11
- data/lib/imap/backup/cli/restore.rb +8 -7
- data/lib/imap/backup/cli/setup.rb +10 -8
- data/lib/imap/backup/cli/stats.rb +78 -0
- data/lib/imap/backup/cli/status.rb +2 -2
- data/lib/imap/backup/cli/utils.rb +6 -8
- data/lib/imap/backup/cli.rb +24 -3
- data/lib/imap/backup/configuration.rb +9 -21
- data/lib/imap/backup/downloader.rb +56 -34
- data/lib/imap/backup/migrator.rb +5 -5
- data/lib/imap/backup/sanitizer.rb +3 -2
- data/lib/imap/backup/serializer/appender.rb +49 -0
- data/lib/imap/backup/serializer/directory.rb +37 -0
- data/lib/imap/backup/serializer/imap.rb +144 -0
- data/lib/imap/backup/serializer/mbox.rb +33 -88
- data/lib/imap/backup/serializer/mbox_enumerator.rb +2 -0
- data/lib/imap/backup/serializer/message_enumerator.rb +29 -0
- data/lib/imap/backup/serializer/unused_name_finder.rb +25 -0
- data/lib/imap/backup/serializer.rb +160 -3
- data/lib/imap/backup/setup/account/header.rb +75 -0
- data/lib/imap/backup/setup/account.rb +41 -95
- data/lib/imap/backup/setup/asker.rb +4 -15
- data/lib/imap/backup/setup/backup_path.rb +41 -0
- data/lib/imap/backup/setup/email.rb +45 -0
- data/lib/imap/backup/setup/folder_chooser.rb +3 -3
- data/lib/imap/backup/setup/helpers.rb +2 -2
- data/lib/imap/backup/setup.rb +5 -4
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +41 -22
- data/lib/imap/backup/uploader.rb +46 -8
- data/lib/imap/backup/utils.rb +1 -1
- data/lib/imap/backup/version.rb +3 -3
- data/lib/imap/backup.rb +0 -2
- metadata +31 -105
- data/lib/imap/backup/serializer/mbox_store.rb +0 -217
- data/spec/features/backup_spec.rb +0 -108
- data/spec/features/configuration/minimal_configuration.rb +0 -15
- data/spec/features/configuration/missing_configuration.rb +0 -14
- data/spec/features/folders_spec.rb +0 -36
- data/spec/features/helper.rb +0 -2
- data/spec/features/local/list_accounts_spec.rb +0 -12
- data/spec/features/local/list_emails_spec.rb +0 -21
- data/spec/features/local/list_folders_spec.rb +0 -21
- data/spec/features/local/show_an_email_spec.rb +0 -34
- data/spec/features/migrate_spec.rb +0 -35
- data/spec/features/remote/list_account_folders_spec.rb +0 -16
- data/spec/features/restore_spec.rb +0 -162
- data/spec/features/status_spec.rb +0 -43
- data/spec/features/support/aruba.rb +0 -77
- data/spec/features/support/backup_directory.rb +0 -43
- data/spec/features/support/email_server.rb +0 -110
- data/spec/features/support/shared/connection_context.rb +0 -14
- data/spec/features/support/shared/message_fixtures.rb +0 -16
- data/spec/fixtures/connection.yml +0 -7
- data/spec/spec_helper.rb +0 -15
- data/spec/support/fixtures.rb +0 -11
- data/spec/support/higline_test_helpers.rb +0 -8
- data/spec/support/silence_logging.rb +0 -7
- data/spec/unit/email/mboxrd/message_spec.rb +0 -177
- data/spec/unit/email/provider/apple_mail_spec.rb +0 -7
- data/spec/unit/email/provider/base_spec.rb +0 -11
- data/spec/unit/email/provider/fastmail_spec.rb +0 -7
- data/spec/unit/email/provider/gmail_spec.rb +0 -7
- data/spec/unit/email/provider_spec.rb +0 -27
- data/spec/unit/imap/backup/account/connection_spec.rb +0 -405
- data/spec/unit/imap/backup/account/folder_spec.rb +0 -251
- data/spec/unit/imap/backup/cli/accounts_spec.rb +0 -47
- data/spec/unit/imap/backup/cli/helpers_spec.rb +0 -87
- data/spec/unit/imap/backup/cli/local_spec.rb +0 -81
- data/spec/unit/imap/backup/cli/utils_spec.rb +0 -62
- data/spec/unit/imap/backup/client/default_spec.rb +0 -22
- data/spec/unit/imap/backup/configuration_spec.rb +0 -238
- data/spec/unit/imap/backup/downloader_spec.rb +0 -44
- data/spec/unit/imap/backup/logger_spec.rb +0 -48
- data/spec/unit/imap/backup/migrator_spec.rb +0 -58
- data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +0 -45
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +0 -222
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +0 -329
- data/spec/unit/imap/backup/setup/account_spec.rb +0 -366
- data/spec/unit/imap/backup/setup/asker_spec.rb +0 -137
- data/spec/unit/imap/backup/setup/connection_tester_spec.rb +0 -51
- data/spec/unit/imap/backup/setup/folder_chooser_spec.rb +0 -146
- data/spec/unit/imap/backup/setup_spec.rb +0 -301
- data/spec/unit/imap/backup/uploader_spec.rb +0 -54
- data/spec/unit/imap/backup/utils_spec.rb +0 -92
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
msg_no_from = <<~NO_FROM
|
|
2
|
-
Delivered-To: you@example.com
|
|
3
|
-
From: example <www.example.com>
|
|
4
|
-
To: FirstName LastName <you@example.com>
|
|
5
|
-
Subject: Re: no subject
|
|
6
|
-
NO_FROM
|
|
7
|
-
|
|
8
|
-
msg_bad_from = <<~BAD_FROM
|
|
9
|
-
Delivered-To: you@example.com
|
|
10
|
-
from: "FirstName LastName (TEXT)" <"TEXT*" <no-reply@example.com>>
|
|
11
|
-
To: FirstName LastName <you@example.com>
|
|
12
|
-
Subject: Re: no subject
|
|
13
|
-
BAD_FROM
|
|
14
|
-
|
|
15
|
-
msg_no_from_but_return_path = <<~RETURN_PATH
|
|
16
|
-
Delivered-To: you@example.com
|
|
17
|
-
From: example <www.example.com>
|
|
18
|
-
To: FirstName LastName <you@example.com>
|
|
19
|
-
Return-Path: <me@example.com>
|
|
20
|
-
Subject: Re: no subject
|
|
21
|
-
RETURN_PATH
|
|
22
|
-
|
|
23
|
-
msg_no_from_but_sender = <<~NOT_SENDER
|
|
24
|
-
Delivered-To: you@example.com
|
|
25
|
-
To: FirstName LastName <you@example.com>
|
|
26
|
-
Subject: Re: no subject
|
|
27
|
-
Sender: FistName LastName <me@example.com>
|
|
28
|
-
NOT_SENDER
|
|
29
|
-
|
|
30
|
-
describe Email::Mboxrd::Message do
|
|
31
|
-
subject { described_class.new(message_body) }
|
|
32
|
-
|
|
33
|
-
let(:from) { "me@example.com" }
|
|
34
|
-
let(:date) { DateTime.new(2012, 12, 13, 18, 23, 45) }
|
|
35
|
-
let(:message_body) do
|
|
36
|
-
instance_double(String, clone: cloned_message_body, force_encoding: nil)
|
|
37
|
-
end
|
|
38
|
-
let(:cloned_message_body) do
|
|
39
|
-
"Foo\nBar\nFrom at the beginning of the line\n>>From quoted"
|
|
40
|
-
end
|
|
41
|
-
let(:msg_good) do
|
|
42
|
-
<<~GOOD
|
|
43
|
-
Delivered-To: you@example.com
|
|
44
|
-
From: Foo <foo@example.com>
|
|
45
|
-
To: FirstName LastName <you@example.com>
|
|
46
|
-
Date: #{date.rfc822}
|
|
47
|
-
Subject: Re: no subject
|
|
48
|
-
GOOD
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
let(:msg_bad_date) do
|
|
52
|
-
<<~BAD
|
|
53
|
-
Delivered-To: you@example.com
|
|
54
|
-
From: Foo <foo@example.com>
|
|
55
|
-
To: FirstName LastName <you@example.com>
|
|
56
|
-
Date: Mon,5 May 2014 08:97:99 GMT
|
|
57
|
-
Subject: Re: no subject
|
|
58
|
-
BAD
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
describe ".from_serialized" do
|
|
62
|
-
let(:serialized_message) { "From foo@a.com\n#{imap_message}" }
|
|
63
|
-
let(:imap_message) { "Delivered-To: me@example.com\nFrom Me\n" }
|
|
64
|
-
let!(:result) { described_class.from_serialized(serialized_message) }
|
|
65
|
-
|
|
66
|
-
it "returns the message" do
|
|
67
|
-
expect(result).to be_a(described_class)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "removes one level of > before From" do
|
|
71
|
-
expect(result.supplied_body).to eq(imap_message)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "#to_serialized" do
|
|
76
|
-
let(:mail) { instance_double(Mail::Message, from: [from], date: date) }
|
|
77
|
-
|
|
78
|
-
before do
|
|
79
|
-
allow(Mail).to receive(:new).with(cloned_message_body) { mail }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it "adds a 'From ' line at the start" do
|
|
83
|
-
expected = "From #{from} #{date.asctime}\n"
|
|
84
|
-
expect(subject.to_serialized).to start_with(expected)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it "replaces existing 'From ' with '>From '" do
|
|
88
|
-
expect(subject.to_serialized).to include("\n>From at the beginning")
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it "appends > before '>+From '" do
|
|
92
|
-
expect(subject.to_serialized).to include("\n>>>From quoted")
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
context "when date is missing" do
|
|
96
|
-
let(:date) { nil }
|
|
97
|
-
|
|
98
|
-
it "does no fail" do
|
|
99
|
-
expect { subject.to_serialized }.to_not raise_error
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
describe "From" do
|
|
105
|
-
before do
|
|
106
|
-
# call original for these tests because we want to test the behaviour of
|
|
107
|
-
# class-under-test given different behaviour of the Mail parser
|
|
108
|
-
allow(Mail).to receive(:new).and_call_original
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
context "when original message 'from' is missing" do
|
|
112
|
-
let(:message_body) { msg_no_from }
|
|
113
|
-
|
|
114
|
-
it "'from' is empty string" do
|
|
115
|
-
expect(subject.to_serialized).to start_with("From \n")
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
context "when original message 'from' is not a well-formed address" do
|
|
120
|
-
let(:message_body) { msg_bad_from }
|
|
121
|
-
|
|
122
|
-
it "'from' is empty string" do
|
|
123
|
-
expect(subject.to_serialized).to start_with("From \n")
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
context "when original message 'from' is nil and " \
|
|
128
|
-
"'envelope from' is nil and 'return path' is available" do
|
|
129
|
-
let(:message_body) { msg_no_from_but_return_path }
|
|
130
|
-
|
|
131
|
-
it "'return path' is used as 'from'" do
|
|
132
|
-
expect(subject.to_serialized).to start_with("From #{from}\n")
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
context "with no from and a 'Sender'" do
|
|
137
|
-
let(:message_body) { msg_no_from_but_sender }
|
|
138
|
-
|
|
139
|
-
it "Sender is used as 'from'" do
|
|
140
|
-
expect(subject.to_serialized).to start_with("From #{from}\n")
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
describe "#date" do
|
|
146
|
-
let(:message_body) { msg_good }
|
|
147
|
-
|
|
148
|
-
it "returns the date" do
|
|
149
|
-
expect(subject.date).to eq(date)
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
context "with incorrect minutes and seconds" do
|
|
153
|
-
let(:message_body) { msg_bad_date }
|
|
154
|
-
|
|
155
|
-
it "returns nil" do
|
|
156
|
-
expect(subject.date).to be_nil
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
describe "#imap_body" do
|
|
162
|
-
let(:message_body) { "Ciao" }
|
|
163
|
-
|
|
164
|
-
it "returns the supplied body" do
|
|
165
|
-
expect(subject.imap_body).to eq(message_body)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
context "when newlines are not IMAP standard" do
|
|
169
|
-
let(:message_body) { "Ciao\nHello" }
|
|
170
|
-
let(:corrected) { "Ciao\r\nHello" }
|
|
171
|
-
|
|
172
|
-
it "corrects them" do
|
|
173
|
-
expect(subject.imap_body).to eq(corrected)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
describe Email::Provider do
|
|
2
|
-
describe ".for_address" do
|
|
3
|
-
context "with known providers" do
|
|
4
|
-
[
|
|
5
|
-
["fastmail.com", "Fastmail .com", Email::Provider::Fastmail],
|
|
6
|
-
["fastmail.fm", "Fastmail .fm", Email::Provider::Fastmail],
|
|
7
|
-
["gmail.com", "GMail", Email::Provider::GMail],
|
|
8
|
-
["icloud.com", "Apple Mail icloud.com", Email::Provider::AppleMail],
|
|
9
|
-
["mac.com", "Apple Mail mac.com", Email::Provider::AppleMail],
|
|
10
|
-
["me.com", "Apple Mail me.com", Email::Provider::AppleMail]
|
|
11
|
-
].each do |domain, name, klass|
|
|
12
|
-
it "recognizes #{name} addresses" do
|
|
13
|
-
address = "foo@#{domain}"
|
|
14
|
-
expect(described_class.for_address(address)).to be_a(klass)
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
context "with unknown providers" do
|
|
20
|
-
it "returns the Unknown provider" do
|
|
21
|
-
result = described_class.for_address("foo@unknown.com")
|
|
22
|
-
|
|
23
|
-
expect(result).to be_a(Email::Provider::Unknown)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,405 +0,0 @@
|
|
|
1
|
-
require "ostruct"
|
|
2
|
-
|
|
3
|
-
describe Imap::Backup::Account::Connection do
|
|
4
|
-
BACKUP_FOLDER = "backup_folder".freeze
|
|
5
|
-
FOLDER_CONFIG = {name: BACKUP_FOLDER}.freeze
|
|
6
|
-
FOLDER_NAME = "my_folder".freeze
|
|
7
|
-
GMAIL_IMAP_SERVER = "imap.gmail.com".freeze
|
|
8
|
-
IMAP_FOLDER = "imap_folder".freeze
|
|
9
|
-
LOCAL_PATH = "local_path".freeze
|
|
10
|
-
LOCAL_UID = "local_uid".freeze
|
|
11
|
-
PASSWORD = "secret".freeze
|
|
12
|
-
ROOT_NAME = "foo".freeze
|
|
13
|
-
SERVER = "imap.example.com".freeze
|
|
14
|
-
USERNAME = "username@example.com".freeze
|
|
15
|
-
|
|
16
|
-
subject { described_class.new(account) }
|
|
17
|
-
|
|
18
|
-
let(:client) do
|
|
19
|
-
instance_double(
|
|
20
|
-
Imap::Backup::Client::Default, authenticate: nil, login: nil, disconnect: nil
|
|
21
|
-
)
|
|
22
|
-
end
|
|
23
|
-
let(:imap_folders) { [] }
|
|
24
|
-
let(:account) do
|
|
25
|
-
instance_double(
|
|
26
|
-
Imap::Backup::Account,
|
|
27
|
-
username: USERNAME,
|
|
28
|
-
password: PASSWORD,
|
|
29
|
-
local_path: LOCAL_PATH,
|
|
30
|
-
folders: config_folders,
|
|
31
|
-
server: server,
|
|
32
|
-
connection_options: nil
|
|
33
|
-
)
|
|
34
|
-
end
|
|
35
|
-
let(:config_folders) { [FOLDER_CONFIG] }
|
|
36
|
-
let(:root_info) do
|
|
37
|
-
instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)
|
|
38
|
-
end
|
|
39
|
-
let(:serializer) do
|
|
40
|
-
instance_double(
|
|
41
|
-
Imap::Backup::Serializer::Mbox,
|
|
42
|
-
folder: serialized_folder,
|
|
43
|
-
force_uid_validity: nil,
|
|
44
|
-
apply_uid_validity: new_uid_validity,
|
|
45
|
-
uids: [LOCAL_UID]
|
|
46
|
-
)
|
|
47
|
-
end
|
|
48
|
-
let(:serialized_folder) { nil }
|
|
49
|
-
let(:server) { SERVER }
|
|
50
|
-
let(:new_uid_validity) { nil }
|
|
51
|
-
|
|
52
|
-
before do
|
|
53
|
-
allow(Imap::Backup::Client::Default).to receive(:new) { client }
|
|
54
|
-
allow(client).to receive(:list) { imap_folders }
|
|
55
|
-
allow(Imap::Backup::Utils).to receive(:make_folder)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
shared_examples "connects to IMAP" do
|
|
59
|
-
it "logs in to the imap server" do
|
|
60
|
-
expect(client).to have_received(:login)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
describe "#client" do
|
|
65
|
-
let(:result) { subject.client }
|
|
66
|
-
|
|
67
|
-
it "returns the IMAP connection" do
|
|
68
|
-
expect(result).to eq(client)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it "uses the password" do
|
|
72
|
-
result
|
|
73
|
-
|
|
74
|
-
expect(client).to have_received(:login).with(USERNAME, PASSWORD)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
context "when the first login attempt fails" do
|
|
78
|
-
before do
|
|
79
|
-
outcomes = [-> { raise EOFError }, -> { true }]
|
|
80
|
-
allow(client).to receive(:login) { outcomes.shift.call }
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it "retries" do
|
|
84
|
-
subject.client
|
|
85
|
-
|
|
86
|
-
expect(client).to have_received(:login).twice
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
context "when run" do
|
|
91
|
-
before { subject.client }
|
|
92
|
-
|
|
93
|
-
include_examples "connects to IMAP"
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
describe "#folder_names" do
|
|
98
|
-
let(:imap_folders) do
|
|
99
|
-
[IMAP_FOLDER]
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it "returns the list of folders" do
|
|
103
|
-
expect(subject.folder_names).to eq([IMAP_FOLDER])
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
describe "#status" do
|
|
108
|
-
let(:folder) do
|
|
109
|
-
instance_double(
|
|
110
|
-
Imap::Backup::Account::Folder,
|
|
111
|
-
uids: [remote_uid],
|
|
112
|
-
name: IMAP_FOLDER
|
|
113
|
-
)
|
|
114
|
-
end
|
|
115
|
-
let(:remote_uid) { "remote_uid" }
|
|
116
|
-
|
|
117
|
-
before do
|
|
118
|
-
allow(Imap::Backup::Account::Folder).to receive(:new) { folder }
|
|
119
|
-
allow(Imap::Backup::Serializer::Mbox).to receive(:new) { serializer }
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
it "creates the path" do
|
|
123
|
-
expect(Imap::Backup::Utils).to receive(:make_folder)
|
|
124
|
-
|
|
125
|
-
subject.status
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
it "returns the names of folders" do
|
|
129
|
-
expect(subject.status[0][:name]).to eq(IMAP_FOLDER)
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
it "returns local message uids" do
|
|
133
|
-
expect(subject.status[0][:local]).to eq([LOCAL_UID])
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
it "retrieves the available uids" do
|
|
137
|
-
expect(subject.status[0][:remote]).to eq([remote_uid])
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
describe "#run_backup" do
|
|
142
|
-
let(:folder) do
|
|
143
|
-
instance_double(
|
|
144
|
-
Imap::Backup::Account::Folder,
|
|
145
|
-
name: IMAP_FOLDER,
|
|
146
|
-
exist?: exists,
|
|
147
|
-
uid_validity: uid_validity
|
|
148
|
-
)
|
|
149
|
-
end
|
|
150
|
-
let(:exists) { true }
|
|
151
|
-
let(:uid_validity) { 123 }
|
|
152
|
-
let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
|
|
153
|
-
|
|
154
|
-
before do
|
|
155
|
-
allow(Imap::Backup::Downloader).
|
|
156
|
-
to receive(:new).with(folder, serializer, anything) { downloader }
|
|
157
|
-
allow(Imap::Backup::Account::Folder).to receive(:new).
|
|
158
|
-
with(subject, BACKUP_FOLDER) { folder }
|
|
159
|
-
allow(Imap::Backup::Serializer::Mbox).to receive(:new).
|
|
160
|
-
with(LOCAL_PATH, IMAP_FOLDER) { serializer }
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
context "with supplied config_folders" do
|
|
164
|
-
it "runs the downloader" do
|
|
165
|
-
expect(downloader).to receive(:run)
|
|
166
|
-
|
|
167
|
-
subject.run_backup
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
context "when a folder does not exist" do
|
|
171
|
-
let(:exists) { false }
|
|
172
|
-
|
|
173
|
-
it "does not run the downloader" do
|
|
174
|
-
expect(downloader).to_not receive(:run)
|
|
175
|
-
|
|
176
|
-
subject.run_backup
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
context "without supplied config_folders" do
|
|
182
|
-
let(:imap_folders) { [ROOT_NAME] }
|
|
183
|
-
|
|
184
|
-
before do
|
|
185
|
-
allow(Imap::Backup::Account::Folder).to receive(:new).
|
|
186
|
-
with(subject, ROOT_NAME) { folder }
|
|
187
|
-
allow(Imap::Backup::Serializer::Mbox).to receive(:new).
|
|
188
|
-
with(LOCAL_PATH, ROOT_NAME) { serializer }
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
context "when supplied config_folders is nil" do
|
|
192
|
-
let(:config_folders) { nil }
|
|
193
|
-
|
|
194
|
-
it "runs the downloader for each folder" do
|
|
195
|
-
expect(downloader).to receive(:run).exactly(:once)
|
|
196
|
-
|
|
197
|
-
subject.run_backup
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
context "when supplied config_folders is an empty list" do
|
|
202
|
-
let(:config_folders) { [] }
|
|
203
|
-
|
|
204
|
-
it "runs the downloader for each folder" do
|
|
205
|
-
expect(downloader).to receive(:run).exactly(:once)
|
|
206
|
-
|
|
207
|
-
subject.run_backup
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
context "when the imap server doesn't return folders" do
|
|
212
|
-
let(:config_folders) { nil }
|
|
213
|
-
let(:imap_folders) { [] }
|
|
214
|
-
|
|
215
|
-
it "fails" do
|
|
216
|
-
expect do
|
|
217
|
-
subject.run_backup
|
|
218
|
-
end.to raise_error(RuntimeError, /Unable to get folder list/)
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
context "when the IMAP session expires" do
|
|
224
|
-
before do
|
|
225
|
-
data = OpenStruct.new(data: "Session expired")
|
|
226
|
-
response = OpenStruct.new(data: data)
|
|
227
|
-
outcomes = [
|
|
228
|
-
-> { raise Net::IMAP::ByeResponseError, response },
|
|
229
|
-
-> { nil }
|
|
230
|
-
]
|
|
231
|
-
allow(downloader).to receive(:run) { outcomes.shift.call }
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
it "reconnects" do
|
|
235
|
-
expect(downloader).to receive(:run).exactly(:twice)
|
|
236
|
-
|
|
237
|
-
subject.run_backup
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
context "when run" do
|
|
242
|
-
before { subject.run_backup }
|
|
243
|
-
|
|
244
|
-
include_examples "connects to IMAP"
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
describe "#restore" do
|
|
249
|
-
let(:folder) do
|
|
250
|
-
instance_double(
|
|
251
|
-
Imap::Backup::Account::Folder,
|
|
252
|
-
create: nil,
|
|
253
|
-
uids: uids,
|
|
254
|
-
name: IMAP_FOLDER,
|
|
255
|
-
uid_validity: uid_validity
|
|
256
|
-
)
|
|
257
|
-
end
|
|
258
|
-
let(:uids) { [99] }
|
|
259
|
-
let(:uid_validity) { 123 }
|
|
260
|
-
let(:serialized_folder) { "old name" }
|
|
261
|
-
let(:uploader) do
|
|
262
|
-
instance_double(Imap::Backup::Uploader, run: false)
|
|
263
|
-
end
|
|
264
|
-
let(:updated_uploader) do
|
|
265
|
-
instance_double(Imap::Backup::Uploader, run: false)
|
|
266
|
-
end
|
|
267
|
-
let(:updated_folder) do
|
|
268
|
-
instance_double(
|
|
269
|
-
Imap::Backup::Account::Folder,
|
|
270
|
-
create: nil,
|
|
271
|
-
uid_validity: "new uid validity"
|
|
272
|
-
)
|
|
273
|
-
end
|
|
274
|
-
let(:updated_serializer) do
|
|
275
|
-
instance_double(
|
|
276
|
-
Imap::Backup::Serializer::Mbox, force_uid_validity: nil
|
|
277
|
-
)
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
before do
|
|
281
|
-
allow(Imap::Backup::Account::Folder).to receive(:new).
|
|
282
|
-
with(subject, FOLDER_NAME) { folder }
|
|
283
|
-
allow(Imap::Backup::Serializer::Mbox).to receive(:new).
|
|
284
|
-
with(anything, FOLDER_NAME) { serializer }
|
|
285
|
-
allow(Imap::Backup::Account::Folder).to receive(:new).
|
|
286
|
-
with(subject, "new name") { updated_folder }
|
|
287
|
-
allow(Imap::Backup::Serializer::Mbox).to receive(:new).
|
|
288
|
-
with(anything, "new name") { updated_serializer }
|
|
289
|
-
allow(Imap::Backup::Uploader).to receive(:new).
|
|
290
|
-
with(folder, serializer) { uploader }
|
|
291
|
-
allow(Imap::Backup::Uploader).to receive(:new).
|
|
292
|
-
with(updated_folder, updated_serializer) { updated_uploader }
|
|
293
|
-
allow(Pathname).to receive(:glob).
|
|
294
|
-
and_yield(Pathname.new(File.join(LOCAL_PATH, "#{FOLDER_NAME}.imap")))
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
it "sets local uid validity" do
|
|
298
|
-
expect(serializer).to receive(:apply_uid_validity).with(uid_validity)
|
|
299
|
-
|
|
300
|
-
subject.restore
|
|
301
|
-
end
|
|
302
|
-
|
|
303
|
-
context "when folders exist with contents" do
|
|
304
|
-
context "when the local folder is renamed" do
|
|
305
|
-
let(:new_uid_validity) { "new name" }
|
|
306
|
-
|
|
307
|
-
it "creates the new folder" do
|
|
308
|
-
expect(updated_folder).to receive(:create)
|
|
309
|
-
|
|
310
|
-
subject.restore
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
it "sets the renamed folder's uid validity" do
|
|
314
|
-
expect(updated_serializer).
|
|
315
|
-
to receive(:force_uid_validity).with("new uid validity")
|
|
316
|
-
|
|
317
|
-
subject.restore
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
it "creates the uploader with updated folder and serializer" do
|
|
321
|
-
expect(updated_uploader).to receive(:run)
|
|
322
|
-
|
|
323
|
-
subject.restore
|
|
324
|
-
end
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
context "when the local folder is not renamed" do
|
|
328
|
-
it "runs the uploader" do
|
|
329
|
-
expect(uploader).to receive(:run)
|
|
330
|
-
|
|
331
|
-
subject.restore
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
context "when folders don't exist or are empty" do
|
|
337
|
-
let(:uids) { [] }
|
|
338
|
-
|
|
339
|
-
it "creates the folder" do
|
|
340
|
-
expect(folder).to receive(:create)
|
|
341
|
-
|
|
342
|
-
subject.restore
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
it "forces local uid validity" do
|
|
346
|
-
expect(serializer).to receive(:force_uid_validity).with(uid_validity)
|
|
347
|
-
|
|
348
|
-
subject.restore
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
it "runs the uploader" do
|
|
352
|
-
expect(uploader).to receive(:run)
|
|
353
|
-
|
|
354
|
-
subject.restore
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
describe "#reconnect" do
|
|
360
|
-
context "when the IMAP connection has been used" do
|
|
361
|
-
before { subject.client }
|
|
362
|
-
|
|
363
|
-
it "disconnects from the server" do
|
|
364
|
-
expect(client).to receive(:disconnect)
|
|
365
|
-
|
|
366
|
-
subject.reconnect
|
|
367
|
-
end
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
context "when the IMAP connection has not been used" do
|
|
371
|
-
it "does not disconnect from the server" do
|
|
372
|
-
expect(client).to_not receive(:disconnect)
|
|
373
|
-
|
|
374
|
-
subject.reconnect
|
|
375
|
-
end
|
|
376
|
-
end
|
|
377
|
-
|
|
378
|
-
it "causes reconnection on future access" do
|
|
379
|
-
expect(Imap::Backup::Client::Default).to receive(:new)
|
|
380
|
-
|
|
381
|
-
subject.reconnect
|
|
382
|
-
subject.client
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
describe "#disconnect" do
|
|
387
|
-
context "when the IMAP connection has been used" do
|
|
388
|
-
it "disconnects from the server" do
|
|
389
|
-
subject.client
|
|
390
|
-
|
|
391
|
-
expect(client).to receive(:disconnect)
|
|
392
|
-
|
|
393
|
-
subject.disconnect
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
context "when the IMAP connection has not been used" do
|
|
398
|
-
it "does not disconnect from the server" do
|
|
399
|
-
expect(client).to_not receive(:disconnect)
|
|
400
|
-
|
|
401
|
-
subject.disconnect
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
end
|