azuki 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +71 -0
  3. data/bin/azuki +17 -0
  4. data/data/cacert.pem +3988 -0
  5. data/lib/azuki.rb +17 -0
  6. data/lib/azuki/auth.rb +339 -0
  7. data/lib/azuki/cli.rb +38 -0
  8. data/lib/azuki/client.rb +764 -0
  9. data/lib/azuki/client/azuki_postgresql.rb +141 -0
  10. data/lib/azuki/client/cisaurus.rb +26 -0
  11. data/lib/azuki/client/pgbackups.rb +113 -0
  12. data/lib/azuki/client/rendezvous.rb +108 -0
  13. data/lib/azuki/client/ssl_endpoint.rb +25 -0
  14. data/lib/azuki/command.rb +294 -0
  15. data/lib/azuki/command/account.rb +23 -0
  16. data/lib/azuki/command/accounts.rb +34 -0
  17. data/lib/azuki/command/addons.rb +305 -0
  18. data/lib/azuki/command/apps.rb +393 -0
  19. data/lib/azuki/command/auth.rb +86 -0
  20. data/lib/azuki/command/base.rb +230 -0
  21. data/lib/azuki/command/certs.rb +209 -0
  22. data/lib/azuki/command/config.rb +137 -0
  23. data/lib/azuki/command/db.rb +218 -0
  24. data/lib/azuki/command/domains.rb +85 -0
  25. data/lib/azuki/command/drains.rb +46 -0
  26. data/lib/azuki/command/fork.rb +164 -0
  27. data/lib/azuki/command/git.rb +64 -0
  28. data/lib/azuki/command/help.rb +179 -0
  29. data/lib/azuki/command/keys.rb +115 -0
  30. data/lib/azuki/command/labs.rb +147 -0
  31. data/lib/azuki/command/logs.rb +45 -0
  32. data/lib/azuki/command/maintenance.rb +61 -0
  33. data/lib/azuki/command/pg.rb +269 -0
  34. data/lib/azuki/command/pgbackups.rb +329 -0
  35. data/lib/azuki/command/plugins.rb +110 -0
  36. data/lib/azuki/command/ps.rb +232 -0
  37. data/lib/azuki/command/regions.rb +22 -0
  38. data/lib/azuki/command/releases.rb +124 -0
  39. data/lib/azuki/command/run.rb +180 -0
  40. data/lib/azuki/command/sharing.rb +89 -0
  41. data/lib/azuki/command/ssl.rb +43 -0
  42. data/lib/azuki/command/stack.rb +62 -0
  43. data/lib/azuki/command/status.rb +51 -0
  44. data/lib/azuki/command/update.rb +47 -0
  45. data/lib/azuki/command/version.rb +23 -0
  46. data/lib/azuki/deprecated.rb +5 -0
  47. data/lib/azuki/deprecated/help.rb +38 -0
  48. data/lib/azuki/distribution.rb +9 -0
  49. data/lib/azuki/excon.rb +9 -0
  50. data/lib/azuki/helpers.rb +517 -0
  51. data/lib/azuki/helpers/azuki_postgresql.rb +165 -0
  52. data/lib/azuki/helpers/log_displayer.rb +70 -0
  53. data/lib/azuki/plugin.rb +163 -0
  54. data/lib/azuki/updater.rb +171 -0
  55. data/lib/azuki/version.rb +3 -0
  56. data/lib/vendor/azuki/okjson.rb +598 -0
  57. data/spec/azuki/auth_spec.rb +256 -0
  58. data/spec/azuki/client/azuki_postgresql_spec.rb +71 -0
  59. data/spec/azuki/client/pgbackups_spec.rb +43 -0
  60. data/spec/azuki/client/rendezvous_spec.rb +62 -0
  61. data/spec/azuki/client/ssl_endpoint_spec.rb +48 -0
  62. data/spec/azuki/client_spec.rb +564 -0
  63. data/spec/azuki/command/addons_spec.rb +601 -0
  64. data/spec/azuki/command/apps_spec.rb +351 -0
  65. data/spec/azuki/command/auth_spec.rb +38 -0
  66. data/spec/azuki/command/base_spec.rb +109 -0
  67. data/spec/azuki/command/certs_spec.rb +178 -0
  68. data/spec/azuki/command/config_spec.rb +144 -0
  69. data/spec/azuki/command/db_spec.rb +110 -0
  70. data/spec/azuki/command/domains_spec.rb +87 -0
  71. data/spec/azuki/command/drains_spec.rb +34 -0
  72. data/spec/azuki/command/fork_spec.rb +56 -0
  73. data/spec/azuki/command/git_spec.rb +144 -0
  74. data/spec/azuki/command/help_spec.rb +93 -0
  75. data/spec/azuki/command/keys_spec.rb +120 -0
  76. data/spec/azuki/command/labs_spec.rb +100 -0
  77. data/spec/azuki/command/logs_spec.rb +60 -0
  78. data/spec/azuki/command/maintenance_spec.rb +51 -0
  79. data/spec/azuki/command/pg_spec.rb +236 -0
  80. data/spec/azuki/command/pgbackups_spec.rb +307 -0
  81. data/spec/azuki/command/plugins_spec.rb +104 -0
  82. data/spec/azuki/command/ps_spec.rb +195 -0
  83. data/spec/azuki/command/releases_spec.rb +130 -0
  84. data/spec/azuki/command/run_spec.rb +83 -0
  85. data/spec/azuki/command/sharing_spec.rb +59 -0
  86. data/spec/azuki/command/stack_spec.rb +46 -0
  87. data/spec/azuki/command/status_spec.rb +48 -0
  88. data/spec/azuki/command/version_spec.rb +16 -0
  89. data/spec/azuki/command_spec.rb +211 -0
  90. data/spec/azuki/helpers/azuki_postgresql_spec.rb +155 -0
  91. data/spec/azuki/helpers_spec.rb +48 -0
  92. data/spec/azuki/plugin_spec.rb +172 -0
  93. data/spec/azuki/updater_spec.rb +44 -0
  94. data/spec/helper/legacy_help.rb +16 -0
  95. data/spec/spec.opts +1 -0
  96. data/spec/spec_helper.rb +224 -0
  97. data/spec/support/display_message_matcher.rb +49 -0
  98. data/spec/support/openssl_mock_helper.rb +8 -0
  99. metadata +211 -0
@@ -0,0 +1,130 @@
1
+ require "spec_helper"
2
+ require "azuki/command/releases"
3
+
4
+ describe Azuki::Command::Releases do
5
+
6
+ before(:each) do
7
+ stub_core
8
+ end
9
+
10
+ describe "releases" do
11
+
12
+ before(:each) do
13
+ api.post_app("name" => "example", "stack" => "cedar")
14
+ api.put_config_vars("example", { 'FOO_BAR' => 'BAZ' })
15
+ api.put_config_vars("example", { 'BAR_BAZ' => 'QUX' })
16
+ api.put_config_vars("example", { 'BAZ_QUX' => 'QUUX' })
17
+ api.put_config_vars("example", { 'QUX_QUUX' => 'XYZZY' })
18
+ api.put_config_vars("example", { 'SUPER_LONG_CONFIG_VAR_TO_GET_PAST_THE_TRUNCATION_LIMIT' => 'VALUE' })
19
+ Azuki::Command::Releases.any_instance.should_receive(:time_ago).exactly(5).times.and_return('2012/09/10 11:36:44 (~ 0s ago)', '2012/09/10 11:36:43 (~ 1s ago)', '2012/09/10 11:35:44 (~ 1m ago)', '2012/09/10 10:36:44 (~ 1h ago)', '2012/01/02 12:34:56')
20
+ end
21
+
22
+ after(:each) do
23
+ api.delete_app("example")
24
+ end
25
+
26
+ it "should list releases" do
27
+ @stderr, @stdout = execute("releases")
28
+ @stderr.should == ""
29
+ @stdout.should == <<-STDOUT
30
+ === example Releases
31
+ v5 Config add SUPER_LONG_CONFIG_VAR_TO_GE.. email@example.com 2012/09/10 11:36:44 (~ 0s ago)
32
+ v4 Config add QUX_QUUX email@example.com 2012/09/10 11:36:43 (~ 1s ago)
33
+ v3 Config add BAZ_QUX email@example.com 2012/09/10 11:35:44 (~ 1m ago)
34
+ v2 Config add BAR_BAZ email@example.com 2012/09/10 10:36:44 (~ 1h ago)
35
+ v1 Config add FOO_BAR email@example.com 2012/01/02 12:34:56
36
+
37
+ STDOUT
38
+ end
39
+
40
+ end
41
+
42
+ describe "releases:info" do
43
+ before(:each) do
44
+ api.post_app("name" => "example", "stack" => "cedar")
45
+ api.put_config_vars("example", { 'FOO_BAR' => 'BAZ' })
46
+ end
47
+
48
+ after(:each) do
49
+ api.delete_app("example")
50
+ end
51
+
52
+ it "requires a release to be specified" do
53
+ stderr, stdout = execute("releases:info")
54
+ stderr.should == <<-STDERR
55
+ ! Usage: azuki releases:info RELEASE
56
+ STDERR
57
+ stdout.should == ""
58
+ end
59
+
60
+ it "shows info for a single release" do
61
+ Azuki::Command::Releases.any_instance.should_receive(:time_ago).and_return("2012/09/11 12:34:56 (~ 0s ago)")
62
+ stderr, stdout = execute("releases:info v1")
63
+ stderr.should == ""
64
+ stdout.should == <<-STDOUT
65
+ === Release v1
66
+ By: email@example.com
67
+ Change: Config add FOO_BAR
68
+ When: 2012/09/11 12:34:56 (~ 0s ago)
69
+
70
+ === v1 Config Vars
71
+ BUNDLE_WITHOUT: development:test
72
+ DATABASE_URL: postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
73
+ LANG: en_US.UTF-8
74
+ RACK_ENV: production
75
+ SHARED_DATABASE_URL: postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
76
+ STDOUT
77
+ end
78
+
79
+ it "shows info for a single release in shell compatible format" do
80
+ Azuki::Command::Releases.any_instance.should_receive(:time_ago).and_return("2012/09/11 12:34:56 (~ 0s ago)")
81
+ stderr, stdout = execute("releases:info v1 --shell")
82
+ stderr.should == ""
83
+ stdout.should == <<-STDOUT
84
+ === Release v1
85
+ By: email@example.com
86
+ Change: Config add FOO_BAR
87
+ When: 2012/09/11 12:34:56 (~ 0s ago)
88
+
89
+ === v1 Config Vars
90
+ BUNDLE_WITHOUT=development:test
91
+ DATABASE_URL=postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
92
+ LANG=en_US.UTF-8
93
+ RACK_ENV=production
94
+ SHARED_DATABASE_URL=postgres://username:password@ec2-123-123-123-123.compute-1.amazonaws.com/username
95
+ STDOUT
96
+ end
97
+ end
98
+
99
+ describe "rollback" do
100
+ before(:each) do
101
+ api.post_app("name" => "example", "stack" => "cedar")
102
+ api.put_config_vars("example", { 'FOO_BAR' => 'BAZ' })
103
+ api.put_config_vars("example", { 'BAR_BAZ' => 'QUX' })
104
+ api.put_config_vars("example", { 'BAZ_QUX' => 'QUUX' })
105
+ end
106
+
107
+ after(:each) do
108
+ api.delete_app("example")
109
+ end
110
+
111
+ it "rolls back to the latest release with no argument" do
112
+ stderr, stdout = execute("releases:rollback")
113
+ stderr.should == ""
114
+ stdout.should == <<-STDOUT
115
+ Rolling back example... done, v2
116
+ STDOUT
117
+ end
118
+
119
+ it "rolls back to the specified release" do
120
+ stderr, stdout = execute("releases:rollback v1")
121
+ stderr.should == ""
122
+ stdout.should == <<-STDOUT
123
+ Rolling back example... done, v1
124
+ STDOUT
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+
@@ -0,0 +1,83 @@
1
+ require "spec_helper"
2
+ require "azuki/command/run"
3
+ require "azuki/helpers"
4
+
5
+ describe Azuki::Command::Run do
6
+
7
+ include Azuki::Helpers
8
+
9
+ before(:each) do
10
+ stub_core
11
+ api.post_app("name" => "example", "stack" => "cedar")
12
+ end
13
+
14
+ after(:each) do
15
+ api.delete_app("example")
16
+ end
17
+
18
+ describe "run" do
19
+ it "runs a command" do
20
+ stub_rendezvous.start { $stdout.puts "output" }
21
+
22
+ stderr, stdout = execute("run bin/foo")
23
+ stderr.should == ""
24
+ stdout.should == <<-STDOUT
25
+ Running `bin/foo` attached to terminal... up, run.1
26
+ output
27
+ STDOUT
28
+ end
29
+ end
30
+
31
+ describe "run:detached" do
32
+ it "runs a command detached" do
33
+ stderr, stdout = execute("run:detached bin/foo")
34
+ stderr.should == ""
35
+ stdout.should == <<-STDOUT
36
+ Running `bin/foo` detached... up, run.1
37
+ Use `azuki logs -p run.1` to view the output.
38
+ STDOUT
39
+ end
40
+
41
+ it "runs with options" do
42
+ stub_core.read_logs("example", [
43
+ "tail=1",
44
+ "ps=run.1"
45
+ ])
46
+ execute "run:detached bin/foo --tail"
47
+ end
48
+ end
49
+
50
+ describe "run:rake" do
51
+ it "runs a rake command" do
52
+ stub_rendezvous.start { $stdout.puts("rake_output") }
53
+
54
+ stderr, stdout = execute("run:rake foo")
55
+ stderr.should == ""
56
+ stdout.should == <<-STDOUT
57
+ WARNING: `azuki run:rake` has been deprecated. Please use `azuki run rake` instead.
58
+ Running `rake foo` attached to terminal... up, run.1
59
+ rake_output
60
+ STDOUT
61
+ end
62
+
63
+ it "shows the proper command in the deprecation warning" do
64
+ stub_rendezvous.start { $stdout.puts("rake_output") }
65
+
66
+ stderr, stdout = execute("rake foo")
67
+ stderr.should == ""
68
+ stdout.should == <<-STDOUT
69
+ WARNING: `azuki rake` has been deprecated. Please use `azuki run rake` instead.
70
+ Running `rake foo` attached to terminal... up, run.1
71
+ rake_output
72
+ STDOUT
73
+ end
74
+ end
75
+
76
+ describe "run:console" do
77
+ it "has been removed" do
78
+ stderr, stdout = execute("run:console")
79
+ stderr.should == ""
80
+ stdout.should =~ /has been removed/
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+ require "azuki/command/sharing"
3
+
4
+ module Azuki::Command
5
+ describe Sharing do
6
+
7
+ before(:each) do
8
+ stub_core
9
+ api.post_app("name" => "example")
10
+ end
11
+
12
+ after(:each) do
13
+ api.delete_app("example")
14
+ end
15
+
16
+ context("list") do
17
+
18
+ it "lists collaborators" do
19
+ api.post_collaborator("example", "collaborator@example.com")
20
+ stderr, stdout = execute("sharing")
21
+ stderr.should == ""
22
+ stdout.should == <<-STDOUT
23
+ === example Collaborators
24
+ collaborator@example.com
25
+ email@example.com
26
+
27
+ STDOUT
28
+ end
29
+
30
+ end
31
+
32
+ it "adds collaborators with default access to view only" do
33
+ stderr, stdout = execute("sharing:add collaborator@example.com")
34
+ stderr.should == ""
35
+ stdout.should == <<-STDOUT
36
+ Adding collaborator@example.com to example collaborators... done
37
+ STDOUT
38
+ end
39
+
40
+ it "removes collaborators" do
41
+ api.post_collaborator("example", "collaborator@example.com")
42
+ stderr, stdout = execute("sharing:remove collaborator@example.com")
43
+ stderr.should == ""
44
+ stdout.should == <<-STDOUT
45
+ Removing collaborator@example.com from example collaborators... done
46
+ STDOUT
47
+ end
48
+
49
+ it "transfers ownership" do
50
+ api.post_collaborator("example", "collaborator@example.com")
51
+ stderr, stdout = execute("sharing:transfer collaborator@example.com")
52
+ stderr.should == ""
53
+ stdout.should == <<-STDOUT
54
+ Transferring example to collaborator@example.com... done
55
+ STDOUT
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,46 @@
1
+ require "spec_helper"
2
+ require "azuki/command/stack"
3
+
4
+ module Azuki::Command
5
+ describe Stack do
6
+ describe "index" do
7
+ before(:each) do
8
+ stub_core
9
+ api.post_app("name" => "example", "stack" => "bamboo-mri-1.9.2")
10
+ end
11
+
12
+ after(:each) do
13
+ api.delete_app("example")
14
+ end
15
+
16
+ it "index should provide list" do
17
+ stderr, stdout = execute("stack")
18
+ stderr.should == ""
19
+ stdout.should == <<-STDOUT
20
+ === example Available Stacks
21
+ aspen-mri-1.8.6
22
+ bamboo-ree-1.8.7
23
+ cedar (beta)
24
+ * bamboo-mri-1.9.2
25
+
26
+ STDOUT
27
+ end
28
+
29
+ it "migrate should succeed" do
30
+ stderr, stdout = execute("stack:migrate bamboo-ree-1.8.7")
31
+ stderr.should == ""
32
+ stdout.should == <<-STDOUT
33
+ -----> Preparing to migrate example
34
+ bamboo-mri-1.9.2 -> bamboo-ree-1.8.7
35
+
36
+ NOTE: Additional details here
37
+
38
+ -----> Migration prepared.
39
+ Run 'git push azuki master' to execute migration.
40
+ STDOUT
41
+ end
42
+
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+ require "azuki/command/status"
3
+
4
+ module Azuki::Command
5
+ describe Status do
6
+
7
+ before(:each) do
8
+ stub_core
9
+ end
10
+
11
+ it "displays status information" do
12
+ Excon.stub(
13
+ {
14
+ :host => 'status.azukiapp.com',
15
+ :method => :get,
16
+ :path => '/api/v3/current-status.json'
17
+ },
18
+ {
19
+ :body => Azuki::API::OkJson.encode({"status"=>{"Production"=>"red", "Development"=>"red"}, "issues"=>[{"created_at"=>"2012-06-07T15:55:51Z", "id"=>372, "resolved"=>false, "title"=>"HTTP Routing Errors", "updated_at"=>"2012-06-07T16:14:37Z", "href"=>"https://status.azukiapp.com/api/v3/issues/372", "updates"=>[{"contents"=>"The number of applications seeing H99 errors is continuing to decrease as we continue to work toward a full resolution of the HTTP routing issues. The API is back online now as well. ", "created_at"=>"2012-06-07T17:47:26Z", "id"=>1088, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T17:47:26Z"}, {"contents"=>"Our engineers are continuing to work toward a full resolution of the HTTP routing issues. The API is currently in maintenance mode intentionally as we restore application operations. ", "created_at"=>"2012-06-07T17:16:40Z", "id"=>1086, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T17:26:55Z"}, {"contents"=>"Most applications are back online at this time. Our engineers are working on getting the remaining apps back online. ", "created_at"=>"2012-06-07T16:50:21Z", "id"=>1085, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:50:21Z"}, {"contents"=>"Our routing engineers have pushed out a patch to our routing tier. The platform is recovering and applications are coming back online. Our engineers are continuing to fully restore service.", "created_at"=>"2012-06-07T16:36:37Z", "id"=>1084, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:36:37Z"}, {"contents"=>"We have identified an issue with our routers that is causing errors on HTTP requests to applications. Engineers are working to resolve the issue.\r\n", "created_at"=>"2012-06-07T16:15:25Z", "id"=>1083, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T16:15:28Z"}, {"contents"=>"We have confirmed widespread errors on the platform. Our engineers are continuing to investigate.\r\n", "created_at"=>"2012-06-07T15:58:56Z", "id"=>1082, "incident_id"=>372, "status_dev"=>"red", "status_prod"=>"red", "update_type"=>"update", "updated_at"=>"2012-06-07T15:58:58Z"}, {"contents"=>"Our automated systems have detected potential platform errors. We are investigating.\r\n", "created_at"=>"2012-06-07T15:55:51Z", "id"=>1081, "incident_id"=>372, "status_dev"=>"yellow", "status_prod"=>"yellow", "update_type"=>"issue", "updated_at"=>"2012-06-07T15:55:55Z"}]}]}),
20
+ :status => 200
21
+ }
22
+ )
23
+
24
+ Azuki::Command::Status.any_instance.should_receive(:time_ago).and_return('2012/09/11 09:34:56 (~ 3h ago)', '2012/09/11 12:33:56 (~ 1m ago)', '2012/09/11 12:29:56 (~ 5m ago)', '2012/09/11 12:24:56 (~ 10m ago)', '2012/09/11 12:04:56 (~ 30m ago)', '2012/09/11 11:34:56 (~ 1h ago)', '2012/09/11 10:34:56 (~ 2h ago)', '2012/09/11 09:34:56 (~ 3h ago)')
25
+
26
+ stderr, stdout = execute("status")
27
+ stderr.should == ''
28
+ stdout.should == <<-STDOUT
29
+ === Azuki Status
30
+ Development: red
31
+ Production: red
32
+
33
+ === HTTP Routing Errors 2012/09/11 09:34:56 (~ 3h+)
34
+ 2012/09/11 12:33:56 (~ 1m ago) update The number of applications seeing H99 errors is continuing to decrease as we continue to work toward a full resolution of the HTTP routing issues. The API is back online now as well.
35
+ 2012/09/11 12:29:56 (~ 5m ago) update Our engineers are continuing to work toward a full resolution of the HTTP routing issues. The API is currently in maintenance mode intentionally as we restore application operations.
36
+ 2012/09/11 12:24:56 (~ 10m ago) update Most applications are back online at this time. Our engineers are working on getting the remaining apps back online.
37
+ 2012/09/11 12:04:56 (~ 30m ago) update Our routing engineers have pushed out a patch to our routing tier. The platform is recovering and applications are coming back online. Our engineers are continuing to fully restore service.
38
+ 2012/09/11 11:34:56 (~ 1h ago) update We have identified an issue with our routers that is causing errors on HTTP requests to applications. Engineers are working to resolve the issue.
39
+ 2012/09/11 10:34:56 (~ 2h ago) update We have confirmed widespread errors on the platform. Our engineers are continuing to investigate.
40
+ 2012/09/11 09:34:56 (~ 3h ago) issue Our automated systems have detected potential platform errors. We are investigating.
41
+
42
+ STDOUT
43
+
44
+ Excon.stubs.shift
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+ require "azuki/command/version"
3
+
4
+ module Azuki::Command
5
+ describe Version do
6
+
7
+ it "shows version info" do
8
+ stderr, stdout = execute("version")
9
+ stderr.should == ""
10
+ stdout.should == <<-STDOUT
11
+ #{Azuki.user_agent}
12
+ STDOUT
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,211 @@
1
+ require "spec_helper"
2
+ require "azuki/command"
3
+ require 'json' #FOR WEBMOCK
4
+
5
+ class FakeResponse
6
+
7
+ attr_accessor :body, :headers
8
+
9
+ def initialize(attributes)
10
+ self.body, self.headers = attributes[:body], attributes[:headers]
11
+ end
12
+
13
+ def to_s
14
+ body
15
+ end
16
+
17
+ end
18
+
19
+ describe Azuki::Command do
20
+ before {
21
+ Azuki::Command.load
22
+ stub_core # setup fake auth
23
+ }
24
+
25
+ describe "when the command requires confirmation" do
26
+
27
+ let(:response_that_requires_confirmation) do
28
+ {:status => 423,
29
+ :headers => { :x_confirmation_required => 'my_addon' },
30
+ :body => 'terms of service required'}
31
+ end
32
+
33
+ context "when the app is unknown" do
34
+ context "and the user includes --confirm APP" do
35
+ it "should set --app to APP and not ask for confirmation" do
36
+ stub_request(:post, %r{apps/XXX/addons/my_addon$}).
37
+ with(:body => {:confirm => "XXX"})
38
+ run "addons:add my_addon --confirm XXX"
39
+ end
40
+ end
41
+
42
+ context "and the user includes --confirm APP --app APP2" do
43
+ it "should warn that the app and confirm do not match and not continue" do
44
+ capture_stderr do
45
+ run "addons:add my_addon --confirm APP --app APP2"
46
+ end.should == " ! Mismatch between --app and --confirm\n"
47
+ end
48
+ end
49
+ end
50
+
51
+ context "and the app is known" do
52
+ before do
53
+ any_instance_of(Azuki::Command::Base) do |base|
54
+ stub(base).app.returns("example")
55
+ end
56
+ end
57
+
58
+ context "and the user includes --confirm WRONGAPP" do
59
+ it "should not allow include the option" do
60
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
61
+ with(:body => "")
62
+ run "addons:add my_addon --confirm XXX"
63
+ end
64
+ end
65
+
66
+ context "and the user includes --confirm APP" do
67
+ it "should set --app to APP and not ask for confirmation" do
68
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
69
+ with(:body => {:confirm => 'example'})
70
+
71
+ run "addons:add my_addon --confirm example"
72
+ end
73
+ end
74
+
75
+ context "and the user didn't include a confirm flag" do
76
+ it "should ask the user for confirmation" do
77
+ stub(Azuki::Command).confirm_command.returns(true)
78
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
79
+ to_return(response_that_requires_confirmation).then.
80
+ to_return({:status => 200})
81
+
82
+ run "addons:add my_addon"
83
+ end
84
+
85
+ it "should not continue if the confirmation does not match" do
86
+ Azuki::Command.stub(:current_options).and_return(:confirm => 'not_example')
87
+
88
+ lambda do
89
+ Azuki::Command.confirm_command('example')
90
+ end.should raise_error(Azuki::Command::CommandFailed)
91
+ end
92
+
93
+ it "should not continue if the user doesn't confirm" do
94
+ stub(Azuki::Command).confirm_command.returns(false)
95
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
96
+ to_return(response_that_requires_confirmation).then.
97
+ to_raise(Azuki::Command::CommandFailed)
98
+
99
+ run "addons:add my_addon"
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ describe "parsing errors" do
106
+ it "extracts error messages from response when available in XML" do
107
+ Azuki::Command.extract_error('<errors><error>Invalid app name</error></errors>').should == 'Invalid app name'
108
+ end
109
+
110
+ it "extracts error messages from response when available in JSON" do
111
+ Azuki::Command.extract_error("{\"error\":\"Invalid app name\"}").should == 'Invalid app name'
112
+ end
113
+
114
+ it "extracts error messages from response when available in plain text" do
115
+ response = FakeResponse.new(:body => "Invalid app name", :headers => { :content_type => "text/plain; charset=UTF8" })
116
+ Azuki::Command.extract_error(response).should == 'Invalid app name'
117
+ end
118
+
119
+ it "shows Internal Server Error when the response doesn't contain a XML or JSON" do
120
+ Azuki::Command.extract_error('<h1>HTTP 500</h1>').should == "Internal server error.\nRun `azuki status` to check for known platform issues."
121
+ end
122
+
123
+ it "shows Internal Server Error when the response is not plain text" do
124
+ response = FakeResponse.new(:body => "Foobar", :headers => { :content_type => "application/xml" })
125
+ Azuki::Command.extract_error(response).should == "Internal server error.\nRun `azuki status` to check for known platform issues."
126
+ end
127
+
128
+ it "allows a block to redefine the default error" do
129
+ Azuki::Command.extract_error("Foobar") { "Ok!" }.should == 'Ok!'
130
+ end
131
+
132
+ it "doesn't format the response if set to raw" do
133
+ Azuki::Command.extract_error("Foobar", :raw => true) { "Ok!" }.should == 'Ok!'
134
+ end
135
+
136
+ it "handles a nil body in parse_error_xml" do
137
+ lambda { Azuki::Command.parse_error_xml(nil) }.should_not raise_error
138
+ end
139
+
140
+ it "handles a nil body in parse_error_json" do
141
+ lambda { Azuki::Command.parse_error_json(nil) }.should_not raise_error
142
+ end
143
+ end
144
+
145
+ it "correctly resolves commands" do
146
+ class Azuki::Command::Test; end
147
+ class Azuki::Command::Test::Multiple; end
148
+
149
+ require "azuki/command/help"
150
+ require "azuki/command/apps"
151
+
152
+ Azuki::Command.parse("unknown").should be_nil
153
+ Azuki::Command.parse("list").should include(:klass => Azuki::Command::Apps, :method => :index)
154
+ Azuki::Command.parse("apps").should include(:klass => Azuki::Command::Apps, :method => :index)
155
+ Azuki::Command.parse("apps:create").should include(:klass => Azuki::Command::Apps, :method => :create)
156
+ end
157
+
158
+ context "help" do
159
+ it "works as a prefix" do
160
+ azuki("help ps:scale").should =~ /scale processes by/
161
+ end
162
+
163
+ it "works as an option" do
164
+ azuki("ps:scale -h").should =~ /scale processes by/
165
+ azuki("ps:scale --help").should =~ /scale processes by/
166
+ end
167
+ end
168
+
169
+ context "when no commands match" do
170
+
171
+ it "displays the version if --version is used" do
172
+ azuki("--version").should == <<-STDOUT
173
+ #{Azuki.user_agent}
174
+ STDOUT
175
+ end
176
+
177
+ it "suggests similar commands if there are any" do
178
+ original_stderr, original_stdout = $stderr, $stdout
179
+ $stderr = captured_stderr = StringIO.new
180
+ $stdout = captured_stdout = StringIO.new
181
+ begin
182
+ execute("aps")
183
+ rescue SystemExit
184
+ end
185
+ captured_stderr.string.should == <<-STDERR
186
+ ! `aps` is not a azuki command.
187
+ ! Perhaps you meant `apps` or `ps`.
188
+ ! See `azuki help` for a list of available commands.
189
+ STDERR
190
+ captured_stdout.string.should == ""
191
+ $stderr, $stdout = original_stderr, original_stdout
192
+ end
193
+
194
+ it "does not suggest similar commands if there are none" do
195
+ original_stderr, original_stdout = $stderr, $stdout
196
+ $stderr = captured_stderr = StringIO.new
197
+ $stdout = captured_stdout = StringIO.new
198
+ begin
199
+ execute("sandwich")
200
+ rescue SystemExit
201
+ end
202
+ captured_stderr.string.should == <<-STDERR
203
+ ! `sandwich` is not a azuki command.
204
+ ! See `azuki help` for a list of available commands.
205
+ STDERR
206
+ captured_stdout.string.should == ""
207
+ $stderr, $stdout = original_stderr, original_stdout
208
+ end
209
+
210
+ end
211
+ end