mbailey-capistrano 2.5.5

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 (105) hide show
  1. data/CHANGELOG.rdoc +761 -0
  2. data/Manifest +104 -0
  3. data/README.rdoc +66 -0
  4. data/Rakefile +34 -0
  5. data/bin/cap +4 -0
  6. data/bin/capify +78 -0
  7. data/examples/sample.rb +14 -0
  8. data/lib/capistrano/callback.rb +45 -0
  9. data/lib/capistrano/cli/execute.rb +84 -0
  10. data/lib/capistrano/cli/help.rb +125 -0
  11. data/lib/capistrano/cli/help.txt +75 -0
  12. data/lib/capistrano/cli/options.rb +224 -0
  13. data/lib/capistrano/cli/ui.rb +40 -0
  14. data/lib/capistrano/cli.rb +47 -0
  15. data/lib/capistrano/command.rb +283 -0
  16. data/lib/capistrano/configuration/actions/file_transfer.rb +47 -0
  17. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  18. data/lib/capistrano/configuration/actions/invocation.rb +293 -0
  19. data/lib/capistrano/configuration/callbacks.rb +148 -0
  20. data/lib/capistrano/configuration/connections.rb +200 -0
  21. data/lib/capistrano/configuration/execution.rb +132 -0
  22. data/lib/capistrano/configuration/loading.rb +197 -0
  23. data/lib/capistrano/configuration/namespaces.rb +197 -0
  24. data/lib/capistrano/configuration/roles.rb +73 -0
  25. data/lib/capistrano/configuration/servers.rb +85 -0
  26. data/lib/capistrano/configuration/variables.rb +127 -0
  27. data/lib/capistrano/configuration.rb +43 -0
  28. data/lib/capistrano/errors.rb +15 -0
  29. data/lib/capistrano/extensions.rb +57 -0
  30. data/lib/capistrano/logger.rb +59 -0
  31. data/lib/capistrano/processable.rb +53 -0
  32. data/lib/capistrano/recipes/compat.rb +32 -0
  33. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  34. data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
  35. data/lib/capistrano/recipes/deploy/remote_dependency.rb +105 -0
  36. data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
  37. data/lib/capistrano/recipes/deploy/scm/base.rb +196 -0
  38. data/lib/capistrano/recipes/deploy/scm/bzr.rb +83 -0
  39. data/lib/capistrano/recipes/deploy/scm/cvs.rb +152 -0
  40. data/lib/capistrano/recipes/deploy/scm/darcs.rb +85 -0
  41. data/lib/capistrano/recipes/deploy/scm/git.rb +271 -0
  42. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
  43. data/lib/capistrano/recipes/deploy/scm/none.rb +44 -0
  44. data/lib/capistrano/recipes/deploy/scm/perforce.rb +133 -0
  45. data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
  46. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  47. data/lib/capistrano/recipes/deploy/strategy/base.rb +79 -0
  48. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  49. data/lib/capistrano/recipes/deploy/strategy/copy.rb +210 -0
  50. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  51. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  52. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +56 -0
  53. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  54. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +53 -0
  55. data/lib/capistrano/recipes/deploy.rb +562 -0
  56. data/lib/capistrano/recipes/standard.rb +37 -0
  57. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  58. data/lib/capistrano/recipes/upgrade.rb +33 -0
  59. data/lib/capistrano/role.rb +102 -0
  60. data/lib/capistrano/server_definition.rb +56 -0
  61. data/lib/capistrano/shell.rb +260 -0
  62. data/lib/capistrano/ssh.rb +99 -0
  63. data/lib/capistrano/task_definition.rb +70 -0
  64. data/lib/capistrano/transfer.rb +216 -0
  65. data/lib/capistrano/version.rb +18 -0
  66. data/lib/capistrano.rb +2 -0
  67. data/setup.rb +1346 -0
  68. data/test/cli/execute_test.rb +132 -0
  69. data/test/cli/help_test.rb +165 -0
  70. data/test/cli/options_test.rb +317 -0
  71. data/test/cli/ui_test.rb +28 -0
  72. data/test/cli_test.rb +17 -0
  73. data/test/command_test.rb +286 -0
  74. data/test/configuration/actions/file_transfer_test.rb +61 -0
  75. data/test/configuration/actions/inspect_test.rb +65 -0
  76. data/test/configuration/actions/invocation_test.rb +224 -0
  77. data/test/configuration/callbacks_test.rb +220 -0
  78. data/test/configuration/connections_test.rb +349 -0
  79. data/test/configuration/execution_test.rb +175 -0
  80. data/test/configuration/loading_test.rb +132 -0
  81. data/test/configuration/namespace_dsl_test.rb +311 -0
  82. data/test/configuration/roles_test.rb +144 -0
  83. data/test/configuration/servers_test.rb +121 -0
  84. data/test/configuration/variables_test.rb +184 -0
  85. data/test/configuration_test.rb +88 -0
  86. data/test/deploy/local_dependency_test.rb +76 -0
  87. data/test/deploy/remote_dependency_test.rb +114 -0
  88. data/test/deploy/scm/accurev_test.rb +23 -0
  89. data/test/deploy/scm/base_test.rb +55 -0
  90. data/test/deploy/scm/git_test.rb +167 -0
  91. data/test/deploy/scm/mercurial_test.rb +129 -0
  92. data/test/deploy/strategy/copy_test.rb +258 -0
  93. data/test/extensions_test.rb +69 -0
  94. data/test/fixtures/cli_integration.rb +5 -0
  95. data/test/fixtures/config.rb +5 -0
  96. data/test/fixtures/custom.rb +3 -0
  97. data/test/logger_test.rb +123 -0
  98. data/test/role_test.rb +11 -0
  99. data/test/server_definition_test.rb +121 -0
  100. data/test/shell_test.rb +90 -0
  101. data/test/ssh_test.rb +104 -0
  102. data/test/task_definition_test.rb +101 -0
  103. data/test/transfer_test.rb +160 -0
  104. data/test/utils.rb +38 -0
  105. metadata +205 -0
@@ -0,0 +1,286 @@
1
+ require "utils"
2
+ require 'capistrano/command'
3
+ require 'capistrano/configuration'
4
+
5
+ class CommandTest < Test::Unit::TestCase
6
+ def test_command_should_open_channels_on_all_sessions
7
+ s1, s2, s3 = mock_session, mock_session, mock_session
8
+ assert_equal "ls", Capistrano::Command.new("ls", [s1, s2, s3]).tree.fallback.command
9
+ end
10
+
11
+ def test_command_with_newlines_should_be_properly_escaped
12
+ cmd = Capistrano::Command.new("ls\necho", [mock_session])
13
+ assert_equal "ls\\\necho", cmd.tree.fallback.command
14
+ end
15
+
16
+ def test_command_with_windows_newlines_should_be_properly_escaped
17
+ cmd = Capistrano::Command.new("ls\r\necho", [mock_session])
18
+ assert_equal "ls\\\necho", cmd.tree.fallback.command
19
+ end
20
+
21
+ def test_command_with_pty_should_request_pty_and_register_success_callback
22
+ session = setup_for_extracting_channel_action(:request_pty, true) do |ch|
23
+ ch.expects(:exec).with(%(sh -c "ls"))
24
+ end
25
+ Capistrano::Command.new("ls", [session], :pty => true)
26
+ end
27
+
28
+ def test_command_with_env_key_should_have_environment_constructed_and_prepended
29
+ session = setup_for_extracting_channel_action do |ch|
30
+ ch.expects(:request_pty).never
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_with_symbolic_key_should_be_accepted_as_a_string
37
+ session = setup_for_extracting_channel_action do |ch|
38
+ ch.expects(:exec).with(%(env FOO=bar sh -c "ls"))
39
+ end
40
+ Capistrano::Command.new("ls", [session], :env => { :FOO => "bar" })
41
+ end
42
+
43
+ def test_env_as_string_should_be_substituted_in_directly
44
+ session = setup_for_extracting_channel_action do |ch|
45
+ ch.expects(:exec).with(%(env HOWDY=there sh -c "ls"))
46
+ end
47
+ Capistrano::Command.new("ls", [session], :env => "HOWDY=there")
48
+ end
49
+
50
+ def test_env_with_symbolic_value_should_be_accepted_as_string
51
+ session = setup_for_extracting_channel_action 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_value_should_be_escaped
58
+ session = setup_for_extracting_channel_action do |ch|
59
+ ch.expects(:exec).with(%(env FOO=(\\ \\\"bar\\\"\\ ) sh -c "ls"))
60
+ end
61
+ Capistrano::Command.new("ls", [session], :env => { "FOO" => '( "bar" )' })
62
+ end
63
+
64
+ def test_env_with_multiple_keys_should_chain_the_entries_together
65
+ session = setup_for_extracting_channel_action do |ch|
66
+ ch.expects(:exec).with do |command|
67
+ command =~ /^env / &&
68
+ command =~ /\ba=b\b/ &&
69
+ command =~ /\bc=d\b/ &&
70
+ command =~ /\be=f\b/ &&
71
+ command =~ / sh -c "ls"$/
72
+ end
73
+ end
74
+ Capistrano::Command.new("ls", [session], :env => { :a => :b, :c => :d, :e => :f })
75
+ end
76
+
77
+ def test_open_channel_should_set_host_key_on_channel
78
+ channel = nil
79
+ session = setup_for_extracting_channel_action { |ch| channel = ch }
80
+ Capistrano::Command.new("ls", [session])
81
+ assert_equal "capistrano", channel[:host]
82
+ end
83
+
84
+ def test_open_channel_should_set_options_key_on_channel
85
+ channel = nil
86
+ session = setup_for_extracting_channel_action { |ch| channel = ch }
87
+ Capistrano::Command.new("ls", [session], :data => "here we go")
88
+ assert_equal({ :data => 'here we go' }, channel[:options])
89
+ end
90
+
91
+ def test_successful_channel_should_send_command
92
+ session = setup_for_extracting_channel_action do |ch|
93
+ ch.expects(:exec).with(%(sh -c "ls"))
94
+ end
95
+ Capistrano::Command.new("ls", [session])
96
+ end
97
+
98
+ def test_successful_channel_with_shell_option_should_send_command_via_specified_shell
99
+ session = setup_for_extracting_channel_action do |ch|
100
+ ch.expects(:exec).with(%(/bin/bash -c "ls"))
101
+ end
102
+ Capistrano::Command.new("ls", [session], :shell => "/bin/bash")
103
+ end
104
+
105
+ def test_successful_channel_with_shell_false_should_send_command_without_shell
106
+ session = setup_for_extracting_channel_action do |ch|
107
+ ch.expects(:exec).with(%(echo `hostname`))
108
+ end
109
+ Capistrano::Command.new("echo `hostname`", [session], :shell => false)
110
+ end
111
+
112
+ def test_successful_channel_should_send_data_if_data_key_is_present
113
+ session = setup_for_extracting_channel_action do |ch|
114
+ ch.expects(:exec).with(%(sh -c "ls"))
115
+ ch.expects(:send_data).with("here we go")
116
+ end
117
+ Capistrano::Command.new("ls", [session], :data => "here we go")
118
+ end
119
+
120
+ def test_unsuccessful_pty_request_should_close_channel
121
+ session = setup_for_extracting_channel_action(:request_pty, false) do |ch|
122
+ ch.expects(:close)
123
+ end
124
+ Capistrano::Command.new("ls", [session], :pty => true)
125
+ end
126
+
127
+ def test_on_data_should_invoke_callback_as_stdout
128
+ session = setup_for_extracting_channel_action(:on_data, "hello")
129
+ called = false
130
+ Capistrano::Command.new("ls", [session]) do |ch, stream, data|
131
+ called = true
132
+ assert_equal :out, stream
133
+ assert_equal "hello", data
134
+ end
135
+ assert called
136
+ end
137
+
138
+ def test_on_extended_data_should_invoke_callback_as_stderr
139
+ session = setup_for_extracting_channel_action(:on_extended_data, 2, "hello")
140
+ called = false
141
+ Capistrano::Command.new("ls", [session]) do |ch, stream, data|
142
+ called = true
143
+ assert_equal :err, stream
144
+ assert_equal "hello", data
145
+ end
146
+ assert called
147
+ end
148
+
149
+ def test_on_request_should_record_exit_status
150
+ data = mock(:read_long => 5)
151
+ channel = nil
152
+ session = setup_for_extracting_channel_action([:on_request, "exit-status"], data) { |ch| channel = ch }
153
+ Capistrano::Command.new("ls", [session])
154
+ assert_equal 5, channel[:status]
155
+ end
156
+
157
+ def test_on_close_should_set_channel_closed
158
+ channel = nil
159
+ session = setup_for_extracting_channel_action(:on_close) { |ch| channel = ch }
160
+ Capistrano::Command.new("ls", [session])
161
+ assert channel[:closed]
162
+ end
163
+
164
+ def test_stop_should_close_all_open_channels
165
+ sessions = [mock_session(new_channel(false)),
166
+ mock_session(new_channel(true)),
167
+ mock_session(new_channel(false))]
168
+
169
+ cmd = Capistrano::Command.new("ls", sessions)
170
+ cmd.stop!
171
+ end
172
+
173
+ def test_process_should_return_cleanly_if_all_channels_have_zero_exit_status
174
+ sessions = [mock_session(new_channel(true, 0)),
175
+ mock_session(new_channel(true, 0)),
176
+ mock_session(new_channel(true, 0))]
177
+ cmd = Capistrano::Command.new("ls", sessions)
178
+ assert_nothing_raised { cmd.process! }
179
+ end
180
+
181
+ def test_process_should_raise_error_if_any_channel_has_non_zero_exit_status
182
+ sessions = [mock_session(new_channel(true, 0)),
183
+ mock_session(new_channel(true, 0)),
184
+ mock_session(new_channel(true, 1))]
185
+ cmd = Capistrano::Command.new("ls", sessions)
186
+ assert_raises(Capistrano::CommandError) { cmd.process! }
187
+ end
188
+
189
+ def test_command_error_should_include_accessor_with_host_array
190
+ sessions = [mock_session(new_channel(true, 0)),
191
+ mock_session(new_channel(true, 0)),
192
+ mock_session(new_channel(true, 1))]
193
+ cmd = Capistrano::Command.new("ls", sessions)
194
+
195
+ begin
196
+ cmd.process!
197
+ flunk "expected an exception to be raised"
198
+ rescue Capistrano::CommandError => e
199
+ assert e.respond_to?(:hosts)
200
+ assert_equal %w(capistrano), e.hosts.map { |h| h.to_s }
201
+ end
202
+ end
203
+
204
+ def test_process_should_loop_until_all_channels_are_closed
205
+ new_channel = Proc.new do |times|
206
+ ch = mock("channel")
207
+ returns = [false] * (times-1)
208
+ ch.stubs(:[]).with(:closed).returns(*(returns + [true]))
209
+ ch.expects(:[]).with(:status).returns(0)
210
+ ch
211
+ end
212
+
213
+ sessions = [mock_session(new_channel[5]),
214
+ mock_session(new_channel[10]),
215
+ mock_session(new_channel[7])]
216
+ cmd = Capistrano::Command.new("ls", sessions)
217
+ assert_nothing_raised { cmd.process! }
218
+ end
219
+
220
+ def test_process_should_instantiate_command_and_process!
221
+ cmd = mock("command", :process! => nil)
222
+ Capistrano::Command.expects(:new).with("ls -l", %w(a b c), {:foo => "bar"}).returns(cmd)
223
+ Capistrano::Command.process("ls -l", %w(a b c), :foo => "bar")
224
+ end
225
+
226
+ def test_process_with_host_placeholder_should_substitute_placeholder_with_each_host
227
+ session = setup_for_extracting_channel_action do |ch|
228
+ ch.expects(:exec).with(%(sh -c "echo capistrano"))
229
+ end
230
+ Capistrano::Command.new("echo $CAPISTRANO:HOST$", [session])
231
+ end
232
+
233
+ def test_process_with_unknown_placeholder_should_not_replace_placeholder
234
+ session = setup_for_extracting_channel_action do |ch|
235
+ ch.expects(:exec).with(%(sh -c "echo \\$CAPISTRANO:OTHER\\$"))
236
+ end
237
+ Capistrano::Command.new("echo $CAPISTRANO:OTHER$", [session])
238
+ end
239
+
240
+ private
241
+
242
+ def mock_session(channel=nil)
243
+ stub('session', :open_channel => channel,
244
+ :preprocess => true,
245
+ :postprocess => true,
246
+ :listeners => {},
247
+ :xserver => server("capistrano"))
248
+ end
249
+
250
+ class MockChannel < Hash
251
+ def close
252
+ end
253
+ end
254
+
255
+ def new_channel(closed, status=nil)
256
+ ch = MockChannel.new
257
+ ch.update({ :closed => closed, :host => "capistrano", :server => server("capistrano") })
258
+ ch[:status] = status if status
259
+ ch.expects(:close) unless closed
260
+ ch
261
+ end
262
+
263
+ def setup_for_extracting_channel_action(action=nil, *args)
264
+ s = server("capistrano")
265
+ session = mock("session", :xserver => s)
266
+
267
+ channel = {}
268
+ session.expects(:open_channel).yields(channel)
269
+
270
+ channel.stubs(:on_data)
271
+ channel.stubs(:on_extended_data)
272
+ channel.stubs(:on_request)
273
+ channel.stubs(:on_close)
274
+ channel.stubs(:exec)
275
+ channel.stubs(:send_data)
276
+
277
+ if action
278
+ action = Array(action)
279
+ channel.expects(action.first).with(*action[1..-1]).yields(channel, *args)
280
+ end
281
+
282
+ yield channel if block_given?
283
+
284
+ session
285
+ end
286
+ end
@@ -0,0 +1,61 @@
1
+ require "utils"
2
+ require 'capistrano/configuration/actions/file_transfer'
3
+
4
+ class ConfigurationActionsFileTransferTest < Test::Unit::TestCase
5
+ class MockConfig
6
+ include Capistrano::Configuration::Actions::FileTransfer
7
+ attr_accessor :sessions
8
+ end
9
+
10
+ def setup
11
+ @config = MockConfig.new
12
+ @config.stubs(:logger).returns(stub_everything)
13
+ end
14
+
15
+ def test_put_should_delegate_to_upload
16
+ @config.expects(:upload).with { |from, to, opts|
17
+ from.string == "some data" && to == "test.txt" && opts == { :mode => 0777 } }
18
+ @config.expects(:run).never
19
+ @config.put("some data", "test.txt", :mode => 0777)
20
+ end
21
+
22
+ def test_get_should_delegate_to_download_with_once
23
+ @config.expects(:download).with("testr.txt", "testl.txt", :foo => "bar", :once => true)
24
+ @config.get("testr.txt", "testl.txt", :foo => "bar")
25
+ end
26
+
27
+ def test_upload_should_delegate_to_transfer
28
+ @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", :foo => "bar")
29
+ @config.upload("testl.txt", "testr.txt", :foo => "bar")
30
+ end
31
+
32
+ def test_upload_without_mode_should_not_try_to_chmod
33
+ @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", :foo => "bar")
34
+ @config.expects(:run).never
35
+ @config.upload("testl.txt", "testr.txt", :foo => "bar")
36
+ end
37
+
38
+ def test_upload_with_mode_should_try_to_chmod
39
+ @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", :foo => "bar")
40
+ @config.expects(:run).with("chmod 775 testr.txt")
41
+ @config.upload("testl.txt", "testr.txt", :mode => 0775, :foo => "bar")
42
+ end
43
+
44
+ def test_upload_with_symbolic_mode_should_try_to_chmod
45
+ @config.expects(:transfer).with(:up, "testl.txt", "testr.txt", :foo => "bar")
46
+ @config.expects(:run).with("chmod g+w testr.txt")
47
+ @config.upload("testl.txt", "testr.txt", :mode => "g+w", :foo => "bar")
48
+ end
49
+
50
+ def test_download_should_delegate_to_transfer
51
+ @config.expects(:transfer).with(:down, "testr.txt", "testl.txt", :foo => "bar")
52
+ @config.download("testr.txt", "testl.txt", :foo => "bar")
53
+ end
54
+
55
+ def test_transfer_should_invoke_transfer_on_matching_servers
56
+ @config.sessions = { :a => 1, :b => 2, :c => 3, :d => 4 }
57
+ @config.expects(:execute_on_servers).with(:foo => "bar").yields([:a, :b, :c])
58
+ Capistrano::Transfer.expects(:process).with(:up, "testl.txt", "testr.txt", [1,2,3], {:foo => "bar", :logger => @config.logger})
59
+ @config.transfer(:up, "testl.txt", "testr.txt", :foo => "bar")
60
+ end
61
+ end
@@ -0,0 +1,65 @@
1
+ require "utils"
2
+ require 'capistrano/configuration/actions/inspect'
3
+
4
+ class ConfigurationActionsInspectTest < Test::Unit::TestCase
5
+ class MockConfig
6
+ include Capistrano::Configuration::Actions::Inspect
7
+ end
8
+
9
+ def setup
10
+ @config = MockConfig.new
11
+ @config.stubs(:logger).returns(stub_everything)
12
+ end
13
+
14
+ def test_stream_should_pass_options_through_to_run
15
+ @config.expects(:invoke_command).with("tail -f foo.log", :once => true)
16
+ @config.stream("tail -f foo.log", :once => true)
17
+ end
18
+
19
+ def test_stream_should_emit_stdout_via_puts
20
+ @config.expects(:invoke_command).yields(mock("channel"), :out, "something streamed")
21
+ @config.expects(:puts).with("something streamed")
22
+ @config.expects(:warn).never
23
+ @config.stream("tail -f foo.log")
24
+ end
25
+
26
+ def test_stream_should_emit_stderr_via_warn
27
+ ch = mock("channel")
28
+ ch.expects(:[]).with(:server).returns(server("capistrano"))
29
+ @config.expects(:invoke_command).yields(ch, :err, "something streamed")
30
+ @config.expects(:puts).never
31
+ @config.expects(:warn).with("[err :: capistrano] something streamed")
32
+ @config.stream("tail -f foo.log")
33
+ end
34
+
35
+ def test_capture_should_pass_options_merged_with_once_to_run
36
+ @config.expects(:invoke_command).with("hostname", :foo => "bar", :once => true)
37
+ @config.capture("hostname", :foo => "bar")
38
+ end
39
+
40
+ def test_capture_with_stderr_should_emit_stderr_via_warn
41
+ ch = mock("channel")
42
+ ch.expects(:[]).with(:server).returns(server("capistrano"))
43
+ @config.expects(:invoke_command).yields(ch, :err, "boom")
44
+ @config.expects(:warn).with("[err :: capistrano] boom")
45
+ @config.capture("hostname")
46
+ end
47
+
48
+ def test_capture_with_stdout_should_aggregate_and_return_stdout
49
+ config_expects_invoke_command_to_loop_with(mock("channel"), "foo", "bar", "baz")
50
+ assert_equal "foobarbaz", @config.capture("hostname")
51
+ end
52
+
53
+ private
54
+
55
+ def config_expects_invoke_command_to_loop_with(channel, *output)
56
+ class <<@config
57
+ attr_accessor :script, :channel
58
+ def invoke_command(*args)
59
+ script.each { |item| yield channel, :out, item }
60
+ end
61
+ end
62
+ @config.channel = channel
63
+ @config.script = output
64
+ end
65
+ end
@@ -0,0 +1,224 @@
1
+ require "utils"
2
+ require 'capistrano/configuration/actions/invocation'
3
+
4
+ class ConfigurationActionsInvocationTest < Test::Unit::TestCase
5
+ class MockConfig
6
+ attr_reader :options
7
+ attr_accessor :debug
8
+ attr_accessor :dry_run
9
+
10
+ def initialize
11
+ @options = {}
12
+ end
13
+
14
+ def [](*args)
15
+ @options[*args]
16
+ end
17
+
18
+ def set(name, value)
19
+ @options[name] = value
20
+ end
21
+
22
+ def fetch(*args)
23
+ @options.fetch(*args)
24
+ end
25
+
26
+ include Capistrano::Configuration::Actions::Invocation
27
+ end
28
+
29
+ def setup
30
+ @config = MockConfig.new
31
+ @original_io_proc = MockConfig.default_io_proc
32
+ @config.stubs(:logger).returns(stub_everything)
33
+ end
34
+
35
+ def teardown
36
+ MockConfig.default_io_proc = @original_io_proc
37
+ end
38
+
39
+ def test_run_options_should_be_passed_to_execute_on_servers
40
+ @config.expects(:execute_on_servers).with(:foo => "bar")
41
+ @config.run "ls", :foo => "bar"
42
+ end
43
+
44
+ def test_run_will_return_if_dry_run
45
+ @config.expects(:dry_run).returns(true)
46
+ @config.expects(:execute_on_servers).never
47
+ @config.run "ls", :foo => "bar"
48
+ end
49
+
50
+ def test_add_default_command_options_should_return_bare_options_if_there_is_no_env_or_shell_specified
51
+ assert_equal({:foo => "bar"}, @config.add_default_command_options(:foo => "bar"))
52
+ end
53
+
54
+ def test_add_default_command_options_should_merge_default_environment_as_env
55
+ @config[:default_environment][:bang] = "baz"
56
+ assert_equal({:foo => "bar", :env => { :bang => "baz" }}, @config.add_default_command_options(:foo => "bar"))
57
+ end
58
+
59
+ def test_add_default_command_options_should_merge_env_with_default_environment
60
+ @config[:default_environment][:bang] = "baz"
61
+ @config[:default_environment][:bacon] = "crunchy"
62
+ assert_equal({:foo => "bar", :env => { :bang => "baz", :bacon => "chunky", :flip => "flop" }}, @config.add_default_command_options(:foo => "bar", :env => {:bacon => "chunky", :flip => "flop"}))
63
+ end
64
+
65
+ def test_add_default_command_options_should_use_default_shell_if_present
66
+ @config.set :default_shell, "/bin/bash"
67
+ assert_equal({:foo => "bar", :shell => "/bin/bash"}, @config.add_default_command_options(:foo => "bar"))
68
+ end
69
+
70
+ def test_add_default_command_options_should_use_default_shell_of_false_if_present
71
+ @config.set :default_shell, false
72
+ assert_equal({:foo => "bar", :shell => false}, @config.add_default_command_options(:foo => "bar"))
73
+ end
74
+
75
+ def test_add_default_command_options_should_use_shell_in_preference_of_default_shell
76
+ @config.set :default_shell, "/bin/bash"
77
+ assert_equal({:foo => "bar", :shell => "/bin/sh"}, @config.add_default_command_options(:foo => "bar", :shell => "/bin/sh"))
78
+ end
79
+
80
+ def test_default_io_proc_should_log_stdout_arguments_as_info
81
+ ch = { :host => "capistrano",
82
+ :server => server("capistrano"),
83
+ :options => { :logger => mock("logger") } }
84
+ ch[:options][:logger].expects(:info).with("data stuff", "out :: capistrano")
85
+ MockConfig.default_io_proc[ch, :out, "data stuff"]
86
+ end
87
+
88
+ def test_default_io_proc_should_log_stderr_arguments_as_important
89
+ ch = { :host => "capistrano",
90
+ :server => server("capistrano"),
91
+ :options => { :logger => mock("logger") } }
92
+ ch[:options][:logger].expects(:important).with("data stuff", "err :: capistrano")
93
+ MockConfig.default_io_proc[ch, :err, "data stuff"]
94
+ end
95
+
96
+ def test_sudo_should_default_to_sudo
97
+ @config.expects(:run).with("sudo -p 'sudo password: ' ls", {})
98
+ @config.sudo "ls"
99
+ end
100
+
101
+ def test_sudo_should_use_sudo_variable_definition
102
+ @config.expects(:run).with("/opt/local/bin/sudo -p 'sudo password: ' ls", {})
103
+ @config.options[:sudo] = "/opt/local/bin/sudo"
104
+ @config.sudo "ls"
105
+ end
106
+
107
+ def test_sudo_should_interpret_as_option_as_user
108
+ @config.expects(:run).with("sudo -p 'sudo password: ' -u app ls", {})
109
+ @config.sudo "ls", :as => "app"
110
+ end
111
+
112
+ def test_sudo_should_pass_options_through_to_run
113
+ @config.expects(:run).with("sudo -p 'sudo password: ' ls", :foo => "bar")
114
+ @config.sudo "ls", :foo => "bar"
115
+ end
116
+
117
+ def test_sudo_should_avoid_minus_p_when_sudo_prompt_is_empty
118
+ @config.set :sudo_prompt, ""
119
+ @config.expects(:run).with("sudo ls", {})
120
+ @config.sudo "ls"
121
+ end
122
+
123
+ def test_sudo_should_interpret_sudo_prompt_variable_as_custom_prompt
124
+ @config.set :sudo_prompt, "give it to me: "
125
+ @config.expects(:run).with("sudo -p 'give it to me: ' ls", {})
126
+ @config.sudo "ls"
127
+ end
128
+
129
+ def test_sudo_behavior_callback_should_send_password_when_prompted_with_default_sudo_prompt
130
+ ch = mock("channel")
131
+ ch.expects(:send_data).with("g00b3r\n")
132
+ @config.options[:password] = "g00b3r"
133
+ @config.sudo_behavior_callback(nil)[ch, nil, "sudo password: "]
134
+ end
135
+
136
+ def test_sudo_behavior_callback_should_send_password_when_prompted_with_custom_sudo_prompt
137
+ ch = mock("channel")
138
+ ch.expects(:send_data).with("g00b3r\n")
139
+ @config.set :sudo_prompt, "give it to me: "
140
+ @config.options[:password] = "g00b3r"
141
+ @config.sudo_behavior_callback(nil)[ch, nil, "give it to me: "]
142
+ end
143
+
144
+ def test_sudo_behavior_callback_with_incorrect_password_on_first_prompt
145
+ ch = mock("channel")
146
+ ch.stubs(:[]).with(:host).returns("capistrano")
147
+ ch.stubs(:[]).with(:server).returns(server("capistrano"))
148
+ @config.expects(:reset!).with(:password)
149
+ @config.sudo_behavior_callback(nil)[ch, nil, "Sorry, try again."]
150
+ end
151
+
152
+ def test_sudo_behavior_callback_with_incorrect_password_on_subsequent_prompts
153
+ callback = @config.sudo_behavior_callback(nil)
154
+
155
+ ch = mock("channel")
156
+ ch.stubs(:[]).with(:host).returns("capistrano")
157
+ ch.stubs(:[]).with(:server).returns(server("capistrano"))
158
+ ch2 = mock("channel")
159
+ ch2.stubs(:[]).with(:host).returns("cap2")
160
+ ch2.stubs(:[]).with(:server).returns(server("cap2"))
161
+
162
+ @config.expects(:reset!).with(:password).times(2)
163
+
164
+ callback[ch, nil, "Sorry, try again."]
165
+ callback[ch2, nil, "Sorry, try again."] # shouldn't call reset!
166
+ callback[ch, nil, "Sorry, try again."]
167
+ end
168
+
169
+ def test_sudo_behavior_callback_should_reset_password_and_prompt_again_if_output_includes_both_cues
170
+ ch = mock("channel")
171
+ ch.stubs(:[]).with(:host).returns("capistrano")
172
+ ch.stubs(:[]).with(:server).returns(server("capistrano"))
173
+ ch.expects(:send_data, "password!\n").times(2)
174
+
175
+ @config.set(:password, "password!")
176
+ @config.expects(:reset!).with(:password)
177
+
178
+ callback = @config.sudo_behavior_callback(nil)
179
+ callback[ch, :out, "sudo password: "]
180
+ callback[ch, :out, "Sorry, try again.\nsudo password: "]
181
+ end
182
+
183
+ def test_sudo_behavior_callback_should_defer_to_fallback_for_other_output
184
+ callback = @config.sudo_behavior_callback(inspectable_proc)
185
+
186
+ a = mock("channel", :called => true)
187
+ b = mock("stream", :called => true)
188
+ c = mock("data", :called => true)
189
+
190
+ callback[a, b, c]
191
+ end
192
+
193
+ def test_invoke_command_should_default_to_run
194
+ @config.expects(:run).with("ls", :once => true)
195
+ @config.invoke_command("ls", :once => true)
196
+ end
197
+
198
+ def test_invoke_command_should_delegate_to_method_identified_by_via
199
+ @config.expects(:foobar).with("ls", :once => true)
200
+ @config.invoke_command("ls", :once => true, :via => :foobar)
201
+ end
202
+
203
+ private
204
+
205
+ def inspectable_proc
206
+ Proc.new do |ch, stream, data|
207
+ ch.called
208
+ stream.called
209
+ data.called
210
+ end
211
+ end
212
+
213
+ def prepare_command(command, sessions, options)
214
+ a = mock("channel", :called => true)
215
+ b = mock("stream", :called => true)
216
+ c = mock("data", :called => true)
217
+
218
+ compare_args = Proc.new do |tree, sess, opts|
219
+ tree.fallback.command == command && sess == sessions && opts == options
220
+ end
221
+
222
+ Capistrano::Command.expects(:process).with(&compare_args)
223
+ end
224
+ end