backup_checksum 3.0.23

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of backup_checksum might be problematic. Click here for more details.

Files changed (244) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +28 -0
  4. data/Gemfile.lock +130 -0
  5. data/Guardfile +21 -0
  6. data/LICENSE.md +24 -0
  7. data/README.md +476 -0
  8. data/backup_checksum.gemspec +32 -0
  9. data/bin/backup +11 -0
  10. data/lib/backup.rb +217 -0
  11. data/lib/backup/archive.rb +117 -0
  12. data/lib/backup/binder.rb +22 -0
  13. data/lib/backup/checksum/base.rb +44 -0
  14. data/lib/backup/checksum/shasum.rb +16 -0
  15. data/lib/backup/cleaner.rb +121 -0
  16. data/lib/backup/cli/helpers.rb +88 -0
  17. data/lib/backup/cli/utility.rb +247 -0
  18. data/lib/backup/compressor/base.rb +29 -0
  19. data/lib/backup/compressor/bzip2.rb +50 -0
  20. data/lib/backup/compressor/gzip.rb +47 -0
  21. data/lib/backup/compressor/lzma.rb +50 -0
  22. data/lib/backup/compressor/pbzip2.rb +56 -0
  23. data/lib/backup/config.rb +173 -0
  24. data/lib/backup/configuration/base.rb +15 -0
  25. data/lib/backup/configuration/checksum/base.rb +9 -0
  26. data/lib/backup/configuration/checksum/shasum.rb +9 -0
  27. data/lib/backup/configuration/compressor/base.rb +9 -0
  28. data/lib/backup/configuration/compressor/bzip2.rb +23 -0
  29. data/lib/backup/configuration/compressor/gzip.rb +23 -0
  30. data/lib/backup/configuration/compressor/lzma.rb +23 -0
  31. data/lib/backup/configuration/compressor/pbzip2.rb +28 -0
  32. data/lib/backup/configuration/database/base.rb +19 -0
  33. data/lib/backup/configuration/database/mongodb.rb +49 -0
  34. data/lib/backup/configuration/database/mysql.rb +42 -0
  35. data/lib/backup/configuration/database/postgresql.rb +41 -0
  36. data/lib/backup/configuration/database/redis.rb +39 -0
  37. data/lib/backup/configuration/database/riak.rb +29 -0
  38. data/lib/backup/configuration/encryptor/base.rb +9 -0
  39. data/lib/backup/configuration/encryptor/gpg.rb +17 -0
  40. data/lib/backup/configuration/encryptor/open_ssl.rb +32 -0
  41. data/lib/backup/configuration/helpers.rb +52 -0
  42. data/lib/backup/configuration/notifier/base.rb +28 -0
  43. data/lib/backup/configuration/notifier/campfire.rb +25 -0
  44. data/lib/backup/configuration/notifier/hipchat.rb +41 -0
  45. data/lib/backup/configuration/notifier/mail.rb +112 -0
  46. data/lib/backup/configuration/notifier/presently.rb +25 -0
  47. data/lib/backup/configuration/notifier/prowl.rb +23 -0
  48. data/lib/backup/configuration/notifier/twitter.rb +21 -0
  49. data/lib/backup/configuration/storage/base.rb +18 -0
  50. data/lib/backup/configuration/storage/cloudfiles.rb +25 -0
  51. data/lib/backup/configuration/storage/dropbox.rb +58 -0
  52. data/lib/backup/configuration/storage/ftp.rb +29 -0
  53. data/lib/backup/configuration/storage/local.rb +17 -0
  54. data/lib/backup/configuration/storage/ninefold.rb +20 -0
  55. data/lib/backup/configuration/storage/rsync.rb +29 -0
  56. data/lib/backup/configuration/storage/s3.rb +25 -0
  57. data/lib/backup/configuration/storage/scp.rb +25 -0
  58. data/lib/backup/configuration/storage/sftp.rb +25 -0
  59. data/lib/backup/configuration/syncer/base.rb +10 -0
  60. data/lib/backup/configuration/syncer/cloud.rb +23 -0
  61. data/lib/backup/configuration/syncer/cloud_files.rb +30 -0
  62. data/lib/backup/configuration/syncer/rsync/base.rb +28 -0
  63. data/lib/backup/configuration/syncer/rsync/local.rb +11 -0
  64. data/lib/backup/configuration/syncer/rsync/pull.rb +11 -0
  65. data/lib/backup/configuration/syncer/rsync/push.rb +31 -0
  66. data/lib/backup/configuration/syncer/s3.rb +23 -0
  67. data/lib/backup/database/base.rb +59 -0
  68. data/lib/backup/database/mongodb.rb +232 -0
  69. data/lib/backup/database/mysql.rb +163 -0
  70. data/lib/backup/database/postgresql.rb +146 -0
  71. data/lib/backup/database/redis.rb +139 -0
  72. data/lib/backup/database/riak.rb +69 -0
  73. data/lib/backup/dependency.rb +114 -0
  74. data/lib/backup/encryptor/base.rb +29 -0
  75. data/lib/backup/encryptor/gpg.rb +80 -0
  76. data/lib/backup/encryptor/open_ssl.rb +72 -0
  77. data/lib/backup/errors.rb +124 -0
  78. data/lib/backup/logger.rb +152 -0
  79. data/lib/backup/model.rb +386 -0
  80. data/lib/backup/notifier/base.rb +81 -0
  81. data/lib/backup/notifier/campfire.rb +168 -0
  82. data/lib/backup/notifier/hipchat.rb +99 -0
  83. data/lib/backup/notifier/mail.rb +206 -0
  84. data/lib/backup/notifier/presently.rb +88 -0
  85. data/lib/backup/notifier/prowl.rb +65 -0
  86. data/lib/backup/notifier/twitter.rb +70 -0
  87. data/lib/backup/package.rb +51 -0
  88. data/lib/backup/packager.rb +108 -0
  89. data/lib/backup/pipeline.rb +107 -0
  90. data/lib/backup/splitter.rb +75 -0
  91. data/lib/backup/storage/base.rb +119 -0
  92. data/lib/backup/storage/cloudfiles.rb +87 -0
  93. data/lib/backup/storage/cycler.rb +117 -0
  94. data/lib/backup/storage/dropbox.rb +181 -0
  95. data/lib/backup/storage/ftp.rb +119 -0
  96. data/lib/backup/storage/local.rb +82 -0
  97. data/lib/backup/storage/ninefold.rb +116 -0
  98. data/lib/backup/storage/rsync.rb +149 -0
  99. data/lib/backup/storage/s3.rb +94 -0
  100. data/lib/backup/storage/scp.rb +99 -0
  101. data/lib/backup/storage/sftp.rb +108 -0
  102. data/lib/backup/syncer/base.rb +42 -0
  103. data/lib/backup/syncer/cloud.rb +190 -0
  104. data/lib/backup/syncer/cloud_files.rb +56 -0
  105. data/lib/backup/syncer/rsync/base.rb +52 -0
  106. data/lib/backup/syncer/rsync/local.rb +53 -0
  107. data/lib/backup/syncer/rsync/pull.rb +38 -0
  108. data/lib/backup/syncer/rsync/push.rb +113 -0
  109. data/lib/backup/syncer/s3.rb +47 -0
  110. data/lib/backup/template.rb +46 -0
  111. data/lib/backup/version.rb +43 -0
  112. data/spec/archive_spec.rb +335 -0
  113. data/spec/cleaner_spec.rb +304 -0
  114. data/spec/cli/helpers_spec.rb +176 -0
  115. data/spec/cli/utility_spec.rb +363 -0
  116. data/spec/compressor/base_spec.rb +31 -0
  117. data/spec/compressor/bzip2_spec.rb +83 -0
  118. data/spec/compressor/gzip_spec.rb +83 -0
  119. data/spec/compressor/lzma_spec.rb +83 -0
  120. data/spec/compressor/pbzip2_spec.rb +124 -0
  121. data/spec/config_spec.rb +321 -0
  122. data/spec/configuration/base_spec.rb +35 -0
  123. data/spec/configuration/compressor/bzip2_spec.rb +29 -0
  124. data/spec/configuration/compressor/gzip_spec.rb +29 -0
  125. data/spec/configuration/compressor/lzma_spec.rb +29 -0
  126. data/spec/configuration/compressor/pbzip2_spec.rb +32 -0
  127. data/spec/configuration/database/base_spec.rb +17 -0
  128. data/spec/configuration/database/mongodb_spec.rb +56 -0
  129. data/spec/configuration/database/mysql_spec.rb +53 -0
  130. data/spec/configuration/database/postgresql_spec.rb +53 -0
  131. data/spec/configuration/database/redis_spec.rb +50 -0
  132. data/spec/configuration/database/riak_spec.rb +35 -0
  133. data/spec/configuration/encryptor/gpg_spec.rb +26 -0
  134. data/spec/configuration/encryptor/open_ssl_spec.rb +35 -0
  135. data/spec/configuration/notifier/base_spec.rb +32 -0
  136. data/spec/configuration/notifier/campfire_spec.rb +32 -0
  137. data/spec/configuration/notifier/hipchat_spec.rb +44 -0
  138. data/spec/configuration/notifier/mail_spec.rb +71 -0
  139. data/spec/configuration/notifier/presently_spec.rb +35 -0
  140. data/spec/configuration/notifier/prowl_spec.rb +29 -0
  141. data/spec/configuration/notifier/twitter_spec.rb +35 -0
  142. data/spec/configuration/storage/cloudfiles_spec.rb +41 -0
  143. data/spec/configuration/storage/dropbox_spec.rb +38 -0
  144. data/spec/configuration/storage/ftp_spec.rb +44 -0
  145. data/spec/configuration/storage/local_spec.rb +29 -0
  146. data/spec/configuration/storage/ninefold_spec.rb +32 -0
  147. data/spec/configuration/storage/rsync_spec.rb +41 -0
  148. data/spec/configuration/storage/s3_spec.rb +38 -0
  149. data/spec/configuration/storage/scp_spec.rb +41 -0
  150. data/spec/configuration/storage/sftp_spec.rb +41 -0
  151. data/spec/configuration/syncer/cloud_files_spec.rb +44 -0
  152. data/spec/configuration/syncer/rsync/base_spec.rb +33 -0
  153. data/spec/configuration/syncer/rsync/local_spec.rb +10 -0
  154. data/spec/configuration/syncer/rsync/pull_spec.rb +10 -0
  155. data/spec/configuration/syncer/rsync/push_spec.rb +43 -0
  156. data/spec/configuration/syncer/s3_spec.rb +38 -0
  157. data/spec/database/base_spec.rb +54 -0
  158. data/spec/database/mongodb_spec.rb +428 -0
  159. data/spec/database/mysql_spec.rb +335 -0
  160. data/spec/database/postgresql_spec.rb +278 -0
  161. data/spec/database/redis_spec.rb +260 -0
  162. data/spec/database/riak_spec.rb +108 -0
  163. data/spec/dependency_spec.rb +49 -0
  164. data/spec/encryptor/base_spec.rb +30 -0
  165. data/spec/encryptor/gpg_spec.rb +134 -0
  166. data/spec/encryptor/open_ssl_spec.rb +129 -0
  167. data/spec/errors_spec.rb +306 -0
  168. data/spec/logger_spec.rb +363 -0
  169. data/spec/model_spec.rb +649 -0
  170. data/spec/notifier/base_spec.rb +89 -0
  171. data/spec/notifier/campfire_spec.rb +199 -0
  172. data/spec/notifier/hipchat_spec.rb +188 -0
  173. data/spec/notifier/mail_spec.rb +280 -0
  174. data/spec/notifier/presently_spec.rb +181 -0
  175. data/spec/notifier/prowl_spec.rb +117 -0
  176. data/spec/notifier/twitter_spec.rb +132 -0
  177. data/spec/package_spec.rb +61 -0
  178. data/spec/packager_spec.rb +225 -0
  179. data/spec/pipeline_spec.rb +257 -0
  180. data/spec/spec_helper.rb +59 -0
  181. data/spec/splitter_spec.rb +120 -0
  182. data/spec/storage/base_spec.rb +160 -0
  183. data/spec/storage/cloudfiles_spec.rb +230 -0
  184. data/spec/storage/cycler_spec.rb +239 -0
  185. data/spec/storage/dropbox_spec.rb +370 -0
  186. data/spec/storage/ftp_spec.rb +247 -0
  187. data/spec/storage/local_spec.rb +235 -0
  188. data/spec/storage/ninefold_spec.rb +319 -0
  189. data/spec/storage/rsync_spec.rb +345 -0
  190. data/spec/storage/s3_spec.rb +221 -0
  191. data/spec/storage/scp_spec.rb +209 -0
  192. data/spec/storage/sftp_spec.rb +220 -0
  193. data/spec/syncer/base_spec.rb +22 -0
  194. data/spec/syncer/cloud_files_spec.rb +192 -0
  195. data/spec/syncer/rsync/base_spec.rb +118 -0
  196. data/spec/syncer/rsync/local_spec.rb +121 -0
  197. data/spec/syncer/rsync/pull_spec.rb +90 -0
  198. data/spec/syncer/rsync/push_spec.rb +327 -0
  199. data/spec/syncer/s3_spec.rb +192 -0
  200. data/spec/version_spec.rb +21 -0
  201. data/templates/cli/utility/archive +25 -0
  202. data/templates/cli/utility/compressor/bzip2 +7 -0
  203. data/templates/cli/utility/compressor/gzip +7 -0
  204. data/templates/cli/utility/compressor/lzma +7 -0
  205. data/templates/cli/utility/compressor/pbzip2 +7 -0
  206. data/templates/cli/utility/config +31 -0
  207. data/templates/cli/utility/database/mongodb +18 -0
  208. data/templates/cli/utility/database/mysql +21 -0
  209. data/templates/cli/utility/database/postgresql +17 -0
  210. data/templates/cli/utility/database/redis +16 -0
  211. data/templates/cli/utility/database/riak +11 -0
  212. data/templates/cli/utility/encryptor/gpg +12 -0
  213. data/templates/cli/utility/encryptor/openssl +9 -0
  214. data/templates/cli/utility/model.erb +23 -0
  215. data/templates/cli/utility/notifier/campfire +12 -0
  216. data/templates/cli/utility/notifier/hipchat +15 -0
  217. data/templates/cli/utility/notifier/mail +22 -0
  218. data/templates/cli/utility/notifier/presently +13 -0
  219. data/templates/cli/utility/notifier/prowl +11 -0
  220. data/templates/cli/utility/notifier/twitter +13 -0
  221. data/templates/cli/utility/splitter +7 -0
  222. data/templates/cli/utility/storage/cloud_files +22 -0
  223. data/templates/cli/utility/storage/dropbox +20 -0
  224. data/templates/cli/utility/storage/ftp +12 -0
  225. data/templates/cli/utility/storage/local +7 -0
  226. data/templates/cli/utility/storage/ninefold +9 -0
  227. data/templates/cli/utility/storage/rsync +11 -0
  228. data/templates/cli/utility/storage/s3 +19 -0
  229. data/templates/cli/utility/storage/scp +11 -0
  230. data/templates/cli/utility/storage/sftp +11 -0
  231. data/templates/cli/utility/syncer/cloud_files +48 -0
  232. data/templates/cli/utility/syncer/rsync_local +12 -0
  233. data/templates/cli/utility/syncer/rsync_pull +17 -0
  234. data/templates/cli/utility/syncer/rsync_push +17 -0
  235. data/templates/cli/utility/syncer/s3 +45 -0
  236. data/templates/general/links +11 -0
  237. data/templates/general/version.erb +2 -0
  238. data/templates/notifier/mail/failure.erb +9 -0
  239. data/templates/notifier/mail/success.erb +7 -0
  240. data/templates/notifier/mail/warning.erb +9 -0
  241. data/templates/storage/dropbox/authorization_url.erb +6 -0
  242. data/templates/storage/dropbox/authorized.erb +4 -0
  243. data/templates/storage/dropbox/cache_file_written.erb +10 -0
  244. metadata +311 -0
@@ -0,0 +1,649 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../spec_helper.rb', __FILE__)
4
+
5
+ describe 'Backup::Model' do
6
+ let(:model) { Backup::Model.new(:test_trigger, 'test label') }
7
+ let(:s) { sequence '' }
8
+
9
+ before do
10
+ Backup::Model.all.clear
11
+ end
12
+
13
+ describe '.all' do
14
+ it 'should be an empty array by default' do
15
+ Backup::Model.all.should == []
16
+ end
17
+ end
18
+
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
25
+
26
+ describe '.find' do
27
+ it 'should return the first matching model' do
28
+ Backup::Model.find('trigger_b').label.should == 'label1'
29
+ end
30
+
31
+ it 'should accept symbols' do
32
+ Backup::Model.find(:trigger_b).label.should == 'label1'
33
+ end
34
+
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
44
+
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
50
+
51
+ it 'should return an empty array if no matches are found' do
52
+ Backup::Model.find_matching('foo*').should == []
53
+ end
54
+ end
55
+
56
+ end # describe 'finder methods'
57
+
58
+ describe '#initialize' do
59
+
60
+ it 'should convert trigger to a string' do
61
+ Backup::Model.new(:foo, :bar).trigger.should == 'foo'
62
+ end
63
+
64
+ it 'should convert label to a string' do
65
+ Backup::Model.new(:foo, :bar).label.should == 'bar'
66
+ end
67
+
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
72
+ end
73
+
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)
78
+ end
79
+ caught.trigger.should == 'gotcha'
80
+ end
81
+
82
+ it 'should add itself to Model.all' do
83
+ Backup::Model.all.should == [model]
84
+ end
85
+
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
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
119
+
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)
128
+ end
129
+
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
144
+ end
145
+ end
146
+
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
159
+ end
160
+
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
167
+ end
168
+
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
183
+ end
184
+
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
191
+ end
192
+
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
204
+
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
210
+ end
211
+
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
217
+ end
218
+
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
231
+ end
232
+
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
239
+ end
240
+
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
248
+ end
249
+
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
256
+ end
257
+
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
265
+ end
266
+
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
273
+ end
274
+
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
283
+ end
284
+
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
293
+ end
294
+
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)
303
+
304
+ model.prepare!
305
+ end
306
+ end
307
+
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
334
+ end
335
+
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)
341
+ end
342
+
343
+ context 'when databases are configured' do
344
+ before do
345
+ model.instance_variable_set(:@databases, [true])
346
+ end
347
+
348
+ it 'should perform all procedures' do
349
+ model.expects(:log!).in_sequence(s).with(:started)
350
+
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)
357
+
358
+ syncer_a.expects(:perform!).in_sequence(s)
359
+ syncer_b.expects(:perform!).in_sequence(s)
360
+
361
+ notifier_a.expects(:perform!).in_sequence(s)
362
+ notifier_b.expects(:perform!).in_sequence(s)
363
+
364
+ model.expects(:log!).in_sequence(s).with(:finished)
365
+
366
+ model.perform!
367
+ end
368
+ end
369
+
370
+ context 'when archives are configured' do
371
+ before do
372
+ model.instance_variable_set(:@archives, [true])
373
+ end
374
+
375
+ it 'should perform all procedures' do
376
+ model.expects(:log!).in_sequence(s).with(:started)
377
+
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)
384
+
385
+ syncer_a.expects(:perform!).in_sequence(s)
386
+ syncer_b.expects(:perform!).in_sequence(s)
387
+
388
+ notifier_a.expects(:perform!).in_sequence(s)
389
+ notifier_b.expects(:perform!).in_sequence(s)
390
+
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'
398
+
399
+ # for the purposes of testing the error handling, we're just going to
400
+ # stub the first thing this method calls and raise an error
401
+ context 'when errors occur' do
402
+ let(:error_a) { mock }
403
+ let(:error_b) { mock }
404
+ let(:notifier) { mock }
405
+
406
+ before do
407
+ error_a.stubs(:backtrace).returns(['many', 'backtrace', 'lines'])
408
+ end
409
+
410
+ it 'logs, notifies and continues if a StandardError is rescued' do
411
+ Time.stubs(:now).raises(StandardError, 'non-fatal error')
412
+
413
+ Backup::Errors::ModelError.expects(:wrap).in_sequence(s).with do |err, msg|
414
+ err.message.should == 'non-fatal error'
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)
423
+
424
+ Backup::Errors::ModelError.expects(:new).in_sequence(s).with do |msg|
425
+ msg.should match(/Backup will now attempt to continue/)
426
+ end.returns(error_b)
427
+ Backup::Logger.expects(:message).in_sequence(s).with(error_b)
428
+
429
+ # notifiers called, but any Exception is ignored
430
+ notifier.expects(:perform!).with(true).raises(Exception)
431
+ model.expects(:notifiers).returns([notifier])
432
+
433
+ # returns to allow next trigger to run
434
+ expect { model.perform! }.not_to raise_error
435
+ end
436
+
437
+ it 'logs, notifies and exits if an Exception is rescued' do
438
+ Time.stubs(:now).raises(Exception, 'fatal error')
439
+
440
+ Backup::Errors::ModelError.expects(:wrap).in_sequence(s).with do |err, msg|
441
+ err.message.should == 'fatal error'
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)
450
+
451
+ Backup::Errors::ModelError.expects(:new).in_sequence(s).with do |msg|
452
+ msg.should match(/Backup will now exit/)
453
+ end.returns(error_b)
454
+ Backup::Logger.expects(:error).in_sequence(s).with(error_b)
455
+
456
+ expect do
457
+ # notifiers called, but any Exception is ignored
458
+ notifier = mock
459
+ notifier.expects(:perform!).with(true).raises(Exception)
460
+ model.expects(:notifiers).returns([notifier])
461
+ end.not_to raise_error
462
+
463
+ expect do
464
+ model.perform!
465
+ end.to raise_error(SystemExit) {|exit| exit.status.should be(1) }
466
+ end
467
+
468
+ end # context 'when errors occur'
469
+
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
+
649
+ end