sunshine 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/History.txt +237 -0
  2. data/Manifest.txt +70 -0
  3. data/README.txt +277 -0
  4. data/Rakefile +46 -0
  5. data/bin/sunshine +5 -0
  6. data/examples/deploy.rb +61 -0
  7. data/examples/deploy_tasks.rake +112 -0
  8. data/examples/standalone_deploy.rb +31 -0
  9. data/lib/commands/add.rb +96 -0
  10. data/lib/commands/default.rb +169 -0
  11. data/lib/commands/list.rb +322 -0
  12. data/lib/commands/restart.rb +62 -0
  13. data/lib/commands/rm.rb +83 -0
  14. data/lib/commands/run.rb +151 -0
  15. data/lib/commands/start.rb +72 -0
  16. data/lib/commands/stop.rb +61 -0
  17. data/lib/sunshine/app.rb +876 -0
  18. data/lib/sunshine/binder.rb +70 -0
  19. data/lib/sunshine/crontab.rb +143 -0
  20. data/lib/sunshine/daemon.rb +380 -0
  21. data/lib/sunshine/daemons/ar_sendmail.rb +28 -0
  22. data/lib/sunshine/daemons/delayed_job.rb +30 -0
  23. data/lib/sunshine/daemons/nginx.rb +104 -0
  24. data/lib/sunshine/daemons/rainbows.rb +35 -0
  25. data/lib/sunshine/daemons/server.rb +66 -0
  26. data/lib/sunshine/daemons/unicorn.rb +26 -0
  27. data/lib/sunshine/dependencies.rb +103 -0
  28. data/lib/sunshine/dependency_lib.rb +200 -0
  29. data/lib/sunshine/exceptions.rb +54 -0
  30. data/lib/sunshine/healthcheck.rb +83 -0
  31. data/lib/sunshine/output.rb +131 -0
  32. data/lib/sunshine/package_managers/apt.rb +48 -0
  33. data/lib/sunshine/package_managers/dependency.rb +349 -0
  34. data/lib/sunshine/package_managers/gem.rb +54 -0
  35. data/lib/sunshine/package_managers/yum.rb +62 -0
  36. data/lib/sunshine/remote_shell.rb +241 -0
  37. data/lib/sunshine/repo.rb +128 -0
  38. data/lib/sunshine/repos/git_repo.rb +122 -0
  39. data/lib/sunshine/repos/rsync_repo.rb +29 -0
  40. data/lib/sunshine/repos/svn_repo.rb +78 -0
  41. data/lib/sunshine/server_app.rb +554 -0
  42. data/lib/sunshine/shell.rb +384 -0
  43. data/lib/sunshine.rb +391 -0
  44. data/templates/logrotate/logrotate.conf.erb +11 -0
  45. data/templates/nginx/nginx.conf.erb +109 -0
  46. data/templates/nginx/nginx_optimize.conf +23 -0
  47. data/templates/nginx/nginx_proxy.conf +13 -0
  48. data/templates/rainbows/rainbows.conf.erb +18 -0
  49. data/templates/tasks/sunshine.rake +114 -0
  50. data/templates/unicorn/unicorn.conf.erb +6 -0
  51. data/test/fixtures/app_configs/test_app.yml +11 -0
  52. data/test/fixtures/sunshine_test/test_upload +0 -0
  53. data/test/mocks/mock_object.rb +179 -0
  54. data/test/mocks/mock_open4.rb +117 -0
  55. data/test/test_helper.rb +188 -0
  56. data/test/unit/test_app.rb +489 -0
  57. data/test/unit/test_binder.rb +20 -0
  58. data/test/unit/test_crontab.rb +128 -0
  59. data/test/unit/test_git_repo.rb +26 -0
  60. data/test/unit/test_healthcheck.rb +70 -0
  61. data/test/unit/test_nginx.rb +107 -0
  62. data/test/unit/test_rainbows.rb +26 -0
  63. data/test/unit/test_remote_shell.rb +102 -0
  64. data/test/unit/test_repo.rb +42 -0
  65. data/test/unit/test_server.rb +324 -0
  66. data/test/unit/test_server_app.rb +425 -0
  67. data/test/unit/test_shell.rb +97 -0
  68. data/test/unit/test_sunshine.rb +157 -0
  69. data/test/unit/test_svn_repo.rb +55 -0
  70. data/test/unit/test_unicorn.rb +22 -0
  71. metadata +217 -0
@@ -0,0 +1,425 @@
1
+ require 'test_helper'
2
+
3
+ class TestServerApp < Test::Unit::TestCase
4
+
5
+ def setup
6
+ mock_svn_response
7
+
8
+ @app = mock_app
9
+ @app.repo.extend MockObject
10
+
11
+ @sa = Sunshine::ServerApp.new @app, mock_remote_shell
12
+ @sa.extend MockObject
13
+
14
+ use_remote_shell @sa.shell
15
+ end
16
+
17
+
18
+ def test_init
19
+ default_info = {:ports => {}}
20
+ assert_equal default_info, @sa.info
21
+
22
+ assert_equal @app, @sa.app
23
+ assert_equal Hash.new, @sa.scripts
24
+ assert_equal [:all], @sa.roles
25
+ end
26
+
27
+
28
+ def test_init_roles
29
+ sa = Sunshine::ServerApp.new @app, "host", :roles => "role1 role2"
30
+ assert_equal [:role1, :role2], sa.roles
31
+
32
+ sa = Sunshine::ServerApp.new @app, "host", :roles => %w{role3 role4}
33
+ assert_equal [:role3, :role4], sa.roles
34
+ end
35
+
36
+
37
+ def test_add_shell_paths
38
+ @sa.add_shell_paths "test/path1", "test/path2"
39
+ assert_equal "test/path1:test/path2:$PATH", @sa.shell_env['PATH']
40
+
41
+ @sa.add_shell_paths "test/path3", "test/path4"
42
+ assert_equal "test/path3:test/path4:test/path1:test/path2:$PATH",
43
+ @sa.shell_env['PATH']
44
+ end
45
+
46
+
47
+ def test_app_attr
48
+ sa_root_path = "local/server_app/path"
49
+ sa_deploy_name = "local_deploy_name"
50
+
51
+ @sa = Sunshine::ServerApp.new "test", "host",
52
+ :root_path => sa_root_path, :deploy_name => sa_deploy_name
53
+
54
+ assert_equal sa_root_path, @sa.root_path
55
+ assert_equal sa_deploy_name, @sa.deploy_name
56
+ assert_equal "test", @sa.name
57
+
58
+ @sa.app = @app
59
+
60
+ assert_not_equal sa_root_path, @sa.root_path
61
+ assert_not_equal sa_deploy_name, @sa.deploy_name
62
+ assert_not_equal "test", @sa.name
63
+
64
+ assert_equal @app.root_path, @sa.root_path
65
+ assert_equal @app.deploy_name, @sa.deploy_name
66
+ assert_equal @app.name, @sa.name
67
+ end
68
+
69
+
70
+ def test_build_control_scripts
71
+ @sa.scripts[:start] << "start"
72
+ @sa.scripts[:stop] << "stop"
73
+ @sa.scripts[:custom] << "custom"
74
+
75
+ @sa.build_control_scripts
76
+
77
+ [:start, :stop, :custom].each do |script|
78
+ content = @sa.make_bash_script script, @sa.scripts[script]
79
+ assert @sa.method_called?(:write_script, :args => [script, content])
80
+ end
81
+
82
+ content = @sa.make_env_bash_script
83
+ assert @sa.method_called?(:write_script, :args => ["env", content])
84
+
85
+ content = @sa.make_bash_script :restart,
86
+ ["#{@sa.app.root_path}/stop", "#{@sa.app.root_path}/start"]
87
+ assert @sa.method_called?(:write_script, :args => [:restart, content])
88
+ end
89
+
90
+
91
+ def test_build_deploy_info_file
92
+ args = ["#{@app.checkout_path}/info", @sa.get_deploy_info.to_yaml]
93
+
94
+ @sa.build_deploy_info_file
95
+
96
+ assert @sa.shell.method_called?(:make_file, :args => args)
97
+
98
+ args = ["#{@app.current_path}/info", "#{@app.root_path}/info"]
99
+
100
+ assert @sa.shell.method_called?(:symlink, :args => args)
101
+ end
102
+
103
+
104
+ def test_checkout_repo
105
+ @sa.checkout_repo @app.repo
106
+ repo = @sa.app.repo
107
+ args = [@app.checkout_path, @sa.shell]
108
+
109
+ assert repo.method_called?(:checkout_to, :args => args)
110
+
111
+ info = @app.repo.checkout_to @app.checkout_path, @sa.shell
112
+ assert_equal info, @sa.info[:scm]
113
+ end
114
+
115
+
116
+ def test_deploy_details
117
+ deploy_details = {:item => "thing"}
118
+ other_details = {:key => "value"}
119
+
120
+ @sa.shell.mock :call, :args => ["cat #{@sa.current_path}/info"],
121
+ :return => other_details.to_yaml
122
+
123
+ @sa.instance_variable_set "@deploy_details", deploy_details
124
+
125
+ assert_equal deploy_details, @sa.deploy_details
126
+ assert_equal other_details, @sa.deploy_details(true)
127
+ end
128
+
129
+
130
+ def test_not_deployed?
131
+ @sa.mock :deploy_details, :args => [true],
132
+ :return => nil
133
+
134
+ assert_equal false, @sa.deployed?
135
+ end
136
+
137
+
138
+ def test_server_checked_deployed?
139
+ @sa.mock :deploy_details, :args => [true],
140
+ :return => {:deploy_name => @sa.deploy_name}
141
+
142
+ assert_equal true, @sa.deployed?
143
+ end
144
+
145
+
146
+ def test_cached_details_deployed?
147
+ @sa.instance_variable_set "@deploy_details", :deploy_name => @sa.deploy_name
148
+
149
+ assert_equal true, @sa.deployed?
150
+ end
151
+
152
+
153
+ def test_get_deploy_info
154
+ test_info = {
155
+ :deployed_at => Time.now.to_s,
156
+ :deployed_as => @sa.shell.call("whoami"),
157
+ :deployed_by => Sunshine.shell.user,
158
+ :deploy_name => File.basename(@app.checkout_path),
159
+ :roles => @sa.roles,
160
+ :path => @app.root_path
161
+ }.merge @sa.info
162
+
163
+ deploy_info = @sa.get_deploy_info
164
+
165
+ deploy_info.each do |key, val|
166
+ next if key == :deployed_at
167
+ assert_equal test_info[key], val
168
+ end
169
+ end
170
+
171
+
172
+ def test_install_deps
173
+ nginx_dep = Sunshine.dependencies.get 'nginx', :prefer => @sa.pkg_manager
174
+
175
+ @sa.install_deps "ruby", nginx_dep
176
+
177
+ assert_dep_install 'ruby'
178
+ assert_dep_install 'nginx'
179
+ end
180
+
181
+
182
+ def test_install_deps_bad_type
183
+ nginx_dep = Sunshine.dependencies.get 'nginx'
184
+
185
+ @sa.install_deps nginx_dep, :type => Sunshine::Gem
186
+ raise "Didn't raise missing dependency when it should have."
187
+
188
+ rescue Sunshine::DependencyLib::MissingDependency => e
189
+ assert_equal "No dependency 'nginx' [Sunshine::Gem]", e.message
190
+ end
191
+
192
+
193
+ def test_make_app_directories
194
+ @sa.make_app_directories
195
+
196
+ assert_server_call "mkdir -p #{@sa.directories.join(" ")}"
197
+ end
198
+
199
+
200
+ def test_make_bash_script
201
+ app_script = @sa.make_bash_script "blah", [1,2,3,4]
202
+
203
+ assert_bash_script "blah", [1,2,3,4], app_script
204
+ end
205
+
206
+
207
+ def test_make_env_bash_script
208
+ @sa.shell.env = {"BLAH" => "blarg", "HOME" => "/home/blah"}
209
+
210
+ test_script = "#!/bin/bash\nenv BLAH=blarg HOME=/home/blah \"$@\""
211
+
212
+ assert_equal test_script, @sa.make_env_bash_script
213
+ end
214
+
215
+
216
+ def test_rake
217
+ @sa.rake "db:migrate"
218
+
219
+ assert_dep_install 'rake'
220
+ assert_server_call "cd #{@app.checkout_path} && rake db:migrate"
221
+ end
222
+
223
+
224
+ def test_register_as_deployed
225
+ Sunshine::AddCommand.extend MockObject unless
226
+ MockObject === Sunshine::AddCommand
227
+
228
+ @sa.register_as_deployed
229
+
230
+ args = [@app.root_path, {'servers' => [@sa.shell]}]
231
+ assert Sunshine::AddCommand.method_called?(:exec, :args => args)
232
+ end
233
+
234
+
235
+ def test_remove_old_deploys
236
+ Sunshine.setup 'max_deploy_versions' => 3
237
+
238
+ deploys = %w{ploy1 ploy2 ploy3 ploy4 ploy5}
239
+
240
+ @sa.shell.mock :call,
241
+ :args => ["ls -1 #{@app.deploys_path}"],
242
+ :return => "#{deploys.join("\n")}\n"
243
+
244
+ removed = deploys[0..1].map{|d| "#{@app.deploys_path}/#{d}"}
245
+
246
+ @sa.remove_old_deploys
247
+
248
+ assert_server_call "rm -rf #{removed.join(" ")}"
249
+ end
250
+
251
+
252
+ def test_remove_old_deploys_unnecessary
253
+ Sunshine.setup 'max_deploy_versions' => 5
254
+
255
+ deploys = %w{ploy1 ploy2 ploy3 ploy4 ploy5}
256
+
257
+ @sa.mock :call,
258
+ :args => ["ls -1 #{@app.deploys_path}"],
259
+ :return => "#{deploys.join("\n")}\n"
260
+
261
+ removed = deploys[0..1].map{|d| "#{@app.deploys_path}/#{d}"}
262
+
263
+ @sa.remove_old_deploys
264
+
265
+ assert_not_called "rm -rf #{removed.join(" ")}"
266
+ end
267
+
268
+
269
+ def test_revert!
270
+ deploys = %w{ploy1 ploy2 ploy3 ploy4 ploy5}
271
+
272
+ @sa.shell.mock :call,
273
+ :args => ["ls -rc1 #{@app.deploys_path}"],
274
+ :return => "#{deploys.join("\n")}\n"
275
+
276
+ @sa.revert!
277
+
278
+ assert_server_call "rm -rf #{@app.checkout_path}"
279
+ assert_server_call "ls -rc1 #{@app.deploys_path}"
280
+
281
+ last_deploy = "#{@app.deploys_path}/ploy5"
282
+ assert_server_call "ln -sfT #{last_deploy} #{@app.current_path}"
283
+
284
+ assert @sa.method_called?(:start, :args =>[:force => true])
285
+ end
286
+
287
+
288
+ def test_no_previous_revert!
289
+ @sa.shell.mock :call,
290
+ :args => ["ls -rc1 #{@app.deploys_path}"],
291
+ :return => "\n"
292
+
293
+ @sa.revert!
294
+
295
+ assert_server_call "rm -rf #{@app.checkout_path}"
296
+ assert_server_call "ls -rc1 #{@app.deploys_path}"
297
+ end
298
+
299
+
300
+ def test_run_bundler
301
+ @sa.run_bundler
302
+
303
+ assert_dep_install 'bundler'
304
+ assert_server_call "cd #{@app.checkout_path} && gem bundle"
305
+ end
306
+
307
+
308
+ def test_run_geminstaller
309
+ @sa.run_geminstaller
310
+
311
+ assert_dep_install 'geminstaller'
312
+ assert_server_call "cd #{@app.checkout_path} && geminstaller -e"
313
+ end
314
+
315
+
316
+ def test_sass
317
+ sass_files = %w{file1 file2 file3}
318
+
319
+ @sa.sass(*sass_files)
320
+
321
+ assert_dep_install 'haml'
322
+
323
+ sass_files.each do |file|
324
+ sass_file = "public/stylesheets/sass/#{file}.sass"
325
+ css_file = "public/stylesheets/#{file}.css"
326
+
327
+ assert_server_call \
328
+ "cd #{@app.checkout_path} && sass #{sass_file} #{css_file}"
329
+ end
330
+ end
331
+
332
+
333
+ def test_shell_env
334
+ assert_equal @sa.shell.env, @sa.shell_env
335
+ end
336
+
337
+
338
+ def test_symlink_current_dir
339
+ @sa.symlink_current_dir
340
+
341
+ assert_server_call "ln -sfT #{@app.checkout_path} #{@app.current_path}"
342
+ end
343
+
344
+
345
+ def test_upload_tasks
346
+ files = %w{task1 task2}
347
+
348
+ @sa.upload_tasks(*files)
349
+
350
+ assert_server_call "mkdir -p #{@app.checkout_path}/lib/tasks"
351
+
352
+ files.each do |f|
353
+ args = ["#{Sunshine::ROOT}/templates/tasks/#{f}.rake",
354
+ "#{@app.checkout_path}/lib/tasks/#{f}.rake"]
355
+
356
+ assert @sa.shell.method_called?(:upload, :args => args)
357
+ end
358
+ end
359
+
360
+
361
+ def test_upload_tasks_with_remote_path
362
+ files = %w{task1 task2}
363
+ path = "/path/to/remote/tasks"
364
+
365
+ @sa.upload_tasks files[0], files[1], :remote_path => path
366
+
367
+ assert_server_call "mkdir -p #{path}"
368
+
369
+ files.each do |f|
370
+ args = ["#{Sunshine::ROOT}/templates/tasks/#{f}.rake",
371
+ "#{path}/#{f}.rake"]
372
+
373
+ assert @sa.shell.method_called?(:upload, :args => args)
374
+ end
375
+ end
376
+
377
+
378
+ def test_upload_tasks_with_local_path
379
+ files = %w{task1 task2}
380
+ path = "/path/to/local/tasks"
381
+ args = files.dup << {:local_path => path}
382
+
383
+ @sa.upload_tasks(*args)
384
+
385
+ assert_server_call "mkdir -p #{@sa.checkout_path}/lib/tasks"
386
+
387
+ files.each do |f|
388
+ args = ["#{path}/#{f}.rake",
389
+ "#{@sa.checkout_path}/lib/tasks/#{f}.rake"]
390
+
391
+ assert @sa.shell.method_called?(:upload, :args => args)
392
+ end
393
+ end
394
+
395
+
396
+ def test_upload_tasks_all
397
+ files = Dir.glob("templates/tasks/*").map{|f| File.basename(f, ".rake")}
398
+
399
+ @sa.upload_tasks
400
+
401
+ assert_server_call "mkdir -p #{@app.checkout_path}/lib/tasks"
402
+
403
+ files.each do |f|
404
+ args = ["#{Sunshine::ROOT}/templates/tasks/#{f}.rake",
405
+ "#{@app.checkout_path}/lib/tasks/#{f}.rake"]
406
+
407
+ assert @sa.shell.method_called?(:upload, :args => args)
408
+ end
409
+ end
410
+
411
+
412
+ def test_write_script
413
+ @sa.write_script "script_name", "script contents"
414
+
415
+ args = ["#{@app.checkout_path}/script_name",
416
+ "script contents", {:flags => "--chmod=ugo=rwx"}]
417
+
418
+ assert @sa.shell.method_called?(:make_file, :args => args)
419
+
420
+ args = ["#{@app.current_path}/script_name",
421
+ "#{@app.root_path}/script_name"]
422
+
423
+ assert @sa.shell.method_called?(:symlink, :args => args)
424
+ end
425
+ end
@@ -0,0 +1,97 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestShell < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @output = StringIO.new
7
+ @output.extend MockObject
8
+
9
+ @shell = Sunshine::Shell.new @output
10
+ @shell.extend MockOpen4
11
+
12
+ @shell.input.extend MockObject
13
+ @shell.input.mock :ask, :return => "someinput"
14
+ end
15
+
16
+
17
+ def test_initialize
18
+ assert_equal @output, @shell.output
19
+ assert_equal HighLine, @shell.input.class
20
+ assert_equal `whoami`.chomp, @shell.user
21
+ assert_equal `hostname`.chomp, @shell.host
22
+ end
23
+
24
+
25
+ def test_ask
26
+ @shell.ask "input something!"
27
+ assert 1, @shell.input.method_call_count(:ask)
28
+ end
29
+
30
+
31
+ def test_close
32
+ @shell.close
33
+ assert 1, @shell.output.method_call_count(:close)
34
+ end
35
+
36
+
37
+ def test_write
38
+ @shell.write "blah"
39
+ assert @output.method_called?(:write, :args => "blah")
40
+ end
41
+
42
+
43
+ def test_prompt_for_password
44
+ @shell.prompt_for_password
45
+
46
+ args = "#{@shell.user}@#{@shell.host} Password:"
47
+ assert @shell.input.method_called?(:ask, :args => args)
48
+ assert_equal "someinput", @shell.password
49
+ end
50
+
51
+
52
+ def test_execute
53
+ @shell.set_mock_response 0, "say hi" => [:out, "hi\n"]
54
+
55
+ response = @shell.execute("say hi") do |stream, data|
56
+ assert_equal :out, stream
57
+ assert_equal "hi\n", data
58
+ end
59
+ assert_equal "hi", response
60
+ end
61
+
62
+
63
+ def test_execute_errorstatus
64
+ @shell.set_mock_response 1, "error me" => [:err, "ERROR'D!"]
65
+
66
+ begin
67
+ @shell.execute("error me") do |stream, data|
68
+ assert_equal :err, stream
69
+ assert_equal "ERROR'D!", data
70
+ end
71
+ raise "Didn't call CmdError when it should have"
72
+ rescue Sunshine::CmdError => e
73
+ msg = "Execution failed with status 1: error me"
74
+ assert_equal msg, e.message
75
+ end
76
+ end
77
+
78
+
79
+ def test_execute_stderronly
80
+ @shell.set_mock_response 0, "stderr" => [:err, "fake error"]
81
+
82
+ response = @shell.execute("stderr") do |stream, data|
83
+ assert_equal :err, stream
84
+ assert_equal "fake error", data
85
+ end
86
+ assert_equal "", response
87
+ end
88
+
89
+
90
+ def test_execute_password_prompt
91
+ @shell.set_mock_response 0, "do that thing" => [:err, "Password:"]
92
+ @shell.input.mock :ask, :return => "new_password"
93
+
94
+ @shell.execute("do that thing")
95
+ assert_equal "new_password", @shell.password
96
+ end
97
+ end
@@ -0,0 +1,157 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestSunshine < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+
9
+ def test_default_config
10
+ config = Sunshine::DEFAULT_CONFIG
11
+ Sunshine.setup config
12
+
13
+ assert Sunshine::Shell === Sunshine.shell
14
+
15
+ assert_equal config['deploy_env'].to_s, Sunshine.deploy_env
16
+
17
+ assert_equal !config['auto'], Sunshine.interactive?
18
+
19
+ assert Sunshine::Output === Sunshine.logger
20
+ assert_equal Logger::INFO, Sunshine.logger.level
21
+
22
+ assert_equal config['max_deploy_versions'], Sunshine.max_deploy_versions
23
+
24
+ assert_equal config['trace'], Sunshine.trace?
25
+ end
26
+
27
+
28
+ def test_find_command
29
+ assert !Sunshine.find_command('st')
30
+ assert_equal 'start', Sunshine.find_command('sta')
31
+ assert_equal 'stop', Sunshine.find_command('sto')
32
+ assert_equal 'add', Sunshine.find_command('a')
33
+
34
+ Sunshine::COMMANDS.each do |cmd|
35
+ assert_equal cmd, Sunshine.find_command(cmd)
36
+ end
37
+ end
38
+
39
+
40
+ def test_exec_run_command
41
+ mock_sunshine_exit
42
+ mock_sunshine_command Sunshine::RunCommand
43
+
44
+ Sunshine.run %w{run somefile.rb -l debug -e prod --no-trace}
45
+
46
+ assert_command Sunshine::RunCommand, [['somefile.rb'], Sunshine.setup]
47
+ end
48
+
49
+
50
+ def test_exec_control_commands
51
+ mock_sunshine_exit
52
+
53
+ %w{add list restart rm start stop}.each do |name|
54
+ cmd = Sunshine.const_get("#{name.capitalize}Command")
55
+
56
+ mock_sunshine_command cmd
57
+
58
+ Sunshine.run %w{thing1 thing2 -r remoteserver.com}.unshift(name)
59
+
60
+ servers = [Sunshine::RemoteShell.new("remoteserver.com")]
61
+
62
+ args = [%w{thing1 thing2}, Sunshine.setup]
63
+ assert_command cmd, args
64
+
65
+ assert_equal servers, Sunshine.setup['servers']
66
+
67
+
68
+ Sunshine.run %w{thing1 thing2 -v}.unshift(name)
69
+ servers = [Sunshine.shell]
70
+
71
+ assert_command cmd, args
72
+
73
+ assert_equal servers, Sunshine.setup['servers']
74
+ assert Sunshine.setup['verbose']
75
+ end
76
+ end
77
+
78
+
79
+ def test_exec_local_cmd
80
+ mock_sunshine_exit
81
+ mock_sunshine_command Sunshine::RmCommand
82
+
83
+ Sunshine.run %w{rm app1 app2}
84
+
85
+ dsd = [Sunshine.shell]
86
+
87
+ args = [['app1', 'app2'], Sunshine.setup]
88
+ assert_command Sunshine::RmCommand, args
89
+
90
+ assert_equal dsd, Sunshine.setup['servers']
91
+ end
92
+
93
+
94
+ def test_exit
95
+ assert_sunshine_exit_status [true], 0
96
+ assert_sunshine_exit_status [false], 1
97
+ assert_sunshine_exit_status [0, "success!"], 0, "success!"
98
+ assert_sunshine_exit_status [2, "failed!"], 2, "failed!"
99
+ end
100
+
101
+
102
+ def assert_sunshine_exit_status args, expected_status, msg=""
103
+ args.map!{|a| a.inspect.gsub("\"", "\\\"")}
104
+ args = args.join(",")
105
+ cmd = "ruby -Ilib -e \"require 'sunshine'; Sunshine.exit(#{args})\""
106
+
107
+ pid, inn, out, err = Open4.popen4(*cmd)
108
+
109
+ status = Process.waitpid2(pid).last
110
+
111
+ out_data = out.read
112
+ err_data = err.read
113
+
114
+ out.close
115
+ err.close
116
+ inn.close
117
+
118
+ assert_equal expected_status, status.exitstatus
119
+ if expected_status == 0
120
+ assert_equal msg, out_data.strip
121
+ else
122
+ assert_equal msg, err_data.strip
123
+ end
124
+ end
125
+
126
+
127
+ def assert_command cmd, args
128
+ assert cmd.call_log.include?([:exec, args])
129
+ end
130
+
131
+
132
+ def mock_sunshine_command cmd
133
+ cmd.instance_eval do
134
+ undef exec
135
+ undef call_log if defined?(call_log)
136
+
137
+ def call_log
138
+ @call_log ||= []
139
+ end
140
+
141
+ def exec *args
142
+ call_log << [:exec, args]
143
+ true
144
+ end
145
+ end
146
+ end
147
+
148
+ def mock_sunshine_exit
149
+ Sunshine.instance_eval do
150
+ undef exit
151
+
152
+ def exit *args
153
+ end
154
+ end
155
+ end
156
+
157
+ end