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.
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