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,288 @@
1
+ require "#{File.dirname(__FILE__)}/../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_accessor :current_task
9
+
10
+ def initialize
11
+ @original_initialize_called = true
12
+ @values = {}
13
+ end
14
+
15
+ def fetch(*args)
16
+ @values.fetch(*args)
17
+ end
18
+
19
+ def [](key)
20
+ @values[key]
21
+ end
22
+
23
+ def exists?(key)
24
+ @values.key?(key)
25
+ end
26
+
27
+ include Capistrano::Configuration::Connections
28
+ end
29
+
30
+ def setup
31
+ @config = MockConfig.new
32
+ @config.stubs(:logger).returns(stub_everything)
33
+ @ssh_options = {
34
+ :user => "jamis",
35
+ :port => 8080,
36
+ :password => "g00b3r",
37
+ :ssh_options => { :debug => :verbose }
38
+ }
39
+ end
40
+
41
+ def test_initialize_should_initialize_collections_and_call_original_initialize
42
+ assert @config.original_initialize_called
43
+ assert @config.sessions.empty?
44
+ end
45
+
46
+ def test_connection_factory_should_return_default_connection_factory_instance
47
+ factory = @config.connection_factory
48
+ assert_instance_of Capistrano::Configuration::Connections::DefaultConnectionFactory, factory
49
+ end
50
+
51
+ def test_connection_factory_instance_should_be_cached
52
+ assert_same @config.connection_factory, @config.connection_factory
53
+ end
54
+
55
+ def test_default_connection_factory_honors_config_options
56
+ server = server("capistrano")
57
+ Capistrano::SSH.expects(:connect).with(server, @config).returns(:session)
58
+ assert_equal :session, @config.connection_factory.connect_to(server)
59
+ end
60
+
61
+ def test_connection_factory_should_return_gateway_instance_if_gateway_variable_is_set
62
+ @config.values[:gateway] = "capistrano"
63
+ server = server("capistrano")
64
+ Capistrano::SSH.expects(:connect).with { |s,| s.host == "capistrano" }.yields(stub_everything)
65
+ assert_instance_of Capistrano::Gateway, @config.connection_factory
66
+ end
67
+
68
+ def test_connection_factory_as_gateway_should_honor_config_options
69
+ @config.values[:gateway] = "capistrano"
70
+ @config.values.update(@ssh_options)
71
+ Capistrano::SSH.expects(:connect).with { |s,opts| s.host == "capistrano" && opts == @config }.yields(stub_everything)
72
+ assert_instance_of Capistrano::Gateway, @config.connection_factory
73
+ end
74
+
75
+ def test_establish_connections_to_should_accept_a_single_nonarray_parameter
76
+ Capistrano::SSH.expects(:connect).with { |s,| s.host == "capistrano" }.returns(:success)
77
+ assert @config.sessions.empty?
78
+ @config.establish_connections_to(server("capistrano"))
79
+ assert ["capistrano"], @config.sessions.keys
80
+ end
81
+
82
+ def test_establish_connections_to_should_accept_an_array
83
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
84
+ assert @config.sessions.empty?
85
+ @config.establish_connections_to(%w(cap1 cap2 cap3).map { |s| server(s) })
86
+ assert %w(cap1 cap2 cap3), @config.sessions.keys.sort
87
+ end
88
+
89
+ def test_establish_connections_to_should_not_attempt_to_reestablish_existing_connections
90
+ Capistrano::SSH.expects(:connect).times(2).returns(:success)
91
+ @config.sessions[server("cap1")] = :ok
92
+ @config.establish_connections_to(%w(cap1 cap2 cap3).map { |s| server(s) })
93
+ assert %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
94
+ end
95
+
96
+ def test_establish_connections_to_should_raise_one_connection_error_on_failure
97
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception)
98
+ assert_raises(Capistrano::ConnectionError) {
99
+ @config.establish_connections_to(%w(cap1 cap2)).map { |s| servers(s) }
100
+ }
101
+ end
102
+
103
+ def test_connection_error_should_include_accessor_with_host_array
104
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception)
105
+
106
+ begin
107
+ @config.establish_connections_to(%w(cap1 cap2)).map { |s| servers(s) }
108
+ flunk "expected an exception to be raised"
109
+ rescue Capistrano::ConnectionError => e
110
+ assert e.respond_to?(:hosts)
111
+ assert_equal %w(cap1 cap2), e.hosts.map { |h| h.to_s }
112
+ end
113
+ end
114
+
115
+ def test_connection_error_should_only_include_failed_hosts
116
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception).then.returns(:success)
117
+
118
+ begin
119
+ @config.establish_connections_to(%w(cap1 cap2)).map { |s| servers(s) }
120
+ flunk "expected an exception to be raised"
121
+ rescue Capistrano::ConnectionError => e
122
+ assert_equal %w(cap1), e.hosts.map { |h| h.to_s }
123
+ end
124
+ end
125
+
126
+ def test_execute_on_servers_should_require_a_block
127
+ assert_raises(ArgumentError) { @config.execute_on_servers }
128
+ end
129
+
130
+ def test_execute_on_servers_without_current_task_should_call_find_servers
131
+ list = [server("first"), server("second")]
132
+ @config.expects(:find_servers).with(:a => :b, :c => :d).returns(list)
133
+ @config.expects(:establish_connections_to).with(list).returns(:done)
134
+ @config.execute_on_servers(:a => :b, :c => :d) do |result|
135
+ assert_equal list, result
136
+ end
137
+ end
138
+
139
+ def test_execute_on_servers_without_current_task_should_raise_error_if_no_matching_servers
140
+ @config.expects(:find_servers).with(:a => :b, :c => :d).returns([])
141
+ assert_raises(Capistrano::NoMatchingServersError) { @config.execute_on_servers(:a => :b, :c => :d) { |list| } }
142
+ end
143
+
144
+ def test_execute_on_servers_should_raise_an_error_if_the_current_task_has_no_matching_servers_by_default
145
+ @config.current_task = mock_task
146
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([])
147
+ assert_raises(Capistrano::NoMatchingServersError) do
148
+ @config.execute_on_servers do
149
+ flunk "should not get here"
150
+ end
151
+ end
152
+ end
153
+
154
+ def test_execute_on_servers_should_determine_server_list_from_active_task
155
+ assert @config.sessions.empty?
156
+ @config.current_task = mock_task
157
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
158
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
159
+ @config.execute_on_servers {}
160
+ assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
161
+ end
162
+
163
+ def test_execute_on_servers_should_yield_server_list_to_block
164
+ assert @config.sessions.empty?
165
+ @config.current_task = mock_task
166
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
167
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
168
+ block_called = false
169
+ @config.execute_on_servers do |servers|
170
+ block_called = true
171
+ assert servers.detect { |s| s.host == "cap1" }
172
+ assert servers.detect { |s| s.host == "cap2" }
173
+ assert servers.detect { |s| s.host == "cap3" }
174
+ assert servers.all? { |s| @config.sessions[s] }
175
+ end
176
+ assert block_called
177
+ end
178
+
179
+ def test_execute_on_servers_with_once_option_should_establish_connection_to_and_yield_only_the_first_server
180
+ assert @config.sessions.empty?
181
+ @config.current_task = mock_task
182
+ @config.expects(:find_servers_for_task).with(@config.current_task, :once => true).returns([server("cap1"), server("cap2"), server("cap3")])
183
+ Capistrano::SSH.expects(:connect).returns(:success)
184
+ block_called = false
185
+ @config.execute_on_servers(:once => true) do |servers|
186
+ block_called = true
187
+ assert_equal %w(cap1), servers.map { |s| s.host }
188
+ end
189
+ assert block_called
190
+ assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
191
+ end
192
+
193
+ def test_execute_servers_should_raise_connection_error_on_failure_by_default
194
+ @config.current_task = mock_task
195
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1")])
196
+ Capistrano::SSH.expects(:connect).raises(Exception)
197
+ assert_raises(Capistrano::ConnectionError) {
198
+ @config.execute_on_servers do
199
+ flunk "expected an exception to be raised"
200
+ end
201
+ }
202
+ end
203
+
204
+ def test_execute_servers_should_not_raise_connection_error_on_failure_with_on_errors_continue
205
+ @config.current_task = mock_task(:on_error => :continue)
206
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2")])
207
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception).then.returns(:success)
208
+ assert_nothing_raised {
209
+ @config.execute_on_servers do |servers|
210
+ assert_equal %w(cap2), servers.map { |s| s.host }
211
+ end
212
+ }
213
+ end
214
+
215
+ def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_connection_errors_with_on_errors_continue
216
+ list = [server("cap1"), server("cap2")]
217
+ @config.current_task = mock_task(:on_error => :continue)
218
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns(list)
219
+ Capistrano::SSH.expects(:connect).times(2).raises(Exception).then.returns(:success)
220
+ @config.expects(:failed!).with(server("cap1"))
221
+ @config.execute_on_servers do |servers|
222
+ assert_equal %w(cap2), servers.map { |s| s.host }
223
+ end
224
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns(list)
225
+ @config.execute_on_servers do |servers|
226
+ assert_equal %w(cap2), servers.map { |s| s.host }
227
+ end
228
+ end
229
+
230
+ def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_command_errors_with_on_errors_continue
231
+ cap1 = server("cap1")
232
+ cap2 = server("cap2")
233
+ @config.current_task = mock_task(:on_error => :continue)
234
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
235
+ Capistrano::SSH.expects(:connect).times(2).returns(:success)
236
+ @config.execute_on_servers do |servers|
237
+ error = Capistrano::CommandError.new
238
+ error.hosts = [cap1]
239
+ raise error
240
+ end
241
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
242
+ @config.execute_on_servers do |servers|
243
+ assert_equal %w(cap2), servers.map { |s| s.host }
244
+ end
245
+ end
246
+
247
+ def test_execute_on_servers_should_not_try_to_connect_to_hosts_with_upload_errors_with_on_errors_continue
248
+ cap1 = server("cap1")
249
+ cap2 = server("cap2")
250
+ @config.current_task = mock_task(:on_error => :continue)
251
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
252
+ Capistrano::SSH.expects(:connect).times(2).returns(:success)
253
+ @config.execute_on_servers do |servers|
254
+ error = Capistrano::UploadError.new
255
+ error.hosts = [cap1]
256
+ raise error
257
+ end
258
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([cap1, cap2])
259
+ @config.execute_on_servers do |servers|
260
+ assert_equal %w(cap2), servers.map { |s| s.host }
261
+ end
262
+ end
263
+
264
+ def test_connect_should_establish_connections_to_all_servers_in_scope
265
+ assert @config.sessions.empty?
266
+ @config.current_task = mock_task
267
+ @config.expects(:find_servers_for_task).with(@config.current_task, {}).returns([server("cap1"), server("cap2"), server("cap3")])
268
+ Capistrano::SSH.expects(:connect).times(3).returns(:success)
269
+ @config.connect!
270
+ assert_equal %w(cap1 cap2 cap3), @config.sessions.keys.sort.map { |s| s.host }
271
+ end
272
+
273
+ def test_connect_should_honor_once_option
274
+ assert @config.sessions.empty?
275
+ @config.current_task = mock_task
276
+ @config.expects(:find_servers_for_task).with(@config.current_task, :once => true).returns([server("cap1"), server("cap2"), server("cap3")])
277
+ Capistrano::SSH.expects(:connect).returns(:success)
278
+ @config.connect! :once => true
279
+ assert_equal %w(cap1), @config.sessions.keys.sort.map { |s| s.host }
280
+ end
281
+
282
+ private
283
+
284
+ def mock_task(options={})
285
+ continue_on_error = options[:on_error] == :continue
286
+ stub("task", :fully_qualified_name => "name", :options => options, :continue_on_error? => continue_on_error)
287
+ end
288
+ end
@@ -0,0 +1,159 @@
1
+ require "#{File.dirname(__FILE__)}/../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_find_and_execute_task_should_raise_error_when_task_cannot_be_found
120
+ @config.expects(:find_task).with("path:to:task").returns(nil)
121
+ assert_raises(Capistrano::NoSuchTaskError) { @config.find_and_execute_task("path:to:task") }
122
+ end
123
+
124
+ def test_find_and_execute_task_should_execute_task_when_task_is_found
125
+ @config.expects(:find_task).with("path:to:task").returns(:found)
126
+ @config.expects(:execute_task).with(:found)
127
+ assert_nothing_raised { @config.find_and_execute_task("path:to:task") }
128
+ end
129
+
130
+ def test_find_and_execute_task_with_before_option_should_trigger_callback
131
+ @config.expects(:find_task).with("path:to:task").returns(:found)
132
+ @config.expects(:trigger).with(:incoming, :found)
133
+ @config.expects(:execute_task).with(:found)
134
+ @config.find_and_execute_task("path:to:task", :before => :incoming)
135
+ end
136
+
137
+ def test_find_and_execute_task_with_after_option_should_trigger_callback
138
+ @config.expects(:find_task).with("path:to:task").returns(:found)
139
+ @config.expects(:trigger).with(:outgoing, :found)
140
+ @config.expects(:execute_task).with(:found)
141
+ @config.find_and_execute_task("path:to:task", :after => :outgoing)
142
+ end
143
+
144
+ private
145
+
146
+ def stack_inspector
147
+ Proc.new do
148
+ (state[:trail] ||= []) << current_task.fully_qualified_name
149
+ data = state[current_task.name] = {}
150
+ data[:stack] = task_call_frames.map { |frame| frame.task.fully_qualified_name }
151
+ data[:history] = rollback_requests && rollback_requests.map { |frame| frame.task.fully_qualified_name }
152
+ end
153
+ end
154
+
155
+ def new_task(namespace, name, options={}, &block)
156
+ block ||= stack_inspector
157
+ namespace.tasks[name] = Capistrano::TaskDefinition.new(name, namespace, &block)
158
+ end
159
+ end
@@ -0,0 +1,119 @@
1
+ require "#{File.dirname(__FILE__)}/../utils"
2
+ require 'capistrano/configuration/loading'
3
+
4
+ class ConfigurationLoadingTest < Test::Unit::TestCase
5
+ class MockConfig
6
+ attr_accessor :ping
7
+ attr_reader :original_initialize_called
8
+
9
+ def initialize
10
+ @original_initialize_called = true
11
+ end
12
+
13
+ def ping!(value)
14
+ @ping = value
15
+ end
16
+
17
+ include Capistrano::Configuration::Loading
18
+ end
19
+
20
+ def setup
21
+ @config = MockConfig.new
22
+ end
23
+
24
+ def teardown
25
+ MockConfig.instance = nil
26
+ $".delete "#{File.dirname(__FILE__)}/../fixtures/custom.rb"
27
+ end
28
+
29
+ def test_initialize_should_init_collections
30
+ assert @config.original_initialize_called
31
+ assert @config.load_paths.include?(".")
32
+ assert @config.load_paths.detect { |v| v =~ /capistrano\/recipes$/ }
33
+ end
34
+
35
+ def test_load_with_options_and_block_should_raise_argument_error
36
+ assert_raises(ArgumentError) do
37
+ @config.load(:string => "foo") { something }
38
+ end
39
+ end
40
+
41
+ def test_load_with_arguments_and_block_should_raise_argument_error
42
+ assert_raises(ArgumentError) do
43
+ @config.load("foo") { something }
44
+ end
45
+ end
46
+
47
+ def test_load_from_string_should_eval_in_config_scope
48
+ @config.load :string => "ping! :here"
49
+ assert_equal :here, @config.ping
50
+ end
51
+
52
+ def test_load_from_file_shoudld_respect_load_path
53
+ File.stubs(:file?).returns(false)
54
+ File.stubs(:file?).with("custom/path/for/file.rb").returns(true)
55
+ File.stubs(:read).with("custom/path/for/file.rb").returns("ping! :here")
56
+
57
+ @config.load_paths << "custom/path/for"
58
+ @config.load :file => "file.rb"
59
+
60
+ assert_equal :here, @config.ping
61
+ end
62
+
63
+ def test_load_from_file_should_respect_load_path_and_appends_rb
64
+ File.stubs(:file?).returns(false)
65
+ File.stubs(:file?).with("custom/path/for/file.rb").returns(true)
66
+ File.stubs(:read).with("custom/path/for/file.rb").returns("ping! :here")
67
+
68
+ @config.load_paths << "custom/path/for"
69
+ @config.load :file => "file"
70
+
71
+ assert_equal :here, @config.ping
72
+ end
73
+
74
+ def test_load_from_file_should_raise_load_error_if_file_cannot_be_found
75
+ File.stubs(:file?).returns(false)
76
+ assert_raises(LoadError) do
77
+ @config.load :file => "file"
78
+ end
79
+ end
80
+
81
+ def test_load_from_proc_should_eval_proc_in_config_scope
82
+ @config.load :proc => Proc.new { ping! :here }
83
+ assert_equal :here, @config.ping
84
+ end
85
+
86
+ def test_load_with_block_should_treat_block_as_proc_parameter
87
+ @config.load { ping! :here }
88
+ assert_equal :here, @config.ping
89
+ end
90
+
91
+ def test_load_with_unrecognized_option_should_raise_argument_error
92
+ assert_raises(ArgumentError) do
93
+ @config.load :url => "http://www.load-this.test"
94
+ end
95
+ end
96
+
97
+ def test_load_with_arguments_should_treat_arguments_as_files
98
+ File.stubs(:file?).returns(false)
99
+ File.stubs(:file?).with("./first.rb").returns(true)
100
+ File.stubs(:file?).with("./second.rb").returns(true)
101
+ File.stubs(:read).with("./first.rb").returns("ping! 'this'")
102
+ File.stubs(:read).with("./second.rb").returns("ping << 'that'")
103
+ assert_nothing_raised { @config.load "first", "second" }
104
+ assert_equal "thisthat", @config.ping
105
+ end
106
+
107
+ def test_require_from_config_should_load_file_in_config_scope
108
+ assert_nothing_raised do
109
+ @config.require "#{File.dirname(__FILE__)}/../fixtures/custom"
110
+ end
111
+ assert_equal :custom, @config.ping
112
+ end
113
+
114
+ def test_require_without_config_should_raise_load_error
115
+ assert_raises(LoadError) do
116
+ require "#{File.dirname(__FILE__)}/../fixtures/custom"
117
+ end
118
+ end
119
+ end