imap-backup 2.1.1 → 3.0.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -4
- data/.rubocop_todo.yml +29 -11
- data/.travis.yml +1 -1
- data/README.md +10 -13
- data/bin/imap-backup +5 -2
- data/docs/01-credentials-screen.png +0 -0
- data/docs/02-new-project.png +0 -0
- data/docs/03-initial-credentials-for-project.png +0 -0
- data/docs/04-credential-type-selection.png +0 -0
- data/docs/05-cant-create-without-consent-setup.png +0 -0
- data/docs/06-user-type-selection.png +0 -0
- data/docs/07-consent-screen-form.png +0 -0
- data/docs/08-app-scopes.png +0 -0
- data/docs/09-scope-selection.png +0 -0
- data/docs/10-updated-app-scopes.png +0 -0
- data/docs/11-test-users.png +0 -0
- data/docs/12-add-users.png +0 -0
- data/docs/13-create-oauth-client.png +0 -0
- data/docs/14-application-details.png +0 -0
- data/docs/16-initial-menu.png +0 -0
- data/docs/17-inputting-the-email-address.png +0 -0
- data/docs/18-choose-password.png +0 -0
- data/docs/19-supply-client-info.png +0 -0
- data/docs/20-choose-gmail-account.png +0 -0
- data/docs/21-accept-warnings.png +0 -0
- data/docs/22-grant-access.png +0 -0
- data/docs/24-confirm-choices.png +0 -0
- data/docs/25-success-code.png +0 -0
- data/docs/26-type-code-into-imap-backup.png +0 -0
- data/docs/27-success.png +0 -0
- data/docs/setting-up-gmail.md +166 -0
- data/imap-backup.gemspec +3 -9
- data/lib/email/mboxrd/message.rb +4 -3
- data/lib/email/provider.rb +3 -1
- data/lib/gmail/authenticator.rb +160 -0
- data/lib/google/auth/stores/in_memory_token_store.rb +9 -0
- data/lib/imap/backup.rb +2 -1
- data/lib/imap/backup/account/connection.rb +59 -34
- data/lib/imap/backup/account/folder.rb +10 -1
- data/lib/imap/backup/configuration/account.rb +9 -1
- data/lib/imap/backup/configuration/gmail_oauth2.rb +82 -0
- data/lib/imap/backup/configuration/setup.rb +4 -1
- data/lib/imap/backup/serializer/mbox.rb +4 -0
- data/lib/imap/backup/serializer/mbox_enumerator.rb +1 -1
- data/lib/imap/backup/serializer/mbox_store.rb +20 -4
- data/lib/imap/backup/uploader.rb +10 -2
- data/lib/imap/backup/version.rb +5 -4
- data/spec/features/backup_spec.rb +3 -3
- data/spec/features/helper.rb +1 -1
- data/spec/features/restore_spec.rb +75 -27
- data/spec/features/support/backup_directory.rb +2 -2
- data/spec/features/support/email_server.rb +1 -3
- data/spec/features/support/shared/message_fixtures.rb +8 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/fixtures.rb +1 -1
- data/spec/unit/email/mboxrd/message_spec.rb +2 -8
- data/spec/unit/email/provider_spec.rb +2 -2
- data/spec/unit/gmail/authenticator_spec.rb +138 -0
- data/spec/unit/google/auth/stores/in_memory_token_store_spec.rb +15 -0
- data/spec/unit/imap/backup/account/connection_spec.rb +157 -79
- data/spec/unit/imap/backup/account/folder_spec.rb +30 -20
- data/spec/unit/imap/backup/configuration/account_spec.rb +65 -46
- data/spec/unit/imap/backup/configuration/asker_spec.rb +20 -17
- data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +6 -10
- data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +16 -10
- data/spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb +84 -0
- data/spec/unit/imap/backup/configuration/list_spec.rb +6 -3
- data/spec/unit/imap/backup/configuration/setup_spec.rb +89 -54
- data/spec/unit/imap/backup/configuration/store_spec.rb +18 -16
- data/spec/unit/imap/backup/downloader_spec.rb +14 -14
- data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +6 -1
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -40
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +94 -35
- data/spec/unit/imap/backup/uploader_spec.rb +23 -7
- data/spec/unit/imap/backup/utils_spec.rb +10 -9
- metadata +68 -9
@@ -75,7 +75,10 @@ module Imap::Backup
|
|
75
75
|
password: "",
|
76
76
|
local_path: File.join(config.path, username.tr("@", "_")),
|
77
77
|
folders: []
|
78
|
-
}
|
78
|
+
}.tap do |c|
|
79
|
+
server = Email::Provider.for_address(username)
|
80
|
+
c[:server] = server.host if server.host
|
81
|
+
end
|
79
82
|
end
|
80
83
|
|
81
84
|
def edit_account(username)
|
@@ -81,6 +81,22 @@ module Imap::Backup
|
|
81
81
|
load_nth(message_index)
|
82
82
|
end
|
83
83
|
|
84
|
+
def each_message(required_uids)
|
85
|
+
return enum_for(:each_message, required_uids) if !block_given?
|
86
|
+
|
87
|
+
indexes = required_uids.each.with_object({}) do |uid, acc|
|
88
|
+
index = uids.find_index(uid)
|
89
|
+
acc[index] = uid if index
|
90
|
+
end
|
91
|
+
enumerator = Serializer::MboxEnumerator.new(mbox_pathname)
|
92
|
+
enumerator.each.with_index do |raw, i|
|
93
|
+
uid = indexes[i]
|
94
|
+
next if !uid
|
95
|
+
|
96
|
+
yield uid, Email::Mboxrd::Message.from_serialized(raw)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
84
100
|
def update_uid(old, new)
|
85
101
|
index = uids.find_index(old.to_i)
|
86
102
|
return if index.nil?
|
@@ -98,8 +114,8 @@ module Imap::Backup
|
|
98
114
|
end
|
99
115
|
|
100
116
|
def rename(new_name)
|
101
|
-
new_mbox_pathname = absolute_path(new_name
|
102
|
-
new_imap_pathname = absolute_path(new_name
|
117
|
+
new_mbox_pathname = absolute_path("#{new_name}.mbox")
|
118
|
+
new_imap_pathname = absolute_path("#{new_name}.imap")
|
103
119
|
File.rename(mbox_pathname, new_mbox_pathname)
|
104
120
|
File.rename(imap_pathname, new_imap_pathname)
|
105
121
|
@folder = new_name
|
@@ -191,11 +207,11 @@ module Imap::Backup
|
|
191
207
|
end
|
192
208
|
|
193
209
|
def mbox_pathname
|
194
|
-
absolute_path(folder
|
210
|
+
absolute_path("#{folder}.mbox")
|
195
211
|
end
|
196
212
|
|
197
213
|
def imap_pathname
|
198
|
-
absolute_path(folder
|
214
|
+
absolute_path("#{folder}.imap")
|
199
215
|
end
|
200
216
|
end
|
201
217
|
end
|
data/lib/imap/backup/uploader.rb
CHANGED
@@ -9,10 +9,18 @@ module Imap::Backup
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def run
|
12
|
-
missing_uids.
|
13
|
-
|
12
|
+
count = missing_uids.count
|
13
|
+
return if count.zero?
|
14
|
+
|
15
|
+
Imap::Backup.logger.debug "[#{folder.name}] #{count} to restore"
|
16
|
+
serializer.each_message(missing_uids).with_index do |(uid, message), i|
|
14
17
|
next if message.nil?
|
15
18
|
|
19
|
+
log_prefix = "[#{folder.name}] uid: #{uid} (#{i + 1}/#{count}) -"
|
20
|
+
Imap::Backup.logger.debug(
|
21
|
+
"#{log_prefix} #{message.supplied_body.size} bytes"
|
22
|
+
)
|
23
|
+
|
16
24
|
new_uid = folder.append(message)
|
17
25
|
serializer.update_uid(uid, new_uid)
|
18
26
|
end
|
data/lib/imap/backup/version.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
module Imap; end
|
2
2
|
|
3
3
|
module Imap::Backup
|
4
|
-
MAJOR =
|
5
|
-
MINOR =
|
6
|
-
REVISION =
|
7
|
-
|
4
|
+
MAJOR = 3
|
5
|
+
MINOR = 0
|
6
|
+
REVISION = 0
|
7
|
+
PRE = nil
|
8
|
+
VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
|
8
9
|
end
|
@@ -38,7 +38,7 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it "saves a file version" do
|
41
|
-
expect(imap_metadata[:version].to_s).to match(/^[0-9
|
41
|
+
expect(imap_metadata[:version].to_s).to match(/^[0-9.]$/)
|
42
42
|
end
|
43
43
|
|
44
44
|
it "records IMAP ids" do
|
@@ -60,7 +60,7 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
60
60
|
connection.run_backup
|
61
61
|
server_rename_folder folder, new_name
|
62
62
|
end
|
63
|
-
let(:renamed_folder) { folder
|
63
|
+
let(:renamed_folder) { "#{folder}.#{original_folder_uid_validity}" }
|
64
64
|
|
65
65
|
after do
|
66
66
|
server_delete_folder new_name
|
@@ -82,7 +82,7 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it "moves the old backup to a uniquely named directory" do
|
85
|
-
renamed = folder
|
85
|
+
renamed = "#{folder}.#{original_folder_uid_validity}.1"
|
86
86
|
expect(mbox_content(renamed)).to eq(message_as_mbox_entry(msg3))
|
87
87
|
end
|
88
88
|
end
|
data/spec/features/helper.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
support_glob = File.expand_path("support/**/*.rb", __dir__)
|
2
|
-
Dir[support_glob].each { |f| require f }
|
2
|
+
Dir[support_glob].sort.each { |f| require f }
|
@@ -72,41 +72,89 @@ RSpec.describe "restore", type: :feature, docker: true do
|
|
72
72
|
end
|
73
73
|
|
74
74
|
context "when the uid_validity doesn't match" do
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
let(:new_folder) { "#{folder}.#{uid_validity}" }
|
80
|
-
let(:cleanup) do
|
81
|
-
server_delete_folder new_folder
|
82
|
-
super()
|
83
|
-
end
|
75
|
+
context "when the folder is empty" do
|
76
|
+
let(:pre) do
|
77
|
+
server_create_folder folder
|
78
|
+
end
|
84
79
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
80
|
+
it "sets the backup uid_validity to match the folder" do
|
81
|
+
updated_imap_content = imap_parsed(folder)
|
82
|
+
expect(updated_imap_content[:uid_validity]).
|
83
|
+
to eq(server_uid_validity(folder))
|
84
|
+
end
|
90
85
|
|
91
|
-
|
92
|
-
|
86
|
+
it "uploads to the new folder" do
|
87
|
+
messages = server_messages(folder).map do |m|
|
88
|
+
server_message_to_body(m)
|
89
|
+
end
|
90
|
+
expect(messages).to eq(messages_as_server_messages)
|
91
|
+
end
|
93
92
|
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
94
|
+
context "when the folder has content" do
|
95
|
+
let(:new_folder) { "#{folder}.#{uid_validity}" }
|
96
|
+
let(:cleanup) do
|
97
|
+
server_delete_folder new_folder
|
98
|
+
super()
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
101
|
+
let(:pre) do
|
102
|
+
server_create_folder folder
|
103
|
+
email3
|
104
|
+
end
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
106
|
+
it "renames the backup" do
|
107
|
+
expect(mbox_content(new_folder)).to eq(messages_as_mbox)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "leaves the existing folder as is" do
|
111
|
+
messages = server_messages(folder).map do |m|
|
112
|
+
server_message_to_body(m)
|
113
|
+
end
|
114
|
+
expect(messages).to eq([message_as_server_message(msg3)])
|
115
|
+
end
|
116
|
+
|
117
|
+
it "creates the new folder" do
|
118
|
+
expect(server_folders.map(&:name)).to include(new_folder)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "sets the backup uid_validity to match the new folder" do
|
122
|
+
updated_imap_content = imap_parsed(new_folder)
|
123
|
+
expect(updated_imap_content[:uid_validity]).
|
124
|
+
to eq(server_uid_validity(new_folder))
|
125
|
+
end
|
126
|
+
|
127
|
+
it "uploads to the new folder" do
|
128
|
+
messages = server_messages(new_folder).map do |m|
|
129
|
+
server_message_to_body(m)
|
130
|
+
end
|
131
|
+
expect(messages).to eq(messages_as_server_messages)
|
107
132
|
end
|
108
|
-
expect(messages).to eq(messages_as_server_messages)
|
109
133
|
end
|
110
134
|
end
|
111
135
|
end
|
136
|
+
|
137
|
+
context "when non-Unicode encodings are used" do
|
138
|
+
let(:server_message) do
|
139
|
+
message_as_server_message(msg_iso8859)
|
140
|
+
end
|
141
|
+
let(:messages_as_mbox) do
|
142
|
+
message_as_mbox_entry(msg_iso8859)
|
143
|
+
end
|
144
|
+
let(:message_uids) { [uid_iso8859] }
|
145
|
+
let(:uid_validity) { server_uid_validity(folder) }
|
146
|
+
|
147
|
+
let(:pre) do
|
148
|
+
server_create_folder folder
|
149
|
+
uid_validity
|
150
|
+
end
|
151
|
+
|
152
|
+
it "maintains encodings" do
|
153
|
+
message =
|
154
|
+
server_messages(folder).
|
155
|
+
first["RFC822"]
|
156
|
+
|
157
|
+
expect(message).to eq(server_message)
|
158
|
+
end
|
159
|
+
end
|
112
160
|
end
|
@@ -26,11 +26,11 @@ module BackupDirectoryHelpers
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def mbox_path(name)
|
29
|
-
File.join(local_backup_path, name
|
29
|
+
File.join(local_backup_path, "#{name}.mbox")
|
30
30
|
end
|
31
31
|
|
32
32
|
def imap_path(name)
|
33
|
-
File.join(local_backup_path, name
|
33
|
+
File.join(local_backup_path, "#{name}.imap")
|
34
34
|
end
|
35
35
|
|
36
36
|
def imap_content(name)
|
@@ -38,9 +38,7 @@ module EmailServerHelpers
|
|
38
38
|
return nil if fetch_data_items.nil?
|
39
39
|
|
40
40
|
fetch_data_item = fetch_data_items[0]
|
41
|
-
|
42
|
-
attributes["RFC822"].force_encoding("utf-8")
|
43
|
-
attributes
|
41
|
+
fetch_data_item.attr
|
44
42
|
end
|
45
43
|
|
46
44
|
def delete_emails(folder)
|
@@ -2,7 +2,15 @@ shared_context "message-fixtures" do
|
|
2
2
|
let(:uid1) { 123 }
|
3
3
|
let(:uid2) { 345 }
|
4
4
|
let(:uid3) { 567 }
|
5
|
+
let(:uid_iso8859) { 890 }
|
5
6
|
let(:msg1) { {uid: uid1, subject: "Test 1", body: "body 1\nHi"} }
|
6
7
|
let(:msg2) { {uid: uid2, subject: "Test 2", body: "body 2"} }
|
7
8
|
let(:msg3) { {uid: uid3, subject: "Test 3", body: "body 3"} }
|
9
|
+
let(:msg_iso8859) do
|
10
|
+
{
|
11
|
+
uid: uid_iso8859,
|
12
|
+
subject: "iso8859 Body",
|
13
|
+
body: "Ma, perchè?".encode(Encoding::ISO_8859_1).force_encoding("binary")
|
14
|
+
}
|
15
|
+
end
|
8
16
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,7 +7,7 @@ spec_path = File.dirname(__FILE__)
|
|
7
7
|
$LOAD_PATH << File.expand_path("../lib", spec_path)
|
8
8
|
|
9
9
|
support_glob = File.join(spec_path, "support", "**", "*.rb")
|
10
|
-
Dir[support_glob].each { |f| require f }
|
10
|
+
Dir[support_glob].sort.each { |f| require f }
|
11
11
|
|
12
12
|
require "simplecov"
|
13
13
|
SimpleCov.start do
|
data/spec/support/fixtures.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
def fixture(name)
|
2
2
|
spec_root = File.expand_path("..", File.dirname(__FILE__))
|
3
|
-
fixture_path = File.join(spec_root, "fixtures", name
|
3
|
+
fixture_path = File.join(spec_root, "fixtures", "#{name}.yml")
|
4
4
|
fixture = File.read(fixture_path)
|
5
5
|
YAML.safe_load(fixture, [Symbol])
|
6
6
|
end
|
@@ -79,12 +79,6 @@ describe Email::Mboxrd::Message do
|
|
79
79
|
allow(Mail).to receive(:new).with(cloned_message_body) { mail }
|
80
80
|
end
|
81
81
|
|
82
|
-
it "does not modify the message" do
|
83
|
-
subject.to_serialized
|
84
|
-
|
85
|
-
expect(message_body).to_not have_received(:force_encoding).with("binary")
|
86
|
-
end
|
87
|
-
|
88
82
|
it "adds a 'From ' line at the start" do
|
89
83
|
expected = "From #{from} #{date.asctime}\n"
|
90
84
|
expect(subject.to_serialized).to start_with(expected)
|
@@ -135,7 +129,7 @@ describe Email::Mboxrd::Message do
|
|
135
129
|
let(:message_body) { msg_no_from_but_return_path }
|
136
130
|
|
137
131
|
it "'return path' is used as 'from'" do
|
138
|
-
expect(subject.to_serialized).to start_with("From
|
132
|
+
expect(subject.to_serialized).to start_with("From #{from}\n")
|
139
133
|
end
|
140
134
|
end
|
141
135
|
|
@@ -143,7 +137,7 @@ describe Email::Mboxrd::Message do
|
|
143
137
|
let(:message_body) { msg_no_from_but_sender }
|
144
138
|
|
145
139
|
it "Sender is used as 'from'" do
|
146
|
-
expect(subject.to_serialized).to start_with("From
|
140
|
+
expect(subject.to_serialized).to start_with("From #{from}\n")
|
147
141
|
end
|
148
142
|
end
|
149
143
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
describe Email::Provider do
|
2
|
+
subject { described_class.new(:gmail) }
|
3
|
+
|
2
4
|
describe ".for_address" do
|
3
5
|
context "with known providers" do
|
4
6
|
[
|
@@ -20,8 +22,6 @@ describe Email::Provider do
|
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
subject { described_class.new(:gmail) }
|
24
|
-
|
25
25
|
describe "#options" do
|
26
26
|
it "returns options" do
|
27
27
|
expect(subject.options).to be_a(Hash)
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require "gmail/authenticator"
|
2
|
+
require "googleauth"
|
3
|
+
|
4
|
+
describe Gmail::Authenticator do
|
5
|
+
ACCESS_TOKEN = "access_token".freeze
|
6
|
+
AUTHORIZATION_URL = "authorization_url".freeze
|
7
|
+
CLIENT_ID = "client_id".freeze
|
8
|
+
CLIENT_SECRET = "client_secret".freeze
|
9
|
+
CODE = "code".freeze
|
10
|
+
CREDENTIALS = "credentials".freeze
|
11
|
+
EMAIL = "email".freeze
|
12
|
+
EXPIRATION_TIME_MILLIS = "expiration_time_millis".freeze
|
13
|
+
GMAIL_READ_SCOPE = "https://mail.google.com/".freeze
|
14
|
+
IMAP_BACKUP_TOKEN = "imap_backup_token".freeze
|
15
|
+
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
|
16
|
+
REFRESH_TOKEN = "refresh_token".freeze
|
17
|
+
|
18
|
+
subject { described_class.new(**params) }
|
19
|
+
|
20
|
+
let(:params) do
|
21
|
+
{
|
22
|
+
email: EMAIL,
|
23
|
+
token: IMAP_BACKUP_TOKEN
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:authorizer) do
|
28
|
+
instance_double(Google::Auth::UserAuthorizer)
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:imap_backup_token) do
|
32
|
+
instance_double(
|
33
|
+
Gmail::Authenticator::ImapBackupToken,
|
34
|
+
access_token: ACCESS_TOKEN,
|
35
|
+
client_id: CLIENT_ID,
|
36
|
+
client_secret: CLIENT_SECRET,
|
37
|
+
expiration_time_millis: EXPIRATION_TIME_MILLIS,
|
38
|
+
refresh_token: REFRESH_TOKEN,
|
39
|
+
valid?: true
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:token_store) do
|
44
|
+
instance_double(Google::Auth::Stores::InMemoryTokenStore)
|
45
|
+
end
|
46
|
+
|
47
|
+
let(:credentials) do
|
48
|
+
instance_double(Google::Auth::UserRefreshCredentials, refresh!: true)
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:expired) { false }
|
52
|
+
|
53
|
+
before do
|
54
|
+
allow(Google::Auth::UserAuthorizer).
|
55
|
+
to receive(:new).
|
56
|
+
with(
|
57
|
+
instance_of(Google::Auth::ClientId),
|
58
|
+
GMAIL_READ_SCOPE,
|
59
|
+
token_store
|
60
|
+
) { authorizer }
|
61
|
+
allow(authorizer).to receive(:get_authorization_url).
|
62
|
+
with(base_url: OOB_URI) { AUTHORIZATION_URL }
|
63
|
+
allow(authorizer).to receive(:get_credentials).
|
64
|
+
with(EMAIL) { credentials }
|
65
|
+
allow(authorizer).to receive(:get_credentials_from_code).
|
66
|
+
with(user_id: EMAIL, code: CODE, base_url: OOB_URI) { CREDENTIALS }
|
67
|
+
|
68
|
+
allow(Google::Auth::UserRefreshCredentials).
|
69
|
+
to receive(:new) { credentials }
|
70
|
+
allow(credentials).to receive(:expired?) { expired }
|
71
|
+
|
72
|
+
allow(Google::Auth::Stores::InMemoryTokenStore).
|
73
|
+
to receive(:new) { token_store }
|
74
|
+
allow(token_store).to receive(:store).
|
75
|
+
with(EMAIL, anything) # TODO: use a JSON matcher
|
76
|
+
allow(Gmail::Authenticator::ImapBackupToken).
|
77
|
+
to receive(:new).
|
78
|
+
with(IMAP_BACKUP_TOKEN) { imap_backup_token }
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#initialize" do
|
82
|
+
[:email, :token].each do |param|
|
83
|
+
context "parameter #{param}" do
|
84
|
+
let(:params) { super().dup.reject { |k| k == param } }
|
85
|
+
|
86
|
+
it "is expected" do
|
87
|
+
expect { subject }.to raise_error(
|
88
|
+
ArgumentError, /missing keyword: :#{param}/
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#credentials" do
|
96
|
+
let!(:result) { subject.credentials }
|
97
|
+
|
98
|
+
it "attempts to get credentials" do
|
99
|
+
expect(authorizer).to have_received(:get_credentials)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns the result" do
|
103
|
+
expect(result).to eq(credentials)
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when the access_token has expired" do
|
107
|
+
let(:expired) { true }
|
108
|
+
|
109
|
+
it "refreshes it" do
|
110
|
+
expect(credentials).to have_received(:refresh!)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "#authorization_url" do
|
116
|
+
let!(:result) { subject.authorization_url }
|
117
|
+
|
118
|
+
it "requests an authorization URL" do
|
119
|
+
expect(authorizer).to have_received(:get_authorization_url)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "returns the result" do
|
123
|
+
expect(result).to eq(AUTHORIZATION_URL)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "#credentials_from_code" do
|
128
|
+
let!(:result) { subject.credentials_from_code(CODE) }
|
129
|
+
|
130
|
+
it "requests credentials" do
|
131
|
+
expect(authorizer).to have_received(:get_credentials_from_code)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "returns credentials" do
|
135
|
+
expect(result).to eq(CREDENTIALS)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|