azuki 0.0.1

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 (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