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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7b1789e72adf3229dc78e51f865b55b051c1915
4
- data.tar.gz: 2f6918a2cd223adc68cc37a5cff138cd30332d6e
3
+ metadata.gz: c3d7f421192f5e5b15efcb55aa13b55cd769899e
4
+ data.tar.gz: d98f149a116634df9e6aa79898da5e2c61ac770a
5
5
  SHA512:
6
- metadata.gz: 0cf0f94758f44e4b5920aea52f363141d10bb3353d92635d8e4285f561498515c60e8c7494bf80245c046a59013c0aa7abf17e1263ebc3aed987479987505b7e
7
- data.tar.gz: f77e1381ddae8bbcf0e99c93bb337f49ee9d2de5940fa30f450432e890e96fc304829d85c2a68f36cf5b6e3b62dec05859fd14195eb7c1203f2c03fd063afe84
6
+ metadata.gz: 7173984ab8c9ef01013d2d6468097aa7b928b59cf12b363b66b3119074f26996c80ffac4c27b36499cd739132eb4105d6f4c0fbd917ee53df69e90f34918a533
7
+ data.tar.gz: 3f5a4e52b736fabb1404ffa0b4ed01ff22b83a1c706f6de90c96f68b66fecaf22eed6af251b674c519dcb2e235aaca56d1b074a347a9a4ec54fb7c6a410e92e2
@@ -3,4 +3,7 @@ rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
5
  - 2.1.1
6
+ branches:
7
+ only:
8
+ - master
6
9
  script: "bundle exec rake spec"
@@ -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|
@@ -34,4 +34,13 @@ module Imap::Backup
34
34
  def self.logger
35
35
  Logger.instance.logger
36
36
  end
37
+
38
+ def self.setup_logging(config)
39
+ logger.level =
40
+ if config.debug?
41
+ ::Logger::Severity::DEBUG
42
+ else
43
+ ::Logger::Severity::ERROR
44
+ end
45
+ end
37
46
  end
@@ -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
- store.accounts.reject! { |a| a[:username] == account[:username] }
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
 
@@ -10,6 +10,10 @@ module Imap::Backup
10
10
  @required_accounts = required_accounts
11
11
  end
12
12
 
13
+ def setup_logging
14
+ Imap::Backup.setup_logging config
15
+ end
16
+
13
17
  def each_connection
14
18
  accounts.each do |account|
15
19
  connection = Account::Connection.new(account)
@@ -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(:quit) { throw :done }
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
- menu.choice("#{account[:username]}") do
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 config
55
- @config ||= Configuration::Store.new
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 setup_logging
59
- Imap::Backup.logger.level =
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.include?(:debug)
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
- @data = JSON.parse(File.read(pathname), :symbolize_names => true)
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)
@@ -13,6 +13,7 @@ module Imap::Backup
13
13
 
14
14
  def run
15
15
  uids = folder.uids - serializer.uids
16
+ Imap::Backup.logger.debug "New messages: #{uids.count}"
16
17
  uids.each do |uid|
17
18
  message = folder.fetch(uid)
18
19
  next if message.nil?
@@ -23,6 +23,7 @@ module Imap::Backup
23
23
  CSV.foreach(imap_pathname) do |row|
24
24
  @uids << row[0]
25
25
  end
26
+ @uids = @uids.map(&:to_i).sort
26
27
  @uids
27
28
  end
28
29
 
@@ -3,6 +3,6 @@ module Imap; end
3
3
  module Imap::Backup
4
4
  MAJOR = 1
5
5
  MINOR = 0
6
- REVISION = 14
6
+ REVISION = 15
7
7
  VERSION = [MAJOR, MINOR, REVISION].map(&:to_s).join('.')
8
8
  end
@@ -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
- it 'modifies the email address' do
175
- expect(account[:username]).to eq(new_email)
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
- it 'updates the password' do
201
- expect(account[:password]).to eq(new_password)
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
- it 'deletes the account' do
295
- expect(accounts.find { |a| a[:username] == existing_email }).to be_nil
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 'does nothing' do
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(:account1) { {:username => 'account@example.com'} }
17
- let(:account) { double('Imap::Backup::Configuration::Account', :run => nil) }
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? => false,
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("q\n")
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 quit).each do |choice|
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 'should list accounts' do
59
+ it 'updates logging status' do
56
60
  subject.run
57
61
 
58
- expect(@output.string).to match /account@example.com/
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", "q\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
- it 'should save the configuration' do
85
- allow(@input).to receive(:gets).and_return("save\n")
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
- subject.run
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
- expect(store).to have_received(:save)
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
- it 'should exit' do
93
- allow(@input).to receive(:gets).and_return("quit\n")
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
- subject.run
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) { {:the => :config} }
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
- context '.exist?' do
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
- context '#path' do
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
- context '#save' do
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
- it 'sets config perms to 0600' do
70
- subject.save
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
- expect(FileUtils).to have_received(:chmod).with(0600, file_path)
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 123) }
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.14
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-12 00:00:00.000000000 Z
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