backup 3.0.20 → 3.0.21
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.
- data/Gemfile +1 -5
- data/Gemfile.lock +46 -50
- data/README.md +54 -27
- data/lib/backup.rb +16 -39
- data/lib/backup/archive.rb +42 -18
- data/lib/backup/cleaner.rb +110 -25
- data/lib/backup/cli/helpers.rb +17 -32
- data/lib/backup/cli/utility.rb +46 -107
- data/lib/backup/compressor/base.rb +14 -2
- data/lib/backup/compressor/bzip2.rb +10 -24
- data/lib/backup/compressor/gzip.rb +10 -24
- data/lib/backup/compressor/lzma.rb +10 -23
- data/lib/backup/compressor/pbzip2.rb +12 -32
- data/lib/backup/config.rb +171 -0
- data/lib/backup/configuration/compressor/base.rb +1 -2
- data/lib/backup/configuration/compressor/pbzip2.rb +4 -4
- data/lib/backup/configuration/database/base.rb +2 -1
- data/lib/backup/configuration/database/mongodb.rb +8 -0
- data/lib/backup/configuration/database/mysql.rb +4 -0
- data/lib/backup/configuration/database/postgresql.rb +4 -0
- data/lib/backup/configuration/database/redis.rb +4 -0
- data/lib/backup/configuration/database/riak.rb +5 -1
- data/lib/backup/configuration/encryptor/base.rb +1 -2
- data/lib/backup/configuration/encryptor/open_ssl.rb +1 -1
- data/lib/backup/configuration/helpers.rb +7 -2
- data/lib/backup/configuration/notifier/base.rb +4 -28
- data/lib/backup/configuration/storage/base.rb +1 -1
- data/lib/backup/configuration/storage/dropbox.rb +14 -4
- data/lib/backup/configuration/syncer/base.rb +10 -0
- data/lib/backup/configuration/syncer/rsync/base.rb +28 -0
- data/lib/backup/configuration/syncer/rsync/local.rb +11 -0
- data/lib/backup/configuration/syncer/rsync/pull.rb +11 -0
- data/lib/backup/configuration/syncer/rsync/push.rb +31 -0
- data/lib/backup/configuration/syncer/s3.rb +0 -4
- data/lib/backup/database/base.rb +25 -7
- data/lib/backup/database/mongodb.rb +112 -75
- data/lib/backup/database/mysql.rb +54 -29
- data/lib/backup/database/postgresql.rb +60 -42
- data/lib/backup/database/redis.rb +61 -39
- data/lib/backup/database/riak.rb +35 -11
- data/lib/backup/dependency.rb +4 -5
- data/lib/backup/encryptor/base.rb +13 -1
- data/lib/backup/encryptor/gpg.rb +39 -39
- data/lib/backup/encryptor/open_ssl.rb +28 -38
- data/lib/backup/logger.rb +20 -11
- data/lib/backup/model.rb +206 -163
- data/lib/backup/notifier/base.rb +27 -25
- data/lib/backup/notifier/campfire.rb +7 -13
- data/lib/backup/notifier/hipchat.rb +28 -28
- data/lib/backup/notifier/mail.rb +24 -26
- data/lib/backup/notifier/presently.rb +10 -18
- data/lib/backup/notifier/prowl.rb +9 -17
- data/lib/backup/notifier/twitter.rb +11 -18
- data/lib/backup/package.rb +47 -0
- data/lib/backup/packager.rb +81 -16
- data/lib/backup/splitter.rb +48 -35
- data/lib/backup/storage/base.rb +44 -172
- data/lib/backup/storage/cloudfiles.rb +31 -46
- data/lib/backup/storage/cycler.rb +117 -0
- data/lib/backup/storage/dropbox.rb +92 -76
- data/lib/backup/storage/ftp.rb +30 -40
- data/lib/backup/storage/local.rb +44 -45
- data/lib/backup/storage/ninefold.rb +55 -49
- data/lib/backup/storage/rsync.rb +49 -56
- data/lib/backup/storage/s3.rb +33 -44
- data/lib/backup/storage/scp.rb +21 -48
- data/lib/backup/storage/sftp.rb +26 -40
- data/lib/backup/syncer/base.rb +7 -0
- data/lib/backup/syncer/rsync/base.rb +78 -0
- data/lib/backup/syncer/rsync/local.rb +53 -0
- data/lib/backup/syncer/rsync/pull.rb +38 -0
- data/lib/backup/syncer/rsync/push.rb +113 -0
- data/lib/backup/syncer/s3.rb +42 -32
- data/lib/backup/version.rb +1 -1
- data/spec/archive_spec.rb +235 -69
- data/spec/cleaner_spec.rb +304 -0
- data/spec/cli/helpers_spec.rb +142 -1
- data/spec/cli/utility_spec.rb +338 -13
- data/spec/compressor/base_spec.rb +31 -0
- data/spec/compressor/bzip2_spec.rb +60 -35
- data/spec/compressor/gzip_spec.rb +60 -35
- data/spec/compressor/lzma_spec.rb +60 -35
- data/spec/compressor/pbzip2_spec.rb +98 -37
- data/spec/config_spec.rb +321 -0
- data/spec/configuration/base_spec.rb +4 -4
- data/spec/configuration/compressor/bzip2_spec.rb +1 -0
- data/spec/configuration/compressor/gzip_spec.rb +1 -0
- data/spec/configuration/compressor/lzma_spec.rb +1 -0
- data/spec/configuration/compressor/pbzip2_spec.rb +32 -0
- data/spec/configuration/database/base_spec.rb +2 -1
- data/spec/configuration/database/mongodb_spec.rb +26 -16
- data/spec/configuration/database/mysql_spec.rb +4 -0
- data/spec/configuration/database/postgresql_spec.rb +4 -0
- data/spec/configuration/database/redis_spec.rb +4 -0
- data/spec/configuration/database/riak_spec.rb +4 -0
- data/spec/configuration/encryptor/gpg_spec.rb +1 -0
- data/spec/configuration/encryptor/open_ssl_spec.rb +1 -0
- data/spec/configuration/notifier/base_spec.rb +32 -0
- data/spec/configuration/notifier/campfire_spec.rb +1 -0
- data/spec/configuration/notifier/hipchat_spec.rb +1 -0
- data/spec/configuration/notifier/mail_spec.rb +1 -0
- data/spec/configuration/notifier/presently_spec.rb +1 -0
- data/spec/configuration/notifier/prowl_spec.rb +1 -0
- data/spec/configuration/notifier/twitter_spec.rb +1 -0
- data/spec/configuration/storage/cloudfiles_spec.rb +1 -0
- data/spec/configuration/storage/dropbox_spec.rb +4 -3
- data/spec/configuration/storage/ftp_spec.rb +1 -0
- data/spec/configuration/storage/local_spec.rb +1 -0
- data/spec/configuration/storage/ninefold_spec.rb +1 -0
- data/spec/configuration/storage/rsync_spec.rb +3 -1
- data/spec/configuration/storage/s3_spec.rb +1 -0
- data/spec/configuration/storage/scp_spec.rb +1 -0
- data/spec/configuration/storage/sftp_spec.rb +1 -0
- data/spec/configuration/syncer/rsync/base_spec.rb +33 -0
- data/spec/configuration/syncer/rsync/local_spec.rb +10 -0
- data/spec/configuration/syncer/rsync/pull_spec.rb +10 -0
- data/spec/configuration/syncer/{rsync_spec.rb → rsync/push_spec.rb} +12 -15
- data/spec/configuration/syncer/s3_spec.rb +2 -3
- data/spec/database/base_spec.rb +35 -20
- data/spec/database/mongodb_spec.rb +298 -119
- data/spec/database/mysql_spec.rb +147 -72
- data/spec/database/postgresql_spec.rb +155 -100
- data/spec/database/redis_spec.rb +200 -97
- data/spec/database/riak_spec.rb +82 -24
- data/spec/dependency_spec.rb +49 -0
- data/spec/encryptor/base_spec.rb +30 -0
- data/spec/encryptor/gpg_spec.rb +105 -28
- data/spec/encryptor/open_ssl_spec.rb +85 -114
- data/spec/logger_spec.rb +74 -8
- data/spec/model_spec.rb +528 -220
- data/spec/notifier/base_spec.rb +89 -0
- data/spec/notifier/campfire_spec.rb +147 -119
- data/spec/notifier/hipchat_spec.rb +140 -145
- data/spec/notifier/mail_spec.rb +190 -248
- data/spec/notifier/presently_spec.rb +147 -282
- data/spec/notifier/prowl_spec.rb +79 -111
- data/spec/notifier/twitter_spec.rb +87 -106
- data/spec/package_spec.rb +61 -0
- data/spec/packager_spec.rb +154 -0
- data/spec/spec_helper.rb +36 -13
- data/spec/splitter_spec.rb +90 -41
- data/spec/storage/base_spec.rb +95 -239
- data/spec/storage/cloudfiles_spec.rb +185 -75
- data/spec/storage/cycler_spec.rb +239 -0
- data/spec/storage/dropbox_spec.rb +318 -87
- data/spec/storage/ftp_spec.rb +165 -152
- data/spec/storage/local_spec.rb +206 -54
- data/spec/storage/ninefold_spec.rb +264 -128
- data/spec/storage/rsync_spec.rb +244 -163
- data/spec/storage/s3_spec.rb +175 -64
- data/spec/storage/scp_spec.rb +156 -150
- data/spec/storage/sftp_spec.rb +153 -135
- data/spec/syncer/base_spec.rb +22 -0
- data/spec/syncer/rsync/base_spec.rb +118 -0
- data/spec/syncer/rsync/local_spec.rb +121 -0
- data/spec/syncer/rsync/pull_spec.rb +90 -0
- data/spec/syncer/rsync/push_spec.rb +327 -0
- data/spec/syncer/s3_spec.rb +180 -91
- data/templates/cli/utility/config +1 -1
- data/templates/cli/utility/database/mongodb +4 -0
- data/templates/cli/utility/database/mysql +3 -0
- data/templates/cli/utility/database/postgresql +3 -0
- data/templates/cli/utility/database/redis +3 -0
- data/templates/cli/utility/database/riak +3 -0
- data/templates/cli/utility/storage/dropbox +4 -1
- data/templates/cli/utility/syncer/rsync_local +12 -0
- data/templates/cli/utility/syncer/{rsync → rsync_pull} +2 -2
- data/templates/cli/utility/syncer/rsync_push +17 -0
- data/templates/storage/dropbox/authorization_url.erb +1 -1
- metadata +42 -17
- data/lib/backup/configuration/syncer/rsync.rb +0 -45
- data/lib/backup/finder.rb +0 -87
- data/lib/backup/storage/object.rb +0 -47
- data/lib/backup/syncer/rsync.rb +0 -152
- data/spec/backup_spec.rb +0 -11
- data/spec/finder_spec.rb +0 -91
- data/spec/storage/object_spec.rb +0 -74
- data/spec/syncer/rsync_spec.rb +0 -195
data/spec/logger_spec.rb
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
4
|
-
require 'timecop'
|
|
5
4
|
|
|
6
5
|
describe Backup::Logger do
|
|
7
6
|
let(:logger_time) { Time.now.strftime("%Y/%m/%d %H:%M:%S") }
|
|
8
|
-
let(:logfile_path) { File.join(Backup::
|
|
7
|
+
let(:logfile_path) { File.join(Backup::Config.log_path, 'backup.log') }
|
|
9
8
|
let(:logfile_mock) { mock }
|
|
10
9
|
let(:s) { sequence '' }
|
|
11
10
|
|
|
@@ -17,7 +16,7 @@ describe Backup::Logger do
|
|
|
17
16
|
subject.unstub(message_type)
|
|
18
17
|
end
|
|
19
18
|
|
|
20
|
-
subject.
|
|
19
|
+
subject.quiet = nil
|
|
21
20
|
subject.send(:remove_instance_variable, :@messages) rescue nil
|
|
22
21
|
subject.send(:remove_instance_variable, :@has_warnings) rescue nil
|
|
23
22
|
end
|
|
@@ -145,6 +144,66 @@ describe Backup::Logger do
|
|
|
145
144
|
end
|
|
146
145
|
end # describe '#clear!'
|
|
147
146
|
|
|
147
|
+
describe '#truncate!' do
|
|
148
|
+
context 'when log file is <= max_bytes' do
|
|
149
|
+
it 'should do nothing' do
|
|
150
|
+
stat = mock
|
|
151
|
+
[1, 2].each do |size|
|
|
152
|
+
File.expects(:stat).with(
|
|
153
|
+
File.join(Backup::Config.log_path, 'backup.log')
|
|
154
|
+
).returns(stat)
|
|
155
|
+
stat.expects(:size).returns(size)
|
|
156
|
+
|
|
157
|
+
FileUtils.expects(:mv).never
|
|
158
|
+
File.expects(:open).never
|
|
159
|
+
FileUtils.expects(:rm_f).never
|
|
160
|
+
|
|
161
|
+
subject.truncate!(2)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'when log file is > max_bytes' do
|
|
167
|
+
before do
|
|
168
|
+
FileUtils.unstub(:mkdir_p)
|
|
169
|
+
FileUtils.unstub(:mv)
|
|
170
|
+
FileUtils.unstub(:rm)
|
|
171
|
+
FileUtils.unstub(:rm_f)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'should truncate the file, removing older lines' do
|
|
175
|
+
max_bytes = 5_000
|
|
176
|
+
line_len = 120
|
|
177
|
+
Dir.mktmpdir do |dir|
|
|
178
|
+
Backup::Config.update(:root_path => dir)
|
|
179
|
+
FileUtils.mkdir_p(Backup::Config.log_path)
|
|
180
|
+
log_file = File.join(Backup::Config.log_path, 'backup.log')
|
|
181
|
+
|
|
182
|
+
lineno = 0
|
|
183
|
+
over_max = (max_bytes * 1.25).to_i
|
|
184
|
+
File.open(log_file, 'w') do |f|
|
|
185
|
+
until f.pos > over_max
|
|
186
|
+
f.puts "#{ lineno += 1 }: ".ljust(line_len, 'x')
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
File.stat(log_file).size.
|
|
190
|
+
should be_within(line_len + 2).of(over_max)
|
|
191
|
+
|
|
192
|
+
subject.truncate!(max_bytes)
|
|
193
|
+
|
|
194
|
+
File.stat(log_file).size.
|
|
195
|
+
should be_within(line_len + 2).of(max_bytes)
|
|
196
|
+
|
|
197
|
+
File.readlines(log_file).last.chomp.
|
|
198
|
+
should == "#{ lineno }: ".ljust(line_len, 'x')
|
|
199
|
+
|
|
200
|
+
File.exist?(log_file + '~').should be_false
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
end # describe '#truncate!'
|
|
206
|
+
|
|
148
207
|
describe '#loggify' do
|
|
149
208
|
|
|
150
209
|
it 'returns an array of strings split on newline separators' do
|
|
@@ -224,7 +283,7 @@ describe Backup::Logger do
|
|
|
224
283
|
end
|
|
225
284
|
|
|
226
285
|
it 'returns nil if quiet? is true' do
|
|
227
|
-
subject.
|
|
286
|
+
subject.quiet = true
|
|
228
287
|
subject.expects(:puts).never
|
|
229
288
|
subject.send(:to_console, 'a string')
|
|
230
289
|
end
|
|
@@ -277,10 +336,17 @@ describe Backup::Logger do
|
|
|
277
336
|
|
|
278
337
|
end # color methods
|
|
279
338
|
|
|
280
|
-
describe '#quiet
|
|
281
|
-
it '
|
|
282
|
-
|
|
283
|
-
|
|
339
|
+
describe '#quiet' do
|
|
340
|
+
it 'should be nil by default' do
|
|
341
|
+
# of course, 'before' is setting it to nil ;)
|
|
342
|
+
subject.quiet.should be_nil
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
it 'can be set true/false' do
|
|
346
|
+
subject.quiet = true
|
|
347
|
+
subject.quiet.should be_true
|
|
348
|
+
subject.quiet = false
|
|
349
|
+
subject.quiet.should be_false
|
|
284
350
|
end
|
|
285
351
|
end
|
|
286
352
|
|
data/spec/model_spec.rb
CHANGED
|
@@ -2,308 +2,432 @@
|
|
|
2
2
|
|
|
3
3
|
require File.expand_path('../spec_helper.rb', __FILE__)
|
|
4
4
|
|
|
5
|
-
describe Backup::Model do
|
|
5
|
+
describe 'Backup::Model' do
|
|
6
|
+
let(:model) { Backup::Model.new(:test_trigger, 'test label') }
|
|
7
|
+
let(:s) { sequence '' }
|
|
6
8
|
|
|
7
9
|
before do
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
Backup::Model.all.clear
|
|
11
|
+
end
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
class Backup::Storage::TestStorage
|
|
16
|
-
def initialize(storage_id = nil, &block); end
|
|
17
|
-
end
|
|
18
|
-
class Backup::Compressor::TestGzip
|
|
19
|
-
def initialize(&block); end
|
|
20
|
-
end
|
|
21
|
-
class Backup::Compressor::TestSevenZip
|
|
22
|
-
def initialize(&block); end
|
|
23
|
-
end
|
|
24
|
-
class Backup::Encryptor::TestOpenSSL
|
|
25
|
-
def initialize(&block); end
|
|
26
|
-
end
|
|
27
|
-
class Backup::Encryptor::TestGPG
|
|
28
|
-
def initialize(&block); end
|
|
13
|
+
describe '.all' do
|
|
14
|
+
it 'should be an empty array by default' do
|
|
15
|
+
Backup::Model.all.should == []
|
|
29
16
|
end
|
|
30
|
-
class Backup::Notifier::TestMail
|
|
31
|
-
def initialize(&block); end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
17
|
end
|
|
35
18
|
|
|
36
|
-
|
|
19
|
+
describe 'finder methods' do
|
|
20
|
+
before do
|
|
21
|
+
[:a, :b, :c, :b, :d].each_with_index do |sym, i|
|
|
22
|
+
Backup::Model.new("trigger_#{sym}", "label#{i}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
describe '.find' do
|
|
27
|
+
it 'should return the first matching model' do
|
|
28
|
+
Backup::Model.find('trigger_b').label.should == 'label1'
|
|
29
|
+
end
|
|
41
30
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
31
|
+
it 'should accept symbols' do
|
|
32
|
+
Backup::Model.find(:trigger_b).label.should == 'label1'
|
|
33
|
+
end
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
it 'should raise an error if trigger is not found' do
|
|
36
|
+
expect do
|
|
37
|
+
Backup::Model.find(:f)
|
|
38
|
+
end.to raise_error(
|
|
39
|
+
Backup::Errors::Model::MissingTriggerError,
|
|
40
|
+
"Model::MissingTriggerError: Could not find trigger 'f'."
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
50
44
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
describe '.find_matching' do
|
|
46
|
+
it 'should find all triggers matching a wildcard' do
|
|
47
|
+
Backup::Model.find_matching('tri*_b').count.should be(2)
|
|
48
|
+
Backup::Model.find_matching('trigg*').count.should be(5)
|
|
49
|
+
end
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
it 'should return an empty array if no matches are found' do
|
|
52
|
+
Backup::Model.find_matching('foo*').should == []
|
|
53
|
+
end
|
|
54
|
+
end
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
Backup::Model.new('blah', 'blah') {}
|
|
63
|
-
File.basename(Backup::Model.file).should == "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar"
|
|
64
|
-
end
|
|
56
|
+
end # describe 'finder methods'
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
Backup::Model.new('blah', 'blah') {}
|
|
68
|
-
Backup::Model.tmp_path.should == File.join(Backup::TMP_PATH, Backup::TRIGGER)
|
|
69
|
-
end
|
|
58
|
+
describe '#initialize' do
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
model.label.should == 'MySQL S3 Backup for MyApp'
|
|
75
|
-
end
|
|
60
|
+
it 'should convert trigger to a string' do
|
|
61
|
+
Backup::Model.new(:foo, :bar).trigger.should == 'foo'
|
|
62
|
+
end
|
|
76
63
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
end
|
|
64
|
+
it 'should convert label to a string' do
|
|
65
|
+
Backup::Model.new(:foo, :bar).label.should == 'bar'
|
|
66
|
+
end
|
|
81
67
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
68
|
+
it 'should set all procedure variables to an empty array' do
|
|
69
|
+
model.send(:procedure_instance_variables).each do |var|
|
|
70
|
+
model.instance_variable_get(var).should == []
|
|
71
|
+
end
|
|
85
72
|
end
|
|
86
|
-
end
|
|
87
73
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
74
|
+
it 'should accept and instance_eval a block' do
|
|
75
|
+
block = lambda {|model| throw(:instance, model) }
|
|
76
|
+
caught = catch(:instance) do
|
|
77
|
+
Backup::Model.new('gotcha', '', &block)
|
|
92
78
|
end
|
|
79
|
+
caught.trigger.should == 'gotcha'
|
|
80
|
+
end
|
|
93
81
|
|
|
94
|
-
|
|
82
|
+
it 'should add itself to Model.all' do
|
|
83
|
+
Backup::Model.all.should == [model]
|
|
95
84
|
end
|
|
96
85
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
86
|
+
end # describe '#initialize'
|
|
87
|
+
|
|
88
|
+
describe 'DSL Methods' do
|
|
89
|
+
|
|
90
|
+
module Fake
|
|
91
|
+
module NoArg
|
|
92
|
+
class Base
|
|
93
|
+
attr_accessor :block_arg
|
|
94
|
+
def initialize(&block)
|
|
95
|
+
instance_eval(&block) if block_given?
|
|
96
|
+
end
|
|
97
|
+
end
|
|
101
98
|
end
|
|
99
|
+
module OneArg
|
|
100
|
+
class Base
|
|
101
|
+
attr_accessor :arg1, :block_arg
|
|
102
|
+
def initialize(arg1, &block)
|
|
103
|
+
@arg1 = arg1
|
|
104
|
+
instance_eval(&block) if block_given?
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
module TwoArgs
|
|
109
|
+
class Base
|
|
110
|
+
attr_accessor :arg1, :arg2, :block_arg
|
|
111
|
+
def initialize(arg1, arg2, &block)
|
|
112
|
+
@arg1 = arg1
|
|
113
|
+
@arg2 = arg2
|
|
114
|
+
instance_eval(&block) if block_given?
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
102
119
|
|
|
103
|
-
|
|
120
|
+
# Set +const+ to +replacement+ for the calling block
|
|
121
|
+
def using_fake(const, replacement)
|
|
122
|
+
orig = Backup.const_get(const)
|
|
123
|
+
Backup.send(:remove_const, const)
|
|
124
|
+
Backup.const_set(const, replacement)
|
|
125
|
+
yield
|
|
126
|
+
Backup.send(:remove_const, const)
|
|
127
|
+
Backup.const_set(const, orig)
|
|
104
128
|
end
|
|
105
|
-
end
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
describe '#archive' do
|
|
131
|
+
it 'should add archives' do
|
|
132
|
+
using_fake('Archive', Fake::TwoArgs::Base) do
|
|
133
|
+
model.archive('foo') {|a| a.block_arg = :foo }
|
|
134
|
+
model.archive('bar') {|a| a.block_arg = :bar }
|
|
135
|
+
model.archives.count.should == 2
|
|
136
|
+
a1, a2 = model.archives
|
|
137
|
+
a1.arg1.should be(model)
|
|
138
|
+
a1.arg2.should == 'foo'
|
|
139
|
+
a1.block_arg.should == :foo
|
|
140
|
+
a2.arg1.should be(model)
|
|
141
|
+
a2.arg2.should == 'bar'
|
|
142
|
+
a2.block_arg.should == :bar
|
|
143
|
+
end
|
|
111
144
|
end
|
|
112
|
-
|
|
113
|
-
model.storages.count.should == 1
|
|
114
145
|
end
|
|
115
146
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
147
|
+
describe '#database' do
|
|
148
|
+
it 'should add databases' do
|
|
149
|
+
using_fake('Database', Fake::OneArg) do
|
|
150
|
+
model.database('Base') {|a| a.block_arg = :foo }
|
|
151
|
+
model.database('Base') {|a| a.block_arg = :bar }
|
|
152
|
+
model.databases.count.should be(2)
|
|
153
|
+
d1, d2 = model.databases
|
|
154
|
+
d1.arg1.should be(model)
|
|
155
|
+
d1.block_arg.should == :foo
|
|
156
|
+
d2.arg1.should be(model)
|
|
157
|
+
d2.block_arg.should == :bar
|
|
158
|
+
end
|
|
120
159
|
end
|
|
121
160
|
|
|
122
|
-
|
|
161
|
+
it 'should accept a nested class name' do
|
|
162
|
+
using_fake('Database', Fake) do
|
|
163
|
+
model.database('OneArg::Base')
|
|
164
|
+
model.databases.first.should be_an_instance_of Fake::OneArg::Base
|
|
165
|
+
end
|
|
166
|
+
end
|
|
123
167
|
end
|
|
124
168
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
169
|
+
describe '#store_with' do
|
|
170
|
+
it 'should add storages' do
|
|
171
|
+
using_fake('Storage', Fake::TwoArgs) do
|
|
172
|
+
model.store_with('Base', 'foo') {|a| a.block_arg = :foo }
|
|
173
|
+
model.store_with('Base', 'bar') {|a| a.block_arg = :bar }
|
|
174
|
+
model.storages.count.should be(2)
|
|
175
|
+
s1, s2 = model.storages
|
|
176
|
+
s1.arg1.should be(model)
|
|
177
|
+
s1.arg2.should == 'foo'
|
|
178
|
+
s1.block_arg.should == :foo
|
|
179
|
+
s2.arg1.should be(model)
|
|
180
|
+
s2.arg2.should == 'bar'
|
|
181
|
+
s2.block_arg.should == :bar
|
|
182
|
+
end
|
|
128
183
|
end
|
|
129
184
|
|
|
130
|
-
|
|
185
|
+
it 'should accept a nested class name' do
|
|
186
|
+
using_fake('Storage', Fake) do
|
|
187
|
+
model.store_with('TwoArgs::Base')
|
|
188
|
+
model.storages.first.should be_an_instance_of Fake::TwoArgs::Base
|
|
189
|
+
end
|
|
190
|
+
end
|
|
131
191
|
end
|
|
132
192
|
|
|
133
|
-
|
|
193
|
+
describe '#sync_with' do
|
|
194
|
+
it 'should add syncers' do
|
|
195
|
+
using_fake('Syncer', Fake::NoArg) do
|
|
196
|
+
model.sync_with('Base') {|a| a.block_arg = :foo }
|
|
197
|
+
model.sync_with('Base') {|a| a.block_arg = :bar }
|
|
198
|
+
model.syncers.count.should be(2)
|
|
199
|
+
s1, s2 = model.syncers
|
|
200
|
+
s1.block_arg.should == :foo
|
|
201
|
+
s2.block_arg.should == :bar
|
|
202
|
+
end
|
|
203
|
+
end
|
|
134
204
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
205
|
+
it 'should accept a nested class name' do
|
|
206
|
+
using_fake('Syncer', Fake) do
|
|
207
|
+
model.sync_with('NoArg::Base')
|
|
208
|
+
model.syncers.first.should be_an_instance_of Fake::NoArg::Base
|
|
209
|
+
end
|
|
139
210
|
end
|
|
140
211
|
|
|
141
|
-
|
|
212
|
+
it 'should warn user of change from RSync to RSync::Local' do
|
|
213
|
+
Backup::Logger.expects(:warn)
|
|
214
|
+
model.sync_with('Backup::Config::RSync')
|
|
215
|
+
model.syncers.first.should be_an_instance_of Backup::Syncer::RSync::Local
|
|
216
|
+
end
|
|
142
217
|
end
|
|
143
218
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
219
|
+
describe '#notify_by' do
|
|
220
|
+
it 'should add notifiers' do
|
|
221
|
+
using_fake('Notifier', Fake::OneArg) do
|
|
222
|
+
model.notify_by('Base') {|a| a.block_arg = :foo }
|
|
223
|
+
model.notify_by('Base') {|a| a.block_arg = :bar }
|
|
224
|
+
model.notifiers.count.should be(2)
|
|
225
|
+
n1, n2 = model.notifiers
|
|
226
|
+
n1.arg1.should be(model)
|
|
227
|
+
n1.block_arg.should == :foo
|
|
228
|
+
n2.arg1.should be(model)
|
|
229
|
+
n2.block_arg.should == :bar
|
|
230
|
+
end
|
|
148
231
|
end
|
|
149
232
|
|
|
150
|
-
|
|
233
|
+
it 'should accept a nested class name' do
|
|
234
|
+
using_fake('Notifier', Fake) do
|
|
235
|
+
model.notify_by('OneArg::Base')
|
|
236
|
+
model.notifiers.first.should be_an_instance_of Fake::OneArg::Base
|
|
237
|
+
end
|
|
238
|
+
end
|
|
151
239
|
end
|
|
152
|
-
end
|
|
153
240
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
241
|
+
describe '#encrypt_with' do
|
|
242
|
+
it 'should add an encryptor' do
|
|
243
|
+
using_fake('Encryptor', Fake::NoArg) do
|
|
244
|
+
model.encrypt_with('Base') {|a| a.block_arg = :foo }
|
|
245
|
+
model.encryptor.should be_an_instance_of Fake::NoArg::Base
|
|
246
|
+
model.encryptor.block_arg.should == :foo
|
|
247
|
+
end
|
|
158
248
|
end
|
|
159
249
|
|
|
160
|
-
|
|
250
|
+
it 'should accept a nested class name' do
|
|
251
|
+
using_fake('Encryptor', Fake) do
|
|
252
|
+
model.encrypt_with('NoArg::Base')
|
|
253
|
+
model.encryptor.should be_an_instance_of Fake::NoArg::Base
|
|
254
|
+
end
|
|
255
|
+
end
|
|
161
256
|
end
|
|
162
257
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
258
|
+
describe '#compress_with' do
|
|
259
|
+
it 'should add a compressor' do
|
|
260
|
+
using_fake('Compressor', Fake::NoArg) do
|
|
261
|
+
model.compress_with('Base') {|a| a.block_arg = :foo }
|
|
262
|
+
model.compressor.should be_an_instance_of Fake::NoArg::Base
|
|
263
|
+
model.compressor.block_arg.should == :foo
|
|
264
|
+
end
|
|
167
265
|
end
|
|
168
266
|
|
|
169
|
-
|
|
267
|
+
it 'should accept a nested class name' do
|
|
268
|
+
using_fake('Compressor', Fake) do
|
|
269
|
+
model.compress_with('NoArg::Base')
|
|
270
|
+
model.compressor.should be_an_instance_of Fake::NoArg::Base
|
|
271
|
+
end
|
|
272
|
+
end
|
|
170
273
|
end
|
|
171
|
-
end
|
|
172
274
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
275
|
+
describe '#split_into_chunks_of' do
|
|
276
|
+
it 'should add a splitter' do
|
|
277
|
+
using_fake('Splitter', Fake::TwoArgs::Base) do
|
|
278
|
+
model.split_into_chunks_of(123)
|
|
279
|
+
model.splitter.should be_an_instance_of Fake::TwoArgs::Base
|
|
280
|
+
model.splitter.arg1.should be(model)
|
|
281
|
+
model.splitter.arg2.should == 123
|
|
282
|
+
end
|
|
177
283
|
end
|
|
178
284
|
|
|
179
|
-
|
|
285
|
+
it 'should raise an error if chunk_size is not an Integer' do
|
|
286
|
+
expect do
|
|
287
|
+
model.split_into_chunks_of('345')
|
|
288
|
+
end.to raise_error {|err|
|
|
289
|
+
err.should be_an_instance_of Backup::Errors::Model::ConfigurationError
|
|
290
|
+
err.message.should match(/must be an Integer/)
|
|
291
|
+
}
|
|
292
|
+
end
|
|
180
293
|
end
|
|
181
294
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
295
|
+
end # describe 'DSL Methods'
|
|
296
|
+
|
|
297
|
+
describe '#prepare!' do
|
|
298
|
+
it 'should prepare for the backup' do
|
|
299
|
+
FileUtils.expects(:mkdir_p).with(
|
|
300
|
+
File.join(Backup::Config.data_path, 'test_trigger')
|
|
301
|
+
)
|
|
302
|
+
Backup::Cleaner.expects(:prepare).with(model)
|
|
187
303
|
|
|
188
|
-
model.
|
|
304
|
+
model.prepare!
|
|
189
305
|
end
|
|
190
306
|
end
|
|
191
307
|
|
|
192
|
-
describe '#
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
308
|
+
describe '#perform!' do
|
|
309
|
+
let(:procedure_a) { lambda {} }
|
|
310
|
+
let(:procedure_b) { mock }
|
|
311
|
+
let(:procedure_c) { mock }
|
|
312
|
+
let(:procedure_d) { lambda {} }
|
|
313
|
+
let(:procedure_e) { lambda {} }
|
|
314
|
+
let(:procedure_f) { mock }
|
|
315
|
+
let(:procedures) do
|
|
316
|
+
[ procedure_a, [procedure_b, procedure_c],
|
|
317
|
+
procedure_d, procedure_e, [procedure_f] ]
|
|
318
|
+
end
|
|
319
|
+
let(:syncer_a) { mock }
|
|
320
|
+
let(:syncer_b) { mock }
|
|
321
|
+
let(:syncers) { [syncer_a, syncer_b] }
|
|
322
|
+
let(:notifier_a) { mock }
|
|
323
|
+
let(:notifier_b) { mock }
|
|
324
|
+
let(:notifiers) { [notifier_a, notifier_b] }
|
|
325
|
+
|
|
326
|
+
it 'should set the @time and @started_at variables' do
|
|
327
|
+
Timecop.freeze(Time.now)
|
|
328
|
+
started_at = Time.now
|
|
329
|
+
time = started_at.strftime("%Y.%m.%d.%H.%M.%S")
|
|
330
|
+
|
|
331
|
+
model.perform!
|
|
332
|
+
model.time.should == time
|
|
333
|
+
model.instance_variable_get(:@started_at).should == started_at
|
|
199
334
|
end
|
|
200
335
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
336
|
+
context 'when no errors occur' do
|
|
337
|
+
before do
|
|
338
|
+
model.expects(:procedures).returns(procedures)
|
|
339
|
+
model.expects(:syncers).returns(syncers)
|
|
340
|
+
model.expects(:notifiers).returns(notifiers)
|
|
205
341
|
end
|
|
206
342
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
343
|
+
context 'when databases are configured' do
|
|
344
|
+
before do
|
|
345
|
+
model.instance_variable_set(:@databases, [true])
|
|
346
|
+
end
|
|
210
347
|
|
|
211
|
-
|
|
212
|
-
|
|
348
|
+
it 'should perform all procedures' do
|
|
349
|
+
model.expects(:log!).in_sequence(s).with(:started)
|
|
213
350
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
351
|
+
procedure_a.expects(:call).in_sequence(s)
|
|
352
|
+
procedure_b.expects(:perform!).in_sequence(s)
|
|
353
|
+
procedure_c.expects(:perform!).in_sequence(s)
|
|
354
|
+
procedure_d.expects(:call).in_sequence(s)
|
|
355
|
+
procedure_e.expects(:call).in_sequence(s)
|
|
356
|
+
procedure_f.expects(:perform!).in_sequence(s)
|
|
217
357
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
packager.expects(:run).with(%|tar -c -f '#{ File.join( Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar" ) }' -C '#{ Backup::TMP_PATH }' '#{ Backup::TRIGGER }'|)
|
|
221
|
-
Backup::Logger.expects(:message).with("Backup::Packager started packaging the backup files.")
|
|
222
|
-
packager.package!
|
|
223
|
-
end
|
|
224
|
-
end
|
|
358
|
+
syncer_a.expects(:perform!).in_sequence(s)
|
|
359
|
+
syncer_b.expects(:perform!).in_sequence(s)
|
|
225
360
|
|
|
226
|
-
|
|
227
|
-
|
|
361
|
+
notifier_a.expects(:perform!).in_sequence(s)
|
|
362
|
+
notifier_b.expects(:perform!).in_sequence(s)
|
|
228
363
|
|
|
229
|
-
|
|
230
|
-
[:utility, :run, :rm].each { |method| model.stubs(method) }
|
|
231
|
-
end
|
|
364
|
+
model.expects(:log!).in_sequence(s).with(:finished)
|
|
232
365
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
cleaner.expects(:utility).with(:rm).returns(:rm)
|
|
236
|
-
Backup::Model.chunk_suffixes = []
|
|
237
|
-
cleaner.expects(:run).with "rm -rf '#{ File.join(Backup::TMP_PATH, Backup::TRIGGER) }' " +
|
|
238
|
-
"'#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }'"
|
|
239
|
-
cleaner.clean!
|
|
366
|
+
model.perform!
|
|
367
|
+
end
|
|
240
368
|
end
|
|
241
|
-
end
|
|
242
369
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
cleaner.expects(:run).with "rm -rf '#{ File.join(Backup::TMP_PATH, Backup::TRIGGER) }' " +
|
|
248
|
-
"'#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }' " +
|
|
249
|
-
"'#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar-aa") }' " +
|
|
250
|
-
"'#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar-ab") }' " +
|
|
251
|
-
"'#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar-ac") }'"
|
|
252
|
-
cleaner.clean!
|
|
253
|
-
end
|
|
254
|
-
end
|
|
370
|
+
context 'when archives are configured' do
|
|
371
|
+
before do
|
|
372
|
+
model.instance_variable_set(:@archives, [true])
|
|
373
|
+
end
|
|
255
374
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
model.send(:clean!)
|
|
259
|
-
end
|
|
260
|
-
end
|
|
375
|
+
it 'should perform all procedures' do
|
|
376
|
+
model.expects(:log!).in_sequence(s).with(:started)
|
|
261
377
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
378
|
+
procedure_a.expects(:call).in_sequence(s)
|
|
379
|
+
procedure_b.expects(:perform!).in_sequence(s)
|
|
380
|
+
procedure_c.expects(:perform!).in_sequence(s)
|
|
381
|
+
procedure_d.expects(:call).in_sequence(s)
|
|
382
|
+
procedure_e.expects(:call).in_sequence(s)
|
|
383
|
+
procedure_f.expects(:perform!).in_sequence(s)
|
|
266
384
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
model.chunk_size.should == 500
|
|
270
|
-
end
|
|
385
|
+
syncer_a.expects(:perform!).in_sequence(s)
|
|
386
|
+
syncer_b.expects(:perform!).in_sequence(s)
|
|
271
387
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
end
|
|
275
|
-
end
|
|
388
|
+
notifier_a.expects(:perform!).in_sequence(s)
|
|
389
|
+
notifier_b.expects(:perform!).in_sequence(s)
|
|
276
390
|
|
|
277
|
-
|
|
391
|
+
model.expects(:log!).in_sequence(s).with(:finished)
|
|
392
|
+
|
|
393
|
+
model.perform!
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
end # context 'when no errors occur'
|
|
278
398
|
|
|
279
399
|
# for the purposes of testing the error handling, we're just going to
|
|
280
400
|
# stub the first thing this method calls and raise an error
|
|
281
|
-
|
|
282
|
-
let(:
|
|
401
|
+
context 'when errors occur' do
|
|
402
|
+
let(:error_a) { mock }
|
|
403
|
+
let(:error_b) { mock }
|
|
404
|
+
let(:notifier) { mock }
|
|
283
405
|
|
|
284
406
|
before do
|
|
285
|
-
|
|
286
|
-
model.expects(:clean!)
|
|
407
|
+
error_a.stubs(:backtrace).returns(['many', 'backtrace', 'lines'])
|
|
287
408
|
end
|
|
288
409
|
|
|
289
410
|
it 'logs, notifies and continues if a StandardError is rescued' do
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
Backup::Logger.expects(:error).twice
|
|
293
|
-
Backup::Logger.expects(:message).once
|
|
411
|
+
Time.stubs(:now).raises(StandardError, 'non-fatal error')
|
|
294
412
|
|
|
295
|
-
Backup::Errors::ModelError.expects(:wrap).with do |err, msg|
|
|
413
|
+
Backup::Errors::ModelError.expects(:wrap).in_sequence(s).with do |err, msg|
|
|
296
414
|
err.message.should == 'non-fatal error'
|
|
297
|
-
msg.should match(/Backup for
|
|
298
|
-
end
|
|
415
|
+
msg.should match(/Backup for test label \(test_trigger\) Failed!/)
|
|
416
|
+
end.returns(error_a)
|
|
417
|
+
Backup::Logger.expects(:error).in_sequence(s).with(error_a)
|
|
418
|
+
Backup::Logger.expects(:error).in_sequence(s).with(
|
|
419
|
+
"\nBacktrace:\n\s\smany\n\s\sbacktrace\n\s\slines\n\n"
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
Backup::Cleaner.expects(:warnings).in_sequence(s).with(model)
|
|
299
423
|
|
|
300
|
-
Backup::Errors::ModelError.expects(:new).with do |msg|
|
|
424
|
+
Backup::Errors::ModelError.expects(:new).in_sequence(s).with do |msg|
|
|
301
425
|
msg.should match(/Backup will now attempt to continue/)
|
|
302
|
-
end
|
|
426
|
+
end.returns(error_b)
|
|
427
|
+
Backup::Logger.expects(:message).in_sequence(s).with(error_b)
|
|
303
428
|
|
|
304
429
|
# notifiers called, but any Exception is ignored
|
|
305
|
-
notifier
|
|
306
|
-
notifier.expects(:perform!).raises(Exception)
|
|
430
|
+
notifier.expects(:perform!).with(true).raises(Exception)
|
|
307
431
|
model.expects(:notifiers).returns([notifier])
|
|
308
432
|
|
|
309
433
|
# returns to allow next trigger to run
|
|
@@ -311,31 +435,215 @@ describe Backup::Model do
|
|
|
311
435
|
end
|
|
312
436
|
|
|
313
437
|
it 'logs, notifies and exits if an Exception is rescued' do
|
|
314
|
-
|
|
438
|
+
Time.stubs(:now).raises(Exception, 'fatal error')
|
|
315
439
|
|
|
316
|
-
Backup::
|
|
317
|
-
Backup::Logger.expects(:message).never
|
|
318
|
-
|
|
319
|
-
Backup::Errors::ModelError.expects(:wrap).with do |err, msg|
|
|
440
|
+
Backup::Errors::ModelError.expects(:wrap).in_sequence(s).with do |err, msg|
|
|
320
441
|
err.message.should == 'fatal error'
|
|
321
|
-
msg.should match(/Backup for
|
|
322
|
-
end
|
|
442
|
+
msg.should match(/Backup for test label \(test_trigger\) Failed!/)
|
|
443
|
+
end.returns(error_a)
|
|
444
|
+
Backup::Logger.expects(:error).in_sequence(s).with(error_a)
|
|
445
|
+
Backup::Logger.expects(:error).in_sequence(s).with(
|
|
446
|
+
"\nBacktrace:\n\s\smany\n\s\sbacktrace\n\s\slines\n\n"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
Backup::Cleaner.expects(:warnings).in_sequence(s).with(model)
|
|
323
450
|
|
|
324
|
-
Backup::Errors::ModelError.expects(:new).with do |msg|
|
|
451
|
+
Backup::Errors::ModelError.expects(:new).in_sequence(s).with do |msg|
|
|
325
452
|
msg.should match(/Backup will now exit/)
|
|
326
|
-
end
|
|
453
|
+
end.returns(error_b)
|
|
454
|
+
Backup::Logger.expects(:error).in_sequence(s).with(error_b)
|
|
327
455
|
|
|
328
456
|
expect do
|
|
329
457
|
# notifiers called, but any Exception is ignored
|
|
330
458
|
notifier = mock
|
|
331
|
-
notifier.expects(:perform!).raises(Exception)
|
|
459
|
+
notifier.expects(:perform!).with(true).raises(Exception)
|
|
332
460
|
model.expects(:notifiers).returns([notifier])
|
|
333
461
|
end.not_to raise_error
|
|
334
462
|
|
|
335
|
-
expect
|
|
463
|
+
expect do
|
|
464
|
+
model.perform!
|
|
465
|
+
end.to raise_error(SystemExit) {|exit| exit.status.should be(1) }
|
|
336
466
|
end
|
|
337
467
|
|
|
338
468
|
end # context 'when errors occur'
|
|
339
469
|
|
|
340
470
|
end # describe '#perform!'
|
|
471
|
+
|
|
472
|
+
describe '#package!' do
|
|
473
|
+
it 'should package the backup' do
|
|
474
|
+
Backup::Packager.expects(:package!).in_sequence(s).with(model)
|
|
475
|
+
Backup::Cleaner.expects(:remove_packaging).in_sequence(s).with(model)
|
|
476
|
+
|
|
477
|
+
model.send(:package!)
|
|
478
|
+
model.package.should be_an_instance_of Backup::Package
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
describe '#clean' do
|
|
483
|
+
it 'should remove the final packaged files' do
|
|
484
|
+
package = mock
|
|
485
|
+
model.instance_variable_set(:@package, package)
|
|
486
|
+
Backup::Cleaner.expects(:remove_package).with(package)
|
|
487
|
+
|
|
488
|
+
model.send(:clean!)
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
describe '#procedures' do
|
|
493
|
+
it 'should return an array of specific, ordered procedures' do
|
|
494
|
+
model.stubs(:databases).returns(:databases)
|
|
495
|
+
model.stubs(:archives).returns(:archives)
|
|
496
|
+
model.stubs(:package!).returns(:package)
|
|
497
|
+
model.stubs(:storages).returns(:storages)
|
|
498
|
+
model.stubs(:clean!).returns(:clean)
|
|
499
|
+
|
|
500
|
+
one, two, three, four, five = model.send(:procedures)
|
|
501
|
+
one.should == :databases
|
|
502
|
+
two.should == :archives
|
|
503
|
+
three.call.should == :package
|
|
504
|
+
four.should == :storages
|
|
505
|
+
five.call.should == :clean
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
describe '#procedure_instance_variables' do
|
|
510
|
+
# these are all set to an empty Array in #initialize
|
|
511
|
+
it 'should return an array of Array holding instance variables' do
|
|
512
|
+
model.send(:procedure_instance_variables).should ==
|
|
513
|
+
[:@databases, :@archives, :@storages, :@notifiers, :@syncers]
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
describe '#get_class_from_scope' do
|
|
518
|
+
|
|
519
|
+
module Fake
|
|
520
|
+
module TestScope
|
|
521
|
+
class TestKlass; end
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
module TestScope
|
|
525
|
+
module TestKlass; end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
context 'when name is given as a string' do
|
|
529
|
+
it 'should return the constant for the given scope and name' do
|
|
530
|
+
model.send(
|
|
531
|
+
:get_class_from_scope,
|
|
532
|
+
Fake,
|
|
533
|
+
'TestScope'
|
|
534
|
+
).should == Fake::TestScope
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
it 'should accept a nested class name' do
|
|
538
|
+
model.send(
|
|
539
|
+
:get_class_from_scope,
|
|
540
|
+
Fake,
|
|
541
|
+
'TestScope::TestKlass'
|
|
542
|
+
).should == Fake::TestScope::TestKlass
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
context 'when name is given as a module' do
|
|
547
|
+
it 'should return the constant for the given scope and name' do
|
|
548
|
+
model.send(
|
|
549
|
+
:get_class_from_scope,
|
|
550
|
+
Fake,
|
|
551
|
+
TestScope
|
|
552
|
+
).should == Fake::TestScope
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
it 'should accept a nested class name' do
|
|
556
|
+
model.send(
|
|
557
|
+
:get_class_from_scope,
|
|
558
|
+
Fake,
|
|
559
|
+
TestScope::TestKlass
|
|
560
|
+
).should == Fake::TestScope::TestKlass
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
context 'when name is given as a module defined under Backup::Config' do
|
|
565
|
+
# this is necessary since the specs in spec/config_spec.rb
|
|
566
|
+
# remove all the constants from Backup::Config as part of those tests.
|
|
567
|
+
before(:all) do
|
|
568
|
+
module Backup::Config
|
|
569
|
+
module TestScope
|
|
570
|
+
module TestKlass; end
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
it 'should return the constant for the given scope and name' do
|
|
576
|
+
model.send(
|
|
577
|
+
:get_class_from_scope,
|
|
578
|
+
Fake,
|
|
579
|
+
Backup::Config::TestScope
|
|
580
|
+
).should == Fake::TestScope
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
it 'should accept a nested class name' do
|
|
584
|
+
model.send(
|
|
585
|
+
:get_class_from_scope,
|
|
586
|
+
Fake,
|
|
587
|
+
Backup::Config::TestScope::TestKlass
|
|
588
|
+
).should == Fake::TestScope::TestKlass
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
end # describe '#get_class_from_scope'
|
|
593
|
+
|
|
594
|
+
describe '#log!' do
|
|
595
|
+
context 'when action is :started' do
|
|
596
|
+
it 'should log that the backup has started with the version' do
|
|
597
|
+
Backup::Logger.expects(:message).with(
|
|
598
|
+
"Performing Backup for 'test label (test_trigger)'!\n" +
|
|
599
|
+
"[ backup #{ Backup::Version.current } : #{ RUBY_DESCRIPTION } ]"
|
|
600
|
+
)
|
|
601
|
+
model.send(:log!, :started)
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
context 'when action is :finished' do
|
|
606
|
+
before { model.expects(:elapsed_time).returns('01:02:03') }
|
|
607
|
+
context 'when warnings were issued' do
|
|
608
|
+
before { Backup::Logger.expects(:has_warnings?).returns(true) }
|
|
609
|
+
it 'should log a warning that the backup has finished with warnings' do
|
|
610
|
+
Backup::Logger.expects(:warn).with(
|
|
611
|
+
"Backup for 'test label (test_trigger)' " +
|
|
612
|
+
"Completed Successfully (with Warnings) in 01:02:03"
|
|
613
|
+
)
|
|
614
|
+
model.send(:log!, :finished)
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
context 'when no warnings were issued' do
|
|
619
|
+
it 'should log that the backup has finished with the elapsed time' do
|
|
620
|
+
Backup::Logger.expects(:message).with(
|
|
621
|
+
"Backup for 'test label (test_trigger)' " +
|
|
622
|
+
"Completed Successfully in 01:02:03"
|
|
623
|
+
)
|
|
624
|
+
model.send(:log!, :finished)
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
end
|
|
628
|
+
end # describe '#log!'
|
|
629
|
+
|
|
630
|
+
describe '#elapsed_time' do
|
|
631
|
+
it 'should return a string representing the elapsed time' do
|
|
632
|
+
Timecop.freeze(Time.now)
|
|
633
|
+
{ 0 => '00:00:00', 1 => '00:00:01', 59 => '00:00:59',
|
|
634
|
+
60 => '00:01:00', 61 => '00:01:01', 119 => '00:01:59',
|
|
635
|
+
3540 => '00:59:00', 3541 => '00:59:01', 3599 => '00:59:59',
|
|
636
|
+
3600 => '01:00:00', 3601 => '01:00:01', 3659 => '01:00:59',
|
|
637
|
+
3660 => '01:01:00', 3661 => '01:01:01', 3719 => '01:01:59',
|
|
638
|
+
7140 => '01:59:00', 7141 => '01:59:01', 7199 => '01:59:59',
|
|
639
|
+
212400 => '59:00:00', 212401 => '59:00:01', 212459 => '59:00:59',
|
|
640
|
+
212460 => '59:01:00', 212461 => '59:01:01', 212519 => '59:01:59',
|
|
641
|
+
215940 => '59:59:00', 215941 => '59:59:01', 215999 => '59:59:59'
|
|
642
|
+
}.each do |duration, expected|
|
|
643
|
+
model.instance_variable_set(:@started_at, Time.now - duration)
|
|
644
|
+
model.send(:elapsed_time).should == expected
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
341
649
|
end
|