PageRenderTime 47ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/database/mongodb_spec.rb

https://github.com/xamenrax/backup
Ruby | 382 lines | 315 code | 66 blank | 1 comment | 0 complexity | 0a6d35af18c89062635300210a083c2d MD5 | raw file
  1. # encoding: utf-8
  2. require File.expand_path('../../spec_helper.rb', __FILE__)
  3. module Backup
  4. describe Database::MongoDB do
  5. let(:model) { Model.new(:test_trigger, 'test label') }
  6. let(:db) { Database::MongoDB.new(model) }
  7. let(:s) { sequence '' }
  8. before do
  9. Database::MongoDB.any_instance.stubs(:utility).
  10. with(:mongodump).returns('mongodump')
  11. Database::MongoDB.any_instance.stubs(:utility).
  12. with(:mongo).returns('mongo')
  13. Database::MongoDB.any_instance.stubs(:utility).
  14. with(:cat).returns('cat')
  15. Database::MongoDB.any_instance.stubs(:utility).
  16. with(:tar).returns('tar')
  17. end
  18. it_behaves_like 'a class that includes Config::Helpers'
  19. it_behaves_like 'a subclass of Database::Base'
  20. describe '#initialize' do
  21. it 'provides default values' do
  22. expect( db.database_id ).to be_nil
  23. expect( db.name ).to be_nil
  24. expect( db.username ).to be_nil
  25. expect( db.password ).to be_nil
  26. expect( db.host ).to be_nil
  27. expect( db.port ).to be_nil
  28. expect( db.ipv6 ).to be_nil
  29. expect( db.only_collections ).to be_nil
  30. expect( db.additional_options ).to be_nil
  31. expect( db.lock ).to be_nil
  32. expect( db.oplog ).to be_nil
  33. end
  34. it 'configures the database' do
  35. db = Database::MongoDB.new(model, :my_id) do |mongodb|
  36. mongodb.name = 'my_name'
  37. mongodb.username = 'my_username'
  38. mongodb.password = 'my_password'
  39. mongodb.host = 'my_host'
  40. mongodb.port = 'my_port'
  41. mongodb.ipv6 = 'my_ipv6'
  42. mongodb.only_collections = 'my_only_collections'
  43. mongodb.additional_options = 'my_additional_options'
  44. mongodb.lock = 'my_lock'
  45. mongodb.oplog = 'my_oplog'
  46. end
  47. expect( db.database_id ).to eq 'my_id'
  48. expect( db.name ).to eq 'my_name'
  49. expect( db.username ).to eq 'my_username'
  50. expect( db.password ).to eq 'my_password'
  51. expect( db.host ).to eq 'my_host'
  52. expect( db.port ).to eq 'my_port'
  53. expect( db.ipv6 ).to eq 'my_ipv6'
  54. expect( db.only_collections ).to eq 'my_only_collections'
  55. expect( db.additional_options ).to eq 'my_additional_options'
  56. expect( db.lock ).to eq 'my_lock'
  57. expect( db.oplog ).to eq 'my_oplog'
  58. end
  59. end # describe '#initialize'
  60. describe '#perform!' do
  61. before do
  62. db.expects(:log!).in_sequence(s).with(:started)
  63. db.expects(:prepare!).in_sequence(s)
  64. end
  65. context 'with #lock set to false' do
  66. it 'does not lock the database' do
  67. db.expects(:lock_database).never
  68. db.expects(:unlock_database).never
  69. db.expects(:dump!).in_sequence(s)
  70. db.expects(:package!).in_sequence(s)
  71. db.perform!
  72. end
  73. end
  74. context 'with #lock set to true' do
  75. before { db.lock = true }
  76. it 'locks the database' do
  77. db.expects(:lock_database).in_sequence(s)
  78. db.expects(:dump!).in_sequence(s)
  79. db.expects(:package!).in_sequence(s)
  80. db.expects(:unlock_database).in_sequence(s)
  81. db.perform!
  82. end
  83. it 'ensures the database is unlocked' do
  84. db.expects(:lock_database).in_sequence(s)
  85. db.expects(:dump!).in_sequence(s)
  86. db.expects(:package!).in_sequence(s).raises('an error')
  87. db.expects(:unlock_database).in_sequence(s)
  88. expect do
  89. db.perform!
  90. end.to raise_error 'an error'
  91. end
  92. end
  93. end # describe '#perform!'
  94. describe '#dump!' do
  95. before do
  96. db.stubs(:mongodump).returns('mongodump_command')
  97. db.stubs(:dump_path).returns('/tmp/trigger/databases')
  98. FileUtils.expects(:mkdir_p).in_sequence(s).
  99. with('/tmp/trigger/databases/MongoDB')
  100. end
  101. context 'when #only_collections are not specified' do
  102. it 'runs mongodump once' do
  103. db.expects(:run).in_sequence(s).with('mongodump_command')
  104. db.send(:dump!)
  105. end
  106. end
  107. context 'when #only_collections are specified' do
  108. it 'runs mongodump for each collection' do
  109. db.only_collections = ['collection_a', 'collection_b']
  110. db.expects(:run).in_sequence(s).with(
  111. "mongodump_command --collection='collection_a'"
  112. )
  113. db.expects(:run).in_sequence(s).with(
  114. "mongodump_command --collection='collection_b'"
  115. )
  116. db.send(:dump!)
  117. end
  118. it 'allows only_collections to be a single string' do
  119. db.only_collections = 'collection_a'
  120. db.expects(:run).in_sequence(s).with(
  121. "mongodump_command --collection='collection_a'"
  122. )
  123. db.send(:dump!)
  124. end
  125. end
  126. end # describe '#dump!'
  127. describe '#package!' do
  128. let(:pipeline) { mock }
  129. let(:compressor) { mock }
  130. before do
  131. db.stubs(:dump_path).returns('/tmp/trigger/databases')
  132. end
  133. context 'without a compressor' do
  134. it 'packages the dump without compression' do
  135. Pipeline.expects(:new).in_sequence(s).returns(pipeline)
  136. pipeline.expects(:<<).in_sequence(s).with(
  137. "tar -cf - -C '/tmp/trigger/databases' 'MongoDB'"
  138. )
  139. pipeline.expects(:<<).in_sequence(s).with(
  140. "cat > '/tmp/trigger/databases/MongoDB.tar'"
  141. )
  142. pipeline.expects(:run).in_sequence(s)
  143. pipeline.expects(:success?).in_sequence(s).returns(true)
  144. FileUtils.expects(:rm_rf).in_sequence(s).with(
  145. '/tmp/trigger/databases/MongoDB'
  146. )
  147. db.expects(:log!).in_sequence(s).with(:finished)
  148. db.send(:package!)
  149. end
  150. end # context 'without a compressor'
  151. context 'with a compressor' do
  152. before do
  153. model.stubs(:compressor).returns(compressor)
  154. compressor.stubs(:compress_with).yields('cmp_cmd', '.cmp_ext')
  155. end
  156. it 'packages the dump with compression' do
  157. Pipeline.expects(:new).in_sequence(s).returns(pipeline)
  158. pipeline.expects(:<<).in_sequence(s).with(
  159. "tar -cf - -C '/tmp/trigger/databases' 'MongoDB'"
  160. )
  161. pipeline.expects(:<<).in_sequence(s).with('cmp_cmd')
  162. pipeline.expects(:<<).in_sequence(s).with(
  163. "cat > '/tmp/trigger/databases/MongoDB.tar.cmp_ext'"
  164. )
  165. pipeline.expects(:run).in_sequence(s)
  166. pipeline.expects(:success?).in_sequence(s).returns(true)
  167. FileUtils.expects(:rm_rf).in_sequence(s).with(
  168. '/tmp/trigger/databases/MongoDB'
  169. )
  170. db.expects(:log!).in_sequence(s).with(:finished)
  171. db.send(:package!)
  172. end
  173. end # context 'with a compressor'
  174. context 'when the pipeline fails' do
  175. before do
  176. Pipeline.any_instance.stubs(:success?).returns(false)
  177. Pipeline.any_instance.stubs(:error_messages).returns('error messages')
  178. end
  179. it 'raises an error and does not remove the packaging path' do
  180. FileUtils.expects(:rm_rf).never
  181. db.expects(:log!).never
  182. expect do
  183. db.send(:package!)
  184. end.to raise_error(Database::MongoDB::Error) {|err|
  185. expect( err.message ).to eq(
  186. "Database::MongoDB::Error: Dump Failed!\n error messages"
  187. )
  188. }
  189. end
  190. end # context 'when the pipeline fails'
  191. end # describe '#package!'
  192. describe '#mongodump' do
  193. let(:option_methods) {%w[
  194. name_option credential_options connectivity_options
  195. ipv6_option oplog_option user_options dump_packaging_path
  196. ]}
  197. it 'returns full mongodump command built from all options' do
  198. option_methods.each {|name| db.stubs(name).returns(name) }
  199. expect( db.send(:mongodump) ).to eq(
  200. "mongodump name_option credential_options connectivity_options " +
  201. "ipv6_option oplog_option user_options --out='dump_packaging_path'"
  202. )
  203. end
  204. it 'handles nil values from option methods' do
  205. option_methods.each {|name| db.stubs(name).returns(nil) }
  206. expect( db.send(:mongodump) ).to eq "mongodump --out=''"
  207. end
  208. end # describe '#mongodump'
  209. describe 'mongo and monogodump option methods' do
  210. describe '#name_option' do
  211. it 'returns database argument if #name is specified' do
  212. expect( db.send(:name_option) ).to be_nil
  213. db.name = 'my_database'
  214. expect( db.send(:name_option) ).to eq "--db='my_database'"
  215. end
  216. end # describe '#name_option'
  217. describe '#credential_options' do
  218. it 'returns credentials arguments based on #username and #password' do
  219. expect( db.send(:credential_options) ).to eq ''
  220. db.username = 'my_user'
  221. expect( db.send(:credential_options) ).to eq(
  222. "--username='my_user'"
  223. )
  224. db.password = 'my_password'
  225. expect( db.send(:credential_options) ).to eq(
  226. "--username='my_user' --password='my_password'"
  227. )
  228. db.username = nil
  229. expect( db.send(:credential_options) ).to eq(
  230. "--password='my_password'"
  231. )
  232. end
  233. end # describe '#credential_options'
  234. describe '#connectivity_options' do
  235. it 'returns connectivity arguments based on #host and #port' do
  236. expect( db.send(:connectivity_options) ).to eq ''
  237. db.host = 'my_host'
  238. expect( db.send(:connectivity_options) ).to eq(
  239. "--host='my_host'"
  240. )
  241. db.port = 'my_port'
  242. expect( db.send(:connectivity_options) ).to eq(
  243. "--host='my_host' --port='my_port'"
  244. )
  245. db.host = nil
  246. expect( db.send(:connectivity_options) ).to eq(
  247. "--port='my_port'"
  248. )
  249. end
  250. end # describe '#connectivity_options'
  251. describe '#ipv6_option' do
  252. it 'returns the ipv6 argument if #ipv6 is true' do
  253. expect( db.send(:ipv6_option) ).to be_nil
  254. db.ipv6 = true
  255. expect( db.send(:ipv6_option) ).to eq '--ipv6'
  256. end
  257. end # describe '#ipv6_option'
  258. describe '#oplog_option' do
  259. it 'returns the oplog argument if #oplog is true' do
  260. expect( db.send(:oplog_option) ).to be_nil
  261. db.oplog = true
  262. expect( db.send(:oplog_option) ).to eq '--oplog'
  263. end
  264. end # describe '#oplog_option'
  265. describe '#user_options' do
  266. it 'returns arguments for any #additional_options specified' do
  267. expect( db.send(:user_options) ).to eq ''
  268. db.additional_options = ['--opt1', '--opt2']
  269. expect( db.send(:user_options) ).to eq '--opt1 --opt2'
  270. db.additional_options = '--opta --optb'
  271. expect( db.send(:user_options) ).to eq '--opta --optb'
  272. end
  273. end # describe '#user_options'
  274. end # describe 'mongo and monogodump option methods'
  275. describe '#lock_database' do
  276. it 'runs command to disable profiling and lock the database' do
  277. db = Database::MongoDB.new(model)
  278. db.stubs(:mongo_shell).returns('mongo_shell')
  279. db.expects(:run).with(
  280. "echo 'use admin\n" +
  281. "db.setProfilingLevel(0)\n" +
  282. "db.fsyncLock()' | mongo_shell\n"
  283. )
  284. db.send(:lock_database)
  285. end
  286. end # describe '#lock_database'
  287. describe '#unlock_database' do
  288. it 'runs command to unlock the database' do
  289. db = Database::MongoDB.new(model)
  290. db.stubs(:mongo_shell).returns('mongo_shell')
  291. db.expects(:run).with(
  292. "echo 'use admin\n" +
  293. "db.fsyncUnlock()' | mongo_shell\n"
  294. )
  295. db.send(:unlock_database)
  296. end
  297. end # describe '#unlock_database'
  298. describe '#mongo_shell' do
  299. specify 'with all options' do
  300. db.host = 'my_host'
  301. db.port = 'my_port'
  302. db.username = 'my_user'
  303. db.password = 'my_pwd'
  304. db.ipv6 = true
  305. db.name = 'my_db'
  306. expect( db.send(:mongo_shell) ).to eq(
  307. "mongo --host='my_host' --port='my_port' --username='my_user' " +
  308. "--password='my_pwd' --ipv6 'my_db'"
  309. )
  310. end
  311. specify 'with no options' do
  312. expect( db.send(:mongo_shell) ).to eq 'mongo'
  313. end
  314. end # describe '#mongo_shell'
  315. end
  316. end