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
@@ -1,30 +1,16 @@
|
|
1
|
-
# rubocop:disable RSpec/NestedGroups
|
2
|
-
|
3
1
|
describe Imap::Backup::Configuration::Account do
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
ACCOUNT = "account".freeze
|
3
|
+
GMAIL_IMAP_SERVER = "imap.gmail.com".freeze
|
4
|
+
HIGHLINE = "highline".freeze
|
5
|
+
STORE = "store".freeze
|
7
6
|
|
8
|
-
|
9
|
-
@choices = {}
|
10
|
-
end
|
7
|
+
subject { described_class.new(store, account, highline) }
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def hidden(name, &block)
|
17
|
-
choices[name] = block
|
18
|
-
end
|
19
|
-
end
|
9
|
+
let(:account) { ACCOUNT }
|
10
|
+
let(:highline) { HIGHLINE }
|
11
|
+
let(:store) { STORE }
|
20
12
|
|
21
13
|
describe "#initialize" do
|
22
|
-
subject { described_class.new(store, account, highline) }
|
23
|
-
|
24
|
-
let(:store) { "store" }
|
25
|
-
let(:account) { "account" }
|
26
|
-
let(:highline) { "highline" }
|
27
|
-
|
28
14
|
[:store, :account, :highline].each do |param|
|
29
15
|
it "expects #{param}" do
|
30
16
|
expect(subject.send(param)).to eq(send(param))
|
@@ -33,10 +19,27 @@ describe Imap::Backup::Configuration::Account do
|
|
33
19
|
end
|
34
20
|
|
35
21
|
describe "#run" do
|
36
|
-
|
22
|
+
let(:highline_menu_class) do
|
23
|
+
Class.new do
|
24
|
+
attr_reader :choices
|
25
|
+
attr_accessor :header
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@choices = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def choice(name, &block)
|
32
|
+
choices[name] = block
|
33
|
+
end
|
34
|
+
|
35
|
+
def hidden(name, &block)
|
36
|
+
choices[name] = block
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
37
40
|
|
38
41
|
let(:highline) { instance_double(HighLine) }
|
39
|
-
let(:menu) {
|
42
|
+
let(:menu) { highline_menu_class.new }
|
40
43
|
let(:store) do
|
41
44
|
instance_double(Imap::Backup::Configuration::Store, accounts: accounts)
|
42
45
|
end
|
@@ -44,7 +47,7 @@ describe Imap::Backup::Configuration::Account do
|
|
44
47
|
let(:account) do
|
45
48
|
{
|
46
49
|
username: existing_email,
|
47
|
-
server:
|
50
|
+
server: current_server,
|
48
51
|
local_path: "/backup/path",
|
49
52
|
folders: [{name: "my_folder"}],
|
50
53
|
password: existing_password
|
@@ -58,7 +61,7 @@ describe Imap::Backup::Configuration::Account do
|
|
58
61
|
end
|
59
62
|
let(:existing_email) { "user@example.com" }
|
60
63
|
let(:new_email) { "foo@example.com" }
|
61
|
-
let(:
|
64
|
+
let(:current_server) { "imap.example.com" }
|
62
65
|
let(:existing_password) { "password" }
|
63
66
|
let(:other_email) { "other@example.com" }
|
64
67
|
let(:other_existing_path) { "/other/existing/path" }
|
@@ -73,15 +76,17 @@ describe Imap::Backup::Configuration::Account do
|
|
73
76
|
end
|
74
77
|
|
75
78
|
describe "preparation" do
|
76
|
-
before { subject.run }
|
77
|
-
|
78
79
|
it "clears the screen" do
|
79
|
-
expect(Kernel).to
|
80
|
+
expect(Kernel).to receive(:system).with("clear")
|
81
|
+
|
82
|
+
subject.run
|
80
83
|
end
|
81
84
|
|
82
85
|
describe "menu" do
|
83
86
|
it "shows the menu" do
|
84
|
-
expect(highline).to
|
87
|
+
expect(highline).to receive(:choose)
|
88
|
+
|
89
|
+
subject.run
|
85
90
|
end
|
86
91
|
end
|
87
92
|
end
|
@@ -132,7 +137,7 @@ describe Imap::Backup::Configuration::Account do
|
|
132
137
|
end
|
133
138
|
end
|
134
139
|
|
135
|
-
describe "email" do
|
140
|
+
describe "choosing 'modify email'" do
|
136
141
|
before do
|
137
142
|
allow(Imap::Backup::Configuration::Asker).
|
138
143
|
to receive(:email) { new_email }
|
@@ -142,7 +147,7 @@ describe Imap::Backup::Configuration::Account do
|
|
142
147
|
|
143
148
|
context "when the server is blank" do
|
144
149
|
[
|
145
|
-
["GMail", "foo@gmail.com",
|
150
|
+
["GMail", "foo@gmail.com", GMAIL_IMAP_SERVER],
|
146
151
|
["Fastmail", "bar@fastmail.fm", "imap.fastmail.com"],
|
147
152
|
["Fastmail", "bar@fastmail.com", "imap.fastmail.com"]
|
148
153
|
].each do |service, email, expected|
|
@@ -150,7 +155,7 @@ describe Imap::Backup::Configuration::Account do
|
|
150
155
|
let(:new_email) { email }
|
151
156
|
|
152
157
|
context "with nil" do
|
153
|
-
let(:
|
158
|
+
let(:current_server) { nil }
|
154
159
|
|
155
160
|
it "sets a default server" do
|
156
161
|
expect(account[:server]).to eq(expected)
|
@@ -158,7 +163,7 @@ describe Imap::Backup::Configuration::Account do
|
|
158
163
|
end
|
159
164
|
|
160
165
|
context "with an empty string" do
|
161
|
-
let(:
|
166
|
+
let(:current_server) { "" }
|
162
167
|
|
163
168
|
it "sets a default server" do
|
164
169
|
expect(account[:server]).to eq(expected)
|
@@ -168,7 +173,7 @@ describe Imap::Backup::Configuration::Account do
|
|
168
173
|
end
|
169
174
|
|
170
175
|
context "when the domain is unrecognized" do
|
171
|
-
let(:
|
176
|
+
let(:current_server) { nil }
|
172
177
|
let(:provider) do
|
173
178
|
instance_double(Email::Provider, provider: :default)
|
174
179
|
end
|
@@ -207,12 +212,18 @@ describe Imap::Backup::Configuration::Account do
|
|
207
212
|
end
|
208
213
|
end
|
209
214
|
|
210
|
-
describe "password" do
|
215
|
+
describe "choosing 'modify password'" do
|
211
216
|
let(:new_password) { "new_password" }
|
217
|
+
let(:gmail_oauth2) do
|
218
|
+
instance_double(Imap::Backup::Configuration::GmailOauth2, run: nil)
|
219
|
+
end
|
212
220
|
|
213
221
|
before do
|
214
222
|
allow(Imap::Backup::Configuration::Asker).
|
215
223
|
to receive(:password) { new_password }
|
224
|
+
allow(Imap::Backup::Configuration::GmailOauth2).
|
225
|
+
to receive(:new).
|
226
|
+
with(account) { gmail_oauth2 }
|
216
227
|
subject.run
|
217
228
|
menu.choices["modify password"].call
|
218
229
|
end
|
@@ -234,14 +245,24 @@ describe Imap::Backup::Configuration::Account do
|
|
234
245
|
|
235
246
|
include_examples "it doesn't flag the account as modified"
|
236
247
|
end
|
248
|
+
|
249
|
+
context "when the server is for GMail" do
|
250
|
+
let(:current_server) { GMAIL_IMAP_SERVER }
|
251
|
+
|
252
|
+
it "sets up GMail OAuth2" do
|
253
|
+
expect(gmail_oauth2).to have_received(:run)
|
254
|
+
end
|
255
|
+
end
|
237
256
|
end
|
238
257
|
|
239
|
-
describe "server" do
|
258
|
+
describe "choosing 'modify server'" do
|
240
259
|
let(:server) { "server" }
|
241
260
|
|
242
261
|
before do
|
243
|
-
allow(highline).to receive(:ask).with("server: ")
|
262
|
+
allow(highline).to receive(:ask).with("server: ") { server }
|
263
|
+
|
244
264
|
subject.run
|
265
|
+
|
245
266
|
menu.choices["modify server"].call
|
246
267
|
end
|
247
268
|
|
@@ -252,7 +273,7 @@ describe Imap::Backup::Configuration::Account do
|
|
252
273
|
include_examples "it flags the account as modified"
|
253
274
|
end
|
254
275
|
|
255
|
-
describe "
|
276
|
+
describe "choosing 'modify backup path'" do
|
256
277
|
let(:new_backup_path) { "/new/path" }
|
257
278
|
|
258
279
|
before do
|
@@ -290,7 +311,7 @@ describe Imap::Backup::Configuration::Account do
|
|
290
311
|
include_examples "it flags the account as modified"
|
291
312
|
end
|
292
313
|
|
293
|
-
describe "folders" do
|
314
|
+
describe "choosing 'choose backup folders'" do
|
294
315
|
let(:chooser) do
|
295
316
|
instance_double(Imap::Backup::Configuration::FolderChooser, run: nil)
|
296
317
|
end
|
@@ -307,10 +328,10 @@ describe Imap::Backup::Configuration::Account do
|
|
307
328
|
end
|
308
329
|
end
|
309
330
|
|
310
|
-
describe "
|
331
|
+
describe "choosing 'test connection'" do
|
311
332
|
before do
|
312
333
|
allow(Imap::Backup::Configuration::ConnectionTester).
|
313
|
-
to receive(:test)
|
334
|
+
to receive(:test) { "All fine" }
|
314
335
|
allow(highline).to receive(:ask)
|
315
336
|
subject.run
|
316
337
|
menu.choices["test connection"].call
|
@@ -322,11 +343,11 @@ describe Imap::Backup::Configuration::Account do
|
|
322
343
|
end
|
323
344
|
end
|
324
345
|
|
325
|
-
describe "
|
346
|
+
describe "choosing 'delete'" do
|
326
347
|
let(:confirmed) { true }
|
327
348
|
|
328
349
|
before do
|
329
|
-
allow(highline).to receive(:agree)
|
350
|
+
allow(highline).to receive(:agree) { confirmed }
|
330
351
|
subject.run
|
331
352
|
catch :done do
|
332
353
|
menu.choices["delete"].call
|
@@ -349,5 +370,3 @@ describe Imap::Backup::Configuration::Account do
|
|
349
370
|
end
|
350
371
|
end
|
351
372
|
end
|
352
|
-
|
353
|
-
# rubocop:enable RSpec/NestedGroups
|
@@ -30,9 +30,9 @@ module Imap::Backup
|
|
30
30
|
].each do |method, params, prompt|
|
31
31
|
context ".#{method}" do
|
32
32
|
it "asks for input" do
|
33
|
-
|
33
|
+
expect(highline).to receive(:ask).with("#{prompt}: ")
|
34
34
|
|
35
|
-
|
35
|
+
described_class.send(method, *params)
|
36
36
|
end
|
37
37
|
|
38
38
|
it "returns the answer" do
|
@@ -56,16 +56,15 @@ module Imap::Backup
|
|
56
56
|
describe "#email" do
|
57
57
|
let(:email) { "email@example.com" }
|
58
58
|
let(:answer) { email }
|
59
|
-
let(:result) { subject.email }
|
60
|
-
|
61
|
-
before { result }
|
62
59
|
|
63
60
|
it "asks for an email" do
|
64
|
-
expect(highline).to
|
61
|
+
expect(highline).to receive(:ask).with(/email/)
|
62
|
+
|
63
|
+
subject.email
|
65
64
|
end
|
66
65
|
|
67
66
|
it "returns the address" do
|
68
|
-
expect(
|
67
|
+
expect(subject.email).to eq(email)
|
69
68
|
end
|
70
69
|
end
|
71
70
|
|
@@ -73,7 +72,6 @@ module Imap::Backup
|
|
73
72
|
let(:password1) { "password" }
|
74
73
|
let(:password2) { "password" }
|
75
74
|
let(:answers) { [true, false] }
|
76
|
-
let(:result) { subject.password }
|
77
75
|
|
78
76
|
before do
|
79
77
|
i = 0
|
@@ -84,27 +82,32 @@ module Imap::Backup
|
|
84
82
|
i += 1
|
85
83
|
answer
|
86
84
|
end
|
87
|
-
result
|
88
85
|
end
|
89
86
|
|
90
87
|
it "asks for a password" do
|
91
|
-
expect(highline).to
|
88
|
+
expect(highline).to receive(:ask).with("password: ")
|
89
|
+
|
90
|
+
subject.password
|
92
91
|
end
|
93
92
|
|
94
93
|
it "asks for confirmation" do
|
95
|
-
expect(highline).to
|
94
|
+
expect(highline).to receive(:ask).with("repeat password: ")
|
95
|
+
|
96
|
+
subject.password
|
96
97
|
end
|
97
98
|
|
98
99
|
it "returns the password" do
|
99
|
-
expect(
|
100
|
+
expect(subject.password).to eq(password1)
|
100
101
|
end
|
101
102
|
|
102
103
|
context "with different answers" do
|
103
104
|
let(:password2) { "secret" }
|
104
105
|
|
105
106
|
it "asks to continue" do
|
106
|
-
expect(highline).to
|
107
|
+
expect(highline).to receive(:agree).
|
107
108
|
at_least(:once).with(/Continue\?/)
|
109
|
+
|
110
|
+
subject.password
|
108
111
|
end
|
109
112
|
end
|
110
113
|
end
|
@@ -112,22 +115,22 @@ module Imap::Backup
|
|
112
115
|
describe "#backup_path" do
|
113
116
|
let(:path) { "/path" }
|
114
117
|
let(:answer) { path }
|
115
|
-
let(:result) { subject.backup_path("", //) }
|
116
118
|
|
117
119
|
before do
|
118
120
|
allow(highline).to receive(:ask) do |&b|
|
119
121
|
b.call query
|
120
122
|
path
|
121
123
|
end
|
122
|
-
result
|
123
124
|
end
|
124
125
|
|
125
126
|
it "asks for a directory" do
|
126
|
-
expect(highline).to
|
127
|
+
expect(highline).to receive(:ask).with(/directory/)
|
128
|
+
|
129
|
+
subject.backup_path("", //)
|
127
130
|
end
|
128
131
|
|
129
132
|
it "returns the path" do
|
130
|
-
expect(
|
133
|
+
expect(subject.backup_path("", //)).to eq(path)
|
131
134
|
end
|
132
135
|
end
|
133
136
|
end
|
@@ -3,32 +3,28 @@ describe Imap::Backup::Configuration::ConnectionTester do
|
|
3
3
|
let(:connection) do
|
4
4
|
instance_double(Imap::Backup::Account::Connection, imap: nil)
|
5
5
|
end
|
6
|
-
let(:result) { subject.test("foo") }
|
7
6
|
|
8
7
|
before do
|
9
8
|
allow(Imap::Backup::Account::Connection).to receive(:new) { connection }
|
10
9
|
end
|
11
10
|
|
12
11
|
describe "call" do
|
13
|
-
before { result }
|
14
|
-
|
15
12
|
it "tries to connect" do
|
16
|
-
expect(connection).to
|
13
|
+
expect(connection).to receive(:imap)
|
14
|
+
|
15
|
+
subject.test("foo")
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
19
|
describe "success" do
|
21
|
-
before { result }
|
22
|
-
|
23
20
|
it "returns success" do
|
24
|
-
expect(
|
21
|
+
expect(subject.test("foo")).to match(/successful/)
|
25
22
|
end
|
26
23
|
end
|
27
24
|
|
28
25
|
describe "failure" do
|
29
26
|
before do
|
30
27
|
allow(connection).to receive(:imap).and_raise(error)
|
31
|
-
result
|
32
28
|
end
|
33
29
|
|
34
30
|
context "with no connection" do
|
@@ -39,7 +35,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
|
|
39
35
|
end
|
40
36
|
|
41
37
|
it "returns error" do
|
42
|
-
expect(
|
38
|
+
expect(subject.test("foo")).to match(/no response/i)
|
43
39
|
end
|
44
40
|
end
|
45
41
|
|
@@ -47,7 +43,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
|
|
47
43
|
let(:error) { "Error" }
|
48
44
|
|
49
45
|
it "returns error" do
|
50
|
-
expect(
|
46
|
+
expect(subject.test("foo")).to match(/unexpected error/i)
|
51
47
|
end
|
52
48
|
end
|
53
49
|
end
|
@@ -22,13 +22,15 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
describe "display" do
|
25
|
-
before { subject.run }
|
26
|
-
|
27
25
|
it "clears the screen" do
|
28
|
-
expect(Kernel).to
|
26
|
+
expect(Kernel).to receive(:system).with("clear")
|
27
|
+
|
28
|
+
subject.run
|
29
29
|
end
|
30
30
|
|
31
31
|
it "shows the menu" do
|
32
|
+
subject.run
|
33
|
+
|
32
34
|
expect(output.string).to match %r{Add/remove folders}
|
33
35
|
end
|
34
36
|
end
|
@@ -114,13 +116,14 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
114
116
|
|
115
117
|
before do
|
116
118
|
allow(Imap::Backup::Configuration::Setup.highline).
|
117
|
-
to receive(:ask)
|
118
|
-
subject.run
|
119
|
+
to receive(:ask) { "q" }
|
119
120
|
end
|
120
121
|
|
121
122
|
it "asks to press a key" do
|
122
123
|
expect(Imap::Backup::Configuration::Setup.highline).
|
123
|
-
to
|
124
|
+
to receive(:ask).with("Press a key ")
|
125
|
+
|
126
|
+
subject.run
|
124
127
|
end
|
125
128
|
end
|
126
129
|
|
@@ -129,18 +132,21 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
129
132
|
allow(Imap::Backup::Account::Connection).
|
130
133
|
to receive(:new).with(account).and_raise("error")
|
131
134
|
allow(Imap::Backup::Configuration::Setup.highline).
|
132
|
-
to receive(:ask)
|
133
|
-
subject.run
|
135
|
+
to receive(:ask) { "q" }
|
134
136
|
end
|
135
137
|
|
136
138
|
it "prints an error message" do
|
137
139
|
expect(Imap::Backup.logger).
|
138
|
-
to
|
140
|
+
to receive(:warn).with("Connection failed")
|
141
|
+
|
142
|
+
subject.run
|
139
143
|
end
|
140
144
|
|
141
145
|
it "asks to continue" do
|
142
146
|
expect(Imap::Backup::Configuration::Setup.highline).
|
143
|
-
to
|
147
|
+
to receive(:ask).with("Press a key ")
|
148
|
+
|
149
|
+
subject.run
|
144
150
|
end
|
145
151
|
end
|
146
152
|
end
|