capistrano 1.4.2 → 2.0.0

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 (113) hide show
  1. data/CHANGELOG +140 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README +22 -14
  4. data/bin/cap +1 -8
  5. data/bin/capify +77 -0
  6. data/examples/sample.rb +10 -109
  7. data/lib/capistrano.rb +1 -0
  8. data/lib/capistrano/callback.rb +41 -0
  9. data/lib/capistrano/cli.rb +17 -317
  10. data/lib/capistrano/cli/execute.rb +82 -0
  11. data/lib/capistrano/cli/help.rb +102 -0
  12. data/lib/capistrano/cli/help.txt +53 -0
  13. data/lib/capistrano/cli/options.rb +183 -0
  14. data/lib/capistrano/cli/ui.rb +28 -0
  15. data/lib/capistrano/command.rb +62 -29
  16. data/lib/capistrano/configuration.rb +25 -226
  17. data/lib/capistrano/configuration/actions/file_transfer.rb +35 -0
  18. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  19. data/lib/capistrano/configuration/actions/invocation.rb +127 -0
  20. data/lib/capistrano/configuration/callbacks.rb +148 -0
  21. data/lib/capistrano/configuration/connections.rb +159 -0
  22. data/lib/capistrano/configuration/execution.rb +126 -0
  23. data/lib/capistrano/configuration/loading.rb +112 -0
  24. data/lib/capistrano/configuration/namespaces.rb +190 -0
  25. data/lib/capistrano/configuration/roles.rb +51 -0
  26. data/lib/capistrano/configuration/servers.rb +75 -0
  27. data/lib/capistrano/configuration/variables.rb +127 -0
  28. data/lib/capistrano/errors.rb +15 -0
  29. data/lib/capistrano/extensions.rb +27 -8
  30. data/lib/capistrano/gateway.rb +54 -29
  31. data/lib/capistrano/logger.rb +11 -11
  32. data/lib/capistrano/recipes/compat.rb +32 -0
  33. data/lib/capistrano/recipes/deploy.rb +483 -0
  34. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  35. data/lib/capistrano/recipes/deploy/local_dependency.rb +46 -0
  36. data/lib/capistrano/recipes/deploy/remote_dependency.rb +65 -0
  37. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  38. data/lib/capistrano/recipes/deploy/scm/base.rb +180 -0
  39. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  40. data/lib/capistrano/recipes/deploy/scm/cvs.rb +151 -0
  41. data/lib/capistrano/recipes/deploy/scm/darcs.rb +85 -0
  42. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +129 -0
  43. data/lib/capistrano/recipes/deploy/scm/perforce.rb +126 -0
  44. data/lib/capistrano/recipes/deploy/scm/subversion.rb +103 -0
  45. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  46. data/lib/capistrano/recipes/deploy/strategy/base.rb +64 -0
  47. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  48. data/lib/capistrano/recipes/deploy/strategy/copy.rb +143 -0
  49. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  50. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  51. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +47 -0
  52. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +53 -0
  53. data/lib/capistrano/recipes/standard.rb +26 -276
  54. data/lib/capistrano/recipes/templates/maintenance.rhtml +1 -1
  55. data/lib/capistrano/recipes/upgrade.rb +33 -0
  56. data/lib/capistrano/server_definition.rb +51 -0
  57. data/lib/capistrano/shell.rb +125 -81
  58. data/lib/capistrano/ssh.rb +80 -36
  59. data/lib/capistrano/task_definition.rb +69 -0
  60. data/lib/capistrano/upload.rb +146 -0
  61. data/lib/capistrano/version.rb +13 -17
  62. data/test/cli/execute_test.rb +132 -0
  63. data/test/cli/help_test.rb +139 -0
  64. data/test/cli/options_test.rb +226 -0
  65. data/test/cli/ui_test.rb +28 -0
  66. data/test/cli_test.rb +17 -0
  67. data/test/command_test.rb +284 -25
  68. data/test/configuration/actions/file_transfer_test.rb +40 -0
  69. data/test/configuration/actions/inspect_test.rb +62 -0
  70. data/test/configuration/actions/invocation_test.rb +195 -0
  71. data/test/configuration/callbacks_test.rb +206 -0
  72. data/test/configuration/connections_test.rb +288 -0
  73. data/test/configuration/execution_test.rb +159 -0
  74. data/test/configuration/loading_test.rb +119 -0
  75. data/test/configuration/namespace_dsl_test.rb +283 -0
  76. data/test/configuration/roles_test.rb +47 -0
  77. data/test/configuration/servers_test.rb +90 -0
  78. data/test/configuration/variables_test.rb +180 -0
  79. data/test/configuration_test.rb +60 -212
  80. data/test/deploy/scm/base_test.rb +55 -0
  81. data/test/deploy/strategy/copy_test.rb +146 -0
  82. data/test/extensions_test.rb +69 -0
  83. data/test/fixtures/cli_integration.rb +5 -0
  84. data/test/fixtures/custom.rb +2 -2
  85. data/test/gateway_test.rb +167 -0
  86. data/test/logger_test.rb +123 -0
  87. data/test/server_definition_test.rb +108 -0
  88. data/test/shell_test.rb +64 -0
  89. data/test/ssh_test.rb +67 -154
  90. data/test/task_definition_test.rb +101 -0
  91. data/test/upload_test.rb +131 -0
  92. data/test/utils.rb +31 -39
  93. data/test/version_test.rb +24 -0
  94. metadata +145 -98
  95. data/THANKS +0 -4
  96. data/lib/capistrano/actor.rb +0 -567
  97. data/lib/capistrano/generators/rails/deployment/deployment_generator.rb +0 -25
  98. data/lib/capistrano/generators/rails/deployment/templates/capistrano.rake +0 -49
  99. data/lib/capistrano/generators/rails/deployment/templates/deploy.rb +0 -122
  100. data/lib/capistrano/generators/rails/loader.rb +0 -20
  101. data/lib/capistrano/scm/base.rb +0 -61
  102. data/lib/capistrano/scm/baz.rb +0 -118
  103. data/lib/capistrano/scm/bzr.rb +0 -70
  104. data/lib/capistrano/scm/cvs.rb +0 -129
  105. data/lib/capistrano/scm/darcs.rb +0 -27
  106. data/lib/capistrano/scm/mercurial.rb +0 -83
  107. data/lib/capistrano/scm/perforce.rb +0 -139
  108. data/lib/capistrano/scm/subversion.rb +0 -128
  109. data/lib/capistrano/transfer.rb +0 -97
  110. data/lib/capistrano/utils.rb +0 -26
  111. data/test/actor_test.rb +0 -402
  112. data/test/scm/cvs_test.rb +0 -196
  113. data/test/scm/subversion_test.rb +0 -145
@@ -0,0 +1,226 @@
1
+ require "#{File.dirname(__FILE__)}/../utils"
2
+ require 'capistrano/cli/options'
3
+
4
+ class CLIOptionsTest < Test::Unit::TestCase
5
+ class ExitException < Exception; end
6
+
7
+ class MockCLI
8
+ def initialize
9
+ @args = []
10
+ end
11
+
12
+ attr_reader :args
13
+
14
+ include Capistrano::CLI::Options
15
+ end
16
+
17
+ def setup
18
+ @cli = MockCLI.new
19
+ end
20
+
21
+ def test_parse_options_should_require_non_empty_args_list
22
+ @cli.stubs(:warn)
23
+ @cli.expects(:exit).raises(ExitException)
24
+ assert_raises(ExitException) { @cli.parse_options! }
25
+ end
26
+
27
+ def test_parse_options_with_e_should_set_explain_option
28
+ @cli.args << "-e" << "sample"
29
+ @cli.parse_options!
30
+ assert_equal "sample", @cli.options[:explain]
31
+ end
32
+
33
+ def test_parse_options_with_f_should_add_recipe_file
34
+ @cli.args << "-f" << "deploy"
35
+ @cli.parse_options!
36
+ assert_equal %w(deploy), @cli.options[:recipes]
37
+ end
38
+
39
+ def test_parse_options_with_multiple_f_should_add_each_as_recipe_file
40
+ @cli.args << "-f" << "deploy" << "-f" << "monitor"
41
+ @cli.parse_options!
42
+ assert_equal %w(deploy monitor), @cli.options[:recipes]
43
+ end
44
+
45
+ def test_parse_options_with_H_should_show_verbose_help_and_exit
46
+ @cli.expects(:exit).raises(ExitException)
47
+ @cli.expects(:long_help)
48
+ @cli.args << "-H"
49
+ assert_raises(ExitException) { @cli.parse_options! }
50
+ end
51
+
52
+ def test_parse_options_with_h_should_show_options_and_exit
53
+ @cli.expects(:puts).with(@cli.option_parser)
54
+ @cli.expects(:exit).raises(ExitException)
55
+ @cli.args << "-h"
56
+ assert_raises(ExitException) { @cli.parse_options! }
57
+ end
58
+
59
+ def test_parse_options_with_p_should_prompt_for_password
60
+ MockCLI.expects(:password_prompt).returns(:the_password)
61
+ @cli.args << "-p"
62
+ @cli.parse_options!
63
+ assert_equal :the_password, @cli.options[:password]
64
+ end
65
+
66
+ def test_parse_options_without_p_should_set_proc_for_password
67
+ @cli.args << "-e" << "sample"
68
+ @cli.parse_options!
69
+ assert_instance_of Proc, @cli.options[:password]
70
+ end
71
+
72
+ def test_parse_options_with_q_should_set_verbose_to_0
73
+ @cli.args << "-q"
74
+ @cli.parse_options!
75
+ assert_equal 0, @cli.options[:verbose]
76
+ end
77
+
78
+ def test_parse_options_with_S_should_set_pre_vars
79
+ @cli.args << "-S" << "foo=bar"
80
+ @cli.parse_options!
81
+ assert_equal "bar", @cli.options[:pre_vars][:foo]
82
+ end
83
+
84
+ def test_parse_options_with_s_should_set_vars
85
+ @cli.args << "-s" << "foo=bar"
86
+ @cli.parse_options!
87
+ assert_equal "bar", @cli.options[:vars][:foo]
88
+ end
89
+
90
+ def test_parse_options_with_T_should_set_tasks_option_and_set_verbose_off
91
+ @cli.args << "-T"
92
+ @cli.parse_options!
93
+ assert @cli.options[:tasks]
94
+ assert_equal 0, @cli.options[:verbose]
95
+ end
96
+
97
+ def test_parse_options_with_V_should_show_version_and_exit
98
+ @cli.args << "-V"
99
+ @cli.expects(:puts).with { |s| s.include?(Capistrano::Version::STRING) }
100
+ @cli.expects(:exit).raises(ExitException)
101
+ assert_raises(ExitException) { @cli.parse_options! }
102
+ end
103
+
104
+ def test_parse_options_with_v_should_set_verbose_to_1
105
+ @cli.args << "-v"
106
+ @cli.parse_options!
107
+ assert_equal 1, @cli.options[:verbose]
108
+ end
109
+
110
+ def test_parse_options_with_multiple_v_should_set_verbose_accordingly
111
+ @cli.args << "-vvvvvvv"
112
+ @cli.parse_options!
113
+ assert_equal 7, @cli.options[:verbose]
114
+ end
115
+
116
+ def test_parse_options_without_X_should_set_sysconf
117
+ @cli.args << "-v"
118
+ @cli.parse_options!
119
+ assert @cli.options.key?(:sysconf)
120
+ end
121
+
122
+ def test_parse_options_with_X_should_unset_sysconf
123
+ @cli.args << "-X"
124
+ @cli.parse_options!
125
+ assert !@cli.options.key?(:sysconf)
126
+ end
127
+
128
+ def test_parse_options_without_x_should_set_dotfile
129
+ @cli.args << "-v"
130
+ @cli.parse_options!
131
+ assert @cli.options.key?(:dotfile)
132
+ end
133
+
134
+ def test_parse_options_with_x_should_unset_dotfile
135
+ @cli.args << "-x"
136
+ @cli.parse_options!
137
+ assert !@cli.options.key?(:dotfile)
138
+ end
139
+
140
+ def test_parse_options_without_q_or_v_should_set_verbose_to_3
141
+ @cli.args << "-x"
142
+ @cli.parse_options!
143
+ assert_equal 3, @cli.options[:verbose]
144
+ end
145
+
146
+ def test_should_search_for_default_recipes_if_f_not_given
147
+ @cli.expects(:look_for_default_recipe_file!)
148
+ @cli.args << "-v"
149
+ @cli.parse_options!
150
+ end
151
+
152
+ def test_should_not_search_for_default_recipes_if_f_given
153
+ @cli.expects(:look_for_default_recipe_file!).never
154
+ @cli.args << "-f" << "hello"
155
+ @cli.parse_options!
156
+ end
157
+
158
+ def test_F_should_search_for_default_recipes_even_if_f_is_given
159
+ @cli.expects(:look_for_default_recipe_file!)
160
+ @cli.args << "-Ff" << "hello"
161
+ @cli.parse_options!
162
+ end
163
+
164
+ def test_should_extract_env_vars_from_command_line
165
+ assert_nil ENV["HELLO"]
166
+ assert_nil ENV["ANOTHER"]
167
+
168
+ @cli.args << "HELLO=world" << "hello" << "ANOTHER=value"
169
+ @cli.parse_options!
170
+
171
+ assert_equal "world", ENV["HELLO"]
172
+ assert_equal "value", ENV["ANOTHER"]
173
+ ensure
174
+ ENV["HELLO"] = ENV["ANOTHER"] = nil
175
+ end
176
+
177
+ def test_remaining_args_should_be_added_to_actions_list
178
+ @cli.args << "-v" << "HELLO=world" << "-f" << "foo" << "something" << "else"
179
+ @cli.parse_options!
180
+ assert_equal %w(something else), @cli.args
181
+ ensure
182
+ ENV["HELLO"] = nil
183
+ end
184
+
185
+ def test_search_for_default_recipe_file_should_look_for_Capfile
186
+ File.stubs(:file?).returns(false)
187
+ File.expects(:file?).with("Capfile").returns(true)
188
+ @cli.args << "-v"
189
+ @cli.parse_options!
190
+ assert_equal %w(Capfile), @cli.options[:recipes]
191
+ end
192
+
193
+ def test_search_for_default_recipe_file_should_look_for_capfile
194
+ File.stubs(:file?).returns(false)
195
+ File.expects(:file?).with("capfile").returns(true)
196
+ @cli.args << "-v"
197
+ @cli.parse_options!
198
+ assert_equal %w(capfile), @cli.options[:recipes]
199
+ end
200
+
201
+ def test_search_for_default_recipe_should_hike_up_the_directory_tree_until_it_finds_default_recipe
202
+ File.stubs(:file?).returns(false)
203
+ File.expects(:file?).with("capfile").times(2).returns(false,true)
204
+ Dir.expects(:pwd).times(3).returns(*%w(/bar/baz /bar/baz /bar))
205
+ Dir.expects(:chdir).with("..")
206
+ @cli.args << "-v"
207
+ @cli.parse_options!
208
+ assert_equal %w(capfile), @cli.options[:recipes]
209
+ end
210
+
211
+ def test_search_for_default_recipe_should_halt_at_root_directory
212
+ File.stubs(:file?).returns(false)
213
+ Dir.expects(:pwd).times(7).returns(*%w(/bar/baz /bar/baz /bar /bar / / /))
214
+ Dir.expects(:chdir).with("..").times(3)
215
+ Dir.expects(:chdir).with("/bar/baz")
216
+ @cli.args << "-v"
217
+ @cli.parse_options!
218
+ assert @cli.options[:recipes].empty?
219
+ end
220
+
221
+ def test_parse_should_instantiate_new_cli_and_call_parse_options
222
+ cli = mock("cli", :parse_options! => nil)
223
+ MockCLI.expects(:new).with(%w(a b c)).returns(cli)
224
+ assert_equal cli, MockCLI.parse(%w(a b c))
225
+ end
226
+ end
@@ -0,0 +1,28 @@
1
+ require "#{File.dirname(__FILE__)}/../utils"
2
+ require 'capistrano/cli/ui'
3
+
4
+ class CLIUITest < Test::Unit::TestCase
5
+ class MockCLI
6
+ include Capistrano::CLI::UI
7
+ end
8
+
9
+ def test_ui_should_return_highline_instance
10
+ assert_instance_of HighLine, MockCLI.ui
11
+ end
12
+
13
+ def test_password_prompt_should_have_default_prompt_and_set_echo_false
14
+ q = mock("question")
15
+ q.expects(:echo=).with(false)
16
+ ui = mock("ui")
17
+ ui.expects(:ask).with("Password: ").yields(q).returns("sayuncle")
18
+ MockCLI.expects(:ui).returns(ui)
19
+ assert_equal "sayuncle", MockCLI.password_prompt
20
+ end
21
+
22
+ def test_password_prompt_with_custom_prompt_should_use_custom_prompt
23
+ ui = mock("ui")
24
+ ui.expects(:ask).with("Give the passphrase: ").returns("sayuncle")
25
+ MockCLI.expects(:ui).returns(ui)
26
+ assert_equal "sayuncle", MockCLI.password_prompt("Give the passphrase: ")
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ require "#{File.dirname(__FILE__)}/utils"
2
+ require 'capistrano/cli'
3
+
4
+ class CLI_Test < Test::Unit::TestCase
5
+ def test_options_ui_and_help_modules_should_integrate_successfully_with_configuration
6
+ cli = Capistrano::CLI.parse(%w(-T))
7
+ cli.expects(:puts).at_least_once
8
+ cli.execute!
9
+ end
10
+
11
+ def test_options_and_execute_modules_should_integrate_successfully_with_configuration
12
+ path = "#{File.dirname(__FILE__)}/fixtures/cli_integration.rb"
13
+ cli = Capistrano::CLI.parse(%W(-q -f #{path} testing))
14
+ config = cli.execute!
15
+ assert config[:testing_occurred]
16
+ end
17
+ end
@@ -1,43 +1,302 @@
1
- $:.unshift File.dirname(__FILE__) + "/../lib"
2
-
3
- require 'stringio'
4
- require 'test/unit'
1
+ require "#{File.dirname(__FILE__)}/utils"
5
2
  require 'capistrano/command'
6
3
 
7
4
  class CommandTest < Test::Unit::TestCase
8
- class MockSession
9
- def open_channel
10
- { :closed => true, :status => 0 }
5
+ def test_command_should_open_channels_on_all_sessions
6
+ s1 = mock(:open_channel => nil)
7
+ s2 = mock(:open_channel => nil)
8
+ s3 = mock(:open_channel => nil)
9
+ assert_equal "ls", Capistrano::Command.new("ls", [s1, s2, s3]).command
10
+ end
11
+
12
+ def test_command_with_newlines_should_be_properly_escaped
13
+ cmd = Capistrano::Command.new("ls\necho", [mock(:open_channel => nil)])
14
+ assert_equal "ls\\\necho", cmd.command
15
+ end
16
+
17
+ def test_command_with_windows_newlines_should_be_properly_escaped
18
+ cmd = Capistrano::Command.new("ls\r\necho", [mock(:open_channel => nil)])
19
+ assert_equal "ls\\\necho", cmd.command
20
+ end
21
+
22
+ def test_command_with_env_key_should_have_environment_constructed_and_prepended
23
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
24
+ ch.expects(:exec).with(%(env FOO=bar sh -c "ls"))
25
+ end
26
+ Capistrano::Command.new("ls", [session], :env => { "FOO" => "bar" })
27
+ end
28
+
29
+ def test_env_with_symbolic_key_should_be_accepted_as_a_string
30
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
31
+ ch.expects(:exec).with(%(env FOO=bar sh -c "ls"))
32
+ end
33
+ Capistrano::Command.new("ls", [session], :env => { :FOO => "bar" })
34
+ end
35
+
36
+ def test_env_as_string_should_be_substituted_in_directly
37
+ session = setup_for_extracting_channel_action(:on_success) 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
+
43
+ def test_env_with_symbolic_value_should_be_accepted_as_string
44
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
45
+ ch.expects(:exec).with(%(env FOO=bar sh -c "ls"))
46
+ end
47
+ Capistrano::Command.new("ls", [session], :env => { "FOO" => :bar })
48
+ end
49
+
50
+ def test_env_value_should_be_escaped
51
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
52
+ ch.expects(:exec).with(%(env FOO=(\\ \\\"bar\\\"\\ ) sh -c "ls"))
53
+ end
54
+ Capistrano::Command.new("ls", [session], :env => { "FOO" => '( "bar" )' })
55
+ end
56
+
57
+ def test_env_with_multiple_keys_should_chain_the_entries_together
58
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
59
+ ch.expects(:exec).with do |command|
60
+ command =~ /^env / &&
61
+ command =~ /\ba=b\b/ &&
62
+ command =~ /\bc=d\b/ &&
63
+ command =~ /\be=f\b/ &&
64
+ command =~ / sh -c "ls"$/
65
+ end
11
66
  end
67
+ Capistrano::Command.new("ls", [session], :env => { :a => :b, :c => :d, :e => :f })
68
+ end
69
+
70
+ def test_open_channel_should_set_host_key_on_channel
71
+ session = mock(:xserver => server("capistrano"))
72
+ channel = stub_everything
73
+
74
+ session.expects(:open_channel).yields(channel)
75
+ channel.expects(:[]=).with(:host, "capistrano")
76
+
77
+ Capistrano::Command.new("ls", [session])
12
78
  end
13
79
 
14
- class MockActor
15
- attr_reader :sessions
80
+ def test_open_channel_should_set_options_key_on_channel
81
+ session = mock(:xserver => server("capistrano"))
82
+ channel = stub_everything
83
+
84
+ session.expects(:open_channel).yields(channel)
85
+ channel.expects(:[]=).with(:options, {:data => "here we go"})
16
86
 
17
- def initialize
18
- @sessions = Hash.new { |h,k| h[k] = MockSession.new }
87
+ Capistrano::Command.new("ls", [session], :data => "here we go")
88
+ end
89
+
90
+ def test_open_channel_should_request_pty
91
+ session = mock(:xserver => server("capistrano"))
92
+ channel = stub_everything
93
+
94
+ session.expects(:open_channel).yields(channel)
95
+ channel.expects(:request_pty).with(:want_reply => true)
96
+
97
+ Capistrano::Command.new("ls", [session])
98
+ end
99
+
100
+ def test_successful_channel_should_send_command
101
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
102
+ ch.expects(:exec).with(%(sh -c "ls"))
19
103
  end
104
+ Capistrano::Command.new("ls", [session])
20
105
  end
21
106
 
22
- def setup
23
- @actor = MockActor.new
107
+ def test_successful_channel_with_shell_option_should_send_command_via_specified_shell
108
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
109
+ ch.expects(:exec).with(%(/bin/bash -c "ls"))
110
+ end
111
+ Capistrano::Command.new("ls", [session], :shell => "/bin/bash")
112
+ end
113
+
114
+ def test_successful_channel_should_send_data_if_data_key_is_present
115
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
116
+ ch.expects(:exec).with(%(sh -c "ls"))
117
+ ch.expects(:send_data).with("here we go")
118
+ end
119
+ Capistrano::Command.new("ls", [session], :data => "here we go")
24
120
  end
25
121
 
26
- def test_command_executes_on_all_servers
27
- command = Capistrano::Command.new(%w(server1 server2 server3),
28
- "hello", nil, {}, @actor)
29
- assert_equal %w(server1 server2 server3), @actor.sessions.keys.sort
122
+ def test_unsuccessful_channel_should_close_channel
123
+ session = setup_for_extracting_channel_action(:on_failure) do |ch|
124
+ ch.expects(:close)
125
+ end
126
+ Capistrano::Command.new("ls", [session])
30
127
  end
31
128
 
32
- def test_command_with_newlines
33
- command = Capistrano::Command.new(%w(server1), "hello\nworld", nil, {},
34
- @actor)
35
- assert_equal "hello\\\nworld", command.command
129
+ def test_on_data_should_invoke_callback_as_stdout
130
+ session = setup_for_extracting_channel_action(:on_data, "hello")
131
+ called = false
132
+ Capistrano::Command.new("ls", [session]) do |ch, stream, data|
133
+ called = true
134
+ assert_equal :out, stream
135
+ assert_equal "hello", data
136
+ end
137
+ assert called
36
138
  end
37
139
 
38
- def test_command_with_windows_newlines
39
- command = Capistrano::Command.new(%w(server1), "hello\r\nworld", nil, {},
40
- @actor)
41
- assert_equal "hello\\\nworld", command.command
140
+ def test_on_extended_data_should_invoke_callback_as_stderr
141
+ session = setup_for_extracting_channel_action(:on_extended_data, 2, "hello")
142
+ called = false
143
+ Capistrano::Command.new("ls", [session]) do |ch, stream, data|
144
+ called = true
145
+ assert_equal :err, stream
146
+ assert_equal "hello", data
147
+ end
148
+ assert called
42
149
  end
150
+
151
+ def test_on_request_should_record_exit_status
152
+ data = mock(:read_long => 5)
153
+ session = setup_for_extracting_channel_action(:on_request, "exit-status", nil, data) do |ch|
154
+ ch.expects(:[]=).with(:status, 5)
155
+ end
156
+ Capistrano::Command.new("ls", [session])
157
+ end
158
+
159
+ def test_on_close_should_set_channel_closed
160
+ session = setup_for_extracting_channel_action(:on_close) do |ch|
161
+ ch.expects(:[]=).with(:closed, true)
162
+ end
163
+ Capistrano::Command.new("ls", [session])
164
+ end
165
+
166
+ def test_stop_should_close_all_open_channels
167
+ sessions = [mock("session", :open_channel => new_channel(false)),
168
+ mock("session", :open_channel => new_channel(true)),
169
+ mock("session", :open_channel => new_channel(false))]
170
+
171
+ cmd = Capistrano::Command.new("ls", sessions)
172
+ cmd.stop!
173
+ end
174
+
175
+ def test_process_should_return_cleanly_if_all_channels_have_zero_exit_status
176
+ sessions = [mock("session", :open_channel => new_channel(true, 0)),
177
+ mock("session", :open_channel => new_channel(true, 0)),
178
+ mock("session", :open_channel => new_channel(true, 0))]
179
+ cmd = Capistrano::Command.new("ls", sessions)
180
+ assert_nothing_raised { cmd.process! }
181
+ end
182
+
183
+ def test_process_should_raise_error_if_any_channel_has_non_zero_exit_status
184
+ sessions = [mock("session", :open_channel => new_channel(true, 0)),
185
+ mock("session", :open_channel => new_channel(true, 0)),
186
+ mock("session", :open_channel => new_channel(true, 1))]
187
+ cmd = Capistrano::Command.new("ls", sessions)
188
+ assert_raises(Capistrano::CommandError) { cmd.process! }
189
+ end
190
+
191
+ def test_command_error_should_include_accessor_with_host_array
192
+ sessions = [mock("session", :open_channel => new_channel(true, 0)),
193
+ mock("session", :open_channel => new_channel(true, 0)),
194
+ mock("session", :open_channel => new_channel(true, 1))]
195
+ cmd = Capistrano::Command.new("ls", sessions)
196
+
197
+ begin
198
+ cmd.process!
199
+ flunk "expected an exception to be raised"
200
+ rescue Capistrano::CommandError => e
201
+ assert e.respond_to?(:hosts)
202
+ assert_equal %w(capistrano), e.hosts.map { |h| h.to_s }
203
+ end
204
+ end
205
+
206
+ def test_process_should_loop_until_all_channels_are_closed
207
+ new_channel = Proc.new do |times|
208
+ ch = mock("channel")
209
+ returns = [false] * (times-1)
210
+ ch.stubs(:[]).with(:closed).returns(*(returns + [true]))
211
+ con = mock("connection")
212
+ con.expects(:process).with(true).times(times-1)
213
+ ch.expects(:connection).times(times-1).returns(con)
214
+ ch.expects(:[]).with(:status).returns(0)
215
+ ch
216
+ end
217
+
218
+ sessions = [mock("session", :open_channel => new_channel[5]),
219
+ mock("session", :open_channel => new_channel[10]),
220
+ mock("session", :open_channel => new_channel[7])]
221
+ cmd = Capistrano::Command.new("ls", sessions)
222
+ assert_nothing_raised { cmd.process! }
223
+ end
224
+
225
+ def test_process_should_ping_all_connections_each_second
226
+ now = Time.now
227
+
228
+ new_channel = Proc.new do
229
+ ch = mock("channel")
230
+ ch.stubs(:now => now)
231
+ def ch.[](key)
232
+ case key
233
+ when :status then 0
234
+ when :closed then Time.now - now < 1.1 ? false : true
235
+ else raise "unknown key: #{key}"
236
+ end
237
+ end
238
+ con = mock("connection")
239
+ con.stubs(:process)
240
+ con.expects(:ping!)
241
+ ch.stubs(:connection).returns(con)
242
+ ch
243
+ end
244
+
245
+ sessions = [mock("session", :open_channel => new_channel[]),
246
+ mock("session", :open_channel => new_channel[]),
247
+ mock("session", :open_channel => new_channel[])]
248
+ cmd = Capistrano::Command.new("ls", sessions)
249
+ assert_nothing_raised { cmd.process! }
250
+ end
251
+
252
+ def test_process_should_instantiate_command_and_process!
253
+ cmd = mock("command", :process! => nil)
254
+ Capistrano::Command.expects(:new).with("ls -l", %w(a b c), {:foo => "bar"}).yields(:command).returns(cmd)
255
+ parameter = nil
256
+ Capistrano::Command.process("ls -l", %w(a b c), :foo => "bar") { |cmd| parameter = cmd }
257
+ assert_equal :command, parameter
258
+ end
259
+
260
+ def test_process_with_host_placeholder_should_substitute_placeholder_with_each_host
261
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
262
+ ch.expects(:exec).with(%(sh -c "echo capistrano"))
263
+ end
264
+ Capistrano::Command.new("echo $CAPISTRANO:HOST$", [session])
265
+ end
266
+
267
+ def test_process_with_unknown_placeholder_should_not_replace_placeholder
268
+ session = setup_for_extracting_channel_action(:on_success) do |ch|
269
+ ch.expects(:exec).with(%(sh -c "echo \\$CAPISTRANO:OTHER\\$"))
270
+ end
271
+ Capistrano::Command.new("echo $CAPISTRANO:OTHER$", [session])
272
+ end
273
+
274
+ private
275
+
276
+ def new_channel(closed, status=nil)
277
+ ch = mock("channel")
278
+ ch.expects(:[]).with(:closed).returns(closed)
279
+ ch.expects(:[]).with(:status).returns(status) if status
280
+ ch.expects(:close) unless closed
281
+ ch.stubs(:[]).with(:host).returns("capistrano")
282
+ ch.stubs(:[]).with(:server).returns(server("capistrano"))
283
+ ch
284
+ end
285
+
286
+ def setup_for_extracting_channel_action(action, *args)
287
+ s = server("capistrano")
288
+ session = mock("session", :xserver => s)
289
+
290
+ channel = stub_everything
291
+ session.expects(:open_channel).yields(channel)
292
+
293
+ ch = mock
294
+ ch.stubs(:[]).with(:server).returns(s)
295
+ ch.stubs(:[]).with(:host).returns(s.host)
296
+ channel.expects(action).yields(ch, *args)
297
+
298
+ yield ch if block_given?
299
+
300
+ session
301
+ end
43
302
  end