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,564 @@
1
+ require "spec_helper"
2
+ require "azuki/client"
3
+ require "azuki/helpers"
4
+ require 'azuki/command'
5
+
6
+ describe Azuki::Client do
7
+ include Azuki::Helpers
8
+
9
+ before do
10
+ @client = Azuki::Client.new(nil, nil)
11
+ @resource = mock('azuki rest resource')
12
+ @client.stub!(:extract_warning)
13
+ end
14
+
15
+ it "Client.auth -> get user details" do
16
+ user_info = { "api_key" => "abc" }
17
+ stub_request(:post, "https://foo:bar@api.azukiapp.com/login").to_return(:body => json_encode(user_info))
18
+ capture_stderr do # capture deprecation message
19
+ Azuki::Client.auth("foo", "bar").should == user_info
20
+ end
21
+ end
22
+
23
+ it "list -> get a list of this user's apps" do
24
+ stub_api_request(:get, "/apps").to_return(:body => <<-EOXML)
25
+ <?xml version='1.0' encoding='UTF-8'?>
26
+ <apps type="array">
27
+ <app><name>example</name><owner>test@azukiapp.com</owner></app>
28
+ <app><name>example2</name><owner>test@azukiapp.com</owner></app>
29
+ </apps>
30
+ EOXML
31
+ capture_stderr do # capture deprecation message
32
+ @client.list.should == [
33
+ ["example", "test@azukiapp.com"],
34
+ ["example2", "test@azukiapp.com"]
35
+ ]
36
+ end
37
+ end
38
+
39
+ it "info -> get app attributes" do
40
+ stub_api_request(:get, "/apps/example").to_return(:body => <<-EOXML)
41
+ <?xml version='1.0' encoding='UTF-8'?>
42
+ <app>
43
+ <blessed type='boolean'>true</blessed>
44
+ <created-at type='datetime'>2008-07-08T17:21:50-07:00</created-at>
45
+ <id type='integer'>49134</id>
46
+ <name>example</name>
47
+ <production type='boolean'>true</production>
48
+ <share-public type='boolean'>true</share-public>
49
+ <domain_name/>
50
+ </app>
51
+ EOXML
52
+ @client.stub!(:list_collaborators).and_return([:jon, :mike])
53
+ @client.stub!(:installed_addons).and_return([:addon1])
54
+ capture_stderr do # capture deprecation message
55
+ @client.info('example').should == { :blessed => 'true', :created_at => '2008-07-08T17:21:50-07:00', :id => '49134', :name => 'example', :production => 'true', :share_public => 'true', :domain_name => nil, :collaborators => [:jon, :mike], :addons => [:addon1] }
56
+ end
57
+ end
58
+
59
+ it "create_request -> create a new blank app" do
60
+ stub_api_request(:post, "/apps").with(:body => "").to_return(:body => <<-EOXML)
61
+ <?xml version="1.0" encoding="UTF-8"?>
62
+ <app><name>untitled-123</name></app>
63
+ EOXML
64
+ capture_stderr do # capture deprecation message
65
+ @client.create_request.should == "untitled-123"
66
+ end
67
+ end
68
+
69
+ it "create_request(name) -> create a new blank app with a specified name" do
70
+ stub_api_request(:post, "/apps").with(:body => "app[name]=newapp").to_return(:body => <<-EOXML)
71
+ <?xml version="1.0" encoding="UTF-8"?>
72
+ <app><name>newapp</name></app>
73
+ EOXML
74
+ capture_stderr do # capture deprecation message
75
+ @client.create_request("newapp").should == "newapp"
76
+ end
77
+ end
78
+
79
+ it "create_complete?(name) -> checks if a create request is complete" do
80
+ @response = mock('response')
81
+ @response.should_receive(:code).and_return(202)
82
+ @client.should_receive(:resource).and_return(@resource)
83
+ @resource.should_receive(:put).with({}, @client.azuki_headers).and_return(@response)
84
+ capture_stderr do # capture deprecation message
85
+ @client.create_complete?('example').should be_false
86
+ end
87
+ end
88
+
89
+ it "update(name, attributes) -> updates existing apps" do
90
+ stub_api_request(:put, "/apps/example").with(:body => "app[mode]=production")
91
+ capture_stderr do # capture deprecation message
92
+ @client.update("example", :mode => 'production')
93
+ end
94
+ end
95
+
96
+ it "destroy(name) -> destroy the named app" do
97
+ stub_api_request(:delete, "/apps/destroyme")
98
+ capture_stderr do # capture deprecation message
99
+ @client.destroy("destroyme")
100
+ end
101
+ end
102
+
103
+ it "rake(app_name, cmd) -> run a rake command on the app" do
104
+ stub_api_request(:post, "/apps/example/services").with(:body => "rake db:migrate").to_return(:body => "foo")
105
+ stub_api_request(:get, "/foo").to_return(:body => "output")
106
+ capture_stderr do # capture deprecation message
107
+ @client.rake('example', 'db:migrate')
108
+ end
109
+ end
110
+
111
+ it "console(app_name, cmd) -> run a console command on the app" do
112
+ stub_api_request(:post, "/apps/example/console").with(:body => "command=2%2B2")
113
+ @client.console('example', '2+2')
114
+ end
115
+
116
+ it "console(app_name) { |c| } -> opens a console session, yields one accessor and closes it after the block" do
117
+ stub_api_request(:post, "/apps/example/consoles").to_return(:body => "consolename")
118
+ stub_api_request(:post, "/apps/example/consoles/consolename/command").with(:body => "command=1%2B1").to_return(:body => "2")
119
+ stub_api_request(:delete, "/apps/example/consoles/consolename")
120
+
121
+ @client.console('example') do |c|
122
+ c.run("1+1").should == '=> 2'
123
+ end
124
+ end
125
+
126
+ it "shows an error message when a console request fails" do
127
+ stub_request(:post, %r{.*/apps/example/console}).to_return({
128
+ :body => "ERRMSG", :status => 502
129
+ })
130
+ lambda { @client.console('example') }.should raise_error(Azuki::Client::AppCrashed, /Your application may have crashed/)
131
+ end
132
+
133
+ it "restart(app_name) -> restarts the app servers" do
134
+ stub_api_request(:delete, "/apps/example/server")
135
+ capture_stderr do # capture deprecation message
136
+ @client.restart('example')
137
+ end
138
+ end
139
+
140
+ describe "read_logs" do
141
+ describe "old style" do
142
+ before(:each) do
143
+ stub_api_request(:get, "/apps/example/logs?logplex=true").to_return(:body => "Use old logs")
144
+ stub_api_request(:get, "/apps/example/logs").to_return(:body => "oldlogs")
145
+ end
146
+
147
+ it "can read old style logs" do
148
+ @client.should_receive(:puts).with("oldlogs")
149
+ @client.read_logs("example")
150
+ end
151
+ end
152
+
153
+ describe "new style" do
154
+ before(:each) do
155
+ stub_api_request(:get, "/apps/example/logs?logplex=true").to_return(:body => "https://logplex.azukiapp.com/identifier")
156
+ stub_request(:get, "https://logplex.azukiapp.com/identifier").to_return(:body => "newlogs")
157
+ end
158
+
159
+ it "can read new style logs" do
160
+ @client.read_logs("example") do |logs|
161
+ logs.should == "newlogs"
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ it "logs(app_name) -> returns recent output of the app logs" do
168
+ stub_api_request(:get, "/apps/example/logs").to_return(:body => "log")
169
+ capture_stderr do # capture deprecation message
170
+ @client.logs('example').should == 'log'
171
+ end
172
+ end
173
+
174
+ it "can get the number of dynos" do
175
+ stub_api_request(:get, "/apps/example").to_return(:body => <<-EOXML)
176
+ <?xml version='1.0' encoding='UTF-8'?>
177
+ <app>
178
+ <dynos type='integer'>5</dynos>
179
+ </app>
180
+ EOXML
181
+ capture_stderr do # capture deprecation message
182
+ @client.dynos('example').should == 5
183
+ end
184
+ end
185
+
186
+ it "can get the number of workers" do
187
+ stub_api_request(:get, "/apps/example").to_return(:body => <<-EOXML)
188
+ <?xml version='1.0' encoding='UTF-8'?>
189
+ <app>
190
+ <workers type='integer'>5</workers>
191
+ </app>
192
+ EOXML
193
+ capture_stderr do # capture deprecation message
194
+ @client.workers('example').should == 5
195
+ end
196
+ end
197
+
198
+ it "set_dynos(app_name, qty) -> scales the app" do
199
+ stub_api_request(:put, "/apps/example/dynos").with(:body => "dynos=3")
200
+ capture_stderr do # capture deprecation message
201
+ @client.set_dynos('example', 3)
202
+ end
203
+ end
204
+
205
+ it "rake catches 502s and shows the app crashlog" do
206
+ e = RestClient::RequestFailed.new
207
+ e.stub!(:http_code).and_return(502)
208
+ e.stub!(:http_body).and_return('the crashlog')
209
+ @client.should_receive(:post).and_raise(e)
210
+ capture_stderr do # capture deprecation message
211
+ lambda { @client.rake('example', '') }.should raise_error(Azuki::Client::AppCrashed)
212
+ end
213
+ end
214
+
215
+ it "rake passes other status codes (i.e., 500) as standard restclient exceptions" do
216
+ e = RestClient::RequestFailed.new
217
+ e.stub!(:http_code).and_return(500)
218
+ e.stub!(:http_body).and_return('not a crashlog')
219
+ @client.should_receive(:post).and_raise(e)
220
+ capture_stderr do # capture deprecation message
221
+ lambda { @client.rake('example', '') }.should raise_error(RestClient::RequestFailed)
222
+ end
223
+ end
224
+
225
+ describe "ps_scale" do
226
+ it "scales a process and returns the new count" do
227
+ stub_api_request(:post, "/apps/example/ps/scale").with(:body => { :type => "web", :qty => "5" }).to_return(:body => "5")
228
+ capture_stderr do # capture deprecation message
229
+ @client.ps_scale("example", :type => "web", :qty => "5").should == 5
230
+ end
231
+ end
232
+ end
233
+
234
+ describe "collaborators" do
235
+ it "list(app_name) -> list app collaborators" do
236
+ stub_api_request(:get, "/apps/example/collaborators").to_return(:body => <<-EOXML)
237
+ <?xml version="1.0" encoding="UTF-8"?>
238
+ <collaborators type="array">
239
+ <collaborator><email>joe@example.com</email></collaborator>
240
+ <collaborator><email>jon@example.com</email></collaborator>
241
+ </collaborators>
242
+ EOXML
243
+ capture_stderr do # capture deprecation message
244
+ @client.list_collaborators('example').should == [
245
+ { :email => 'joe@example.com' },
246
+ { :email => 'jon@example.com' }
247
+ ]
248
+ end
249
+ end
250
+
251
+ it "add_collaborator(app_name, email) -> adds collaborator to app" do
252
+ stub_api_request(:post, "/apps/example/collaborators").with(:body => "collaborator%5Bemail%5D=joe%40example.com")
253
+ capture_stderr do # capture deprecation message
254
+ @client.add_collaborator('example', 'joe@example.com')
255
+ end
256
+ end
257
+
258
+ it "remove_collaborator(app_name, email) -> removes collaborator from app" do
259
+ stub_api_request(:delete, "/apps/example/collaborators/joe%40example%2Ecom")
260
+ capture_stderr do # capture deprecation message
261
+ @client.remove_collaborator('example', 'joe@example.com')
262
+ end
263
+ end
264
+ end
265
+
266
+ describe "domain names" do
267
+ it "list(app_name) -> list app domain names" do
268
+ stub_api_request(:get, "/apps/example/domains").to_return(:body => <<-EOXML)
269
+ <?xml version="1.0" encoding="UTF-8"?>
270
+ <domain-names type="array">
271
+ <domain-name><domain>example1.com</domain></domain-name>
272
+ <domain-name><domain>example2.com</domain></domain-name>
273
+ </domain-names>
274
+ EOXML
275
+ capture_stderr do # capture deprecation message
276
+ @client.list_domains('example').should == [{:domain => 'example1.com'}, {:domain => 'example2.com'}]
277
+ end
278
+ end
279
+
280
+ it "add_domain(app_name, domain) -> adds domain name to app" do
281
+ stub_api_request(:post, "/apps/example/domains").with(:body => "example.com")
282
+ capture_stderr do # capture deprecation message
283
+ @client.add_domain('example', 'example.com')
284
+ end
285
+ end
286
+
287
+ it "remove_domain(app_name, domain) -> removes domain name from app" do
288
+ stub_api_request(:delete, "/apps/example/domains/example.com")
289
+ capture_stderr do # capture deprecation message
290
+ @client.remove_domain('example', 'example.com')
291
+ end
292
+ end
293
+
294
+ it "remove_domain(app_name, domain) -> makes sure a domain is set" do
295
+ lambda do
296
+ capture_stderr do # capture deprecation message
297
+ @client.remove_domain('example', '')
298
+ end
299
+ end.should raise_error(ArgumentError)
300
+ end
301
+
302
+ it "remove_domains(app_name) -> removes all domain names from app" do
303
+ stub_api_request(:delete, "/apps/example/domains")
304
+ capture_stderr do # capture deprecation message
305
+ @client.remove_domains('example')
306
+ end
307
+ end
308
+
309
+ it "add_ssl(app_name, pem, key) -> adds a ssl cert to the domain" do
310
+ stub_api_request(:post, "/apps/example/ssl").with do |request|
311
+ body = CGI::parse(request.body)
312
+ body["key"].first.should == "thekey"
313
+ body["pem"].first.should == "thepem"
314
+ end.to_return(:body => "{}")
315
+ @client.add_ssl('example', 'thepem', 'thekey')
316
+ end
317
+
318
+ it "remove_ssl(app_name, domain) -> removes the ssl cert for the domain" do
319
+ stub_api_request(:delete, "/apps/example/domains/example.com/ssl")
320
+ @client.remove_ssl('example', 'example.com')
321
+ end
322
+ end
323
+
324
+ describe "SSH keys" do
325
+ it "fetches a list of the user's current keys" do
326
+ stub_api_request(:get, "/user/keys").to_return(:body => <<-EOXML)
327
+ <?xml version="1.0" encoding="UTF-8"?>
328
+ <keys type="array">
329
+ <key>
330
+ <contents>ssh-dss thekey== joe@workstation</contents>
331
+ </key>
332
+ </keys>
333
+ EOXML
334
+ capture_stderr do # capture deprecation message
335
+ @client.keys.should == [ "ssh-dss thekey== joe@workstation" ]
336
+ end
337
+ end
338
+
339
+ it "add_key(key) -> add an SSH key (e.g., the contents of id_rsa.pub) to the user" do
340
+ stub_api_request(:post, "/user/keys").with(:body => "a key")
341
+ capture_stderr do # capture deprecation message
342
+ @client.add_key('a key')
343
+ end
344
+ end
345
+
346
+ it "remove_key(key) -> remove an SSH key by name (user@box)" do
347
+ stub_api_request(:delete, "/user/keys/joe%40workstation")
348
+ capture_stderr do # capture deprecation message
349
+ @client.remove_key('joe@workstation')
350
+ end
351
+ end
352
+
353
+ it "remove_all_keys -> removes all SSH keys for the user" do
354
+ stub_api_request(:delete, "/user/keys")
355
+ capture_stderr do # capture deprecation message
356
+ @client.remove_all_keys
357
+ end
358
+ end
359
+
360
+ it "database_session(app_name) -> creates a taps database session" do
361
+ module ::Taps
362
+ def self.version
363
+ "0.3.0"
364
+ end
365
+ end
366
+
367
+ stub_api_request(:post, "/apps/example/database/session2").to_return(:body => "{\"session_id\":\"x234\"}")
368
+ @client.database_session('example')
369
+ end
370
+
371
+ it "database_reset(app_name) -> reset an app's database" do
372
+ stub_api_request(:post, "/apps/example/database/reset")
373
+ @client.database_reset('example')
374
+ end
375
+
376
+ it "maintenance(app_name, :on) -> sets maintenance mode for an app" do
377
+ stub_api_request(:post, "/apps/example/server/maintenance").with(:body => "maintenance_mode=1")
378
+ capture_stderr do # capture deprecation message
379
+ @client.maintenance('example', :on)
380
+ end
381
+ end
382
+
383
+ it "maintenance(app_name, :off) -> turns off maintenance mode for an app" do
384
+ stub_api_request(:post, "/apps/example/server/maintenance").with(:body => "maintenance_mode=0")
385
+ capture_stderr do # capture deprecation message
386
+ @client.maintenance('example', :off)
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "config vars" do
392
+ it "config_vars(app_name) -> json hash of config vars for the app" do
393
+ stub_api_request(:get, "/apps/example/config_vars").to_return(:body => '{"A":"one", "B":"two"}')
394
+ capture_stderr do # capture deprecation message
395
+ @client.config_vars('example').should == { 'A' => 'one', 'B' => 'two'}
396
+ end
397
+ end
398
+
399
+ it "add_config_vars(app_name, vars)" do
400
+ stub_api_request(:put, "/apps/example/config_vars").with(:body => '{"x":"y"}')
401
+ capture_stderr do # capture deprecation message
402
+ @client.add_config_vars('example', {'x'=> 'y'})
403
+ end
404
+ end
405
+
406
+ it "remove_config_var(app_name, key)" do
407
+ stub_api_request(:delete, "/apps/example/config_vars/mykey")
408
+ capture_stderr do # capture deprecation message
409
+ @client.remove_config_var('example', 'mykey')
410
+ end
411
+ end
412
+
413
+ it "clear_config_vars(app_name) -> resets all config vars for this app" do
414
+ stub_api_request(:delete, "/apps/example/config_vars")
415
+ capture_stderr do # capture deprecation message
416
+ @client.clear_config_vars('example')
417
+ end
418
+ end
419
+
420
+ it "can handle config vars with special characters" do
421
+ stub_api_request(:delete, "/apps/example/config_vars/foo%5Bbar%5D")
422
+ capture_stderr do # capture deprecation message
423
+ lambda { @client.remove_config_var('example', 'foo[bar]') }.should_not raise_error
424
+ end
425
+ end
426
+ end
427
+
428
+ describe "addons" do
429
+ it "addons -> array with addons available for installation" do
430
+ stub_api_request(:get, "/addons").to_return(:body => '[{"name":"addon1"}, {"name":"addon2"}]')
431
+ @client.addons.should == [{'name' => 'addon1'}, {'name' => 'addon2'}]
432
+ end
433
+
434
+ it "installed_addons(app_name) -> array of installed addons" do
435
+ stub_api_request(:get, "/apps/example/addons").to_return(:body => '[{"name":"addon1"}]')
436
+ @client.installed_addons('example').should == [{'name' => 'addon1'}]
437
+ end
438
+
439
+ it "install_addon(app_name, addon_name)" do
440
+ stub_api_request(:post, "/apps/example/addons/addon1")
441
+ @client.install_addon('example', 'addon1').should be_nil
442
+ end
443
+
444
+ it "upgrade_addon(app_name, addon_name)" do
445
+ stub_api_request(:put, "/apps/example/addons/addon1")
446
+ @client.upgrade_addon('example', 'addon1').should be_nil
447
+ end
448
+
449
+ it "downgrade_addon(app_name, addon_name)" do
450
+ stub_api_request(:put, "/apps/example/addons/addon1")
451
+ @client.downgrade_addon('example', 'addon1').should be_nil
452
+ end
453
+
454
+ it "uninstall_addon(app_name, addon_name)" do
455
+ stub_api_request(:delete, "/apps/example/addons/addon1?").
456
+ to_return(:body => json_encode({"message" => nil, "price" => "free", "status" => "uninstalled"}))
457
+
458
+ @client.uninstall_addon('example', 'addon1').should be_true
459
+ end
460
+
461
+ it "uninstall_addon(app_name, addon_name) with confirmation" do
462
+ stub_api_request(:delete, "/apps/example/addons/addon1?confirm=example").
463
+ to_return(:body => json_encode({"message" => nil, "price" => "free", "status" => "uninstalled"}))
464
+
465
+ @client.uninstall_addon('example', 'addon1', :confirm => "example").should be_true
466
+ end
467
+
468
+ it "install_addon(app_name, addon_name) with response" do
469
+ stub_request(:post, "https://api.azukiapp.com/apps/example/addons/addon1").
470
+ to_return(:body => json_encode({'price' => 'free', 'message' => "Don't Panic"}))
471
+
472
+ @client.install_addon('example', 'addon1').
473
+ should == { 'price' => 'free', 'message' => "Don't Panic" }
474
+ end
475
+
476
+ it "upgrade_addon(app_name, addon_name) with response" do
477
+ stub_request(:put, "https://api.azukiapp.com/apps/example/addons/addon1").
478
+ to_return(:body => json_encode('price' => 'free', 'message' => "Don't Panic"))
479
+
480
+ @client.upgrade_addon('example', 'addon1').
481
+ should == { 'price' => 'free', 'message' => "Don't Panic" }
482
+ end
483
+
484
+ it "downgrade_addon(app_name, addon_name) with response" do
485
+ stub_request(:put, "https://api.azukiapp.com/apps/example/addons/addon1").
486
+ to_return(:body => json_encode('price' => 'free', 'message' => "Don't Panic"))
487
+
488
+ @client.downgrade_addon('example', 'addon1').
489
+ should == { 'price' => 'free', 'message' => "Don't Panic" }
490
+ end
491
+
492
+ it "uninstall_addon(app_name, addon_name) with response" do
493
+ stub_api_request(:delete, "/apps/example/addons/addon1?").
494
+ to_return(:body => json_encode('price'=> 'free', 'message'=> "Don't Panic"))
495
+
496
+ @client.uninstall_addon('example', 'addon1').
497
+ should == { 'price' => 'free', 'message' => "Don't Panic" }
498
+ end
499
+ end
500
+
501
+ describe "internal" do
502
+ before do
503
+ @client = Azuki::Client.new(nil, nil)
504
+ end
505
+
506
+ it "creates a RestClient resource for making calls" do
507
+ @client.stub!(:host).and_return('azukiapp.com')
508
+ @client.stub!(:user).and_return('joe@example.com')
509
+ @client.stub!(:password).and_return('secret')
510
+
511
+ res = @client.resource('/xyz')
512
+
513
+ res.url.should == 'https://api.azukiapp.com/xyz'
514
+ res.user.should == 'joe@example.com'
515
+ res.password.should == 'secret'
516
+ end
517
+
518
+ it "appends the api. prefix to the host" do
519
+ @client.host = "azukiapp.com"
520
+ @client.resource('/xyz').url.should == 'https://api.azukiapp.com/xyz'
521
+ end
522
+
523
+ it "doesn't add the api. prefix to full hosts" do
524
+ @client.host = 'http://resource'
525
+ res = @client.resource('/xyz')
526
+ res.url.should == 'http://resource/xyz'
527
+ end
528
+
529
+ it "runs a callback when the API sets a warning header" do
530
+ response = mock('rest client response', :headers => { :x_azuki_warning => 'Warning' })
531
+ @client.should_receive(:resource).and_return(@resource)
532
+ @resource.should_receive(:get).and_return(response)
533
+ @client.on_warning { |msg| @callback = msg }
534
+ @client.get('test')
535
+ @callback.should == 'Warning'
536
+ end
537
+
538
+ it "doesn't run the callback twice for the same warning" do
539
+ response = mock('rest client response', :headers => { :x_azuki_warning => 'Warning' })
540
+ @client.stub!(:resource).and_return(@resource)
541
+ @resource.stub!(:get).and_return(response)
542
+ @client.on_warning { |msg| @callback_called ||= 0; @callback_called += 1 }
543
+ @client.get('test1')
544
+ @client.get('test2')
545
+ @callback_called.should == 1
546
+ end
547
+ end
548
+
549
+ describe "stacks" do
550
+ it "list_stacks(app_name) -> json hash of available stacks" do
551
+ stub_api_request(:get, "/apps/example/stack?include_deprecated=false").to_return(:body => '{"stack":"one"}')
552
+ capture_stderr do # capture deprecation message
553
+ @client.list_stacks("example").should == { 'stack' => 'one' }
554
+ end
555
+ end
556
+
557
+ it "list_stacks(app_name, include_deprecated=true) passes the deprecated option" do
558
+ stub_api_request(:get, "/apps/example/stack?include_deprecated=true").to_return(:body => '{"stack":"one"}')
559
+ capture_stderr do # capture deprecation message
560
+ @client.list_stacks("example", :include_deprecated => true).should == { 'stack' => 'one' }
561
+ end
562
+ end
563
+ end
564
+ end