imap-backup 3.4.0 → 4.0.0.rc3
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/.circleci/config.yml +2 -2
- data/.rubocop_todo.yml +1 -1
- data/CHANGELOG.md +19 -0
- data/README.md +22 -8
- data/bin/imap-backup +3 -93
- data/imap-backup +9 -0
- data/imap-backup.gemspec +2 -3
- data/lib/email/mboxrd/message.rb +2 -2
- data/lib/imap/backup/account/connection.rb +16 -33
- data/lib/imap/backup/cli/backup.rb +21 -0
- data/lib/imap/backup/cli/folders.rb +27 -0
- data/lib/imap/backup/cli/helpers.rb +20 -0
- data/lib/imap/backup/cli/local.rb +70 -0
- data/lib/imap/backup/cli/restore.rb +19 -0
- data/lib/imap/backup/cli/setup.rb +13 -0
- data/lib/imap/backup/cli/status.rb +26 -0
- data/lib/imap/backup/cli.rb +89 -0
- data/lib/imap/backup/configuration/account.rb +1 -11
- data/lib/imap/backup/configuration/list.rb +13 -12
- data/lib/imap/backup/version.rb +3 -3
- data/lib/imap/backup.rb +0 -1
- data/spec/unit/imap/backup/account/connection_spec.rb +31 -51
- data/spec/unit/imap/backup/configuration/account_spec.rb +0 -43
- data/spec/unit/imap/backup/configuration/list_spec.rb +1 -0
- metadata +23 -62
- 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-with-oauth2.md +0 -166
- data/lib/gmail/authenticator.rb +0 -160
- data/lib/google/auth/stores/in_memory_token_store.rb +0 -9
- data/lib/imap/backup/configuration/gmail_oauth2.rb +0 -102
- data/spec/unit/gmail/authenticator_spec.rb +0 -138
- data/spec/unit/google/auth/stores/in_memory_token_store_spec.rb +0 -15
- data/spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb +0 -121
data/lib/gmail/authenticator.rb
DELETED
@@ -1,160 +0,0 @@
|
|
1
|
-
require "googleauth"
|
2
|
-
require "google/auth/stores/in_memory_token_store"
|
3
|
-
|
4
|
-
module Gmail; end
|
5
|
-
|
6
|
-
class Gmail::Authenticator
|
7
|
-
class MalformedImapBackupToken < StandardError; end
|
8
|
-
|
9
|
-
class ImapBackupToken
|
10
|
-
attr_reader :token
|
11
|
-
|
12
|
-
def self.from(
|
13
|
-
access_token:,
|
14
|
-
client_id:,
|
15
|
-
client_secret:,
|
16
|
-
expiration_time_millis:,
|
17
|
-
refresh_token:
|
18
|
-
)
|
19
|
-
{
|
20
|
-
access_token: access_token,
|
21
|
-
client_id: client_id,
|
22
|
-
client_secret: client_secret,
|
23
|
-
expiration_time_millis: expiration_time_millis,
|
24
|
-
refresh_token: refresh_token
|
25
|
-
}.to_json
|
26
|
-
end
|
27
|
-
|
28
|
-
def initialize(token)
|
29
|
-
@token = token
|
30
|
-
end
|
31
|
-
|
32
|
-
def valid?
|
33
|
-
return false if !body
|
34
|
-
return false if !access_token
|
35
|
-
return false if !client_id
|
36
|
-
return false if !client_secret
|
37
|
-
return false if !expiration_time_millis
|
38
|
-
return false if !refresh_token
|
39
|
-
|
40
|
-
true
|
41
|
-
end
|
42
|
-
|
43
|
-
def access_token
|
44
|
-
body["access_token"]
|
45
|
-
end
|
46
|
-
|
47
|
-
def client_id
|
48
|
-
body["client_id"]
|
49
|
-
end
|
50
|
-
|
51
|
-
def client_secret
|
52
|
-
body["client_secret"]
|
53
|
-
end
|
54
|
-
|
55
|
-
def expiration_time_millis
|
56
|
-
body["expiration_time_millis"]
|
57
|
-
end
|
58
|
-
|
59
|
-
def refresh_token
|
60
|
-
body["refresh_token"]
|
61
|
-
end
|
62
|
-
|
63
|
-
private
|
64
|
-
|
65
|
-
def body
|
66
|
-
@body ||= JSON.parse(token)
|
67
|
-
rescue JSON::ParserError
|
68
|
-
nil
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
GMAIL_READ_SCOPE = "https://mail.google.com/".freeze
|
73
|
-
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
|
74
|
-
|
75
|
-
attr_reader :email
|
76
|
-
attr_reader :token
|
77
|
-
|
78
|
-
def self.refresh_token?(text)
|
79
|
-
ImapBackupToken.new(text).valid?
|
80
|
-
end
|
81
|
-
|
82
|
-
def initialize(email:, token:)
|
83
|
-
@email = email
|
84
|
-
@token = token
|
85
|
-
end
|
86
|
-
|
87
|
-
def authorization_url
|
88
|
-
authorizer.get_authorization_url(base_url: OOB_URI)
|
89
|
-
end
|
90
|
-
|
91
|
-
def credentials
|
92
|
-
authorizer.get_credentials(email).tap do |c|
|
93
|
-
c.refresh! if c.expired?
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def credentials_from_code(code)
|
98
|
-
authorizer.get_credentials_from_code(
|
99
|
-
user_id: email,
|
100
|
-
code: code,
|
101
|
-
base_url: OOB_URI
|
102
|
-
)
|
103
|
-
end
|
104
|
-
|
105
|
-
private
|
106
|
-
|
107
|
-
def auth_client_id
|
108
|
-
@auth_client_id = Google::Auth::ClientId.new(client_id, client_secret)
|
109
|
-
end
|
110
|
-
|
111
|
-
def authorizer
|
112
|
-
@authorizer ||= Google::Auth::UserAuthorizer.new(
|
113
|
-
auth_client_id, GMAIL_READ_SCOPE, token_store
|
114
|
-
)
|
115
|
-
end
|
116
|
-
|
117
|
-
def access_token
|
118
|
-
imap_backup_token.access_token
|
119
|
-
end
|
120
|
-
|
121
|
-
def client_id
|
122
|
-
imap_backup_token.client_id
|
123
|
-
end
|
124
|
-
|
125
|
-
def client_secret
|
126
|
-
imap_backup_token.client_secret
|
127
|
-
end
|
128
|
-
|
129
|
-
def expiration_time_millis
|
130
|
-
imap_backup_token.expiration_time_millis
|
131
|
-
end
|
132
|
-
|
133
|
-
def refresh_token
|
134
|
-
imap_backup_token.refresh_token
|
135
|
-
end
|
136
|
-
|
137
|
-
def imap_backup_token
|
138
|
-
@imap_backup_token ||=
|
139
|
-
ImapBackupToken.new(token).tap do |t|
|
140
|
-
raise MalformedImapBackupToken if !t.valid?
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def store_token
|
145
|
-
{
|
146
|
-
"client_id" => client_id,
|
147
|
-
"access_token" => access_token,
|
148
|
-
"refresh_token" => refresh_token,
|
149
|
-
"scope": [GMAIL_READ_SCOPE],
|
150
|
-
"expiration_time_millis": expiration_time_millis
|
151
|
-
}.to_json
|
152
|
-
end
|
153
|
-
|
154
|
-
def token_store
|
155
|
-
@token_store ||=
|
156
|
-
Google::Auth::Stores::InMemoryTokenStore.new.tap do |t|
|
157
|
-
t.store(email, store_token)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
module Imap::Backup
|
2
|
-
module Configuration; end
|
3
|
-
|
4
|
-
class Configuration::GmailOauth2
|
5
|
-
BANNER = <<~BANNER.freeze
|
6
|
-
GMail OAuth2 Setup
|
7
|
-
|
8
|
-
You need to authorize imap_backup to get access to your email.
|
9
|
-
To do so, please follow the instructions here:
|
10
|
-
|
11
|
-
https://github.com/joeyates/imap-backup/blob/main/docs/setting-up-gmail-with-oauth2.md
|
12
|
-
|
13
|
-
BANNER
|
14
|
-
|
15
|
-
GMAIL_READ_SCOPE = "https://mail.google.com/".freeze
|
16
|
-
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
|
17
|
-
|
18
|
-
attr_reader :account
|
19
|
-
attr_reader :client_id
|
20
|
-
attr_reader :client_secret
|
21
|
-
|
22
|
-
def initialize(account)
|
23
|
-
@account = account
|
24
|
-
end
|
25
|
-
|
26
|
-
def run
|
27
|
-
Kernel.system("clear")
|
28
|
-
Kernel.puts BANNER
|
29
|
-
|
30
|
-
keep = if token.valid?
|
31
|
-
highline.agree("Use existing client info?")
|
32
|
-
else
|
33
|
-
false
|
34
|
-
end
|
35
|
-
|
36
|
-
if keep
|
37
|
-
@client_id = token.client_id
|
38
|
-
@client_secret = token.client_secret
|
39
|
-
else
|
40
|
-
@client_id = highline.ask("client_id: ")
|
41
|
-
@client_secret = highline.ask("client_secret: ")
|
42
|
-
end
|
43
|
-
|
44
|
-
Kernel.puts <<~MESSAGE
|
45
|
-
|
46
|
-
Open the following URL in your browser
|
47
|
-
|
48
|
-
#{authorization_url}
|
49
|
-
|
50
|
-
Then copy the success code
|
51
|
-
|
52
|
-
MESSAGE
|
53
|
-
|
54
|
-
@code = highline.ask("success code: ")
|
55
|
-
@credentials = authorizer.get_and_store_credentials_from_code(
|
56
|
-
user_id: email, code: @code, base_url: OOB_URI
|
57
|
-
)
|
58
|
-
|
59
|
-
raise "Failed" if !@credentials
|
60
|
-
|
61
|
-
new_token = JSON.parse(token_store.load(email))
|
62
|
-
new_token["client_secret"] = client_secret
|
63
|
-
new_token.to_json
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def email
|
69
|
-
account[:username]
|
70
|
-
end
|
71
|
-
|
72
|
-
def password
|
73
|
-
account[:password]
|
74
|
-
end
|
75
|
-
|
76
|
-
def token
|
77
|
-
@token ||= Gmail::Authenticator::ImapBackupToken.new(password)
|
78
|
-
end
|
79
|
-
|
80
|
-
def highline
|
81
|
-
Configuration::Setup.highline
|
82
|
-
end
|
83
|
-
|
84
|
-
def auth_client_id
|
85
|
-
@auth_client_id = Google::Auth::ClientId.new(client_id, client_secret)
|
86
|
-
end
|
87
|
-
|
88
|
-
def authorizer
|
89
|
-
@authorizer ||= Google::Auth::UserAuthorizer.new(
|
90
|
-
auth_client_id, GMAIL_READ_SCOPE, token_store
|
91
|
-
)
|
92
|
-
end
|
93
|
-
|
94
|
-
def token_store
|
95
|
-
@token_store ||= Google::Auth::Stores::InMemoryTokenStore.new
|
96
|
-
end
|
97
|
-
|
98
|
-
def authorization_url
|
99
|
-
authorizer.get_authorization_url(base_url: OOB_URI)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,138 +0,0 @@
|
|
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
|
@@ -1,15 +0,0 @@
|
|
1
|
-
require "google/auth/stores/in_memory_token_store"
|
2
|
-
|
3
|
-
describe Google::Auth::Stores::InMemoryTokenStore do
|
4
|
-
KEY = "key".freeze
|
5
|
-
VALUE = "value".freeze
|
6
|
-
|
7
|
-
subject { described_class.new }
|
8
|
-
|
9
|
-
describe "#load" do
|
10
|
-
it "returns an item's value" do
|
11
|
-
subject[KEY] = VALUE
|
12
|
-
expect(subject.load(KEY)).to eq(VALUE)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,121 +0,0 @@
|
|
1
|
-
describe Imap::Backup::Configuration::GmailOauth2 do
|
2
|
-
include HighLineTestHelpers
|
3
|
-
|
4
|
-
CLIENT_ID = "my_client_id".freeze
|
5
|
-
CLIENT_SECRET = "my_client_secret".freeze
|
6
|
-
|
7
|
-
subject { described_class.new(account) }
|
8
|
-
|
9
|
-
let(:authorization_url) { "some long authorization_url" }
|
10
|
-
let(:credentials) { "credentials" }
|
11
|
-
let(:json_token) { '{"sentinel":"foo"}' }
|
12
|
-
let!(:highline_streams) { prepare_highline }
|
13
|
-
let(:highline) { Imap::Backup::Configuration::Setup.highline }
|
14
|
-
let(:input) { highline_streams[0] }
|
15
|
-
let(:output) { highline_streams[1] }
|
16
|
-
let(:account) { {} }
|
17
|
-
let(:user_input) { %W(my_client_id\n my_secret\n my_code\n) }
|
18
|
-
|
19
|
-
let(:authorizer) do
|
20
|
-
instance_double(
|
21
|
-
Google::Auth::UserAuthorizer,
|
22
|
-
get_authorization_url: authorization_url,
|
23
|
-
get_and_store_credentials_from_code: credentials
|
24
|
-
)
|
25
|
-
end
|
26
|
-
let(:token_store) do
|
27
|
-
instance_double(
|
28
|
-
Google::Auth::Stores::InMemoryTokenStore,
|
29
|
-
load: json_token
|
30
|
-
)
|
31
|
-
end
|
32
|
-
let(:token) do
|
33
|
-
instance_double(
|
34
|
-
Gmail::Authenticator::ImapBackupToken,
|
35
|
-
valid?: valid,
|
36
|
-
client_id: CLIENT_ID,
|
37
|
-
client_secret: CLIENT_SECRET
|
38
|
-
)
|
39
|
-
end
|
40
|
-
let(:valid) { false }
|
41
|
-
|
42
|
-
before do
|
43
|
-
allow(Google::Auth::UserAuthorizer).
|
44
|
-
to receive(:new) { authorizer }
|
45
|
-
allow(Google::Auth::Stores::InMemoryTokenStore).
|
46
|
-
to receive(:new) { token_store }
|
47
|
-
allow(Gmail::Authenticator::ImapBackupToken).
|
48
|
-
to receive(:new) { token }
|
49
|
-
|
50
|
-
allow(highline).to receive(:ask).and_call_original
|
51
|
-
allow(highline).to receive(:agree).and_call_original
|
52
|
-
|
53
|
-
allow(Kernel).to receive(:system)
|
54
|
-
allow(Kernel).to receive(:puts)
|
55
|
-
|
56
|
-
allow(input).to receive(:gets).and_return(*user_input)
|
57
|
-
end
|
58
|
-
|
59
|
-
describe "#run" do
|
60
|
-
let!(:result) { subject.run }
|
61
|
-
|
62
|
-
it "clears the screen" do
|
63
|
-
expect(Kernel).to have_received(:system).with("clear")
|
64
|
-
end
|
65
|
-
|
66
|
-
it "requests client_id" do
|
67
|
-
expect(highline).to have_received(:ask).with("client_id: ")
|
68
|
-
end
|
69
|
-
|
70
|
-
it "requests client_secret" do
|
71
|
-
expect(highline).to have_received(:ask).with("client_secret: ")
|
72
|
-
end
|
73
|
-
|
74
|
-
it "displays the authorization URL" do
|
75
|
-
expect(Kernel).
|
76
|
-
to have_received(:puts).
|
77
|
-
with(/#{authorization_url}/)
|
78
|
-
end
|
79
|
-
|
80
|
-
it "requests the success code" do
|
81
|
-
expect(highline).to have_received(:ask).with("success code: ")
|
82
|
-
end
|
83
|
-
|
84
|
-
it "requests an access_token via the code" do
|
85
|
-
expect(authorizer).to have_received(:get_and_store_credentials_from_code)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "returns the credentials" do
|
89
|
-
expect(result).to match('"sentinel":"foo"')
|
90
|
-
end
|
91
|
-
|
92
|
-
it "includes the client_secret in the credentials" do
|
93
|
-
expect(result).to match('"client_secret":"my_secret"')
|
94
|
-
end
|
95
|
-
|
96
|
-
context "when the account already has client info" do
|
97
|
-
let(:valid) { true }
|
98
|
-
let(:user_input) { %W(yes\n) }
|
99
|
-
|
100
|
-
it "requests confirmation of client info" do
|
101
|
-
expect(highline).to have_received(:agree).with("Use existing client info?")
|
102
|
-
end
|
103
|
-
|
104
|
-
context "when yhe user says 'no'" do
|
105
|
-
let(:user_input) { %W(no\n) }
|
106
|
-
|
107
|
-
it "requests client_id" do
|
108
|
-
expect(highline).to have_received(:ask).with("client_id: ")
|
109
|
-
end
|
110
|
-
|
111
|
-
it "requests client_secret" do
|
112
|
-
expect(highline).to have_received(:ask).with("client_secret: ")
|
113
|
-
end
|
114
|
-
|
115
|
-
it "requests the success code" do
|
116
|
-
expect(highline).to have_received(:ask).with("success code: ")
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|