minmb-capistrano 2.15.4

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 (119) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +7 -0
  3. data/CHANGELOG +1170 -0
  4. data/Gemfile +13 -0
  5. data/README.md +94 -0
  6. data/Rakefile +11 -0
  7. data/bin/cap +4 -0
  8. data/bin/capify +92 -0
  9. data/capistrano.gemspec +40 -0
  10. data/lib/capistrano.rb +5 -0
  11. data/lib/capistrano/callback.rb +45 -0
  12. data/lib/capistrano/cli.rb +47 -0
  13. data/lib/capistrano/cli/execute.rb +85 -0
  14. data/lib/capistrano/cli/help.rb +125 -0
  15. data/lib/capistrano/cli/help.txt +81 -0
  16. data/lib/capistrano/cli/options.rb +243 -0
  17. data/lib/capistrano/cli/ui.rb +40 -0
  18. data/lib/capistrano/command.rb +303 -0
  19. data/lib/capistrano/configuration.rb +57 -0
  20. data/lib/capistrano/configuration/actions/file_transfer.rb +50 -0
  21. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  22. data/lib/capistrano/configuration/actions/invocation.rb +329 -0
  23. data/lib/capistrano/configuration/alias_task.rb +26 -0
  24. data/lib/capistrano/configuration/callbacks.rb +147 -0
  25. data/lib/capistrano/configuration/connections.rb +237 -0
  26. data/lib/capistrano/configuration/execution.rb +142 -0
  27. data/lib/capistrano/configuration/loading.rb +205 -0
  28. data/lib/capistrano/configuration/log_formatters.rb +75 -0
  29. data/lib/capistrano/configuration/namespaces.rb +223 -0
  30. data/lib/capistrano/configuration/roles.rb +77 -0
  31. data/lib/capistrano/configuration/servers.rb +116 -0
  32. data/lib/capistrano/configuration/variables.rb +127 -0
  33. data/lib/capistrano/errors.rb +19 -0
  34. data/lib/capistrano/ext/multistage.rb +64 -0
  35. data/lib/capistrano/ext/string.rb +5 -0
  36. data/lib/capistrano/extensions.rb +57 -0
  37. data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
  38. data/lib/capistrano/logger.rb +166 -0
  39. data/lib/capistrano/processable.rb +57 -0
  40. data/lib/capistrano/recipes/compat.rb +32 -0
  41. data/lib/capistrano/recipes/deploy.rb +625 -0
  42. data/lib/capistrano/recipes/deploy/assets.rb +201 -0
  43. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  44. data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
  45. data/lib/capistrano/recipes/deploy/remote_dependency.rb +117 -0
  46. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  47. data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
  48. data/lib/capistrano/recipes/deploy/scm/base.rb +200 -0
  49. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  50. data/lib/capistrano/recipes/deploy/scm/cvs.rb +153 -0
  51. data/lib/capistrano/recipes/deploy/scm/darcs.rb +96 -0
  52. data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
  53. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
  54. data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
  55. data/lib/capistrano/recipes/deploy/scm/perforce.rb +152 -0
  56. data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
  57. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  58. data/lib/capistrano/recipes/deploy/strategy/base.rb +92 -0
  59. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  60. data/lib/capistrano/recipes/deploy/strategy/copy.rb +338 -0
  61. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  62. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  63. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +57 -0
  64. data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
  65. data/lib/capistrano/recipes/standard.rb +37 -0
  66. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  67. data/lib/capistrano/role.rb +102 -0
  68. data/lib/capistrano/server_definition.rb +56 -0
  69. data/lib/capistrano/shell.rb +265 -0
  70. data/lib/capistrano/ssh.rb +95 -0
  71. data/lib/capistrano/task_definition.rb +77 -0
  72. data/lib/capistrano/transfer.rb +218 -0
  73. data/lib/capistrano/version.rb +11 -0
  74. data/test/cli/execute_test.rb +132 -0
  75. data/test/cli/help_test.rb +165 -0
  76. data/test/cli/options_test.rb +329 -0
  77. data/test/cli/ui_test.rb +28 -0
  78. data/test/cli_test.rb +17 -0
  79. data/test/command_test.rb +322 -0
  80. data/test/configuration/actions/file_transfer_test.rb +61 -0
  81. data/test/configuration/actions/inspect_test.rb +76 -0
  82. data/test/configuration/actions/invocation_test.rb +288 -0
  83. data/test/configuration/alias_task_test.rb +118 -0
  84. data/test/configuration/callbacks_test.rb +201 -0
  85. data/test/configuration/connections_test.rb +439 -0
  86. data/test/configuration/execution_test.rb +175 -0
  87. data/test/configuration/loading_test.rb +148 -0
  88. data/test/configuration/namespace_dsl_test.rb +332 -0
  89. data/test/configuration/roles_test.rb +157 -0
  90. data/test/configuration/servers_test.rb +183 -0
  91. data/test/configuration/variables_test.rb +190 -0
  92. data/test/configuration_test.rb +77 -0
  93. data/test/deploy/local_dependency_test.rb +76 -0
  94. data/test/deploy/remote_dependency_test.rb +146 -0
  95. data/test/deploy/scm/accurev_test.rb +23 -0
  96. data/test/deploy/scm/base_test.rb +55 -0
  97. data/test/deploy/scm/bzr_test.rb +51 -0
  98. data/test/deploy/scm/darcs_test.rb +37 -0
  99. data/test/deploy/scm/git_test.rb +221 -0
  100. data/test/deploy/scm/mercurial_test.rb +134 -0
  101. data/test/deploy/scm/none_test.rb +35 -0
  102. data/test/deploy/scm/perforce_test.rb +23 -0
  103. data/test/deploy/scm/subversion_test.rb +40 -0
  104. data/test/deploy/strategy/copy_test.rb +360 -0
  105. data/test/extensions_test.rb +69 -0
  106. data/test/fixtures/cli_integration.rb +5 -0
  107. data/test/fixtures/config.rb +5 -0
  108. data/test/fixtures/custom.rb +3 -0
  109. data/test/logger_formatting_test.rb +149 -0
  110. data/test/logger_test.rb +134 -0
  111. data/test/recipes_test.rb +25 -0
  112. data/test/role_test.rb +11 -0
  113. data/test/server_definition_test.rb +121 -0
  114. data/test/shell_test.rb +96 -0
  115. data/test/ssh_test.rb +113 -0
  116. data/test/task_definition_test.rb +117 -0
  117. data/test/transfer_test.rb +168 -0
  118. data/test/utils.rb +37 -0
  119. metadata +316 -0
@@ -0,0 +1,439 @@
1
+ require "utils"
2
+ require 'capistrano/configuration/connections'
3
+
4
+ class ConfigurationConnectionsTest < Test::Unit::TestCase
5
+ class MockConfig
6
+ attr_reader :original_initialize_called
7
+ attr_reader :values
8
+ attr_reader :dry_run
9
+ attr_accessor :current_task
10
+
11
+ def initialize
12
+ @original_initialize_called = true
13
+ @values = {}
14
+ end
15
+
16
+ def fetch(*args)
17
+ @values.fetch(*args)
18
+ end
19
+
20
+ def [](key)
21
+ @values[key]
22
+ end
23
+
24
+ def exists?(key)
25
+ @values.key?(key)
26
+ end
27
+
28
+ include Capistrano::Configuration::Connections
29
+ end
30
+
31
+ def setup
32
+ @config = MockConfig.new
33
+ @config.stubs(:logger).returns(stub_everything)
34
+ Net::SSH.stubs(:configuration_for).returns({})
35
+ @ssh_options = {
36
+ :user => "user",
37
+ :port => 8080,
38
+ :password => "g00b3r",
39
+ :ssh_options => { :debug => :verbose }
40
+ }
41
+ end
42
+
43
+ def test_initialize_should_initialize_collections_and_call_original_initialize
44
+ assert @config.original_initialize_called
45
+ assert @config.sessions.empty?
46
+ end
47
+
48
+ def test_connection_factory_should_return_default_connection_factory_instance
49
+ factory = @config.connection_factory
50
+ assert_instance_of Capistrano::Configuration::Connections::DefaultConnectionFactory, factory
51
+ end
52
+
53
+ def test_connection_factory_instance_should_be_cached
54
+ assert_same @config.connection_factory, @config.connection_factory
55
+ end
56
+
57
+ def test_default_connection_factory_honors_config_options
58
+ server = server("capistrano")
59
+ Capistrano::SSH.expects(:connect).with(server, @config).returns(:session)
60
+ assert_equal :session, @config.connection_factory.connect_to(server)
61
+ end
62
+
63
+ def test_should_connect_through_gateway_if_gateway_variable_is_set
64
+ @config.values[:gateway] = "j@gateway"
65
+ Net::SSH::Gateway.expects(:new).with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
66
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
67
+ end
68
+
69
+ def test_connection_factory_as_gateway_should_honor_config_options
70
+ @config.values[:gateway] = "gateway"
71
+ @config.values.update(@ssh_options)
72
+ Net::SSH::Gateway.expects(:new).with("gateway", "user", :debug => :verbose, :port => 8080, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
73
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
74
+ end
75
+
76
+ def test_connection_factory_as_gateway_should_chain_gateways_if_gateway_variable_is_an_array
77
+ @config.values[:gateway] = ["j@gateway1", "k@gateway2"]
78
+ gateway1 = mock
79
+ Net::SSH::Gateway.expects(:new).with("gateway1", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(gateway1)
80
+ gateway1.expects(:open).returns(65535)
81
+ Net::SSH::Gateway.expects(:new).with("127.0.0.1", "k", :port => 65535, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
82
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
83
+ end
84
+
85
+ def test_connection_factory_as_gateway_should_chain_gateways_if_gateway_variable_is_a_hash
86
+ @config.values[:gateway] = { ["j@gateway1", "k@gateway2"] => :default }
87
+ gateway1 = mock
88
+ Net::SSH::Gateway.expects(:new).with("gateway1", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(gateway1)
89
+ gateway1.expects(:open).returns(65535)
90
+ Net::SSH::Gateway.expects(:new).with("127.0.0.1", "k", :port => 65535, :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
91
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
92
+ end
93
+
94
+ def test_connection_factory_as_gateway_should_share_gateway_between_connections
95
+ @config.values[:gateway] = "j@gateway"
96
+ Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
97
+ Capistrano::SSH.stubs(:connect).returns(stub_everything)
98
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
99
+ @config.establish_connections_to(server("capistrano"))
100
+ @config.establish_connections_to(server("another"))
101
+ end
102
+
103
+ def test_connection_factory_as_gateway_should_share_gateway_between_like_connections_if_gateway_variable_is_a_hash
104
+ @config.values[:gateway] = { "j@gateway" => [ "capistrano", "another"] }
105
+ Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
106
+ Capistrano::SSH.stubs(:connect).returns(stub_everything)
107
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
108
+ @config.establish_connections_to(server("capistrano"))
109
+ @config.establish_connections_to(server("another"))
110
+ end
111
+
112
+ def test_connection_factory_as_gateways_should_not_share_gateway_between_unlike_connections_if_gateway_variable_is_a_hash
113
+ @config.values[:gateway] = { "j@gateway" => [ "capistrano", "another"], "k@gateway2" => "yafhost" }
114
+ Net::SSH::Gateway.expects(:new).once.with("gateway", "j", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
115
+ Net::SSH::Gateway.expects(:new).once.with("gateway2", "k", :password => nil, :auth_methods => %w(publickey hostbased), :config => false).returns(stub_everything)
116
+ Capistrano::SSH.stubs(:connect).returns(stub_everything)
117
+ assert_instance_of Capistrano::Configuration::Connections::GatewayConnectionFactory, @config.connection_factory
118
+ @config.establish_connections_to(server("capistrano"))
119
+ @config.establish_connections_to(server("another"))
120
+ @config.establish_connections_to(server("yafhost"))
121
+ end
122
+
123
+ def test_establish_connections_to_should_accept_a_single_nonarray_parameter
124
+ Capistrano::SSH.expects(:connect).with { |s,| s.host == "capistrano" }.returns(:success)
125
+ assert @config.sessions.empty?
126
+ @config.establish_connections_to(server("capistrano"))
127
+ assert_equal ["capistrano"], @config.sessions.keys.map(&:host)
128
+ end
129
+
130
+ def test_establish_connections_to_should_accept_an_array
131
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
132
+ assert @config.sessions.empty?
133
+ @config.establish_connections_to(%w(cap1 cap2 cap3).map { |s| server(s) })
134
+ assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map(&:host)
135
+ end
136
+
137
+ def test_establish_connections_to_should_not_attempt_to_reestablish_existing_connections
138
+ Capistrano::SSH.expects(:connect).times(2).returns(:success)
139
+ @config.sessions[server("cap1")] = :ok
140
+ @config.establish_connections_to(%w(cap1 cap2 cap3).map { |s| server(s) })
141
+ assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map(&:host)
142
+ end
143
+
144
+ def test_establish_connections_to_should_raise_one_connection_error_on_failure
145
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception)
146
+ assert_raises(Capistrano::ConnectionError) {
147
+ @config.establish_connections_to(%w(cap1 cap2).map { |s| server(s) })
148
+ }
149
+ end
150
+
151
+ def test_connection_error_should_include_accessor_with_host_array
152
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception)
153
+ begin
154
+ @config.establish_connections_to(%w(cap1 cap2).map { |s| server(s) })
155
+ flunk "expected an exception to be raised"
156
+ rescue Capistrano::ConnectionError => e
157
+ assert e.respond_to?(:hosts)
158
+ assert_equal %w(cap1 cap2), e.hosts.map { |h| h.to_s }.sort
159
+ end
160
+ end
161
+
162
+ def test_connection_error_should_only_include_failed_hosts
163
+ Capistrano::SSH.expects(:connect).with(server('cap1'), anything).raises(Exception)
164
+ Capistrano::SSH.expects(:connect).with(server('cap2'), anything).returns(:success)
165
+
166
+ begin
167
+ @config.establish_connections_to(%w(cap1 cap2).map { |s| server(s) })
168
+ flunk "expected an exception to be raised"
169
+ rescue Capistrano::ConnectionError => e
170
+ assert_equal %w(cap1), e.hosts.map { |h| h.to_s }
171
+ end
172
+ end
173
+
174
+ def test_execute_on_servers_should_require_a_block
175
+ assert_raises(ArgumentError) { @config.execute_on_servers }
176
+ end
177
+
178
+ def test_execute_on_servers_without_current_task_should_call_find_servers
179
+ list = [server("first"), server("second")]
180
+ @config.expects(:find_servers).with(:a => :b, :c => :d).returns(list)
181
+ @config.expects(:establish_connections_to).with(list).returns(:done)
182
+ @config.execute_on_servers(:a => :b, :c => :d) do |result|
183
+ assert_equal list, result
184
+ end
185
+ end
186
+
187
+ def test_execute_on_servers_without_current_task_should_raise_error_if_no_matching_servers
188
+ @config.expects(:find_servers).with(:a => :b, :c => :d).returns([])
189
+ assert_raises(Capistrano::NoMatchingServersError) { @config.execute_on_servers(:a => :b, :c => :d) { |list| } }
190
+ end
191
+
192
+ def test_execute_on_servers_without_current_task_should_not_raise_error_if_no_matching_servers_and_continue_on_no_matching_servers
193
+ @config.expects(:find_servers).with(:a => :b, :c => :d, :on_no_matching_servers => :continue).returns([])
194
+ assert_nothing_raised { @config.execute_on_servers(:a => :b, :c => :d, :on_no_matching_servers => :continue) { |list| } }
195
+ end
196
+
197
+ def test_execute_on_servers_should_raise_an_error_if_the_current_task_has_no_matching_servers_by_default
198
+ @config.current_task = mock_task
199
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([])
200
+ assert_raises(Capistrano::NoMatchingServersError) do
201
+ @config.execute_on_servers do
202
+ flunk "should not get here"
203
+ end
204
+ end
205
+ end
206
+
207
+ def test_execute_on_servers_should_not_raise_an_error_if_the_current_task_has_no_matching_servers_by_default_and_continue_on_no_matching_servers
208
+ @config.current_task = mock_task(:on_no_matching_servers => :continue)
209
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([])
210
+ assert_nothing_raised do
211
+ @config.execute_on_servers do
212
+ flunk "should not get here"
213
+ end
214
+ end
215
+ end
216
+
217
+ def test_execute_on_servers_should_not_raise_an_error_if_the_current_task_has_no_matching_servers_by_default_and_command_continues_on_no_matching_servers
218
+ @config.current_task = mock_task
219
+ @config.expects(:find_servers_for_task).with(@config.current_task, :on_no_matching_servers => :continue).returns([])
220
+ assert_nothing_raised do
221
+ @config.execute_on_servers(:on_no_matching_servers => :continue) do
222
+ flunk "should not get here"
223
+ end
224
+ end
225
+ end
226
+
227
+ def test_execute_on_servers_should_determine_server_list_from_active_task
228
+ assert @config.sessions.empty?
229
+ @config.current_task = mock_task
230
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
231
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
232
+ @config.execute_on_servers {}
233
+ assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
234
+ end
235
+
236
+ def test_execute_on_servers_should_yield_server_list_to_block
237
+ assert @config.sessions.empty?
238
+ @config.current_task = mock_task
239
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
240
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
241
+ block_called = false
242
+ @config.execute_on_servers do |servers|
243
+ block_called = true
244
+ assert servers.detect { |s| s.host == "cap1" }
245
+ assert servers.detect { |s| s.host == "cap2" }
246
+ assert servers.detect { |s| s.host == "cap3" }
247
+ assert servers.all? { |s| @config.sessions[s] }
248
+ end
249
+ assert block_called
250
+ end
251
+
252
+ def test_execute_on_servers_with_once_option_should_establish_connection_to_and_yield_only_the_first_server
253
+ assert @config.sessions.empty?
254
+ @config.current_task = mock_task
255
+ @config.expects(:find_servers_for_task).with(@config.current_task, :once => true).returns([server("cap1"), server("cap2"), server("cap3")])
256
+ Capistrano::SSH.expects(:connect).returns(:success)
257
+ block_called = false
258
+ @config.execute_on_servers(:once => true) do |servers|
259
+ block_called = true
260
+ assert_equal %w(cap1), servers.map { |s| s.host }
261
+ end
262
+ assert block_called
263
+ assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
264
+ end
265
+
266
+ def test_execute_servers_should_raise_connection_error_on_failure_by_default
267
+ @config.current_task = mock_task
268
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1")])
269
+ Capistrano::SSH.expects(:connect).raises(Exception)
270
+ assert_raises(Capistrano::ConnectionError) do
271
+ @config.execute_on_servers do
272
+ flunk "expected an exception to be raised"
273
+ end
274
+ end
275
+ end
276
+
277
+ def test_execute_servers_should_not_raise_connection_error_on_failure_with_on_errors_continue
278
+ @config.current_task = mock_task(:on_error => :continue)
279
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2")])
280
+ Capistrano::SSH.expects(:connect).with(server('cap1'), anything).raises(Exception)
281
+ Capistrano::SSH.expects(:connect).with(server('cap2'), anything).returns(:success)
282
+ assert_nothing_raised {
283
+ @config.execute_on_servers do |servers|
284
+ assert_equal %w(cap2), servers.map { |s| s.host }
285
+ end
286
+ }
287
+ end
288
+
289
+ def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_connection_errors_with_on_errors_continue
290
+ list = [server("cap1"), server("cap2")]
291
+ @config.current_task = mock_task(:on_error => :continue)
292
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns(list)
293
+ Capistrano::SSH.expects(:connect).with(server('cap1'), anything).raises(Exception)
294
+ Capistrano::SSH.expects(:connect).with(server('cap2'), anything).returns(:success)
295
+ @config.execute_on_servers do |servers|
296
+ assert_equal %w(cap2), servers.map { |s| s.host }
297
+ end
298
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns(list)
299
+ @config.execute_on_servers do |servers|
300
+ assert_equal %w(cap2), servers.map { |s| s.host }
301
+ end
302
+ end
303
+
304
+ def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_command_errors_with_on_errors_continue
305
+ cap1 = server("cap1")
306
+ cap2 = server("cap2")
307
+ @config.current_task = mock_task(:on_error => :continue)
308
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
309
+ Capistrano::SSH.expects(:connect).times(2).returns(:success)
310
+ @config.execute_on_servers do |servers|
311
+ error = Capistrano::CommandError.new
312
+ error.hosts = [cap1]
313
+ raise error
314
+ end
315
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
316
+ @config.execute_on_servers do |servers|
317
+ assert_equal %w(cap2), servers.map { |s| s.host }
318
+ end
319
+ end
320
+
321
+ def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_transfer_errors_with_on_errors_continue
322
+ cap1 = server("cap1")
323
+ cap2 = server("cap2")
324
+ @config.current_task = mock_task(:on_error => :continue)
325
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
326
+ Capistrano::SSH.expects(:connect).times(2).returns(:success)
327
+ @config.execute_on_servers do |servers|
328
+ error = Capistrano::TransferError.new
329
+ error.hosts = [cap1]
330
+ raise error
331
+ end
332
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
333
+ @config.execute_on_servers do |servers|
334
+ assert_equal %w(cap2), servers.map { |s| s.host }
335
+ end
336
+ end
337
+
338
+ def test_connect_should_establish_connections_to_all_servers_in_scope
339
+ assert @config.sessions.empty?
340
+ @config.current_task = mock_task
341
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
342
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
343
+ @config.connect!
344
+ assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
345
+ end
346
+
347
+ def test_execute_on_servers_should_only_run_on_tasks_max_hosts_hosts_at_once
348
+ cap1 = server("cap1")
349
+ cap2 = server("cap2")
350
+ connection1 = mock()
351
+ connection2 = mock()
352
+ connection1.expects(:close)
353
+ connection2.expects(:close)
354
+ @config.current_task = mock_task(:max_hosts => 1)
355
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
356
+ Capistrano::SSH.expects(:connect).times(2).returns(connection1).then.returns(connection2)
357
+ block_called = 0
358
+ @config.execute_on_servers do |servers|
359
+ block_called += 1
360
+ assert_equal 1, servers.size
361
+ end
362
+ assert_equal 2, block_called
363
+ end
364
+
365
+ def test_execute_on_servers_should_only_run_on_max_hosts_hosts_at_once
366
+ cap1 = server("cap1")
367
+ cap2 = server("cap2")
368
+ connection1 = mock()
369
+ connection2 = mock()
370
+ connection1.expects(:close)
371
+ connection2.expects(:close)
372
+ @config.current_task = mock_task(:max_hosts => 1)
373
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
374
+ Capistrano::SSH.expects(:connect).times(2).returns(connection1).then.returns(connection2)
375
+ block_called = 0
376
+ @config.execute_on_servers do |servers|
377
+ block_called += 1
378
+ assert_equal 1, servers.size
379
+ end
380
+ assert_equal 2, block_called
381
+ end
382
+
383
+ def test_execute_on_servers_should_cope_with_already_dropped_connections_when_attempting_to_close_them
384
+ cap1 = server("cap1")
385
+ cap2 = server("cap2")
386
+ connection1 = mock()
387
+ connection2 = mock()
388
+ connection3 = mock()
389
+ connection4 = mock()
390
+ connection1.expects(:close).raises(IOError)
391
+ connection2.expects(:close)
392
+ connection3.expects(:close)
393
+ connection4.expects(:close)
394
+ @config.current_task = mock_task(:max_hosts => 1)
395
+ @config.expects(:find_servers_for_task).times(2).with(@config.current_task, {}).returns([cap1, cap2])
396
+ Capistrano::SSH.expects(:connect).times(4).returns(connection1).then.returns(connection2).then.returns(connection3).then.returns(connection4)
397
+ @config.execute_on_servers {}
398
+ @config.execute_on_servers {}
399
+ end
400
+
401
+ def test_execute_on_servers_should_cope_with_already_disconnected_connections_when_attempting_to_close_them
402
+ cap1 = server("cap1")
403
+ cap2 = server("cap2")
404
+ connection1 = mock()
405
+ connection2 = mock()
406
+ connection3 = mock()
407
+ connection4 = mock()
408
+ connection1.expects(:close).raises(Net::SSH::Disconnect)
409
+ connection2.expects(:close)
410
+ connection3.expects(:close)
411
+ connection4.expects(:close)
412
+ @config.current_task = mock_task(:max_hosts => 1)
413
+ @config.expects(:find_servers_for_task).times(2).with(@config.current_task, {}).returns([cap1, cap2])
414
+ Capistrano::SSH.expects(:connect).times(4).returns(connection1).then.returns(connection2).then.returns(connection3).then.returns(connection4)
415
+ @config.execute_on_servers {}
416
+ @config.execute_on_servers {}
417
+ end
418
+
419
+ def test_connect_should_honor_once_option
420
+ assert @config.sessions.empty?
421
+ @config.current_task = mock_task
422
+ @config.expects(:find_servers_for_task).with(@config.current_task, :once => true).returns([server("cap1"), server("cap2"), server("cap3")])
423
+ Capistrano::SSH.expects(:connect).returns(:success)
424
+ @config.connect! :once => true
425
+ assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
426
+ end
427
+
428
+ private
429
+
430
+ def mock_task(options={})
431
+ continue_on_error = options[:on_error] == :continue
432
+ stub("task",
433
+ :fully_qualified_name => "name",
434
+ :options => options,
435
+ :continue_on_error? => continue_on_error,
436
+ :max_hosts => options[:max_hosts]
437
+ )
438
+ end
439
+ end
@@ -0,0 +1,175 @@
1
+ require "utils"
2
+ require 'capistrano/configuration/execution'
3
+ require 'capistrano/task_definition'
4
+
5
+ class ConfigurationExecutionTest < Test::Unit::TestCase
6
+ class MockConfig
7
+ attr_reader :tasks, :namespaces, :fully_qualified_name, :parent
8
+ attr_reader :state, :original_initialize_called
9
+ attr_accessor :logger, :default_task
10
+
11
+ def initialize(options={})
12
+ @original_initialize_called = true
13
+ @tasks = {}
14
+ @namespaces = {}
15
+ @state = {}
16
+ @fully_qualified_name = options[:fqn]
17
+ @parent = options[:parent]
18
+ @logger = options.delete(:logger)
19
+ end
20
+
21
+ include Capistrano::Configuration::Execution
22
+ end
23
+
24
+ def setup
25
+ @config = MockConfig.new(:logger => stub(:debug => nil, :info => nil, :important => nil))
26
+ @config.stubs(:search_task).returns(nil)
27
+ end
28
+
29
+ def test_initialize_should_initialize_collections
30
+ assert_nil @config.rollback_requests
31
+ assert @config.original_initialize_called
32
+ assert @config.task_call_frames.empty?
33
+ end
34
+
35
+ def test_execute_task_should_populate_call_stack
36
+ task = new_task @config, :testing
37
+ assert_nothing_raised { @config.execute_task(task) }
38
+ assert_equal %w(testing), @config.state[:testing][:stack]
39
+ assert_nil @config.state[:testing][:history]
40
+ assert @config.task_call_frames.empty?
41
+ end
42
+
43
+ def test_nested_execute_task_should_add_to_call_stack
44
+ testing = new_task @config, :testing
45
+ outer = new_task(@config, :outer) { execute_task(testing) }
46
+
47
+ assert_nothing_raised { @config.execute_task(outer) }
48
+ assert_equal %w(outer testing), @config.state[:testing][:stack]
49
+ assert_nil @config.state[:testing][:history]
50
+ assert @config.task_call_frames.empty?
51
+ end
52
+
53
+ def test_execute_task_should_execute_in_scope_of_tasks_parent
54
+ ns = stub("namespace", :tasks => {}, :default_task => nil, :fully_qualified_name => "ns")
55
+ ns.expects(:instance_eval)
56
+ testing = new_task ns, :testing
57
+ @config.execute_task(testing)
58
+ end
59
+
60
+ def test_transaction_outside_of_task_should_raise_exception
61
+ assert_raises(ScriptError) { @config.transaction {} }
62
+ end
63
+
64
+ def test_transaction_without_block_should_raise_argument_error
65
+ testing = new_task(@config, :testing) { transaction }
66
+ assert_raises(ArgumentError) { @config.execute_task(testing) }
67
+ end
68
+
69
+ def test_transaction_should_initialize_transaction_history
70
+ @config.state[:inspector] = stack_inspector
71
+ testing = new_task(@config, :testing) { transaction { instance_eval(&state[:inspector]) } }
72
+ @config.execute_task(testing)
73
+ assert_equal [], @config.state[:testing][:history]
74
+ end
75
+
76
+ def test_transaction_from_within_transaction_should_not_start_new_transaction
77
+ third = new_task(@config, :third, &stack_inspector)
78
+ second = new_task(@config, :second) { transaction { execute_task(third) } }
79
+ first = new_task(@config, :first) { transaction { execute_task(second) } }
80
+ # kind of fragile...not sure how else to check that transaction was only
81
+ # really run twice...but if the transaction was REALLY run, logger.info
82
+ # will be called once when it starts, and once when it finishes.
83
+ @config.logger = mock()
84
+ @config.logger.stubs(:debug)
85
+ @config.logger.expects(:info).times(2)
86
+ @config.execute_task(first)
87
+ end
88
+
89
+ def test_on_rollback_should_have_no_effect_outside_of_transaction
90
+ aaa = new_task(@config, :aaa) { on_rollback { state[:rollback] = true }; raise "boom" }
91
+ assert_raises(RuntimeError) { @config.execute_task(aaa) }
92
+ assert_nil @config.state[:rollback]
93
+ end
94
+
95
+ def test_exception_raised_in_transaction_should_call_all_registered_rollback_handlers_in_reverse_order
96
+ aaa = new_task(@config, :aaa) { on_rollback { (state[:rollback] ||= []) << :aaa } }
97
+ bbb = new_task(@config, :bbb) { on_rollback { (state[:rollback] ||= []) << :bbb } }
98
+ ccc = new_task(@config, :ccc) {}
99
+ ddd = new_task(@config, :ddd) { on_rollback { (state[:rollback] ||= []) << :ddd }; execute_task(bbb); execute_task(ccc) }
100
+ eee = new_task(@config, :eee) { transaction { execute_task(ddd); execute_task(aaa); raise "boom" } }
101
+ assert_raises(RuntimeError) do
102
+ @config.execute_task(eee)
103
+ end
104
+ assert_equal [:aaa, :bbb, :ddd], @config.state[:rollback]
105
+ assert_nil @config.rollback_requests
106
+ assert @config.task_call_frames.empty?
107
+ end
108
+
109
+ def test_exception_during_rollback_should_simply_be_logged_and_ignored
110
+ aaa = new_task(@config, :aaa) { on_rollback { state[:aaa] = true; raise LoadError, "ouch" }; execute_task(bbb) }
111
+ bbb = new_task(@config, :bbb) { raise MadError, "boom" }
112
+ ccc = new_task(@config, :ccc) { transaction { execute_task(aaa) } }
113
+ assert_raises(NameError) do
114
+ @config.execute_task(ccc)
115
+ end
116
+ assert @config.state[:aaa]
117
+ end
118
+
119
+ def test_on_rollback_called_twice_should_result_in_last_rollback_block_being_effective
120
+ aaa = new_task(@config, :aaa) do
121
+ transaction do
122
+ on_rollback { (state[:rollback] ||= []) << :first }
123
+ on_rollback { (state[:rollback] ||= []) << :second }
124
+ raise "boom"
125
+ end
126
+ end
127
+
128
+ assert_raises(RuntimeError) do
129
+ @config.execute_task(aaa)
130
+ end
131
+
132
+ assert_equal [:second], @config.state[:rollback]
133
+ end
134
+
135
+ def test_find_and_execute_task_should_raise_error_when_task_cannot_be_found
136
+ @config.expects(:find_task).with("path:to:task").returns(nil)
137
+ assert_raises(Capistrano::NoSuchTaskError) { @config.find_and_execute_task("path:to:task") }
138
+ end
139
+
140
+ def test_find_and_execute_task_should_execute_task_when_task_is_found
141
+ @config.expects(:find_task).with("path:to:task").returns(:found)
142
+ @config.expects(:execute_task).with(:found)
143
+ assert_nothing_raised { @config.find_and_execute_task("path:to:task") }
144
+ end
145
+
146
+ def test_find_and_execute_task_with_before_option_should_trigger_callback
147
+ @config.expects(:find_task).with("path:to:task").returns(:found)
148
+ @config.expects(:trigger).with(:incoming, :found)
149
+ @config.expects(:execute_task).with(:found)
150
+ @config.find_and_execute_task("path:to:task", :before => :incoming)
151
+ end
152
+
153
+ def test_find_and_execute_task_with_after_option_should_trigger_callback
154
+ @config.expects(:find_task).with("path:to:task").returns(:found)
155
+ @config.expects(:trigger).with(:outgoing, :found)
156
+ @config.expects(:execute_task).with(:found)
157
+ @config.find_and_execute_task("path:to:task", :after => :outgoing)
158
+ end
159
+
160
+ private
161
+
162
+ def stack_inspector
163
+ Proc.new do
164
+ (state[:trail] ||= []) << current_task.fully_qualified_name
165
+ data = state[current_task.name] = {}
166
+ data[:stack] = task_call_frames.map { |frame| frame.task.fully_qualified_name }
167
+ data[:history] = rollback_requests && rollback_requests.map { |frame| frame.task.fully_qualified_name }
168
+ end
169
+ end
170
+
171
+ def new_task(namespace, name, options={}, &block)
172
+ block ||= stack_inspector
173
+ namespace.tasks[name] = Capistrano::TaskDefinition.new(name, namespace, &block)
174
+ end
175
+ end