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