imap-backup 1.0.14 → 1.0.15
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/.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
|