PageRenderTime 52ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/test/command_test.rb

http://github.com/capistrano/capistrano
Ruby | 304 lines | 260 code | 44 blank | 0 comment | 6 complexity | 269ef5f88ab9d0765c803621c0962a00 MD5 | raw file
  1. require "utils"
  2. require 'capistrano/command'
  3. require 'capistrano/configuration'
  4. class CommandTest < Test::Unit::TestCase
  5. def test_command_should_open_channels_on_all_sessions
  6. s1, s2, s3 = mock_session, mock_session, mock_session
  7. assert_equal "ls", Capistrano::Command.new("ls", [s1, s2, s3]).tree.fallback.command
  8. end
  9. def test_command_with_newlines_should_be_properly_escaped
  10. cmd = Capistrano::Command.new("ls\necho", [mock_session])
  11. assert_equal "ls\\\necho", cmd.tree.fallback.command
  12. end
  13. def test_command_with_windows_newlines_should_be_properly_escaped
  14. cmd = Capistrano::Command.new("ls\r\necho", [mock_session])
  15. assert_equal "ls\\\necho", cmd.tree.fallback.command
  16. end
  17. def test_command_with_pty_should_request_pty_and_register_success_callback
  18. session = setup_for_extracting_channel_action(:request_pty, true) do |ch|
  19. ch.expects(:exec).with(%(sh -c 'ls'))
  20. end
  21. Capistrano::Command.new("ls", [session], :pty => true)
  22. end
  23. def test_command_with_env_key_should_have_environment_constructed_and_prepended
  24. session = setup_for_extracting_channel_action do |ch|
  25. ch.expects(:request_pty).never
  26. ch.expects(:exec).with(%(env FOO=bar sh -c 'ls'))
  27. end
  28. Capistrano::Command.new("ls", [session], :env => { "FOO" => "bar" })
  29. end
  30. def test_env_with_symbolic_key_should_be_accepted_as_a_string
  31. session = setup_for_extracting_channel_action do |ch|
  32. ch.expects(:exec).with(%(env FOO=bar sh -c 'ls'))
  33. end
  34. Capistrano::Command.new("ls", [session], :env => { :FOO => "bar" })
  35. end
  36. def test_env_as_string_should_be_substituted_in_directly
  37. session = setup_for_extracting_channel_action do |ch|
  38. ch.expects(:exec).with(%(env HOWDY=there sh -c 'ls'))
  39. end
  40. Capistrano::Command.new("ls", [session], :env => "HOWDY=there")
  41. end
  42. def test_env_with_symbolic_value_should_be_accepted_as_string
  43. session = setup_for_extracting_channel_action do |ch|
  44. ch.expects(:exec).with(%(env FOO=bar sh -c 'ls'))
  45. end
  46. Capistrano::Command.new("ls", [session], :env => { "FOO" => :bar })
  47. end
  48. def test_env_value_should_be_escaped
  49. session = setup_for_extracting_channel_action do |ch|
  50. ch.expects(:exec).with(%(env FOO=(\\ \\\"bar\\\"\\ ) sh -c 'ls'))
  51. end
  52. Capistrano::Command.new("ls", [session], :env => { "FOO" => '( "bar" )' })
  53. end
  54. def test_env_with_multiple_keys_should_chain_the_entries_together
  55. session = setup_for_extracting_channel_action do |ch|
  56. ch.expects(:exec).with do |command|
  57. command =~ /^env / &&
  58. command =~ /\ba=b\b/ &&
  59. command =~ /\bc=d\b/ &&
  60. command =~ /\be=f\b/ &&
  61. command =~ / sh -c 'ls'$/
  62. end
  63. end
  64. Capistrano::Command.new("ls", [session], :env => { :a => :b, :c => :d, :e => :f })
  65. end
  66. def test_open_channel_should_set_host_key_on_channel
  67. channel = nil
  68. session = setup_for_extracting_channel_action { |ch| channel = ch }
  69. Capistrano::Command.new("ls", [session])
  70. assert_equal "capistrano", channel[:host]
  71. end
  72. def test_open_channel_should_set_options_key_on_channel
  73. channel = nil
  74. session = setup_for_extracting_channel_action { |ch| channel = ch }
  75. Capistrano::Command.new("ls", [session], :data => "here we go")
  76. assert_equal({ :data => 'here we go' }, channel[:options])
  77. end
  78. def test_successful_channel_should_send_command
  79. session = setup_for_extracting_channel_action do |ch|
  80. ch.expects(:exec).with(%(sh -c 'ls'))
  81. end
  82. Capistrano::Command.new("ls", [session])
  83. end
  84. def test_successful_channel_with_shell_option_should_send_command_via_specified_shell
  85. session = setup_for_extracting_channel_action do |ch|
  86. ch.expects(:exec).with(%(/bin/bash -c 'ls'))
  87. end
  88. Capistrano::Command.new("ls", [session], :shell => "/bin/bash")
  89. end
  90. def test_successful_channel_with_shell_false_should_send_command_without_shell
  91. session = setup_for_extracting_channel_action do |ch|
  92. ch.expects(:exec).with(%(echo `hostname`))
  93. end
  94. Capistrano::Command.new("echo `hostname`", [session], :shell => false)
  95. end
  96. def test_successful_channel_should_send_data_if_data_key_is_present
  97. session = setup_for_extracting_channel_action do |ch|
  98. ch.expects(:exec).with(%(sh -c 'ls'))
  99. ch.expects(:send_data).with("here we go")
  100. end
  101. Capistrano::Command.new("ls", [session], :data => "here we go")
  102. end
  103. def test_unsuccessful_pty_request_should_close_channel
  104. session = setup_for_extracting_channel_action(:request_pty, false) do |ch|
  105. ch.expects(:close)
  106. end
  107. Capistrano::Command.new("ls", [session], :pty => true)
  108. end
  109. def test_on_data_should_invoke_callback_as_stdout
  110. session = setup_for_extracting_channel_action(:on_data, "hello")
  111. called = false
  112. Capistrano::Command.new("ls", [session]) do |ch, stream, data|
  113. called = true
  114. assert_equal :out, stream
  115. assert_equal "hello", data
  116. end
  117. assert called
  118. end
  119. def test_on_extended_data_should_invoke_callback_as_stderr
  120. session = setup_for_extracting_channel_action(:on_extended_data, 2, "hello")
  121. called = false
  122. Capistrano::Command.new("ls", [session]) do |ch, stream, data|
  123. called = true
  124. assert_equal :err, stream
  125. assert_equal "hello", data
  126. end
  127. assert called
  128. end
  129. def test_on_request_should_record_exit_status
  130. data = mock(:read_long => 5)
  131. channel = nil
  132. session = setup_for_extracting_channel_action([:on_request, "exit-status"], data) { |ch| channel = ch }
  133. Capistrano::Command.new("ls", [session])
  134. assert_equal 5, channel[:status]
  135. end
  136. def test_on_close_should_set_channel_closed
  137. channel = nil
  138. session = setup_for_extracting_channel_action(:on_close) { |ch| channel = ch }
  139. Capistrano::Command.new("ls", [session])
  140. assert channel[:closed]
  141. end
  142. def test_stop_should_close_all_open_channels
  143. sessions = [mock_session(new_channel(false)),
  144. mock_session(new_channel(true)),
  145. mock_session(new_channel(false))]
  146. cmd = Capistrano::Command.new("ls", sessions)
  147. cmd.stop!
  148. end
  149. def test_process_should_return_cleanly_if_all_channels_have_zero_exit_status
  150. sessions = [mock_session(new_channel(true, 0)),
  151. mock_session(new_channel(true, 0)),
  152. mock_session(new_channel(true, 0))]
  153. cmd = Capistrano::Command.new("ls", sessions)
  154. assert_nothing_raised { cmd.process! }
  155. end
  156. def test_process_should_raise_error_if_any_channel_has_non_zero_exit_status
  157. sessions = [mock_session(new_channel(true, 0)),
  158. mock_session(new_channel(true, 0)),
  159. mock_session(new_channel(true, 1))]
  160. cmd = Capistrano::Command.new("ls", sessions)
  161. assert_raises(Capistrano::CommandError) { cmd.process! }
  162. end
  163. def test_command_error_should_include_accessor_with_host_array
  164. sessions = [mock_session(new_channel(true, 0)),
  165. mock_session(new_channel(true, 0)),
  166. mock_session(new_channel(true, 1))]
  167. cmd = Capistrano::Command.new("ls", sessions)
  168. begin
  169. cmd.process!
  170. flunk "expected an exception to be raised"
  171. rescue Capistrano::CommandError => e
  172. assert e.respond_to?(:hosts)
  173. assert_equal %w(capistrano), e.hosts.map { |h| h.to_s }
  174. end
  175. end
  176. def test_process_should_loop_until_all_channels_are_closed
  177. new_channel = Proc.new do |times|
  178. ch = mock("channel")
  179. returns = [false] * (times-1)
  180. ch.stubs(:to_ary)
  181. ch.stubs(:[]).with(:closed).returns(*(returns + [true]))
  182. ch.expects(:[]).with(:status).returns(0)
  183. ch
  184. end
  185. sessions = [mock_session(new_channel[5]),
  186. mock_session(new_channel[10]),
  187. mock_session(new_channel[7])]
  188. cmd = Capistrano::Command.new("ls", sessions)
  189. assert_nothing_raised do
  190. cmd.process!
  191. end
  192. end
  193. def test_process_should_instantiate_command_and_process!
  194. cmd = mock("command", :process! => nil)
  195. Capistrano::Command.expects(:new).with("ls -l", %w(a b c), {:foo => "bar"}).returns(cmd)
  196. Capistrano::Command.process("ls -l", %w(a b c), :foo => "bar")
  197. end
  198. def test_process_with_host_placeholder_should_substitute_host_placeholder_with_each_host
  199. session = setup_for_extracting_channel_action do |ch|
  200. ch.expects(:exec).with(%(sh -c 'echo capistrano'))
  201. end
  202. Capistrano::Command.new("echo $CAPISTRANO:HOST$", [session])
  203. end
  204. class MockConfig
  205. include Capistrano::Configuration::Roles
  206. end
  207. def test_hostroles_substitution
  208. @config = MockConfig.new
  209. @config.server "capistrano", :db, :worker
  210. server = @config.roles[:db].servers.first
  211. channel = {:server => server, :host => 'capistrano'}
  212. tree = Capistrano::Command::Tree.new(@config) { |t| t.else("echo $CAPISTRANO:HOSTROLES$") }
  213. result = Capistrano::Command.new(tree, []).send(:replace_placeholders, "echo $CAPISTRANO:HOSTROLES$", channel)
  214. assert result == "echo db,worker" || result == "echo worker,db"
  215. end
  216. def test_process_with_unknown_placeholder_should_not_replace_placeholder
  217. session = setup_for_extracting_channel_action do |ch|
  218. ch.expects(:exec).with(%(sh -c 'echo $CAPISTRANO:OTHER$'))
  219. end
  220. Capistrano::Command.new("echo $CAPISTRANO:OTHER$", [session])
  221. end
  222. private
  223. def mock_session(channel=nil)
  224. stub('session',
  225. :open_channel => channel,
  226. :preprocess => true,
  227. :postprocess => true,
  228. :listeners => {},
  229. :xserver => server("capistrano"))
  230. end
  231. class MockChannel < Hash
  232. def close
  233. end
  234. end
  235. def new_channel(closed, status=nil)
  236. ch = MockChannel.new
  237. ch.update({ :closed => closed, :host => "capistrano", :server => server("capistrano") })
  238. ch[:status] = status if status
  239. ch.expects(:close) unless closed
  240. ch
  241. end
  242. def setup_for_extracting_channel_action(action=nil, *args)
  243. s = server("capistrano")
  244. session = mock("session", :xserver => s)
  245. channel = {}
  246. session.expects(:open_channel).yields(channel)
  247. channel.stubs(:on_data)
  248. channel.stubs(:on_extended_data)
  249. channel.stubs(:on_request)
  250. channel.stubs(:on_close)
  251. channel.stubs(:exec)
  252. channel.stubs(:send_data)
  253. if action
  254. action = Array(action)
  255. channel.expects(action.first).with(*action[1..-1]).yields(channel, *args)
  256. end
  257. yield channel if block_given?
  258. session
  259. end
  260. end