backup 3.0.19 → 3.0.20

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 (188) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +9 -8
  3. data/Gemfile.lock +19 -1
  4. data/Guardfile +13 -9
  5. data/README.md +93 -31
  6. data/backup.gemspec +3 -3
  7. data/bin/backup +6 -283
  8. data/lib/backup.rb +101 -72
  9. data/lib/backup/archive.rb +21 -9
  10. data/lib/backup/binder.rb +22 -0
  11. data/lib/backup/cleaner.rb +36 -0
  12. data/lib/backup/cli/helpers.rb +103 -0
  13. data/lib/backup/cli/utility.rb +308 -0
  14. data/lib/backup/compressor/base.rb +2 -2
  15. data/lib/backup/compressor/pbzip2.rb +76 -0
  16. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  17. data/lib/backup/configuration/database/riak.rb +25 -0
  18. data/lib/backup/configuration/encryptor/open_ssl.rb +6 -0
  19. data/lib/backup/configuration/helpers.rb +5 -18
  20. data/lib/backup/configuration/notifier/base.rb +13 -0
  21. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  22. data/lib/backup/configuration/notifier/mail.rb +38 -0
  23. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  24. data/lib/backup/configuration/storage/cloudfiles.rb +4 -0
  25. data/lib/backup/configuration/storage/dropbox.rb +8 -4
  26. data/lib/backup/database/base.rb +10 -2
  27. data/lib/backup/database/mongodb.rb +16 -19
  28. data/lib/backup/database/mysql.rb +2 -2
  29. data/lib/backup/database/postgresql.rb +2 -2
  30. data/lib/backup/database/redis.rb +15 -7
  31. data/lib/backup/database/riak.rb +45 -0
  32. data/lib/backup/dependency.rb +21 -7
  33. data/lib/backup/encryptor/base.rb +1 -1
  34. data/lib/backup/encryptor/open_ssl.rb +20 -5
  35. data/lib/backup/errors.rb +124 -0
  36. data/lib/backup/finder.rb +11 -3
  37. data/lib/backup/logger.rb +121 -82
  38. data/lib/backup/model.rb +103 -44
  39. data/lib/backup/notifier/base.rb +50 -0
  40. data/lib/backup/notifier/campfire.rb +32 -52
  41. data/lib/backup/notifier/hipchat.rb +99 -0
  42. data/lib/backup/notifier/mail.rb +100 -61
  43. data/lib/backup/notifier/presently.rb +31 -40
  44. data/lib/backup/notifier/prowl.rb +73 -0
  45. data/lib/backup/notifier/twitter.rb +29 -39
  46. data/lib/backup/packager.rb +25 -0
  47. data/lib/backup/splitter.rb +62 -0
  48. data/lib/backup/storage/base.rb +178 -18
  49. data/lib/backup/storage/cloudfiles.rb +34 -28
  50. data/lib/backup/storage/dropbox.rb +64 -67
  51. data/lib/backup/storage/ftp.rb +48 -40
  52. data/lib/backup/storage/local.rb +33 -28
  53. data/lib/backup/storage/ninefold.rb +40 -26
  54. data/lib/backup/storage/object.rb +8 -6
  55. data/lib/backup/storage/rsync.rb +61 -51
  56. data/lib/backup/storage/s3.rb +29 -27
  57. data/lib/backup/storage/scp.rb +56 -36
  58. data/lib/backup/storage/sftp.rb +49 -33
  59. data/lib/backup/syncer/base.rb +1 -1
  60. data/lib/backup/syncer/rsync.rb +1 -1
  61. data/lib/backup/template.rb +46 -0
  62. data/lib/backup/version.rb +1 -1
  63. data/spec/archive_spec.rb +34 -9
  64. data/spec/backup_spec.rb +1 -1
  65. data/spec/cli/helpers_spec.rb +35 -0
  66. data/spec/cli/utility_spec.rb +38 -0
  67. data/spec/compressor/bzip2_spec.rb +1 -1
  68. data/spec/compressor/gzip_spec.rb +1 -1
  69. data/spec/compressor/lzma_spec.rb +1 -1
  70. data/spec/compressor/pbzip2_spec.rb +63 -0
  71. data/spec/configuration/base_spec.rb +1 -1
  72. data/spec/configuration/compressor/bzip2_spec.rb +1 -1
  73. data/spec/configuration/compressor/gzip_spec.rb +1 -1
  74. data/spec/configuration/compressor/lzma_spec.rb +1 -1
  75. data/spec/configuration/database/base_spec.rb +1 -1
  76. data/spec/configuration/database/mongodb_spec.rb +1 -1
  77. data/spec/configuration/database/mysql_spec.rb +1 -1
  78. data/spec/configuration/database/postgresql_spec.rb +1 -1
  79. data/spec/configuration/database/redis_spec.rb +1 -1
  80. data/spec/configuration/database/riak_spec.rb +31 -0
  81. data/spec/configuration/encryptor/gpg_spec.rb +1 -1
  82. data/spec/configuration/encryptor/open_ssl_spec.rb +4 -1
  83. data/spec/configuration/notifier/campfire_spec.rb +1 -1
  84. data/spec/configuration/notifier/hipchat_spec.rb +43 -0
  85. data/spec/configuration/notifier/mail_spec.rb +34 -22
  86. data/spec/configuration/notifier/presently_spec.rb +1 -1
  87. data/spec/configuration/notifier/prowl_spec.rb +28 -0
  88. data/spec/configuration/notifier/twitter_spec.rb +1 -1
  89. data/spec/configuration/storage/cloudfiles_spec.rb +19 -16
  90. data/spec/configuration/storage/dropbox_spec.rb +1 -1
  91. data/spec/configuration/storage/ftp_spec.rb +1 -1
  92. data/spec/configuration/storage/local_spec.rb +1 -1
  93. data/spec/configuration/storage/ninefold_spec.rb +1 -1
  94. data/spec/configuration/storage/rsync_spec.rb +1 -1
  95. data/spec/configuration/storage/s3_spec.rb +1 -1
  96. data/spec/configuration/storage/scp_spec.rb +1 -1
  97. data/spec/configuration/storage/sftp_spec.rb +1 -1
  98. data/spec/configuration/syncer/rsync_spec.rb +1 -1
  99. data/spec/configuration/syncer/s3_spec.rb +1 -1
  100. data/spec/database/base_spec.rb +10 -1
  101. data/spec/database/mongodb_spec.rb +34 -7
  102. data/spec/database/mysql_spec.rb +8 -7
  103. data/spec/database/postgresql_spec.rb +8 -7
  104. data/spec/database/redis_spec.rb +39 -9
  105. data/spec/database/riak_spec.rb +50 -0
  106. data/spec/encryptor/gpg_spec.rb +1 -1
  107. data/spec/encryptor/open_ssl_spec.rb +77 -20
  108. data/spec/errors_spec.rb +306 -0
  109. data/spec/finder_spec.rb +91 -0
  110. data/spec/logger_spec.rb +254 -33
  111. data/spec/model_spec.rb +120 -15
  112. data/spec/notifier/campfire_spec.rb +127 -52
  113. data/spec/notifier/hipchat_spec.rb +193 -0
  114. data/spec/notifier/mail_spec.rb +290 -74
  115. data/spec/notifier/presently_spec.rb +290 -73
  116. data/spec/notifier/prowl_spec.rb +149 -0
  117. data/spec/notifier/twitter_spec.rb +106 -41
  118. data/spec/spec_helper.rb +8 -2
  119. data/spec/splitter_spec.rb +71 -0
  120. data/spec/storage/base_spec.rb +280 -19
  121. data/spec/storage/cloudfiles_spec.rb +38 -22
  122. data/spec/storage/dropbox_spec.rb +17 -13
  123. data/spec/storage/ftp_spec.rb +145 -55
  124. data/spec/storage/local_spec.rb +6 -6
  125. data/spec/storage/ninefold_spec.rb +70 -29
  126. data/spec/storage/object_spec.rb +44 -44
  127. data/spec/storage/rsync_spec.rb +186 -63
  128. data/spec/storage/s3_spec.rb +23 -24
  129. data/spec/storage/scp_spec.rb +116 -41
  130. data/spec/storage/sftp_spec.rb +124 -46
  131. data/spec/syncer/rsync_spec.rb +3 -3
  132. data/spec/syncer/s3_spec.rb +1 -1
  133. data/spec/version_spec.rb +1 -1
  134. data/templates/cli/utility/archive +13 -0
  135. data/{lib/templates → templates/cli/utility}/compressor/bzip2 +1 -1
  136. data/{lib/templates → templates/cli/utility}/compressor/gzip +1 -1
  137. data/{lib/templates → templates/cli/utility}/compressor/lzma +0 -0
  138. data/templates/cli/utility/compressor/pbzip2 +7 -0
  139. data/templates/cli/utility/config +31 -0
  140. data/{lib/templates → templates/cli/utility}/database/mongodb +1 -1
  141. data/{lib/templates → templates/cli/utility}/database/mysql +1 -1
  142. data/{lib/templates → templates/cli/utility}/database/postgresql +1 -1
  143. data/{lib/templates → templates/cli/utility}/database/redis +1 -1
  144. data/templates/cli/utility/database/riak +8 -0
  145. data/{lib/templates → templates/cli/utility}/encryptor/gpg +1 -1
  146. data/templates/cli/utility/encryptor/openssl +9 -0
  147. data/templates/cli/utility/model.erb +23 -0
  148. data/{lib/templates → templates/cli/utility}/notifier/campfire +2 -1
  149. data/templates/cli/utility/notifier/hipchat +15 -0
  150. data/{lib/templates → templates/cli/utility}/notifier/mail +6 -1
  151. data/{lib/templates → templates/cli/utility}/notifier/presently +1 -0
  152. data/templates/cli/utility/notifier/prowl +11 -0
  153. data/{lib/templates → templates/cli/utility}/notifier/twitter +2 -1
  154. data/templates/cli/utility/splitter +7 -0
  155. data/templates/cli/utility/storage/cloudfiles +12 -0
  156. data/{lib/templates → templates/cli/utility}/storage/dropbox +1 -1
  157. data/{lib/templates → templates/cli/utility}/storage/ftp +0 -0
  158. data/templates/cli/utility/storage/local +7 -0
  159. data/{lib/templates → templates/cli/utility}/storage/ninefold +1 -1
  160. data/templates/cli/utility/storage/rsync +11 -0
  161. data/{lib/templates → templates/cli/utility}/storage/s3 +0 -2
  162. data/templates/cli/utility/storage/scp +11 -0
  163. data/templates/cli/utility/storage/sftp +11 -0
  164. data/{lib/templates → templates/cli/utility}/syncer/rsync +1 -1
  165. data/{lib/templates → templates/cli/utility}/syncer/s3 +1 -1
  166. data/templates/general/links +11 -0
  167. data/templates/general/version.erb +2 -0
  168. data/templates/notifier/mail/failure.erb +9 -0
  169. data/templates/notifier/mail/success.erb +7 -0
  170. data/templates/notifier/mail/warning.erb +9 -0
  171. data/templates/storage/dropbox/authorization_url.erb +6 -0
  172. data/templates/storage/dropbox/authorized.erb +4 -0
  173. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  174. metadata +81 -45
  175. data/lib/backup/cli.rb +0 -110
  176. data/lib/backup/exception/command_failed.rb +0 -8
  177. data/lib/backup/exception/command_not_found.rb +0 -8
  178. data/lib/backup/notifier/binder.rb +0 -32
  179. data/lib/backup/notifier/templates/notify_failure.erb +0 -33
  180. data/lib/backup/notifier/templates/notify_success.erb +0 -16
  181. data/lib/templates/archive +0 -7
  182. data/lib/templates/encryptor/openssl +0 -8
  183. data/lib/templates/readme +0 -15
  184. data/lib/templates/storage/cloudfiles +0 -11
  185. data/lib/templates/storage/local +0 -7
  186. data/lib/templates/storage/rsync +0 -11
  187. data/lib/templates/storage/scp +0 -11
  188. data/lib/templates/storage/sftp +0 -11
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
+
5
+ describe Backup::Finder do
6
+
7
+ describe '#find' do
8
+ let(:finder) { Backup::Finder.new('test_trigger', 'foo') }
9
+
10
+ before do
11
+ finder.stubs(:load_config!)
12
+ end
13
+
14
+ it 'should return the first model that matches the trigger' do
15
+ models = %w{ foo1 test_trigger foo2 test_trigger }.map do |trigger|
16
+ stub(:trigger => trigger.to_sym)
17
+ end
18
+ Backup::Model.expects(:all).returns(models)
19
+ Backup::Model.expects(:current=).with(models[1])
20
+
21
+ expect do
22
+ finder.find.should == models[1]
23
+ end.not_to raise_error
24
+ end
25
+
26
+ it 'should raise an error when no models match the trigger' do
27
+ models = %w{ foo1 foo2 foo3 }.map do |trigger|
28
+ stub(:trigger => trigger.to_sym)
29
+ end
30
+ Backup::Model.expects(:all).returns(models)
31
+ Backup::Model.expects(:current=).never
32
+
33
+ expect do
34
+ finder.find
35
+ end.to raise_error(
36
+ Backup::Errors::Finder::MissingTriggerError,
37
+ "Finder::MissingTriggerError: Could not find trigger 'test_trigger' in 'foo'."
38
+ )
39
+ end
40
+
41
+ end # describe '#find'
42
+
43
+ describe '#load_config!' do
44
+ let(:finder) { Backup::Finder.new('foo', 'config_file') }
45
+
46
+ context 'when given a valid config file' do
47
+
48
+ before do
49
+ File.expects(:exist?).with('config_file').returns(true)
50
+ end
51
+
52
+ it 'should load the config file' do
53
+ File.expects(:read).with('config_file').returns(:file_contents)
54
+ Backup.expects(:module_eval).with(:file_contents, 'config_file', 1)
55
+
56
+ finder.send(:load_config!)
57
+ end
58
+
59
+ it 'should reset Model.all' do
60
+ File.stubs(:read)
61
+ Backup.stubs(:module_eval)
62
+ Backup::Model.expects(:all=).with([])
63
+
64
+ finder.send(:load_config!)
65
+ end
66
+
67
+ end # context 'when given a valid config file'
68
+
69
+ context 'when given a config file that does not exist' do
70
+
71
+ before do
72
+ File.expects(:exist?).with('config_file').returns(false)
73
+ end
74
+
75
+ it 'should raise an error' do
76
+ Backup::Model.expects(:all=).never
77
+ File.expects(:read).never
78
+ Backup.expects(:module_eval).never
79
+
80
+ expect do
81
+ finder.send(:load_config!)
82
+ end.to raise_error(
83
+ Backup::Errors::Finder::MissingConfigError,
84
+ "Finder::MissingConfigError: Could not find configuration file: 'config_file'."
85
+ )
86
+ end
87
+
88
+ end # context 'when given a config file that does not exist'
89
+
90
+ end # describe '#find'
91
+ end
data/spec/logger_spec.rb CHANGED
@@ -1,66 +1,287 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.dirname(__FILE__) + '/spec_helper'
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
4
  require 'timecop'
5
5
 
6
6
  describe Backup::Logger do
7
+ let(:logger_time) { Time.now.strftime("%Y/%m/%d %H:%M:%S") }
8
+ let(:logfile_path) { File.join(Backup::LOG_PATH, 'backup.log') }
9
+ let(:logfile_mock) { mock }
10
+ let(:s) { sequence '' }
11
+
7
12
  before do
8
13
  Timecop.freeze(Time.now)
9
14
 
15
+ # stubbed in spec_helper
10
16
  [:message, :error, :warn, :normal, :silent].each do |message_type|
11
- Backup::Logger.unstub(message_type)
17
+ subject.unstub(message_type)
12
18
  end
19
+
20
+ subject.send(:remove_const, :QUIET) rescue nil
21
+ subject.send(:remove_instance_variable, :@messages) rescue nil
22
+ subject.send(:remove_instance_variable, :@has_warnings) rescue nil
13
23
  end
14
24
 
15
- describe 'logging messages to STDOUT and a log file' do
16
- before do
17
- File.expects(:open).with(File.join(Backup::LOG_PATH, 'backup.log'), 'a')
25
+ describe '#message' do
26
+ it 'sends a regular message to the console and log file' do
27
+ subject.expects(:loggify).in_sequence(s).
28
+ with('regular message', :message, :green).
29
+ returns(:green_regular_message)
30
+ subject.expects(:to_console).in_sequence(s).
31
+ with(:green_regular_message)
32
+ subject.expects(:loggify).in_sequence(s).
33
+ with('regular message', :message).
34
+ returns(:uncolored_regular_message)
35
+ subject.expects(:to_file).in_sequence(s).
36
+ with(:uncolored_regular_message)
37
+
38
+ subject.message('regular message')
18
39
  end
40
+ end
19
41
 
20
- context 'when logging regular messages' do
21
- it do
22
- Backup::Logger.expects(:puts).with("[#{ Time.now.strftime("%Y/%m/%d %H:%M:%S") }][\e[32mmessage\e[0m] This has been logged.")
42
+ describe '#error' do
43
+ it 'sends an error message to the console (stderr) and log file' do
44
+ subject.expects(:loggify).in_sequence(s).
45
+ with('error message', :error, :red).
46
+ returns(:red_error_message)
47
+ subject.expects(:to_console).in_sequence(s).
48
+ with(:red_error_message, true)
49
+ subject.expects(:loggify).in_sequence(s).
50
+ with('error message', :error).
51
+ returns(:uncolored_error_message)
52
+ subject.expects(:to_file).in_sequence(s).
53
+ with(:uncolored_error_message)
23
54
 
24
- Backup::Logger.message "This has been logged."
25
- end
55
+ subject.error('error message')
26
56
  end
57
+ end
27
58
 
28
- context 'when logging error messages' do
29
- it do
30
- Backup::Logger.expects(:puts).with("[#{ Time.now.strftime("%Y/%m/%d %H:%M:%S") }][\e[31merror\e[0m] This has been logged.")
59
+ describe '#warn' do
60
+ it 'sends a warning message to the console (stderr) and log file' do
61
+ subject.expects(:loggify).in_sequence(s).
62
+ with('warning message', :warning, :yellow).
63
+ returns(:yellow_warning_message)
64
+ subject.expects(:to_console).in_sequence(s).
65
+ with(:yellow_warning_message, true)
66
+ subject.expects(:loggify).in_sequence(s).
67
+ with('warning message', :warning).
68
+ returns(:uncolored_warning_message)
69
+ subject.expects(:to_file).in_sequence(s).
70
+ with(:uncolored_warning_message)
31
71
 
32
- Backup::Logger.error "This has been logged."
33
- end
72
+ subject.warn('warning message')
73
+ end
74
+
75
+ it 'sets has_warnings? to true' do
76
+ subject.stubs(:to_console)
77
+ subject.stubs(:to_file)
78
+ expect { subject.warn('warning') }.to
79
+ change{ subject.has_warnings? }.from(false).to(true)
80
+ end
81
+ end
82
+
83
+ describe '#normal' do
84
+ it 'sends a normal, unformatted message to the console and log file' do
85
+ subject.expects(:loggify).in_sequence(s).
86
+ with('normal message').
87
+ returns(:unformatted_message)
88
+ subject.expects(:to_console).in_sequence(s).
89
+ with(:unformatted_message)
90
+ subject.expects(:loggify).in_sequence(s).
91
+ with('normal message').
92
+ returns(:unformatted_message)
93
+ subject.expects(:to_file).in_sequence(s).
94
+ with(:unformatted_message)
95
+
96
+ subject.normal('normal message')
97
+ end
98
+ end
99
+
100
+ describe '#silent' do
101
+ it 'sends a silent message to the log file' do
102
+ subject.expects(:to_console).never
103
+ subject.expects(:loggify).in_sequence(s).
104
+ with('silent message', :silent).
105
+ returns(:silent_message)
106
+ subject.expects(:to_file).in_sequence(s).
107
+ with(:silent_message)
108
+
109
+ subject.silent('silent message')
110
+ end
111
+ end
112
+
113
+ describe '#messages' do
114
+
115
+ it 'returns an empty array if no messages have been sent' do
116
+ subject.messages.should == []
117
+ end
118
+
119
+ it 'returns an array of all lines sent to the log file' do
120
+ File.stubs(:open).yields(stub(:puts))
121
+ strings = ['an array', 'of message', 'strings']
122
+ subject.send(:to_file, strings)
123
+ subject.messages.should == strings
124
+ end
125
+
126
+ it 'does not track lines sent to the console' do
127
+ subject.stubs(:puts)
128
+ strings = ['an array', 'of message', 'strings']
129
+ subject.send(:to_console, strings)
130
+ subject.messages.should == []
131
+ end
132
+
133
+ end # describe '#messages'
134
+
135
+ describe '#clear!' do
136
+ it 'should clear the log and reset has_warnings?' do
137
+ subject.messages << 'foo'
138
+ subject.instance_variable_set(:@has_warnings, true)
139
+ subject.messages.count.should == 1
140
+ subject.has_warnings?.should be_true
141
+
142
+ subject.clear!
143
+ subject.messages.should be_empty
144
+ subject.has_warnings?.should be_false
145
+ end
146
+ end # describe '#clear!'
147
+
148
+ describe '#loggify' do
149
+
150
+ it 'returns an array of strings split on newline separators' do
151
+ str_aa = "first line\nsecond line"
152
+ str_ab = "first line\nsecond line\n"
153
+ expected_a = ["[#{logger_time}][msg_type] first line",
154
+ "[#{logger_time}][msg_type] second line"]
155
+
156
+ str_b = 'string with no newline'
157
+ expected_b = ["[#{logger_time}][msg_type] string with no newline"]
158
+
159
+ subject.send(:loggify, str_aa, :msg_type).should == expected_a
160
+ subject.send(:loggify, str_ab, :msg_type).should == expected_a
161
+ subject.send(:loggify, str_b, :msg_type).should == expected_b
34
162
  end
35
163
 
36
- context 'when logging warn messages' do
37
- it do
38
- Backup::Logger.expects(:puts).with("[#{ Time.now.strftime("%Y/%m/%d %H:%M:%S") }][\e[33mwarning\e[0m] This has been logged.")
164
+ it 'formats a string with color if color is given' do
165
+ green_type = ["[#{logger_time}][#{"\e[32mmsg_type\e[0m"}] foo"]
166
+ yellow_type = ["[#{logger_time}][#{"\e[33mmsg_type\e[0m"}] foo"]
167
+ red_type = ["[#{logger_time}][#{"\e[31mmsg_type\e[0m"}] foo"]
39
168
 
40
- Backup::Logger.warn "This has been logged."
169
+ subject.send(:loggify, 'foo', :msg_type, :green ).should == green_type
170
+ subject.send(:loggify, 'foo', :msg_type, :yellow).should == yellow_type
171
+ subject.send(:loggify, 'foo', :msg_type, :red ).should == red_type
172
+ end
173
+
174
+ it 'does not colorize if no color given' do
175
+ no_color = ["[#{logger_time}][msg_type] foo"]
176
+ subject.send(:loggify, 'foo', :msg_type).should == no_color
177
+ end
178
+
179
+ it 'accepts blank lines in the message' do
180
+ str = "first line\n\nthird line"
181
+ expected = ["[#{logger_time}][msg_type] first line",
182
+ "[#{logger_time}][msg_type] ",
183
+ "[#{logger_time}][msg_type] third line"]
184
+
185
+ subject.send(:loggify, str, :msg_type).should == expected
186
+ end
187
+
188
+ it 'accepts an object responding to #to_s for the message' do
189
+ obj = StandardError.new("first line\nsecond line")
190
+ expected = ["[#{logger_time}][msg_type] first line",
191
+ "[#{logger_time}][msg_type] second line"]
192
+
193
+ subject.send(:loggify, obj, :msg_type).should == expected
194
+ end
195
+
196
+ it 'returns an unformatted lines if type is not given' do
197
+ str_a = 'single line'
198
+ str_b = "first line\n\nthird line"
199
+ expected_a = ['single line']
200
+ expected_b = ['first line', '', 'third line']
201
+
202
+ subject.send(:loggify, str_a).should == expected_a
203
+ subject.send(:loggify, str_b).should == expected_b
204
+ end
205
+
206
+ end # describe '#loggify'
207
+
208
+ describe '#to_console' do
209
+
210
+ context 'when +stderr+ is not set (false)' do
211
+ it 'writes an array of strings to stdout' do
212
+ lines = [ 'line one', 'line two', 'line three']
213
+ lines.each {|line| subject.expects(:puts).with(line).in_sequence(s) }
214
+ subject.send(:to_console, lines)
41
215
  end
42
216
  end
43
217
 
44
- context 'when logging silent messages' do
45
- it do
46
- Backup::Logger.expects(:puts).never
218
+ context 'when +stderr+ is set (true)' do
219
+ it 'writes an array of strings to stdout' do
220
+ lines = [ 'line one', 'line two', 'line three']
221
+ lines.each {|line| Kernel.expects(:warn).with(line).in_sequence(s) }
222
+ subject.send(:to_console, lines, true)
223
+ end
224
+ end
47
225
 
48
- Backup::Logger.silent "This has been logged."
226
+ it 'returns nil if quiet? is true' do
227
+ subject.send(:const_set, :QUIET, true)
228
+ subject.expects(:puts).never
229
+ subject.send(:to_console, 'a string')
230
+ end
231
+
232
+ end # describe '#to_console'
233
+
234
+ describe '#to_file' do
235
+
236
+ it 'writes an array of strings to the log file' do
237
+ lines = ['line one', 'line two', 'line three']
238
+ File.stubs(:open).yields(logfile_mock)
239
+ lines.each {|line| logfile_mock.expects(:puts).with(line).in_sequence(s) }
240
+ subject.send(:to_file, lines)
241
+ end
242
+
243
+ it 'appends each line written to #messages' do
244
+ lines = ['line one', 'line two', 'line three']
245
+ File.stubs(:open)
246
+ a_mock = mock
247
+ subject.expects(:messages).returns(a_mock)
248
+ a_mock.expects(:push).with('line one', 'line two', 'line three')
249
+ subject.send(:to_file, lines)
250
+ end
251
+
252
+ it 'only opens the log file once to append multiple lines' do
253
+ lines = ['line one', 'line two', 'line three']
254
+ File.expects(:open).once.with(logfile_path, 'a').yields(logfile_mock)
255
+ logfile_mock.expects(:puts).times(3)
256
+ subject.send(:to_file, lines)
257
+ end
258
+
259
+ end # describe '#to_file'
260
+
261
+ describe 'color methods' do
262
+
263
+ it 'color methods send strings to #colorize with color codes' do
264
+ colors = [ [:green, 32], [:yellow, 33], [:red, 31] ]
265
+ colors.each do |color, code|
266
+ subject.expects(:colorize).with('foo', code).in_sequence(s)
49
267
  end
268
+ colors.each {|color, code| subject.send(color, 'foo') }
50
269
  end
51
- end
52
270
 
53
- describe 'logging messages to log file and not STDOUT' do
54
- it do
55
- Backup::Logger.send(:const_set, :QUIET, true)
271
+ it '#colorize adds the code to the string' do
272
+ [32, 33, 31].each do |code|
273
+ subject.send(:colorize, 'foo', code).
274
+ should == "\e[#{code}mfoo\e[0m"
275
+ end
276
+ end
56
277
 
57
- Backup::Logger.expects(:puts).never
58
- File.expects(:open).times(4).with(File.join(Backup::LOG_PATH, 'backup.log'), 'a')
278
+ end # color methods
59
279
 
60
- Backup::Logger.message "This has been logged."
61
- Backup::Logger.error "This has been logged."
62
- Backup::Logger.warn "This has been logged."
63
- Backup::Logger.normal "This has been logged."
280
+ describe '#quiet?' do
281
+ it 'reports if the QUIET constant has been set' do
282
+ expect { subject.send(:const_set, :QUIET, true) }.to
283
+ change{ subject.send(:quiet?) }.from(false).to(true)
64
284
  end
65
285
  end
286
+
66
287
  end
data/spec/model_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require File.dirname(__FILE__) + '/spec_helper'
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
4
 
5
5
  describe Backup::Model do
6
6
 
@@ -13,7 +13,7 @@ describe Backup::Model do
13
13
  def initialize(&block); end
14
14
  end
15
15
  class Backup::Storage::TestStorage
16
- def initialize(&block); end
16
+ def initialize(storage_id = nil, &block); end
17
17
  end
18
18
  class Backup::Compressor::TestGzip
19
19
  def initialize(&block); end
@@ -121,6 +121,15 @@ describe Backup::Model do
121
121
 
122
122
  model.storages.count.should == 2
123
123
  end
124
+
125
+ it 'should accept an optional storage_id parameter' do
126
+ model = Backup::Model.new('mysql-s3', 'MySQL S3 Backup for MyApp') do
127
+ store_with('TestStorage', 'test storage_id')
128
+ end
129
+
130
+ model.storages.count.should == 1
131
+ end
132
+
124
133
  end
125
134
 
126
135
  describe 'archives' do
@@ -200,37 +209,133 @@ describe Backup::Model do
200
209
  end
201
210
 
202
211
  describe '#package!' do
212
+ let(:packager) { Backup::Packager.new(model) }
213
+
203
214
  before do
204
215
  [:utility, :run].each { |method| model.stubs(method) }
205
216
  end
206
217
 
207
218
  it 'should package the folder' do
208
- model.expects(:utility).with(:tar).returns(:tar)
209
- model.expects(:run).with(%|tar -c -f '#{ File.join( Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar" ) }' -C '#{ Backup::TMP_PATH }' '#{ Backup::TRIGGER }'|)
210
- model.send(:package!)
211
- end
212
-
213
- it 'should log' do
214
- Backup::Logger.expects(:message).with("Backup started packaging everything to a single archive file.")
215
- model.send(:package!)
219
+ packager.expects(:utility).with(:tar).returns(:tar)
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!
216
223
  end
217
224
  end
218
225
 
219
226
  describe '#clean!' do
227
+ let(:cleaner) { Backup::Cleaner.new(model) }
228
+
220
229
  before do
221
230
  [:utility, :run, :rm].each { |method| model.stubs(method) }
222
231
  end
223
232
 
224
- it 'should remove the temporary files and folders that were created' do
225
- model.expects(:utility).with(:rm).returns(:rm)
226
- model.expects(:run).with("rm -rf '#{ File.join(Backup::TMP_PATH, Backup::TRIGGER) }' '#{ File.join(Backup::TMP_PATH, "#{ Backup::TIME }.#{ Backup::TRIGGER }.tar") }'")
227
- model.send(:clean!)
233
+ context 'when the backup archive is not chunked' do
234
+ it 'should remove the temporary files and folders that were created' do
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!
240
+ end
241
+ end
242
+
243
+ context 'when the backup archive is chunked' do
244
+ it 'should remove the temporary files and folders that were created' do
245
+ cleaner.expects(:utility).with(:rm).returns(:rm)
246
+ Backup::Model.chunk_suffixes = ["aa", "ab", "ac"]
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
228
254
  end
229
255
 
230
256
  it do
231
- Backup::Logger.expects(:message).with("Backup started cleaning up the temporary files.")
257
+ Backup::Logger.expects(:message).with("Backup::Cleaner started cleaning up the temporary files.")
232
258
  model.send(:clean!)
233
259
  end
234
260
  end
235
261
 
262
+ describe '#split_into_chunks_of' do
263
+ it do
264
+ model.should respond_to(:split_into_chunks_of)
265
+ end
266
+
267
+ it do
268
+ model.split_into_chunks_of(500)
269
+ model.chunk_size.should == 500
270
+ end
271
+
272
+ it do
273
+ model.chunk_size.should == nil
274
+ end
275
+ end
276
+
277
+ describe '#perform!' do
278
+
279
+ # for the purposes of testing the error handling, we're just going to
280
+ # stub the first thing this method calls and raise an error
281
+ describe 'when errors occur' do
282
+ let(:model) { Backup::Model.new('foo', 'foo') {} }
283
+
284
+ before do
285
+ # method ensures that #clean! is always run before exiting
286
+ model.expects(:clean!)
287
+ end
288
+
289
+ it 'logs, notifies and continues if a StandardError is rescued' do
290
+ model.stubs(:databases).raises(StandardError, 'non-fatal error')
291
+
292
+ Backup::Logger.expects(:error).twice
293
+ Backup::Logger.expects(:message).once
294
+
295
+ Backup::Errors::ModelError.expects(:wrap).with do |err, msg|
296
+ err.message.should == 'non-fatal error'
297
+ msg.should match(/Backup for foo \(foo\) Failed!/)
298
+ end
299
+
300
+ Backup::Errors::ModelError.expects(:new).with do |msg|
301
+ msg.should match(/Backup will now attempt to continue/)
302
+ end
303
+
304
+ # notifiers called, but any Exception is ignored
305
+ notifier = mock
306
+ notifier.expects(:perform!).raises(Exception)
307
+ model.expects(:notifiers).returns([notifier])
308
+
309
+ # returns to allow next trigger to run
310
+ expect { model.perform! }.not_to raise_error
311
+ end
312
+
313
+ it 'logs, notifies and exits if an Exception is rescued' do
314
+ model.stubs(:databases).raises(Exception, 'fatal error')
315
+
316
+ Backup::Logger.expects(:error).times(3)
317
+ Backup::Logger.expects(:message).never
318
+
319
+ Backup::Errors::ModelError.expects(:wrap).with do |err, msg|
320
+ err.message.should == 'fatal error'
321
+ msg.should match(/Backup for foo \(foo\) Failed!/)
322
+ end
323
+
324
+ Backup::Errors::ModelError.expects(:new).with do |msg|
325
+ msg.should match(/Backup will now exit/)
326
+ end
327
+
328
+ expect do
329
+ # notifiers called, but any Exception is ignored
330
+ notifier = mock
331
+ notifier.expects(:perform!).raises(Exception)
332
+ model.expects(:notifiers).returns([notifier])
333
+ end.not_to raise_error
334
+
335
+ expect { model.perform! }.to raise_error(SystemExit)
336
+ end
337
+
338
+ end # context 'when errors occur'
339
+
340
+ end # describe '#perform!'
236
341
  end