imap-backup 1.0.14 → 1.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -0
- data/bin/imap-backup +3 -1
- data/lib/imap/backup.rb +9 -0
- data/lib/imap/backup/account/connection.rb +12 -2
- data/lib/imap/backup/configuration/account.rb +6 -1
- data/lib/imap/backup/configuration/folder_chooser.rb +3 -1
- data/lib/imap/backup/configuration/list.rb +4 -0
- data/lib/imap/backup/configuration/setup.rb +18 -12
- data/lib/imap/backup/configuration/store.rb +24 -13
- data/lib/imap/backup/downloader.rb +1 -0
- data/lib/imap/backup/serializer/mbox.rb +1 -0
- data/lib/imap/backup/version.rb +1 -1
- data/spec/support/shared_examples/account_flagging.rb +23 -0
- data/spec/unit/account/connection_spec.rb +10 -1
- data/spec/unit/configuration/account_spec.rb +23 -15
- data/spec/unit/configuration/folder_chooser_spec.rb +4 -0
- data/spec/unit/configuration/setup_spec.rb +128 -16
- data/spec/unit/configuration/store_spec.rb +129 -75
- data/spec/unit/serializer/mbox_spec.rb +3 -3
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3d7f421192f5e5b15efcb55aa13b55cd769899e
|
4
|
+
data.tar.gz: d98f149a116634df9e6aa79898da5e2c61ac770a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7173984ab8c9ef01013d2d6468097aa7b928b59cf12b363b66b3119074f26996c80ffac4c27b36499cd739132eb4105d6f4c0fbd917ee53df69e90f34918a533
|
7
|
+
data.tar.gz: 3f5a4e52b736fabb1404ffa0b4ed01ff22b83a1c706f6de90c96f68b66fecaf22eed6af251b674c519dcb2e235aaca56d1b074a347a9a4ec54fb7c6a410e92e2
|
data/.travis.yml
CHANGED
data/bin/imap-backup
CHANGED
@@ -41,7 +41,7 @@ if ARGV.size > 0
|
|
41
41
|
options[:command] = ARGV.shift
|
42
42
|
end
|
43
43
|
|
44
|
-
if KNOWN_COMMANDS.find{|c| c[:name] == options[:command] }.nil?
|
44
|
+
if KNOWN_COMMANDS.find{ |c| c[:name] == options[:command] }.nil?
|
45
45
|
raise "Unknown command '#{options[:command]}'"
|
46
46
|
end
|
47
47
|
|
@@ -57,6 +57,8 @@ rescue Imap::Backup::ConfigurationNotFound
|
|
57
57
|
exit
|
58
58
|
end
|
59
59
|
|
60
|
+
configuration.setup_logging
|
61
|
+
|
60
62
|
case options[:command]
|
61
63
|
when 'backup'
|
62
64
|
configuration.each_connection do |connection|
|
data/lib/imap/backup.rb
CHANGED
@@ -12,11 +12,17 @@ module Imap::Backup
|
|
12
12
|
@local_path = options[:local_path]
|
13
13
|
@backup_folders = options[:folders]
|
14
14
|
@server = options[:server]
|
15
|
+
@folders = nil
|
15
16
|
end
|
16
17
|
|
17
18
|
def folders
|
19
|
+
return @folders if @folders
|
18
20
|
root = root_for(username)
|
19
|
-
imap.list(root, '*')
|
21
|
+
@folders = imap.list(root, '*')
|
22
|
+
if @folders.nil?
|
23
|
+
Imap::Backup.logger.warn "Unable to get folder list for account #{username}, (root '#{root}'"
|
24
|
+
end
|
25
|
+
@folders
|
20
26
|
end
|
21
27
|
|
22
28
|
def status
|
@@ -28,7 +34,11 @@ module Imap::Backup
|
|
28
34
|
end
|
29
35
|
|
30
36
|
def run_backup
|
37
|
+
Imap::Backup.logger.debug "Running backup of account: #{username}"
|
38
|
+
# start the connection so we get logging messages in the right order
|
39
|
+
imap
|
31
40
|
backup_folders.each do |folder|
|
41
|
+
Imap::Backup.logger.debug "Folder: #{folder[:name]}"
|
32
42
|
f = Account::Folder.new(self, folder[:name])
|
33
43
|
s = Serializer::Mbox.new(local_path, folder[:name])
|
34
44
|
d = Downloader.new(f, s)
|
@@ -66,7 +76,7 @@ module Imap::Backup
|
|
66
76
|
|
67
77
|
def backup_folders
|
68
78
|
return @backup_folders if @backup_folders and @backup_folders.size > 0
|
69
|
-
folders.map { |f| {:name => f} }
|
79
|
+
(folders || []).map { |f| {:name => f.name} }
|
70
80
|
end
|
71
81
|
|
72
82
|
def host_for(username)
|
@@ -58,6 +58,7 @@ Account:
|
|
58
58
|
if account[:server].nil? or account[:server] == ''
|
59
59
|
account[:server] = default_server(username)
|
60
60
|
end
|
61
|
+
account[:modified] = true
|
61
62
|
end
|
62
63
|
end
|
63
64
|
end
|
@@ -67,6 +68,7 @@ Account:
|
|
67
68
|
password = Configuration::Asker.password
|
68
69
|
if ! password.nil?
|
69
70
|
account[:password] = password
|
71
|
+
account[:modified] = true
|
70
72
|
end
|
71
73
|
end
|
72
74
|
end
|
@@ -76,6 +78,7 @@ Account:
|
|
76
78
|
server = highline.ask('server: ')
|
77
79
|
if ! server.nil?
|
78
80
|
account[:server] = server
|
81
|
+
account[:modified] = true
|
79
82
|
end
|
80
83
|
end
|
81
84
|
end
|
@@ -93,7 +96,9 @@ Account:
|
|
93
96
|
true
|
94
97
|
end
|
95
98
|
end
|
99
|
+
existing = account[:local_path].clone
|
96
100
|
account[:local_path] = Configuration::Asker.backup_path(account[:local_path], validator)
|
101
|
+
account[:modified] = true if existing != account[:local_path]
|
97
102
|
end
|
98
103
|
end
|
99
104
|
|
@@ -114,7 +119,7 @@ Account:
|
|
114
119
|
def delete_account(menu)
|
115
120
|
menu.choice('delete') do
|
116
121
|
if highline.agree("Are you sure? (y/n) ")
|
117
|
-
|
122
|
+
account[:delete] = true
|
118
123
|
throw :done
|
119
124
|
end
|
120
125
|
end
|
@@ -59,9 +59,11 @@ module Imap::Backup
|
|
59
59
|
|
60
60
|
def toggle_selection(folder_name)
|
61
61
|
if is_selected?(folder_name)
|
62
|
-
account[:folders].reject! { |f| f[:name] == folder_name }
|
62
|
+
changed = account[:folders].reject! { |f| f[:name] == folder_name }
|
63
|
+
account[:modified] = true if changed
|
63
64
|
else
|
64
65
|
account[:folders] << { :name => folder_name }
|
66
|
+
account[:modified] = true
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
@@ -12,7 +12,7 @@ module Imap::Backup
|
|
12
12
|
self.highline = HighLine.new
|
13
13
|
|
14
14
|
def run
|
15
|
-
setup_logging
|
15
|
+
Imap::Backup.setup_logging config
|
16
16
|
catch :done do
|
17
17
|
loop do
|
18
18
|
system('clear')
|
@@ -28,17 +28,23 @@ module Imap::Backup
|
|
28
28
|
menu.header = 'Choose an action'
|
29
29
|
account_items menu
|
30
30
|
add_account_item menu
|
31
|
+
toggle_logging_item menu
|
31
32
|
menu.choice('save and exit') do
|
32
33
|
config.save
|
33
34
|
throw :done
|
34
35
|
end
|
35
|
-
menu.choice(
|
36
|
+
menu.choice('exit without saving changes') do
|
37
|
+
throw :done
|
38
|
+
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
42
|
def account_items(menu)
|
40
43
|
config.accounts.each do |account|
|
41
|
-
|
44
|
+
next if account[:delete]
|
45
|
+
item = account[:username].clone
|
46
|
+
item << ' *' if account[:modified]
|
47
|
+
menu.choice(item) do
|
42
48
|
edit_account account[:username]
|
43
49
|
end
|
44
50
|
end
|
@@ -51,17 +57,17 @@ module Imap::Backup
|
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
54
|
-
def
|
55
|
-
|
60
|
+
def toggle_logging_item(menu)
|
61
|
+
menu_item = config.debug? ? 'stop logging' : 'start logging'
|
62
|
+
new_setting = ! config.debug?
|
63
|
+
menu.choice(menu_item) do
|
64
|
+
config.debug = new_setting
|
65
|
+
Imap::Backup.setup_logging config
|
66
|
+
end
|
56
67
|
end
|
57
68
|
|
58
|
-
def
|
59
|
-
|
60
|
-
if config.debug?
|
61
|
-
::Logger::Severity::DEBUG
|
62
|
-
else
|
63
|
-
::Logger::Severity::ERROR
|
64
|
-
end
|
69
|
+
def config
|
70
|
+
@config ||= Configuration::Store.new
|
65
71
|
end
|
66
72
|
|
67
73
|
def default_account_config(username)
|
@@ -31,27 +31,26 @@ module Imap::Backup
|
|
31
31
|
Utils.check_permissions path, 0700
|
32
32
|
end
|
33
33
|
mkdir_private path
|
34
|
+
remove_modified_flags
|
35
|
+
remove_deleted_accounts
|
34
36
|
File.open(pathname, 'w') { |f| f.write(JSON.pretty_generate(data)) }
|
35
37
|
FileUtils.chmod 0600, pathname
|
36
|
-
accounts.each do |account|
|
37
|
-
mkdir_private account[:local_path]
|
38
|
-
account[:folders].each do |f|
|
39
|
-
parts = f[:name].split('/')
|
40
|
-
p = account[:local_path].clone
|
41
|
-
parts.each do |part|
|
42
|
-
p = File.join(p, part)
|
43
|
-
mkdir_private p
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
38
|
end
|
48
39
|
|
49
40
|
def accounts
|
50
41
|
data[:accounts]
|
51
42
|
end
|
52
43
|
|
44
|
+
def modified?
|
45
|
+
accounts.any? { |a| a[:modified] || a[:delete] }
|
46
|
+
end
|
47
|
+
|
53
48
|
def debug?
|
54
|
-
data
|
49
|
+
data[:debug]
|
50
|
+
end
|
51
|
+
|
52
|
+
def debug=(value)
|
53
|
+
data[:debug] = [true, false].include?(value) ? value : false
|
55
54
|
end
|
56
55
|
|
57
56
|
private
|
@@ -60,10 +59,22 @@ module Imap::Backup
|
|
60
59
|
return @data if @data
|
61
60
|
if File.exist?(pathname)
|
62
61
|
Utils.check_permissions pathname, 0600
|
63
|
-
|
62
|
+
contents = File.read(pathname)
|
63
|
+
@data = JSON.parse(contents, :symbolize_names => true)
|
64
64
|
else
|
65
65
|
@data = {:accounts => []}
|
66
66
|
end
|
67
|
+
@data[:debug] = false unless @data.include?(:debug)
|
68
|
+
@data[:debug] = false unless [true, false].include?(@data[:debug])
|
69
|
+
@data
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_modified_flags
|
73
|
+
accounts.each { |a| a.delete(:modified) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def remove_deleted_accounts
|
77
|
+
accounts.reject! { |a| a[:delete] }
|
67
78
|
end
|
68
79
|
|
69
80
|
def mkdir_private(path)
|
data/lib/imap/backup/version.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
shared_examples 'it flags the account as modified' do
|
2
|
+
it 'flags that the account has changed' do
|
3
|
+
expect(account[:modified]).to be_truthy
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
shared_examples "it doesn't flag the account as modified" do
|
8
|
+
it 'does not flag that the account has changed' do
|
9
|
+
expect(account[:modified]).to be_falsey
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples 'it flags the account to be deleted' do
|
14
|
+
it 'flags that the account is to be deleted' do
|
15
|
+
expect(account[:delete]).to be_truthy
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_examples "it doesn't flag the account to be deleted" do
|
20
|
+
it 'does not flags that the account is to be deleted' do
|
21
|
+
expect(account[:delete]).to be_falsey
|
22
|
+
end
|
23
|
+
end
|
@@ -158,7 +158,7 @@ describe Imap::Backup::Account::Connection do
|
|
158
158
|
end
|
159
159
|
|
160
160
|
context 'without supplied backup_folders' do
|
161
|
-
let(:imap_folders) { ['foo'] }
|
161
|
+
let(:imap_folders) { [double(:name => 'foo')] }
|
162
162
|
|
163
163
|
before do
|
164
164
|
allow(Imap::Backup::Account::Folder).to receive(:new).
|
@@ -186,6 +186,15 @@ describe Imap::Backup::Account::Connection do
|
|
186
186
|
expect(downloader).to have_received(:run)
|
187
187
|
end
|
188
188
|
end
|
189
|
+
|
190
|
+
context "when the imap server doesn't return folders" do
|
191
|
+
let(:backup_folders) { nil }
|
192
|
+
let(:imap_folders) { nil }
|
193
|
+
|
194
|
+
it 'does not fail' do
|
195
|
+
expect { subject.run_backup }.to_not raise_error
|
196
|
+
end
|
197
|
+
end
|
189
198
|
end
|
190
199
|
end
|
191
200
|
end
|
@@ -20,12 +20,6 @@ describe Imap::Backup::Configuration::Account do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
context '#initialize' do
|
23
|
-
it 'requires 3 parameters' do
|
24
|
-
expect do
|
25
|
-
described_class.new('foo')
|
26
|
-
end.to raise_error(ArgumentError, /1 for 3/)
|
27
|
-
end
|
28
|
-
|
29
23
|
let(:store) { 'store' }
|
30
24
|
let(:account) { 'account' }
|
31
25
|
let(:highline) { 'highline' }
|
@@ -171,8 +165,12 @@ describe Imap::Backup::Configuration::Account do
|
|
171
165
|
end
|
172
166
|
end
|
173
167
|
|
174
|
-
|
175
|
-
|
168
|
+
context 'the email is new' do
|
169
|
+
it 'modifies the email address' do
|
170
|
+
expect(account[:username]).to eq(new_email)
|
171
|
+
end
|
172
|
+
|
173
|
+
include_examples 'it flags the account as modified'
|
176
174
|
end
|
177
175
|
|
178
176
|
context 'the email already exists' do
|
@@ -185,6 +183,8 @@ describe Imap::Backup::Configuration::Account do
|
|
185
183
|
it "doesn't set the email" do
|
186
184
|
expect(account[:username]).to eq(existing_email)
|
187
185
|
end
|
186
|
+
|
187
|
+
include_examples "it doesn't flag the account as modified"
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
@@ -197,8 +197,12 @@ describe Imap::Backup::Configuration::Account do
|
|
197
197
|
menu.choices['modify password'].call
|
198
198
|
end
|
199
199
|
|
200
|
-
|
201
|
-
|
200
|
+
context 'if the user enters a password' do
|
201
|
+
it 'updates the password' do
|
202
|
+
expect(account[:password]).to eq(new_password)
|
203
|
+
end
|
204
|
+
|
205
|
+
include_examples 'it flags the account as modified'
|
202
206
|
end
|
203
207
|
|
204
208
|
context 'if the user cancels' do
|
@@ -207,6 +211,8 @@ describe Imap::Backup::Configuration::Account do
|
|
207
211
|
it 'does nothing' do
|
208
212
|
expect(account[:password]).to eq(existing_password)
|
209
213
|
end
|
214
|
+
|
215
|
+
include_examples "it doesn't flag the account as modified"
|
210
216
|
end
|
211
217
|
end
|
212
218
|
|
@@ -225,6 +231,8 @@ describe Imap::Backup::Configuration::Account do
|
|
225
231
|
it 'updates the server' do
|
226
232
|
expect(account[:server]).to eq(server)
|
227
233
|
end
|
234
|
+
|
235
|
+
include_examples 'it flags the account as modified'
|
228
236
|
end
|
229
237
|
|
230
238
|
context 'backup_path' do
|
@@ -247,6 +255,8 @@ describe Imap::Backup::Configuration::Account do
|
|
247
255
|
it 'validates that the path is not used by other backups' do
|
248
256
|
expect(@validator.call(other_existing_path)).to be_falsey
|
249
257
|
end
|
258
|
+
|
259
|
+
include_examples 'it flags the account as modified'
|
250
260
|
end
|
251
261
|
|
252
262
|
context 'folders' do
|
@@ -291,16 +301,14 @@ describe Imap::Backup::Configuration::Account do
|
|
291
301
|
expect(highline).to have_received(:agree)
|
292
302
|
end
|
293
303
|
|
294
|
-
|
295
|
-
|
304
|
+
context 'when the user confirms deletion' do
|
305
|
+
include_examples 'it flags the account to be deleted'
|
296
306
|
end
|
297
307
|
|
298
308
|
context 'without confirmation' do
|
299
309
|
let(:confirmed) { false }
|
300
310
|
|
301
|
-
it '
|
302
|
-
expect(accounts.find{|a| a[:username] == existing_email}).to eq(account)
|
303
|
-
end
|
311
|
+
include_examples "it doesn't flag the account to be deleted"
|
304
312
|
end
|
305
313
|
end
|
306
314
|
end
|
@@ -60,6 +60,8 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
60
60
|
specify 'are added to the account' do
|
61
61
|
expect(account[:folders]).to include({:name => 'another_folder'})
|
62
62
|
end
|
63
|
+
|
64
|
+
include_examples 'it flags the account as modified'
|
63
65
|
end
|
64
66
|
|
65
67
|
context 'removing folders' do
|
@@ -72,6 +74,8 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
72
74
|
specify 'are removed from the account' do
|
73
75
|
expect(account[:folders]).to_not include({:name => 'my_folder'})
|
74
76
|
end
|
77
|
+
|
78
|
+
include_examples 'it flags the account as modified'
|
75
79
|
end
|
76
80
|
end
|
77
81
|
|
@@ -13,24 +13,28 @@ describe Imap::Backup::Configuration::Setup do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
context '#run' do
|
16
|
-
let(:
|
17
|
-
let(:
|
18
|
-
let(:accounts) { [account1] }
|
16
|
+
let(:normal) { {:username => 'account@example.com'} }
|
17
|
+
let(:accounts) { [normal] }
|
19
18
|
let(:store) do
|
20
19
|
double(
|
21
20
|
'Imap::Backup::Configuration::Store',
|
22
21
|
:accounts => accounts,
|
23
22
|
:path => '/base/path',
|
24
23
|
:save => nil,
|
25
|
-
:debug? =>
|
24
|
+
:debug? => debug,
|
25
|
+
:debug= => nil,
|
26
|
+
:modified? => modified,
|
26
27
|
)
|
27
28
|
end
|
29
|
+
let(:debug) { false }
|
30
|
+
let(:modified) { false }
|
28
31
|
|
29
32
|
before :each do
|
30
33
|
allow(Imap::Backup::Configuration::Store).to receive(:new).and_return(store)
|
34
|
+
allow(Imap::Backup).to receive(:setup_logging)
|
31
35
|
@input, @output = prepare_highline
|
32
36
|
allow(@input).to receive(:eof?).and_return(false)
|
33
|
-
allow(@input).to receive(:gets).and_return("
|
37
|
+
allow(@input).to receive(:gets).and_return("exit\n")
|
34
38
|
allow(subject).to receive(:system)
|
35
39
|
end
|
36
40
|
|
@@ -39,7 +43,7 @@ describe Imap::Backup::Configuration::Setup do
|
|
39
43
|
context 'main menu' do
|
40
44
|
before { subject.run }
|
41
45
|
|
42
|
-
%w(add\ account save\ and\ exit
|
46
|
+
%w(add\ account save\ and\ exit exit\ without\ saving).each do |choice|
|
43
47
|
it "includes #{choice}" do
|
44
48
|
expect(@output.string).to include(choice)
|
45
49
|
end
|
@@ -52,10 +56,36 @@ describe Imap::Backup::Configuration::Setup do
|
|
52
56
|
expect(subject).to have_received(:system).with('clear')
|
53
57
|
end
|
54
58
|
|
55
|
-
it '
|
59
|
+
it 'updates logging status' do
|
56
60
|
subject.run
|
57
61
|
|
58
|
-
expect(
|
62
|
+
expect(Imap::Backup).to have_received(:setup_logging)
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'listing' do
|
66
|
+
let(:accounts) { [normal, modified, deleted] }
|
67
|
+
let(:modified) { {:username => 'modified@example.com', :modified => true} }
|
68
|
+
let(:deleted) { {:username => 'deleted@example.com', :delete => true} }
|
69
|
+
|
70
|
+
before { subject.run }
|
71
|
+
|
72
|
+
context 'normal accounts' do
|
73
|
+
it 'are listed' do
|
74
|
+
expect(@output.string).to match /account@example.com/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'modified accounts' do
|
79
|
+
it 'are flagged' do
|
80
|
+
expect(@output.string).to match /modified@example.com \*/
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'deleted accounts' do
|
85
|
+
it 'are hidden' do
|
86
|
+
expect(@output.string).to_not match /delete@example.com/
|
87
|
+
end
|
88
|
+
end
|
59
89
|
end
|
60
90
|
|
61
91
|
context 'adding accounts' do
|
@@ -67,9 +97,10 @@ describe Imap::Backup::Configuration::Setup do
|
|
67
97
|
:folders => []
|
68
98
|
}
|
69
99
|
end
|
100
|
+
let(:account) { double('Imap::Backup::Configuration::Account', :run => nil) }
|
70
101
|
|
71
102
|
before do
|
72
|
-
allow(@input).to receive(:gets).and_return("add\n", "
|
103
|
+
allow(@input).to receive(:gets).and_return("add\n", "exit\n")
|
73
104
|
allow(Imap::Backup::Configuration::Asker).to receive(:email).with(no_args).and_return('new@example.com')
|
74
105
|
allow(Imap::Backup::Configuration::Account).to receive(:new).with(store, blank_account, anything).and_return(account)
|
75
106
|
|
@@ -79,20 +110,101 @@ describe Imap::Backup::Configuration::Setup do
|
|
79
110
|
it 'adds account data' do
|
80
111
|
expect(accounts[1]).to eq(blank_account)
|
81
112
|
end
|
113
|
+
|
114
|
+
it "doesn't flag the unedited account as modified" do
|
115
|
+
expect(accounts[1][:modified]).to be_nil
|
116
|
+
end
|
82
117
|
end
|
83
118
|
|
84
|
-
|
85
|
-
|
119
|
+
context 'logging' do
|
120
|
+
context 'when debug logging is disabled' do
|
121
|
+
before do
|
122
|
+
allow(@input).to receive(:gets).and_return("start\n", "exit\n")
|
123
|
+
subject.run
|
124
|
+
end
|
86
125
|
|
87
|
-
|
126
|
+
it 'shows a menu item' do
|
127
|
+
expect(@output.string).to include('start logging')
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when selected' do
|
131
|
+
it 'sets the debug flag' do
|
132
|
+
expect(store).to have_received(:debug=).with(true)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'updates logging status' do
|
136
|
+
expect(Imap::Backup).to have_received(:setup_logging).twice
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'when debug logging is enabled' do
|
142
|
+
let(:debug) { true }
|
143
|
+
|
144
|
+
before do
|
145
|
+
allow(@input).to receive(:gets).and_return("stop\n", "exit\n")
|
146
|
+
subject.run
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'shows a menu item' do
|
150
|
+
expect(@output.string).to include('stop logging')
|
151
|
+
end
|
88
152
|
|
89
|
-
|
153
|
+
context 'when selected' do
|
154
|
+
before do
|
155
|
+
allow(@input).to receive(:gets).and_return("stop\n", "exit\n")
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'unsets the debug flag' do
|
159
|
+
expect(store).to have_received(:debug=).with(false)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'updates logging status' do
|
163
|
+
expect(Imap::Backup).to have_received(:setup_logging).twice
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
90
167
|
end
|
91
168
|
|
92
|
-
|
93
|
-
|
169
|
+
context "when 'save' is selected" do
|
170
|
+
before do
|
171
|
+
allow(@input).to receive(:gets).and_return("save\n")
|
172
|
+
subject.run
|
173
|
+
end
|
94
174
|
|
95
|
-
|
175
|
+
it 'exits' do
|
176
|
+
# N.B. this will hang forever if save does not cause an exit
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'saves the configuration' do
|
180
|
+
expect(store).to have_received(:save)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "when 'exit without saving' is selected" do
|
185
|
+
before do
|
186
|
+
allow(@input).to receive(:gets).and_return("exit\n")
|
187
|
+
|
188
|
+
subject.run
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'exits' do
|
192
|
+
# N.B. this will hang forever if quit does not cause an exit
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when the configuration is modified' do
|
196
|
+
let(:modified) { true }
|
197
|
+
|
198
|
+
it "doesn't save the configuration" do
|
199
|
+
expect(store).to_not have_received(:save)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
context "when the configuration isn't modified" do
|
204
|
+
it "doesn't save the configuration" do
|
205
|
+
expect(store).to_not have_received(:save)
|
206
|
+
end
|
207
|
+
end
|
96
208
|
end
|
97
209
|
end
|
98
210
|
end
|
@@ -7,18 +7,22 @@ describe Imap::Backup::Configuration::Store do
|
|
7
7
|
let(:file_path) { File.join(directory, '/config.json') }
|
8
8
|
let(:file_exists) { true }
|
9
9
|
let(:directory_exists) { true }
|
10
|
-
let(:data) { {:
|
10
|
+
let(:data) { {:debug => debug, :accounts => accounts} }
|
11
|
+
let(:debug) { true }
|
12
|
+
let(:accounts) { [] }
|
11
13
|
let(:configuration) { data.to_json }
|
12
14
|
|
13
15
|
before do
|
14
16
|
stub_const('Imap::Backup::Configuration::Store::CONFIGURATION_DIRECTORY', directory)
|
15
17
|
allow(File).to receive(:directory?).with(directory).and_return(directory_exists)
|
16
18
|
allow(File).to receive(:exist?).with(file_path).and_return(file_exists)
|
19
|
+
allow(Imap::Backup::Utils).to receive(:stat).with(directory).and_return(0700)
|
20
|
+
allow(Imap::Backup::Utils).to receive(:stat).with(file_path).and_return(0600)
|
21
|
+
allow(Imap::Backup::Utils).to receive(:check_permissions).and_return(nil)
|
17
22
|
allow(File).to receive(:read).with(file_path).and_return(configuration)
|
18
|
-
allow(JSON).to receive(:parse).with(configuration, anything).and_return(data)
|
19
23
|
end
|
20
24
|
|
21
|
-
|
25
|
+
describe '.exist?' do
|
22
26
|
[true, false].each do |exists|
|
23
27
|
state = exists ? 'exists' : "doesn't exist"
|
24
28
|
context "when the file #{state}" do
|
@@ -31,23 +35,103 @@ describe Imap::Backup::Configuration::Store do
|
|
31
35
|
end
|
32
36
|
end
|
33
37
|
|
34
|
-
|
38
|
+
describe '#path' do
|
35
39
|
it 'is the directory containing the configuration file' do
|
36
40
|
expect(subject.path).to eq(directory)
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
|
44
|
+
describe '#modified?' do
|
45
|
+
context "'with accounts flagged 'modified'" do
|
46
|
+
let(:accounts) { [{:name => 'foo', :modified => true}] }
|
47
|
+
|
48
|
+
it 'is true' do
|
49
|
+
expect(subject.modified?).to be_truthy
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "'with accounts flagged 'delete'" do
|
54
|
+
let(:accounts) { [{:name => 'foo', :delete => true}] }
|
55
|
+
|
56
|
+
it 'is true' do
|
57
|
+
expect(subject.modified?).to be_truthy
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "without accounts flagged 'modified'" do
|
62
|
+
let(:accounts) { [{:name => 'foo'}] }
|
63
|
+
|
64
|
+
it 'is false' do
|
65
|
+
expect(subject.modified?).to be_falsey
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#debug?' do
|
71
|
+
context 'when the debug flag is true' do
|
72
|
+
it 'is true' do
|
73
|
+
expect(subject.debug?).to be_truthy
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when the debug flag is false' do
|
78
|
+
let(:debug) { false }
|
79
|
+
|
80
|
+
it 'is false' do
|
81
|
+
expect(subject.debug?).to be_falsey
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when the debug flag is missing' do
|
86
|
+
let(:data) { {:accounts => accounts} }
|
87
|
+
|
88
|
+
it 'is false' do
|
89
|
+
expect(subject.debug?).to be_falsey
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when the debug flag is neither true nor false' do
|
94
|
+
let(:debug) { 'hi' }
|
95
|
+
|
96
|
+
it 'is false' do
|
97
|
+
expect(subject.debug?).to be_falsey
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#debug=' do
|
103
|
+
before { subject.debug = debug }
|
104
|
+
|
105
|
+
context 'when the supplied value is true' do
|
106
|
+
it 'sets the flag to true' do
|
107
|
+
expect(subject.debug?).to be_truthy
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'when the supplied value is false' do
|
112
|
+
let(:debug) { false }
|
113
|
+
|
114
|
+
it 'sets the flag to false' do
|
115
|
+
expect(subject.debug?).to be_falsey
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when the supplied value is neither true nor false' do
|
120
|
+
let(:debug) { 'ciao' }
|
121
|
+
|
122
|
+
it 'sets the flag to false' do
|
123
|
+
expect(subject.debug?).to be_falsey
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#save' do
|
41
129
|
let(:directory_exists) { false }
|
42
|
-
let(:file_exists) { false }
|
43
130
|
let(:file) { double('File', :write => nil) }
|
44
131
|
|
45
132
|
before do
|
46
133
|
allow(FileUtils).to receive(:mkdir)
|
47
134
|
allow(FileUtils).to receive(:chmod)
|
48
|
-
allow(Imap::Backup::Utils).to receive(:stat).with(directory).and_return(0700)
|
49
|
-
allow(Imap::Backup::Utils).to receive(:stat).with(file_path).and_return(0600)
|
50
|
-
allow(Imap::Backup::Utils).to receive(:check_permissions).and_return(nil)
|
51
135
|
allow(File).to receive(:open).with(file_path, 'w') { |&b| b.call file }
|
52
136
|
allow(JSON).to receive(:pretty_generate).and_return('JSON output')
|
53
137
|
end
|
@@ -66,10 +150,43 @@ describe Imap::Backup::Configuration::Store do
|
|
66
150
|
expect(file).to have_received(:write).with('JSON output')
|
67
151
|
end
|
68
152
|
|
69
|
-
|
70
|
-
|
153
|
+
context 'when accounts are modified' do
|
154
|
+
let(:accounts) { [{:name => 'foo', :modified => true}] }
|
155
|
+
|
156
|
+
before { subject.save }
|
157
|
+
|
158
|
+
it "skips the 'modified' flag" do
|
159
|
+
expected = Marshal.load(Marshal.dump(data))
|
160
|
+
expected[:accounts][0].delete(:modified)
|
71
161
|
|
72
|
-
|
162
|
+
expect(JSON).to have_received(:pretty_generate).with(expected)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'when accounts are to be deleted' do
|
167
|
+
let(:accounts) do
|
168
|
+
[
|
169
|
+
{:name => 'keep_me'},
|
170
|
+
{:name => 'delete_me', :delete => true},
|
171
|
+
]
|
172
|
+
end
|
173
|
+
|
174
|
+
before { subject.save }
|
175
|
+
|
176
|
+
it 'does not save them' do
|
177
|
+
expected = Marshal.load(Marshal.dump(data))
|
178
|
+
expected[:accounts].pop
|
179
|
+
|
180
|
+
expect(JSON).to have_received(:pretty_generate).with(expected)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'when file permissions are too open' do
|
185
|
+
before { subject.save }
|
186
|
+
|
187
|
+
it 'sets them to 0600' do
|
188
|
+
expect(FileUtils).to have_received(:chmod).with(0600, file_path)
|
189
|
+
end
|
73
190
|
end
|
74
191
|
|
75
192
|
context 'if the configuration file is missing' do
|
@@ -95,68 +212,5 @@ describe Imap::Backup::Configuration::Store do
|
|
95
212
|
end.to raise_error(RuntimeError, 'Error')
|
96
213
|
end
|
97
214
|
end
|
98
|
-
|
99
|
-
context 'saving accounts' do
|
100
|
-
let(:folders) { [{ :name => 'A folder' }] }
|
101
|
-
let(:data) do
|
102
|
-
{
|
103
|
-
:accounts => [
|
104
|
-
:local_path => '/my/backup/path',
|
105
|
-
:folders => folders
|
106
|
-
]
|
107
|
-
}
|
108
|
-
end
|
109
|
-
let(:file_exists) { true }
|
110
|
-
let(:a_folder_perms) { 0700 }
|
111
|
-
|
112
|
-
before do
|
113
|
-
allow(Imap::Backup::Utils).to receive(:check_permissions)
|
114
|
-
allow(File).to receive(:directory?).with('/my/backup/path').and_return(false)
|
115
|
-
allow(Imap::Backup::Utils).to receive(:stat).with('/my/backup/path').and_return(0700)
|
116
|
-
allow(File).to receive(:directory?).with('/my/backup/path/A folder').and_return(false)
|
117
|
-
allow(Imap::Backup::Utils).to receive(:stat).with('/my/backup/path/A folder').and_return(a_folder_perms)
|
118
|
-
end
|
119
|
-
|
120
|
-
it 'creates account directories' do
|
121
|
-
subject.save
|
122
|
-
|
123
|
-
expect(FileUtils).to have_received(:mkdir).with('/my/backup/path')
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'creates folder directories' do
|
127
|
-
subject.save
|
128
|
-
|
129
|
-
expect(FileUtils).to have_received(:mkdir).with('/my/backup/path/A folder')
|
130
|
-
end
|
131
|
-
|
132
|
-
context 'when directory permissions are too open' do
|
133
|
-
let(:a_folder_perms) { 0755 }
|
134
|
-
|
135
|
-
it 'sets premissions' do
|
136
|
-
subject.save
|
137
|
-
|
138
|
-
expect(FileUtils).to have_received(:chmod).with(0700, '/my/backup/path/A folder')
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
context 'when folders have slashes' do
|
143
|
-
let(:directory_exists) { true }
|
144
|
-
let(:folders) { [{:name => 'folder/path'}] }
|
145
|
-
|
146
|
-
before do
|
147
|
-
allow(File).to receive(:directory?).with('/my/backup/path/folder').and_return(true)
|
148
|
-
allow(Imap::Backup::Utils).to receive(:stat).with('/my/backup/path/folder').and_return(0700)
|
149
|
-
allow(File).to receive(:directory?).with('/my/backup/path/folder/path').and_return(false)
|
150
|
-
allow(Imap::Backup::Utils).to receive(:stat).with('/my/backup/path/folder/path').and_return(0700)
|
151
|
-
allow(FileUtils).to receive(:mkdir)
|
152
|
-
end
|
153
|
-
|
154
|
-
it 'creates subdirectories' do
|
155
|
-
subject.save
|
156
|
-
|
157
|
-
expect(FileUtils).to have_received(:mkdir).with('/my/backup/path/folder/path')
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
215
|
end
|
162
216
|
end
|
@@ -49,7 +49,7 @@ describe Imap::Backup::Serializer::Mbox do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
context 'instance methods' do
|
52
|
-
let(:ids) { %w(1
|
52
|
+
let(:ids) { %w(3 2 1) }
|
53
53
|
|
54
54
|
before do
|
55
55
|
allow(CSV).to receive(:foreach) { |&b| ids.each { |id| b.call [id] } }
|
@@ -58,8 +58,8 @@ describe Imap::Backup::Serializer::Mbox do
|
|
58
58
|
subject { described_class.new(base_path, 'my/folder') }
|
59
59
|
|
60
60
|
context '#uids' do
|
61
|
-
it 'returns the backed-up uids' do
|
62
|
-
expect(subject.uids).to eq(ids)
|
61
|
+
it 'returns the backed-up uids as sorted integers' do
|
62
|
+
expect(subject.uids).to eq(ids.map(&:to_i).sort)
|
63
63
|
end
|
64
64
|
|
65
65
|
context 'if the mbox does not exist' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imap-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Yates
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- spec/gather_rspec_coverage.rb
|
117
117
|
- spec/spec_helper.rb
|
118
118
|
- spec/support/higline_test_helpers.rb
|
119
|
+
- spec/support/shared_examples/account_flagging.rb
|
119
120
|
- spec/support/silence_logging.rb
|
120
121
|
- spec/unit/account/connection_spec.rb
|
121
122
|
- spec/unit/account/folder_spec.rb
|
@@ -159,6 +160,7 @@ test_files:
|
|
159
160
|
- spec/gather_rspec_coverage.rb
|
160
161
|
- spec/spec_helper.rb
|
161
162
|
- spec/support/higline_test_helpers.rb
|
163
|
+
- spec/support/shared_examples/account_flagging.rb
|
162
164
|
- spec/support/silence_logging.rb
|
163
165
|
- spec/unit/account/connection_spec.rb
|
164
166
|
- spec/unit/account/folder_spec.rb
|