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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -4
  3. data/.rubocop_todo.yml +29 -11
  4. data/.travis.yml +1 -1
  5. data/README.md +10 -13
  6. data/bin/imap-backup +5 -2
  7. data/docs/01-credentials-screen.png +0 -0
  8. data/docs/02-new-project.png +0 -0
  9. data/docs/03-initial-credentials-for-project.png +0 -0
  10. data/docs/04-credential-type-selection.png +0 -0
  11. data/docs/05-cant-create-without-consent-setup.png +0 -0
  12. data/docs/06-user-type-selection.png +0 -0
  13. data/docs/07-consent-screen-form.png +0 -0
  14. data/docs/08-app-scopes.png +0 -0
  15. data/docs/09-scope-selection.png +0 -0
  16. data/docs/10-updated-app-scopes.png +0 -0
  17. data/docs/11-test-users.png +0 -0
  18. data/docs/12-add-users.png +0 -0
  19. data/docs/13-create-oauth-client.png +0 -0
  20. data/docs/14-application-details.png +0 -0
  21. data/docs/16-initial-menu.png +0 -0
  22. data/docs/17-inputting-the-email-address.png +0 -0
  23. data/docs/18-choose-password.png +0 -0
  24. data/docs/19-supply-client-info.png +0 -0
  25. data/docs/20-choose-gmail-account.png +0 -0
  26. data/docs/21-accept-warnings.png +0 -0
  27. data/docs/22-grant-access.png +0 -0
  28. data/docs/24-confirm-choices.png +0 -0
  29. data/docs/25-success-code.png +0 -0
  30. data/docs/26-type-code-into-imap-backup.png +0 -0
  31. data/docs/27-success.png +0 -0
  32. data/docs/setting-up-gmail.md +166 -0
  33. data/imap-backup.gemspec +3 -9
  34. data/lib/email/mboxrd/message.rb +4 -3
  35. data/lib/email/provider.rb +3 -1
  36. data/lib/gmail/authenticator.rb +160 -0
  37. data/lib/google/auth/stores/in_memory_token_store.rb +9 -0
  38. data/lib/imap/backup.rb +2 -1
  39. data/lib/imap/backup/account/connection.rb +59 -34
  40. data/lib/imap/backup/account/folder.rb +10 -1
  41. data/lib/imap/backup/configuration/account.rb +9 -1
  42. data/lib/imap/backup/configuration/gmail_oauth2.rb +82 -0
  43. data/lib/imap/backup/configuration/setup.rb +4 -1
  44. data/lib/imap/backup/serializer/mbox.rb +4 -0
  45. data/lib/imap/backup/serializer/mbox_enumerator.rb +1 -1
  46. data/lib/imap/backup/serializer/mbox_store.rb +20 -4
  47. data/lib/imap/backup/uploader.rb +10 -2
  48. data/lib/imap/backup/version.rb +5 -4
  49. data/spec/features/backup_spec.rb +3 -3
  50. data/spec/features/helper.rb +1 -1
  51. data/spec/features/restore_spec.rb +75 -27
  52. data/spec/features/support/backup_directory.rb +2 -2
  53. data/spec/features/support/email_server.rb +1 -3
  54. data/spec/features/support/shared/message_fixtures.rb +8 -0
  55. data/spec/spec_helper.rb +1 -1
  56. data/spec/support/fixtures.rb +1 -1
  57. data/spec/unit/email/mboxrd/message_spec.rb +2 -8
  58. data/spec/unit/email/provider_spec.rb +2 -2
  59. data/spec/unit/gmail/authenticator_spec.rb +138 -0
  60. data/spec/unit/google/auth/stores/in_memory_token_store_spec.rb +15 -0
  61. data/spec/unit/imap/backup/account/connection_spec.rb +157 -79
  62. data/spec/unit/imap/backup/account/folder_spec.rb +30 -20
  63. data/spec/unit/imap/backup/configuration/account_spec.rb +65 -46
  64. data/spec/unit/imap/backup/configuration/asker_spec.rb +20 -17
  65. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +6 -10
  66. data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +16 -10
  67. data/spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb +84 -0
  68. data/spec/unit/imap/backup/configuration/list_spec.rb +6 -3
  69. data/spec/unit/imap/backup/configuration/setup_spec.rb +89 -54
  70. data/spec/unit/imap/backup/configuration/store_spec.rb +18 -16
  71. data/spec/unit/imap/backup/downloader_spec.rb +14 -14
  72. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +6 -1
  73. data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -40
  74. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +94 -35
  75. data/spec/unit/imap/backup/uploader_spec.rb +23 -7
  76. data/spec/unit/imap/backup/utils_spec.rb +10 -9
  77. metadata +68 -9
@@ -1,30 +1,16 @@
1
- # rubocop:disable RSpec/NestedGroups
2
-
3
1
  describe Imap::Backup::Configuration::Account do
4
- class MockHighlineMenu
5
- attr_reader :choices
6
- attr_accessor :header
2
+ ACCOUNT = "account".freeze
3
+ GMAIL_IMAP_SERVER = "imap.gmail.com".freeze
4
+ HIGHLINE = "highline".freeze
5
+ STORE = "store".freeze
7
6
 
8
- def initialize
9
- @choices = {}
10
- end
7
+ subject { described_class.new(store, account, highline) }
11
8
 
12
- def choice(name, &block)
13
- choices[name] = block
14
- end
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
- subject { described_class.new(store, account, highline) }
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) { MockHighlineMenu.new }
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: existing_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(:existing_server) { "imap.example.com" }
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 have_received(:system).with("clear")
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 have_received(:choose)
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", "imap.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(:existing_server) { nil }
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(:existing_server) { "" }
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(:existing_server) { nil }
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: ").and_return(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 "backup_path" do
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 "connection test" do
331
+ describe "choosing 'test connection'" do
311
332
  before do
312
333
  allow(Imap::Backup::Configuration::ConnectionTester).
313
- to receive(:test).and_return("All fine")
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 "deletion" do
346
+ describe "choosing 'delete'" do
326
347
  let(:confirmed) { true }
327
348
 
328
349
  before do
329
- allow(highline).to receive(:agree).and_return(confirmed)
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
- described_class.send(method, *params)
33
+ expect(highline).to receive(:ask).with("#{prompt}: ")
34
34
 
35
- expect(highline).to have_received(:ask).with("#{prompt}: ")
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 have_received(:ask).with(/email/)
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(result).to eq(email)
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 have_received(:ask).with("password: ")
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 have_received(:ask).with("repeat password: ")
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(result).to eq(password1)
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 have_received(:agree).
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 have_received(:ask).with(/directory/)
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(result).to eq(path)
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 have_received(:imap)
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(result).to match(/successful/)
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(result).to match(/no response/i)
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(result).to match(/unexpected error/i)
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 have_received(:system).with("clear")
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).and_return("q")
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 have_received(:ask).with("Press a key ")
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).and_return("q")
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 have_received(:warn).with("Connection failed")
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 have_received(:ask).with("Press a key ")
147
+ to receive(:ask).with("Press a key ")
148
+
149
+ subject.run
144
150
  end
145
151
  end
146
152
  end