imap-backup 2.1.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|