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,601 @@
1
+ require "spec_helper"
2
+ require "azuki/command/addons"
3
+
4
+ module Azuki::Command
5
+ describe Addons do
6
+ before do
7
+ @addons = prepare_command(Addons)
8
+ stub_core.release("example", "current").returns( "name" => "v99" )
9
+ end
10
+
11
+ describe "index" do
12
+
13
+ before(:each) do
14
+ stub_core
15
+ api.post_app("name" => "example", "stack" => "cedar")
16
+ end
17
+
18
+ after(:each) do
19
+ api.delete_app("example")
20
+ end
21
+
22
+ it "should display no addons when none are configured" do
23
+ stderr, stdout = execute("addons")
24
+ stderr.should == ""
25
+ stdout.should == <<-STDOUT
26
+ example has no add-ons.
27
+ STDOUT
28
+ end
29
+
30
+ it "should list addons and attachments" do
31
+ Excon.stub(
32
+ {
33
+ :expects => 200,
34
+ :method => :get,
35
+ :path => %r{^/apps/example/addons$}
36
+ },
37
+ {
38
+ :body => Azuki::API::OkJson.encode([
39
+ { 'configured' => false, 'name' => 'deployhooks:email' },
40
+ { 'attachment_name' => 'AZUKI_POSTGRESQL_RED', 'configured' => true, 'name' => 'azuki-postgresql:ronin' },
41
+ { 'configured' => true, 'name' => 'deployhooks:http' }
42
+ ]),
43
+ :status => 200,
44
+ }
45
+ )
46
+ stderr, stdout = execute("addons")
47
+ stderr.should == ""
48
+ stdout.should == <<-STDOUT
49
+ === example Configured Add-ons
50
+ deployhooks:http
51
+ azuki-postgresql:ronin AZUKI_POSTGRESQL_RED
52
+
53
+ === example Add-ons to Configure
54
+ deployhooks:email https://api.azukiapp.com/apps/example/addons/deployhooks:email
55
+
56
+ STDOUT
57
+ Excon.stubs.shift
58
+ end
59
+
60
+ end
61
+
62
+ describe "list" do
63
+ before do
64
+ stub_core.addons.returns([
65
+ { "name" => "cloudcounter:basic", "state" => "alpha" },
66
+ { "name" => "cloudcounter:pro", "state" => "public" },
67
+ { "name" => "cloudcounter:gold", "state" => "public" },
68
+ { "name" => "cloudcounter:old", "state" => "disabled" },
69
+ { "name" => "cloudcounter:platinum", "state" => "beta" }
70
+ ])
71
+ end
72
+
73
+ it "lists available addons" do
74
+ stderr, stdout = execute("addons:list")
75
+ stderr.should == ""
76
+ stdout.should == <<-STDOUT
77
+ === alpha
78
+ cloudcounter:basic
79
+
80
+ === available
81
+ cloudcounter:gold, pro
82
+
83
+ === beta
84
+ cloudcounter:platinum
85
+
86
+ === disabled
87
+ cloudcounter:old
88
+
89
+ STDOUT
90
+ end
91
+ end
92
+
93
+ describe 'v1-style command line params' do
94
+ it "understands foo=baz" do
95
+ @addons.stub!(:args).and_return(%w(my_addon foo=baz))
96
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => 'baz' })
97
+ @addons.add
98
+ end
99
+
100
+ it "gives a deprecation notice with an example" do
101
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
102
+ with(:body => {:config => {:foo => 'bar', :extra => "XXX"}}).
103
+ to_return(:body => Azuki::OkJson.encode({ 'price' => 'free' }))
104
+ Excon.stub(
105
+ {
106
+ :expects => 200,
107
+ :method => :get,
108
+ :path => %r{^/apps/example/releases/current}
109
+ },
110
+ {
111
+ :body => Azuki::API::OkJson.encode({ 'name' => 'v99' }),
112
+ :status => 200,
113
+ }
114
+ )
115
+ stderr, stdout = execute("addons:add my_addon --foo=bar extra=XXX")
116
+ stderr.should == ""
117
+ stdout.should == <<-STDOUT
118
+ Warning: non-unix style params have been deprecated, use --extra=XXX instead
119
+ Adding my_addon on example... done, v99 (free)
120
+ Use `azuki addons:docs my_addon` to view documentation.
121
+ STDOUT
122
+ Excon.stubs.shift
123
+ end
124
+ end
125
+
126
+ describe 'unix-style command line params' do
127
+ it "understands --foo=baz" do
128
+ @addons.stub!(:args).and_return(%w(my_addon --foo=baz))
129
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => 'baz' })
130
+ @addons.add
131
+ end
132
+
133
+ it "understands --foo baz" do
134
+ @addons.stub!(:args).and_return(%w(my_addon --foo baz))
135
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => 'baz' })
136
+ @addons.add
137
+ end
138
+
139
+ it "treats lone switches as true" do
140
+ @addons.stub!(:args).and_return(%w(my_addon --foo))
141
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => true })
142
+ @addons.add
143
+ end
144
+
145
+ it "converts 'true' to boolean" do
146
+ @addons.stub!(:args).and_return(%w(my_addon --foo=true))
147
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => true })
148
+ @addons.add
149
+ end
150
+
151
+ it "works with many config vars" do
152
+ @addons.stub!(:args).and_return(%w(my_addon --foo baz --bar yes --baz=foo --bab --bob=true))
153
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => 'baz', 'bar' => 'yes', 'baz' => 'foo', 'bab' => true, 'bob' => true })
154
+ @addons.add
155
+ end
156
+
157
+ it "sends the variables to the server" do
158
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
159
+ with(:body => {:config => { 'foo' => 'baz', 'bar' => 'yes', 'baz' => 'foo', 'bab' => 'true', 'bob' => 'true' }})
160
+ stderr, stdout = execute("addons:add my_addon --foo baz --bar yes --baz=foo --bab --bob=true")
161
+ stderr.should == ""
162
+ end
163
+
164
+ it "raises an error for spurious arguments" do
165
+ @addons.stub!(:args).and_return(%w(my_addon spurious))
166
+ lambda { @addons.add }.should raise_error(CommandFailed)
167
+ end
168
+ end
169
+
170
+ describe "mixed options" do
171
+ it "understands foo=bar and --baz=bar on the same line" do
172
+ @addons.stub!(:args).and_return(%w(my_addon foo=baz --baz=bar bob=true --bar))
173
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', { 'foo' => 'baz', 'baz' => 'bar', 'bar' => true, 'bob' => true })
174
+ @addons.add
175
+ end
176
+
177
+ it "sends the variables to the server" do
178
+ stub_request(:post, %r{apps/example/addons/my_addon$}).
179
+ with(:body => {:config => { 'foo' => 'baz', 'baz' => 'bar', 'bar' => 'true', 'bob' => 'true' }})
180
+ stderr, stdout = execute("addons:add my_addon foo=baz --baz=bar bob=true --bar")
181
+ stderr.should == ""
182
+ stdout.should include("Warning: non-unix style params have been deprecated, use --foo=baz --bob=true instead")
183
+ end
184
+ end
185
+
186
+ describe "fork and follow switches" do
187
+ it "should only resolve for azuki-postgresql addon" do
188
+ %w{fork follow}.each do |switch|
189
+ @addons.stub!(:args).and_return("addon --#{switch} AZUKI_POSTGRESQL_RED".split)
190
+ @addons.azuki.should_receive(:install_addon).
191
+ with('example', 'addon', {switch => 'AZUKI_POSTGRESQL_RED'})
192
+ @addons.add
193
+ end
194
+ end
195
+
196
+ it "should translate --fork and --follow" do
197
+ %w{fork follow}.each do |switch|
198
+ @addons.stub!(:app_config_vars).and_return({})
199
+ @addons.stub!(:app_attachments).and_return([Azuki::Helpers::AzukiPostgresql::Attachment.new({
200
+ 'config_var' => 'AZUKI_POSTGRESQL_RED_URL',
201
+ 'resource' => {'name' => 'loudly-yelling-1232',
202
+ 'value' => 'postgres://red_url',
203
+ 'type' => 'azuki-postgresql:ronin' }})
204
+ ])
205
+ @addons.stub!(:args).and_return("azuki-postgresql --#{switch} AZUKI_POSTGRESQL_RED".split)
206
+ @addons.azuki.should_receive(:install_addon).with('example', 'azuki-postgresql', {switch => 'postgres://red_url'})
207
+ @addons.add
208
+ end
209
+ end
210
+
211
+ it "should NOT translate --fork and --follow if passed in a full postgres url even if there are no databases" do
212
+ %w{fork follow}.each do |switch|
213
+ @addons.stub!(:app_config_vars).and_return({})
214
+ @addons.stub!(:app_attachments).and_return([])
215
+ @addons.stub!(:args).and_return("azuki-postgresql --#{switch} postgres://foo:yeah@awesome.com:234/bestdb".split)
216
+ @addons.azuki.should_receive(:install_addon).with('example', 'azuki-postgresql', {switch => 'postgres://foo:yeah@awesome.com:234/bestdb'})
217
+ @addons.add
218
+ end
219
+ end
220
+ end
221
+
222
+ describe 'adding' do
223
+ before do
224
+ @addons.stub!(:args).and_return(%w(my_addon))
225
+ Excon.stub(
226
+ {
227
+ :expects => 200,
228
+ :method => :get,
229
+ :path => %r{^/apps/example/releases/current}
230
+ },
231
+ {
232
+ :body => Azuki::API::OkJson.encode({ 'name' => 'v99' }),
233
+ :status => 200,
234
+ }
235
+ )
236
+ end
237
+ after do
238
+ Excon.stubs.shift
239
+ end
240
+
241
+
242
+ it "requires an addon name" do
243
+ @addons.stub!(:args).and_return([])
244
+ lambda { @addons.add }.should raise_error(CommandFailed)
245
+ end
246
+
247
+ it "adds an addon" do
248
+ @addons.stub!(:args).and_return(%w(my_addon))
249
+ @addons.azuki.should_receive(:install_addon).with('example', 'my_addon', {})
250
+ @addons.add
251
+ end
252
+
253
+ it "adds an addon with a price" do
254
+ stub_core.install_addon("example", "my_addon", {}).returns({ "price" => "free" })
255
+ stderr, stdout = execute("addons:add my_addon")
256
+ stderr.should == ""
257
+ stdout.should =~ /\(free\)/
258
+ end
259
+
260
+ it "adds an addon with a price and message" do
261
+ stub_core.install_addon("example", "my_addon", {}).returns({ "price" => "free", "message" => "foo" })
262
+ stderr, stdout = execute("addons:add my_addon")
263
+ stderr.should == ""
264
+ stdout.should == <<-OUTPUT
265
+ Adding my_addon on example... done, v99 (free)
266
+ foo
267
+ Use `azuki addons:docs my_addon` to view documentation.
268
+ OUTPUT
269
+ end
270
+
271
+ it "adds an addon with a price and multiline message" do
272
+ stub_core.install_addon("example", "my_addon", {}).returns({ "price" => "$200/mo", "message" => "foo\nbar" })
273
+ stderr, stdout = execute("addons:add my_addon")
274
+ stderr.should == ""
275
+ stdout.should == <<-OUTPUT
276
+ Adding my_addon on example... done, v99 ($200/mo)
277
+ foo
278
+ bar
279
+ Use `azuki addons:docs my_addon` to view documentation.
280
+ OUTPUT
281
+ end
282
+
283
+ it "displays an error with unexpected options" do
284
+ Azuki::Command.should_receive(:error).with("Unexpected arguments: bar")
285
+ run("addons:add redistogo -a foo bar")
286
+ end
287
+ end
288
+
289
+ describe 'upgrading' do
290
+ before do
291
+ @addons.stub!(:args).and_return(%w(my_addon))
292
+ Excon.stub(
293
+ {
294
+ :expects => 200,
295
+ :method => :get,
296
+ :path => %r{^/apps/example/releases/current}
297
+ },
298
+ {
299
+ :body => Azuki::API::OkJson.encode({ 'name' => 'v99' }),
300
+ :status => 200,
301
+ }
302
+ )
303
+ end
304
+ after do
305
+ Excon.stubs.shift
306
+ end
307
+
308
+ it "requires an addon name" do
309
+ @addons.stub!(:args).and_return([])
310
+ lambda { @addons.upgrade }.should raise_error(CommandFailed)
311
+ end
312
+
313
+ it "upgrades an addon" do
314
+ @addons.stub!(:args).and_return(%w(my_addon))
315
+ @addons.azuki.should_receive(:upgrade_addon).with('example', 'my_addon', {})
316
+ @addons.upgrade
317
+ end
318
+
319
+ it "upgrade an addon with config vars" do
320
+ @addons.stub!(:args).and_return(%w(my_addon --foo=baz))
321
+ @addons.azuki.should_receive(:upgrade_addon).with('example', 'my_addon', { 'foo' => 'baz' })
322
+ @addons.upgrade
323
+ end
324
+
325
+ it "adds an addon with a price" do
326
+ stub_core.upgrade_addon("example", "my_addon", {}).returns({ "price" => "free" })
327
+ stderr, stdout = execute("addons:upgrade my_addon")
328
+ stderr.should == ""
329
+ stdout.should == <<-OUTPUT
330
+ Upgrading to my_addon on example... done, v99 (free)
331
+ Use `azuki addons:docs my_addon` to view documentation.
332
+ OUTPUT
333
+ end
334
+
335
+ it "adds an addon with a price and message" do
336
+ stub_core.upgrade_addon("example", "my_addon", {}).returns({ "price" => "free", "message" => "Don't Panic" })
337
+ stderr, stdout = execute("addons:upgrade my_addon")
338
+ stderr.should == ""
339
+ stdout.should == <<-OUTPUT
340
+ Upgrading to my_addon on example... done, v99 (free)
341
+ Don't Panic
342
+ Use `azuki addons:docs my_addon` to view documentation.
343
+ OUTPUT
344
+ end
345
+ end
346
+
347
+ describe 'downgrading' do
348
+ before do
349
+ @addons.stub!(:args).and_return(%w(my_addon))
350
+ Excon.stub(
351
+ {
352
+ :expects => 200,
353
+ :method => :get,
354
+ :path => %r{^/apps/example/releases/current}
355
+ },
356
+ {
357
+ :body => Azuki::API::OkJson.encode({ 'name' => 'v99' }),
358
+ :status => 200,
359
+ }
360
+ )
361
+ end
362
+ after do
363
+ Excon.stubs.shift
364
+ end
365
+
366
+ it "requires an addon name" do
367
+ @addons.stub!(:args).and_return([])
368
+ lambda { @addons.downgrade }.should raise_error(CommandFailed)
369
+ end
370
+
371
+ it "downgrades an addon" do
372
+ @addons.stub!(:args).and_return(%w(my_addon))
373
+ @addons.azuki.should_receive(:upgrade_addon).with('example', 'my_addon', {})
374
+ @addons.downgrade
375
+ end
376
+
377
+ it "downgrade an addon with config vars" do
378
+ @addons.stub!(:args).and_return(%w(my_addon --foo=baz))
379
+ @addons.azuki.should_receive(:upgrade_addon).with('example', 'my_addon', { 'foo' => 'baz' })
380
+ @addons.downgrade
381
+ end
382
+
383
+ it "downgrades an addon with a price" do
384
+ stub_core.upgrade_addon("example", "my_addon", {}).returns({ "price" => "free" })
385
+ stderr, stdout = execute("addons:downgrade my_addon")
386
+ stderr.should == ""
387
+ stdout.should == <<-OUTPUT
388
+ Downgrading to my_addon on example... done, v99 (free)
389
+ Use `azuki addons:docs my_addon` to view documentation.
390
+ OUTPUT
391
+ end
392
+
393
+ it "downgrades an addon with a price and message" do
394
+ stub_core.upgrade_addon("example", "my_addon", {}).returns({ "price" => "free", "message" => "Don't Panic" })
395
+ stderr, stdout = execute("addons:downgrade my_addon")
396
+ stderr.should == ""
397
+ stdout.should == <<-OUTPUT
398
+ Downgrading to my_addon on example... done, v99 (free)
399
+ Don't Panic
400
+ Use `azuki addons:docs my_addon` to view documentation.
401
+ OUTPUT
402
+ end
403
+ end
404
+
405
+ it "asks the user to confirm billing when API responds with 402" do
406
+ @addons.stub!(:args).and_return(%w( addon1 ))
407
+ e = RestClient::RequestFailed.new
408
+ e.stub!(:http_code).and_return(402)
409
+ e.stub!(:http_body).and_return('{"error":"test"}')
410
+ @addons.azuki.should_receive(:install_addon).and_raise(e)
411
+ @addons.should_receive(:confirm_billing).and_return(false)
412
+ STDERR.should_receive(:puts).with(" ! test")
413
+ lambda { @addons.add }.should raise_error(SystemExit)
414
+ end
415
+
416
+ it "does not remove addons with no confirm" do
417
+ @addons.stub!(:args).and_return(%w( addon1 ))
418
+ @addons.should_receive(:confirm_command).once.and_return(false)
419
+ @addons.azuki.should_not_receive(:uninstall_addon)
420
+ @addons.remove
421
+ end
422
+
423
+ it "removes addons after prompting for confirmation" do
424
+ @addons.stub!(:args).and_return(%w( addon1 ))
425
+ @addons.should_receive(:confirm_command).once.and_return(true)
426
+ @addons.azuki.should_receive(:uninstall_addon).with('example', 'addon1', :confirm => "example")
427
+ @addons.remove
428
+ end
429
+
430
+ it "removes addons with confirm option" do
431
+ Azuki::Command.stub!(:current_options).and_return(:confirm => "example")
432
+ @addons.stub!(:args).and_return(%w( addon1 ))
433
+ @addons.azuki.should_receive(:uninstall_addon).with('example', 'addon1', :confirm => "example")
434
+ @addons.remove
435
+ end
436
+
437
+ describe "opening add-on docs" do
438
+
439
+ before(:each) do
440
+ stub_core
441
+ api.post_app("name" => "example", "stack" => "cedar")
442
+ end
443
+
444
+ after(:each) do
445
+ api.delete_app("example")
446
+ end
447
+
448
+ it "displays usage when no argument is specified" do
449
+ stderr, stdout = execute('addons:docs')
450
+ stderr.should == <<-STDERR
451
+ ! Usage: azuki addons:docs ADDON
452
+ ! Must specify ADDON to open docs for.
453
+ STDERR
454
+ stdout.should == ''
455
+ end
456
+
457
+ it "opens the addon if only one matches" do
458
+ require("launchy")
459
+ Launchy.should_receive(:open).with("https://devcenter.azukiapp.com/articles/redistogo").and_return(Thread.new {})
460
+ stderr, stdout = execute('addons:docs redistogo:nano')
461
+ stderr.should == ''
462
+ stdout.should == <<-STDOUT
463
+ Opening redistogo:nano docs... done
464
+ STDOUT
465
+ end
466
+
467
+ it "complains about ambiguity" do
468
+ Excon.stub(
469
+ {
470
+ :expects => 200,
471
+ :method => :get,
472
+ :path => %r{^/addons$}
473
+ },
474
+ {
475
+ :body => Azuki::API::OkJson.encode([
476
+ { 'name' => 'qux:foo' },
477
+ { 'name' => 'quux:bar' }
478
+ ]),
479
+ :status => 200,
480
+ }
481
+ )
482
+ stderr, stdout = execute('addons:docs qu')
483
+ stderr.should == <<-STDERR
484
+ ! Ambiguous addon name: qu
485
+ ! Perhaps you meant `qux:foo` or `quux:bar`.
486
+ STDERR
487
+ stdout.should == ''
488
+ Excon.stubs.shift
489
+ end
490
+
491
+ it "complains if no such addon exists" do
492
+ stderr, stdout = execute('addons:docs unknown')
493
+ stderr.should == <<-STDERR
494
+ ! `unknown` is not a azuki add-on.
495
+ ! See `azuki addons:list` for all available addons.
496
+ STDERR
497
+ stdout.should == ''
498
+ end
499
+
500
+ it "suggests alternatives if addon has typo" do
501
+ stderr, stdout = execute('addons:docs redisgoto')
502
+ stderr.should == <<-STDERR
503
+ ! `redisgoto` is not a azuki add-on.
504
+ ! Perhaps you meant `redistogo`.
505
+ ! See `azuki addons:list` for all available addons.
506
+ STDERR
507
+ stdout.should == ''
508
+ end
509
+
510
+ it "complains if addon is not installed" do
511
+ stderr, stdout = execute('addons:open deployhooks:http')
512
+ stderr.should == <<-STDOUT
513
+ ! Addon not installed: deployhooks:http
514
+ STDOUT
515
+ stdout.should == ''
516
+ end
517
+ end
518
+ describe "opening an addon" do
519
+
520
+ before(:each) do
521
+ stub_core
522
+ api.post_app("name" => "example", "stack" => "cedar")
523
+ end
524
+
525
+ after(:each) do
526
+ api.delete_app("example")
527
+ end
528
+
529
+ it "displays usage when no argument is specified" do
530
+ stderr, stdout = execute('addons:open')
531
+ stderr.should == <<-STDERR
532
+ ! Usage: azuki addons:open ADDON
533
+ ! Must specify ADDON to open.
534
+ STDERR
535
+ stdout.should == ''
536
+ end
537
+
538
+ it "opens the addon if only one matches" do
539
+ api.post_addon('example', 'redistogo:nano')
540
+ require("launchy")
541
+ Launchy.should_receive(:open).with("https://api.#{@addons.azuki.host}/apps/example/addons/redistogo:nano").and_return(Thread.new {})
542
+ stderr, stdout = execute('addons:open redistogo:nano')
543
+ stderr.should == ''
544
+ stdout.should == <<-STDOUT
545
+ Opening redistogo:nano for example... done
546
+ STDOUT
547
+ end
548
+
549
+ it "complains about ambiguity" do
550
+ Excon.stub(
551
+ {
552
+ :expects => 200,
553
+ :method => :get,
554
+ :path => %r{^/apps/example/addons$}
555
+ },
556
+ {
557
+ :body => Azuki::API::OkJson.encode([
558
+ { 'name' => 'deployhooks:email' },
559
+ { 'name' => 'deployhooks:http' }
560
+ ]),
561
+ :status => 200,
562
+ }
563
+ )
564
+ stderr, stdout = execute('addons:open deployhooks')
565
+ stderr.should == <<-STDERR
566
+ ! Ambiguous addon name: deployhooks
567
+ ! Perhaps you meant `deployhooks:email` or `deployhooks:http`.
568
+ STDERR
569
+ stdout.should == ''
570
+ Excon.stubs.shift
571
+ end
572
+
573
+ it "complains if no such addon exists" do
574
+ stderr, stdout = execute('addons:open unknown')
575
+ stderr.should == <<-STDERR
576
+ ! `unknown` is not a azuki add-on.
577
+ ! See `azuki addons:list` for all available addons.
578
+ STDERR
579
+ stdout.should == ''
580
+ end
581
+
582
+ it "suggests alternatives if addon has typo" do
583
+ stderr, stdout = execute('addons:open redisgoto')
584
+ stderr.should == <<-STDERR
585
+ ! `redisgoto` is not a azuki add-on.
586
+ ! Perhaps you meant `redistogo`.
587
+ ! See `azuki addons:list` for all available addons.
588
+ STDERR
589
+ stdout.should == ''
590
+ end
591
+
592
+ it "complains if addon is not installed" do
593
+ stderr, stdout = execute('addons:open deployhooks:http')
594
+ stderr.should == <<-STDOUT
595
+ ! Addon not installed: deployhooks:http
596
+ STDOUT
597
+ stdout.should == ''
598
+ end
599
+ end
600
+ end
601
+ end