imap-backup 1.0.9 → 1.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +1 -0
  3. data/Rakefile +0 -5
  4. data/imap-backup.gemspec +5 -5
  5. data/lib/email/mboxrd/message.rb +1 -1
  6. data/lib/imap/backup.rb +3 -1
  7. data/lib/imap/backup/account/connection.rb +9 -7
  8. data/lib/imap/backup/account/folder.rb +6 -4
  9. data/lib/imap/backup/configuration/account.rb +9 -7
  10. data/lib/imap/backup/configuration/asker.rb +7 -5
  11. data/lib/imap/backup/configuration/connection_tester.rb +5 -3
  12. data/lib/imap/backup/configuration/folder_chooser.rb +10 -8
  13. data/lib/imap/backup/configuration/list.rb +8 -6
  14. data/lib/imap/backup/configuration/setup.rb +7 -5
  15. data/lib/imap/backup/configuration/store.rb +7 -5
  16. data/lib/imap/backup/serializer/base.rb +2 -1
  17. data/lib/imap/backup/serializer/directory.rb +5 -3
  18. data/lib/imap/backup/serializer/mbox.rb +10 -5
  19. data/lib/imap/backup/utils.rb +23 -30
  20. data/lib/imap/backup/version.rb +7 -7
  21. data/spec/spec_helper.rb +10 -27
  22. data/spec/support/higline_test_helpers.rb +8 -0
  23. data/spec/support/silence_logging.rb +7 -0
  24. data/spec/unit/account/connection_spec.rb +1 -1
  25. data/spec/unit/account/folder_spec.rb +33 -52
  26. data/spec/unit/configuration/asker_spec.rb +46 -5
  27. data/spec/unit/configuration/folder_chooser_spec.rb +62 -102
  28. data/spec/unit/configuration/list_spec.rb +40 -48
  29. data/spec/unit/configuration/setup_spec.rb +41 -62
  30. data/spec/unit/configuration/store_spec.rb +107 -99
  31. data/spec/unit/downloader_spec.rb +8 -8
  32. data/spec/unit/email/mboxrd/message_spec.rb +9 -19
  33. data/spec/unit/serializer/base_spec.rb +7 -5
  34. data/spec/unit/serializer/directory_spec.rb +30 -38
  35. data/spec/unit/serializer/mbox_spec.rb +64 -58
  36. data/spec/unit/utils_spec.rb +67 -41
  37. metadata +107 -141
@@ -1,40 +1,33 @@
1
1
  # encoding: utf-8
2
2
  require 'fileutils'
3
3
 
4
- module Imap
5
- module Backup
6
- module Utils
7
- def self.check_permissions(filename, limit)
8
- actual = stat(filename)
9
- mask = ~limit & 0777
10
- if actual & mask != 0
11
- raise "Permissions on '#{filename}' should be #{oct(limit)}, not #{oct(actual)}"
12
- end
13
- end
14
-
15
- def self.stat(filename)
16
- return nil unless File.exist?(filename)
17
-
18
- stat = File.stat(filename)
19
- stat.mode & 0777
4
+ module Imap::Backup
5
+ module Utils
6
+ def self.check_permissions(filename, limit)
7
+ actual = stat(filename)
8
+ return nil if actual.nil?
9
+ mask = ~limit & 0777
10
+ if actual & mask != 0
11
+ raise format("Permissions on '%s' should be 0%o, not 0%o", filename, limit, actual)
20
12
  end
13
+ end
21
14
 
22
- def self.make_folder(base_path, path, permissions)
23
- parts = path.split('/')
24
- return if parts.size == 0
25
- full_path = File.join(base_path, path)
26
- FileUtils.mkdir_p full_path
27
- path = base_path
28
- parts.each do |part|
29
- path = File.join(path, part)
30
- FileUtils.chmod permissions, path
31
- end
32
- end
15
+ def self.stat(filename)
16
+ return nil unless File.exist?(filename)
33
17
 
34
- private
18
+ stat = File.stat(filename)
19
+ stat.mode & 0777
20
+ end
35
21
 
36
- def self.oct(permissions)
37
- "0%o" % permissions
22
+ def self.make_folder(base_path, path, permissions)
23
+ parts = path.split('/')
24
+ return if parts.size == 0
25
+ full_path = File.join(base_path, path)
26
+ FileUtils.mkdir_p full_path
27
+ path = base_path
28
+ parts.each do |part|
29
+ path = File.join(path, part)
30
+ FileUtils.chmod permissions, path
38
31
  end
39
32
  end
40
33
  end
@@ -1,8 +1,8 @@
1
- module Imap
2
- module Backup
3
- MAJOR = 1
4
- MINOR = 0
5
- REVISION = 9
6
- VERSION = [MAJOR, MINOR, REVISION].map(&:to_s).join('.')
7
- end
1
+ module Imap; end
2
+
3
+ module Imap::Backup
4
+ MAJOR = 1
5
+ MINOR = 0
6
+ REVISION = 10
7
+ VERSION = [MAJOR, MINOR, REVISION].map(&:to_s).join('.')
8
8
  end
@@ -1,37 +1,20 @@
1
1
  require 'rspec'
2
+ spec_path = File.dirname(__FILE__)
3
+ $LOAD_PATH << File.expand_path('../lib', spec_path)
4
+
5
+ support_glob = File.join(spec_path, 'support', '**', '*.rb')
6
+ Dir[support_glob].each { |f| require f }
2
7
 
3
8
  if RUBY_VERSION < '1.9'
4
9
  require 'rspec/autorun'
5
10
  else
6
11
  require 'simplecov'
7
- if defined?(GATHER_RSPEC_COVERAGE)
8
- SimpleCov.start do
9
- add_filter "/spec/"
10
- add_filter "/vendor/"
11
- end
12
+ SimpleCov.start do
13
+ add_filter '/spec/'
14
+ add_filter '/vendor/'
12
15
  end
13
16
  end
14
17
 
15
- require File.expand_path(File.dirname(__FILE__) + '/../lib/imap/backup')
16
-
17
- module HighLineTestHelpers
18
- def prepare_highline
19
- @input = stub('stdin', :eof? => false)
20
- # default gets stub
21
- @input.stub!(:gets).with().and_return("q\n")
22
- @output = StringIO.new
23
- Imap::Backup::Configuration::Setup.highline = HighLine.new(@input, @output)
24
- [@input, @output]
25
- end
26
- end
18
+ require 'imap/backup'
27
19
 
28
- module InputOutputTestHelpers
29
- def capturing_output
30
- output = StringIO.new
31
- $stdout = output
32
- yield
33
- output.string
34
- ensure
35
- $stdout = STDOUT
36
- end
37
- end
20
+ silence_logging
@@ -0,0 +1,8 @@
1
+ module HighLineTestHelpers
2
+ def prepare_highline
3
+ @input = double('stdin', :eof? => false, :gets => "q\n")
4
+ @output = StringIO.new
5
+ Imap::Backup::Configuration::Setup.highline = HighLine.new(@input, @output)
6
+ [@input, @output]
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ def silence_logging
2
+ RSpec.configure do |config|
3
+ config.before(:suite) do
4
+ Imap::Backup.logger.level = Logger::UNKNOWN
5
+ end
6
+ end
7
+ end
@@ -21,7 +21,7 @@ describe Imap::Backup::Account::Connection do
21
21
  allow(Net::IMAP).to receive(:new).and_return(imap)
22
22
  end
23
23
 
24
- subject { Imap::Backup::Account::Connection.new(options) }
24
+ subject { described_class.new(options) }
25
25
 
26
26
  shared_examples 'connects to IMAP' do |options|
27
27
  options ||= {}
@@ -2,74 +2,55 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Imap::Backup::Account::Folder do
5
- include InputOutputTestHelpers
6
-
7
- let(:imap) { stub('Net::IMAP') }
8
- let(:connection) { stub('Imap::Backup::Account::Connection', :imap => imap) }
9
- let(:missing_mailbox_data) { stub('Data', :text => 'Unknown Mailbox: my_folder') }
10
- let(:missing_mailbox_response) { stub('Response', :data => missing_mailbox_data) }
5
+ let(:imap) { double('Net::IMAP', :examine => nil) }
6
+ let(:connection) { double('Imap::Backup::Account::Connection', :imap => imap) }
7
+ let(:missing_mailbox_data) { double('Data', :text => 'Unknown Mailbox: my_folder') }
8
+ let(:missing_mailbox_response) { double('Response', :data => missing_mailbox_data) }
11
9
  let(:missing_mailbox_error) { Net::IMAP::NoResponseError.new(missing_mailbox_response) }
12
10
 
13
- context 'with instance' do
14
- subject { Imap::Backup::Account::Folder.new(connection, 'my_folder') }
11
+ subject { described_class.new(connection, 'my_folder') }
15
12
 
16
- context '#uids' do
17
- it 'lists available messages' do
18
- imap.should_receive(:examine).with('my_folder')
19
- imap.should_receive(:uid_search).with(['ALL']).and_return([5678, 123])
13
+ context '#uids' do
14
+ let(:uids) { [5678, 123] }
20
15
 
21
- subject.uids.should == [123, 5678]
22
- end
16
+ before { allow(imap).to receive(:uid_search).and_return(uids) }
17
+
18
+ it 'lists available messages' do
19
+ expect(subject.uids).to eq(uids.reverse)
20
+ end
23
21
 
24
- it 'returns an empty array for missing mailboxes' do
25
- imap.
26
- should_receive(:examine).
27
- with('my_folder').
28
- and_raise(missing_mailbox_error)
22
+ context 'with missing mailboxes' do
23
+ before { allow(imap).to receive(:examine).and_raise(missing_mailbox_error) }
29
24
 
30
- capturing_output do
31
- expect(subject.uids).to eq([])
32
- end
25
+ it 'returns an empty array' do
26
+ expect(subject.uids).to eq([])
33
27
  end
34
28
  end
29
+ end
35
30
 
36
- context '#fetch' do
37
- let(:message_body) { 'the body' }
38
- let(:message) do
39
- {
40
- 'RFC822' => message_body,
41
- 'other' => 'xxx'
42
- }
43
- end
31
+ context '#fetch' do
32
+ let(:message_body) { double('the body', :force_encoding => nil) }
33
+ let(:message) { {'RFC822' => message_body, 'other' => 'xxx'} }
44
34
 
45
- it 'requests the message, the flags and the date' do
46
- imap.should_receive(:examine).with('my_folder')
47
- imap.should_receive(:uid_fetch).
48
- with([123], ['RFC822', 'FLAGS', 'INTERNALDATE']).
49
- and_return([[nil, message]])
35
+ before { allow(imap).to receive(:uid_fetch).and_return([[nil, message]]) }
50
36
 
51
- subject.fetch(123)
52
- end
37
+ it 'returns the message' do
38
+ expect(subject.fetch(123)).to eq(message)
39
+ end
53
40
 
54
- it "returns nil if the mailbox doesn't exist" do
55
- imap.
56
- should_receive(:examine).
57
- with('my_folder').
58
- and_raise(missing_mailbox_error)
41
+ context "if the mailbox doesn't exist" do
42
+ before { allow(imap).to receive(:examine).and_raise(missing_mailbox_error) }
59
43
 
60
- capturing_output do
61
- expect(subject.fetch(123)).to be_nil
62
- end
44
+ it 'is nil' do
45
+ expect(subject.fetch(123)).to be_nil
63
46
  end
47
+ end
64
48
 
65
- if RUBY_VERSION > '1.9'
66
- it 'sets the encoding on the message' do
67
- imap.stub!(:examine => nil, :uid_fetch => [[nil, message]])
68
-
69
- message_body.should_receive(:force_encoding).with('utf-8')
49
+ if RUBY_VERSION > '1.9'
50
+ it 'sets the encoding on the message' do
51
+ subject.fetch(123)
70
52
 
71
- subject.fetch(123)
72
- end
53
+ expect(message_body).to have_received(:force_encoding).with('utf-8')
73
54
  end
74
55
  end
75
56
  end
@@ -1,11 +1,48 @@
1
1
  # encoding: utf-8
2
2
  require 'spec_helper'
3
3
 
4
- module Imap::Backup::Configuration
5
- describe Asker do
4
+ module Imap::Backup
5
+ describe Configuration::Asker do
6
6
  let(:highline) { double }
7
+ let(:query) do
8
+ double(
9
+ 'Query',
10
+ :default= => nil,
11
+ :readline= => nil,
12
+ :validate= => nil,
13
+ :responses => {},
14
+ :echo= => nil
15
+ )
16
+ end
17
+ let(:answer) { 'foo' }
18
+
19
+ before do
20
+ allow(Configuration::Setup).to receive(:highline).and_return(highline)
21
+ allow(highline).to receive(:ask) do |&b|
22
+ b.call query
23
+ answer
24
+ end
25
+ end
26
+
27
+ subject { described_class.new(highline) }
28
+
29
+ [
30
+ [:email, [], 'email address'],
31
+ [:password, [], 'password'],
32
+ [:backup_path, ['x', 'y'], 'backup directory'],
33
+ ].each do |method, params, prompt|
34
+ context ".#{method}" do
35
+ it 'asks for input' do
36
+ described_class.send(method, *params)
7
37
 
8
- subject { Asker.new(highline) }
38
+ expect(highline).to have_received(:ask).with("#{prompt}: ")
39
+ end
40
+
41
+ it 'returns the answer' do
42
+ expect(described_class.send(method, *params)).to eq(answer)
43
+ end
44
+ end
45
+ end
9
46
 
10
47
  context '#initialize' do
11
48
  its(:highline) { should eq(highline) }
@@ -13,9 +50,9 @@ module Imap::Backup::Configuration
13
50
 
14
51
  context '#email' do
15
52
  let(:email) { 'email@example.com' }
53
+ let(:answer) { email }
16
54
 
17
55
  before do
18
- allow(highline).to receive(:ask).and_return(email)
19
56
  @result = subject.email
20
57
  end
21
58
 
@@ -70,9 +107,13 @@ module Imap::Backup::Configuration
70
107
 
71
108
  context '#backup_path' do
72
109
  let(:path) { '/path' }
110
+ let(:answer) { path }
73
111
 
74
112
  before do
75
- allow(highline).to receive(:ask).and_return(path)
113
+ allow(highline).to receive(:ask) do |&b|
114
+ b.call query
115
+ path
116
+ end
76
117
  @result = subject.backup_path('', //)
77
118
  end
78
119
 
@@ -2,145 +2,105 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  describe Imap::Backup::Configuration::FolderChooser do
5
-
6
5
  include HighLineTestHelpers
7
- include InputOutputTestHelpers
8
6
 
9
7
  context '#run' do
10
- let(:connection) do
11
- stub('Imap::Backup::Account::Connection')
12
- end
13
- let(:existing_account) do
14
- {
15
- :folders => [{:name => 'my_folder'}]
16
- }
17
- end
18
- let(:empty_account) do
19
- {
20
- :folders => []
21
- }
22
- end
23
- let(:remote_folders) do
24
- folder1 = stub('folder', :name => 'my_folder') # this one is already backed up
25
- folder2 = stub('folder', :name => 'another_folder')
26
- [folder1, folder2]
27
- end
8
+ let(:connection) { double('Imap::Backup::Account::Connection', :folders => remote_folders) }
9
+ let(:account) { {:folders => []} }
10
+ let(:remote_folders) { [] }
11
+
12
+ subject { described_class.new(account) }
28
13
 
29
14
  before do
15
+ allow(Imap::Backup::Account::Connection).to receive(:new).with(account).and_return(connection)
30
16
  @input, @output = prepare_highline
17
+ allow(subject).to receive(:system)
18
+ allow(Imap::Backup.logger).to receive(:warn)
31
19
  end
32
20
 
33
- context 'empty account' do
34
- let(:account) { empty_account }
35
-
36
- subject { Imap::Backup::Configuration::FolderChooser.new(account) }
21
+ context 'display' do
22
+ before { subject.run }
37
23
 
38
- before do
39
- connection.stub!(:folders).and_return([])
40
- Imap::Backup::Account::Connection.stub!(:new).with(account).and_return(connection)
41
- subject.stub(:system).with('clear')
24
+ it 'clears the screen' do
25
+ expect(subject).to have_received(:system).with('clear')
42
26
  end
43
27
 
44
- it 'should connect to the account' do
45
- Imap::Backup::Account::Connection.should_receive(:new).with(account).and_return(connection)
46
-
47
- subject.run
28
+ it 'should show the menu' do
29
+ @output.string.should =~ %r{Add/remove folders}
48
30
  end
31
+ end
49
32
 
50
- it 'should handle connection errors' do
51
- Imap::Backup::Account::Connection.should_receive(:new).with(account).and_raise('error')
52
- Imap::Backup::Configuration::Setup.highline.should_receive(:ask).with('Press a key ')
53
-
54
- capturing_output do
55
- subject.run
56
- end.should =~ /connection failed/i
33
+ context 'folder listing' do
34
+ let(:account) { {:folders => [{:name => 'my_folder'}]} }
35
+ let(:remote_folders) do
36
+ folder1 = double('folder', :name => 'my_folder') # this one is already backed up
37
+ folder2 = double('folder', :name => 'another_folder')
38
+ [folder1, folder2]
57
39
  end
58
40
 
59
- it 'should get a list of account folders' do
60
- connection.should_receive(:folders).and_return([])
41
+ context 'display' do
42
+ before { subject.run }
61
43
 
62
- subject.run
44
+ it 'shows folders which are being backed up' do
45
+ expect(@output.string).to include('+ my_folder')
46
+ end
47
+
48
+ it 'shows folders which are not being backed up' do
49
+ expect(@output.string).to include('- another_folder')
50
+ end
63
51
  end
64
52
 
65
- it 'clears the screen' do
66
- subject.should_receive(:system).with('clear')
53
+ context 'adding folders' do
54
+ before do
55
+ allow(@input).to receive(:gets).and_return("2\n", "q\n")
67
56
 
68
- subject.run
69
- end
57
+ subject.run
58
+ end
70
59
 
71
- it 'should show the menu' do
72
- subject.run
73
-
74
- @output.string.should =~ %r{Add/remove folders}
60
+ specify 'are added to the account' do
61
+ expect(account[:folders]).to include({:name => 'another_folder'})
62
+ end
75
63
  end
76
64
 
77
- it 'should return to the account menu' do
78
- @input.should_receive(:gets).and_return("return\n")
65
+ context 'removing folders' do
66
+ before do
67
+ allow(@input).to receive(:gets).and_return("1\n", "q\n")
79
68
 
80
- subject.run
81
-
82
- @output.string.should =~ %r{return to the account menu}
69
+ subject.run
70
+ end
71
+
72
+ specify 'are removed from the account' do
73
+ expect(account[:folders]).to_not include({:name => 'my_folder'})
74
+ end
83
75
  end
84
76
  end
85
77
 
86
- context 'folder listing' do
87
- let(:account) { existing_account }
88
-
89
- subject { Imap::Backup::Configuration::FolderChooser.new(account) }
78
+ context 'when folders are not available' do
79
+ let(:remote_folders) { nil }
90
80
 
91
81
  before do
92
- connection.stub!(:folders).and_return(remote_folders)
93
- Imap::Backup::Account::Connection.stub!(:new).with(account).and_return(connection)
94
- subject.stub(:system).with('clear')
95
- end
96
-
97
- it 'should list folders' do
82
+ allow(Imap::Backup::Configuration::Setup.highline).to receive(:ask).and_return("q")
98
83
  subject.run
99
-
100
- @output.string.should =~ /my_folder/
101
84
  end
102
85
 
103
- it 'should show which folders are already being backed up' do
104
- subject.run
105
-
106
- @output.string.should =~ /\d+\. \+ my_folder/
107
- @output.string.should =~ /\d+\. \- another_folder/
86
+ it 'asks to press a key' do
87
+ expect(Imap::Backup::Configuration::Setup.highline).to have_received(:ask).with('Press a key ')
108
88
  end
89
+ end
109
90
 
110
- it 'should add folders' do
111
- state = :initial
112
- @input.stub(:gets) do
113
- case state
114
- when :initial
115
- state = :added
116
- "2\n" # choose 'another_folder'
117
- else
118
- "q\n"
119
- end
120
- end
121
-
91
+ context 'with connection errors' do
92
+ before do
93
+ allow(Imap::Backup::Account::Connection).to receive(:new).with(account).and_raise('error')
94
+ allow(Imap::Backup::Configuration::Setup.highline).to receive(:ask).and_return("q")
122
95
  subject.run
123
-
124
- @output.string.should =~ /\d+\. \+ another_folder/
125
- account[:folders].should include({:name => 'another_folder'})
126
96
  end
127
97
 
128
- it 'should remove folders' do
129
- state = :initial
130
- @input.stub(:gets) do
131
- case state
132
- when :initial
133
- state = :added
134
- "1\n"
135
- else
136
- "q\n"
137
- end
138
- end
139
-
140
- subject.run
98
+ it 'prints an error message' do
99
+ expect(Imap::Backup.logger).to have_received(:warn).with('Connection failed')
100
+ end
141
101
 
142
- @output.string.should =~ /\d+\. \- my_folder/
143
- account[:folders].should_not include({:name => 'my_folder'})
102
+ it 'asks to continue' do
103
+ expect(Imap::Backup::Configuration::Setup.highline).to have_received(:ask).with('Press a key ')
144
104
  end
145
105
  end
146
106
  end