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,178 @@
1
+ require "spec_helper"
2
+ require "azuki/command/certs"
3
+
4
+ module Azuki::Command
5
+ describe Certs do
6
+ let(:certificate_details) {
7
+ <<-CERTIFICATE_DETAILS.chomp
8
+ Common Name(s): example.org
9
+ Expires At: 2013-08-01 21:34 UTC
10
+ Issuer: /C=US/ST=California/L=San Francisco/O=Azuki by Salesforce/CN=secure.example.org
11
+ Starts At: 2012-08-01 21:34 UTC
12
+ Subject: /C=US/ST=California/L=San Francisco/O=Azuki by Salesforce/CN=secure.example.org
13
+ SSL certificate is self signed.
14
+ CERTIFICATE_DETAILS
15
+ }
16
+
17
+ let(:endpoint) {
18
+ { 'cname' => 'tokyo-1050.azukissl.com',
19
+ 'ssl_cert' => {
20
+ 'ca_signed?' => false,
21
+ 'cert_domains' => [ 'example.org' ],
22
+ 'starts_at' => "2012-08-01 21:34:23 UTC",
23
+ 'expires_at' => "2013-08-01 21:34:23 UTC",
24
+ 'issuer' => '/C=US/ST=California/L=San Francisco/O=Azuki by Salesforce/CN=secure.example.org',
25
+ 'subject' => '/C=US/ST=California/L=San Francisco/O=Azuki by Salesforce/CN=secure.example.org',
26
+ }
27
+ }
28
+ }
29
+ let(:endpoint2) {
30
+ { 'cname' => 'akita-7777.azukissl.com',
31
+ 'ssl_cert' => {
32
+ 'ca_signed?' => true,
33
+ 'cert_domains' => [ 'azukiapp.com' ],
34
+ 'starts_at' => "2012-08-01 21:34:23 UTC",
35
+ 'expires_at' => "2013-08-01 21:34:23 UTC",
36
+ 'issuer' => '/C=US/ST=California/L=San Francisco/O=Azuki by Salesforce/CN=secure.example.org',
37
+ 'subject' => '/C=US/ST=California/L=San Francisco/O=Azuki by Salesforce/CN=secure.example.org',
38
+ }
39
+ }
40
+ }
41
+
42
+ describe "certs" do
43
+ it "shows a list of certs" do
44
+ stub_core.ssl_endpoint_list("example").returns([endpoint, endpoint2])
45
+ stderr, stdout = execute("certs")
46
+ stdout.should == <<-STDOUT
47
+ Endpoint Common Name(s) Expires Trusted
48
+ ------------------------ -------------- -------------------- -------
49
+ tokyo-1050.azukissl.com example.org 2013-08-01 21:34 UTC False
50
+ akita-7777.azukissl.com azukiapp.com 2013-08-01 21:34 UTC True
51
+ STDOUT
52
+ end
53
+
54
+ it "warns about no SSL Endpoints if the app has no certs" do
55
+ stub_core.ssl_endpoint_list("example").returns([])
56
+ stderr, stdout = execute("certs")
57
+ stdout.should == <<-STDOUT
58
+ example has no SSL Endpoints.
59
+ Use `azuki certs:add CRT KEY` to add one.
60
+ STDOUT
61
+ end
62
+ end
63
+
64
+ describe "certs:add" do
65
+ it "adds an endpoint" do
66
+ File.should_receive(:read).with("pem_file").and_return("pem content")
67
+ File.should_receive(:read).with("key_file").and_return("key content")
68
+ stub_core.ssl_endpoint_add('example', 'pem content', 'key content').returns(endpoint)
69
+
70
+ stderr, stdout = execute("certs:add --bypass pem_file key_file")
71
+ stdout.should == <<-STDOUT
72
+ Adding SSL Endpoint to example... done
73
+ example now served by tokyo-1050.azukissl.com
74
+ Certificate details:
75
+ #{certificate_details}
76
+ STDOUT
77
+ end
78
+
79
+ it "shows usage if two arguments are not provided" do
80
+ lambda { execute("certs:add --bypass") }.should raise_error(CommandFailed, /Usage:/)
81
+ end
82
+ end
83
+
84
+ describe "certs:info" do
85
+ it "shows certificate details" do
86
+ stub_core.ssl_endpoint_list("example").returns([endpoint])
87
+ stub_core.ssl_endpoint_info('example', 'tokyo-1050.azukissl.com').returns(endpoint)
88
+
89
+ stderr, stdout = execute("certs:info")
90
+ stdout.should == <<-STDOUT
91
+ Fetching SSL Endpoint tokyo-1050.azukissl.com info for example... done
92
+ Certificate details:
93
+ #{certificate_details}
94
+ STDOUT
95
+ end
96
+
97
+ it "shows an error if an app has no endpoints" do
98
+ stub_core.ssl_endpoint_list("example").returns([])
99
+
100
+ stderr, stdout = execute("certs:info")
101
+ stderr.should == <<-STDERR
102
+ ! example has no SSL Endpoints.
103
+ STDERR
104
+ end
105
+ end
106
+
107
+ describe "certs:remove" do
108
+ it "removes an endpoint" do
109
+ stub_core.ssl_endpoint_list("example").returns([endpoint])
110
+ stub_core.ssl_endpoint_remove('example', 'tokyo-1050.azukissl.com').returns(endpoint)
111
+
112
+ stderr, stdout = execute("certs:remove")
113
+ stdout.should include "Removing SSL Endpoint tokyo-1050.azukissl.com from example..."
114
+ stdout.should include "NOTE: Billing is still active. Remove SSL Endpoint add-on to stop billing."
115
+ end
116
+
117
+ it "shows an error if an app has no endpoints" do
118
+ stub_core.ssl_endpoint_list("example").returns([])
119
+
120
+ stderr, stdout = execute("certs:remove")
121
+ stderr.should == <<-STDERR
122
+ ! example has no SSL Endpoints.
123
+ STDERR
124
+ end
125
+ end
126
+
127
+ describe "certs:update" do
128
+ before do
129
+ File.should_receive(:read).with("pem_file").and_return("pem content")
130
+ File.should_receive(:read).with("key_file").and_return("key content")
131
+ end
132
+
133
+ it "updates an endpoint" do
134
+ stub_core.ssl_endpoint_list("example").returns([endpoint])
135
+ stub_core.ssl_endpoint_update('example', 'tokyo-1050.azukissl.com', 'pem content', 'key content').returns(endpoint)
136
+
137
+ stderr, stdout = execute("certs:update --bypass pem_file key_file")
138
+ stdout.should == <<-STDOUT
139
+ Updating SSL Endpoint tokyo-1050.azukissl.com for example... done
140
+ Updated certificate details:
141
+ #{certificate_details}
142
+ STDOUT
143
+ end
144
+
145
+ it "shows an error if an app has no endpoints" do
146
+ stub_core.ssl_endpoint_list("example").returns([])
147
+
148
+ stderr, stdout = execute("certs:update --bypass pem_file key_file")
149
+ stderr.should == <<-STDERR
150
+ ! example has no SSL Endpoints.
151
+ STDERR
152
+ end
153
+ end
154
+
155
+ describe "certs:rollback" do
156
+ it "performs a rollback on an endpoint" do
157
+ stub_core.ssl_endpoint_list("example").returns([endpoint])
158
+ stub_core.ssl_endpoint_rollback('example', 'tokyo-1050.azukissl.com').returns(endpoint)
159
+
160
+ stderr, stdout = execute("certs:rollback")
161
+ stdout.should == <<-STDOUT
162
+ Rolling back SSL Endpoint tokyo-1050.azukissl.com for example... done
163
+ New active certificate details:
164
+ #{certificate_details}
165
+ STDOUT
166
+ end
167
+
168
+ it "shows an error if an app has no endpoints" do
169
+ stub_core.ssl_endpoint_list("example").returns([])
170
+
171
+ stderr, stdout = execute("certs:rollback")
172
+ stderr.should == <<-STDERR
173
+ ! example has no SSL Endpoints.
174
+ STDERR
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,144 @@
1
+ require "spec_helper"
2
+ require "azuki/command/config"
3
+
4
+ module Azuki::Command
5
+ describe Config do
6
+ before(:each) do
7
+ stub_core
8
+ api.post_app("name" => "example", "stack" => "cedar")
9
+ end
10
+
11
+ after(:each) do
12
+ api.delete_app("example")
13
+ end
14
+
15
+ it "shows all configs" do
16
+ api.put_config_vars("example", { 'FOO_BAR' => 'one', 'BAZ_QUX' => 'two' })
17
+ stderr, stdout = execute("config")
18
+ stderr.should == ""
19
+ stdout.should == <<-STDOUT
20
+ === example Config Vars
21
+ BAZ_QUX: two
22
+ FOO_BAR: one
23
+ STDOUT
24
+ end
25
+
26
+ it "does not trim long values" do
27
+ api.put_config_vars("example", { 'LONG' => 'A' * 60 })
28
+ stderr, stdout = execute("config")
29
+ stderr.should == ""
30
+ stdout.should == <<-STDOUT
31
+ === example Config Vars
32
+ LONG: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
33
+ STDOUT
34
+ end
35
+
36
+ it "handles when value is nil" do
37
+ api.put_config_vars("example", { 'FOO_BAR' => 'one', 'BAZ_QUX' => nil })
38
+ stderr, stdout = execute("config")
39
+ stderr.should == ""
40
+ stdout.should == <<-STDOUT
41
+ === example Config Vars
42
+ BAZ_QUX:
43
+ FOO_BAR: one
44
+ STDOUT
45
+ end
46
+
47
+ it "handles when value is a boolean" do
48
+ api.put_config_vars("example", { 'FOO_BAR' => 'one', 'BAZ_QUX' => true })
49
+ stderr, stdout = execute("config")
50
+ stderr.should == ""
51
+ stdout.should == <<-STDOUT
52
+ === example Config Vars
53
+ BAZ_QUX: true
54
+ FOO_BAR: one
55
+ STDOUT
56
+ end
57
+
58
+ it "shows configs in a shell compatible format" do
59
+ api.put_config_vars("example", { 'A' => 'one', 'B' => 'two three' })
60
+ stderr, stdout = execute("config --shell")
61
+ stderr.should == ""
62
+ stdout.should == <<-STDOUT
63
+ A=one
64
+ B=two three
65
+ STDOUT
66
+ end
67
+
68
+ it "shows a single config for get" do
69
+ api.put_config_vars("example", { 'LONG' => 'A' * 60 })
70
+ stderr, stdout = execute("config:get LONG")
71
+ stderr.should == ""
72
+ stdout.should == <<-STDOUT
73
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
74
+ STDOUT
75
+ end
76
+
77
+ context("set") do
78
+
79
+ it "sets config vars" do
80
+ stderr, stdout = execute("config:set A=1 B=2")
81
+ stderr.should == ""
82
+ stdout.should == <<-STDOUT
83
+ Setting config vars and restarting example... done, v1
84
+ A: 1
85
+ B: 2
86
+ STDOUT
87
+ end
88
+
89
+ it "allows config vars with = in the value" do
90
+ stderr, stdout = execute("config:set A=b=c")
91
+ stderr.should == ""
92
+ stdout.should == <<-STDOUT
93
+ Setting config vars and restarting example... done, v1
94
+ A: b=c
95
+ STDOUT
96
+ end
97
+
98
+ it "sets config vars without changing case" do
99
+ stderr, stdout = execute("config:set a=b")
100
+ stderr.should == ""
101
+ stdout.should == <<-STDOUT
102
+ Setting config vars and restarting example... done, v1
103
+ a: b
104
+ STDOUT
105
+ end
106
+
107
+ end
108
+
109
+ describe "config:unset" do
110
+
111
+ it "exits with a help notice when no keys are provides" do
112
+ stderr, stdout = execute("config:unset")
113
+ stderr.should == <<-STDERR
114
+ ! Usage: azuki config:unset KEY1 [KEY2 ...]
115
+ ! Must specify KEY to unset.
116
+ STDERR
117
+ stdout.should == ""
118
+ end
119
+
120
+ context "when one key is provided" do
121
+
122
+ it "unsets a single key" do
123
+ stderr, stdout = execute("config:unset A")
124
+ stderr.should == ""
125
+ stdout.should == <<-STDOUT
126
+ Unsetting A and restarting example... done, v1
127
+ STDOUT
128
+ end
129
+ end
130
+
131
+ context "when more than one key is provided" do
132
+
133
+ it "unsets all given keys" do
134
+ stderr, stdout = execute("config:unset A B")
135
+ stderr.should == ""
136
+ stdout.should == <<-STDOUT
137
+ Unsetting A and restarting example... done, v1
138
+ Unsetting B and restarting example... done, v2
139
+ STDOUT
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,110 @@
1
+ require "spec_helper"
2
+ require "azuki/command/db"
3
+
4
+ module Azuki::Command
5
+ describe Db do
6
+ before do
7
+ @db = prepare_command(Db)
8
+ @taps_client = mock('taps client')
9
+ end
10
+
11
+ it "pull database" do
12
+ pending("requires taps") unless taps_available?
13
+ @db.stub!(:args).and_return(['postgres://postgres@localhost/db'])
14
+ opts = { :database_url => 'postgres://postgres@localhost/db', :default_chunksize => 1000, :indexes_first => true }
15
+ @db.should_receive(:taps_client).with(:pull, opts)
16
+ @db.should_receive(:confirm_command).and_return(true)
17
+ @db.pull
18
+ end
19
+
20
+ it "push database" do
21
+ pending("requires taps") unless taps_available?
22
+ @db.stub!(:args).and_return(['postgres://postgres@localhost/db'])
23
+ opts = { :database_url => 'postgres://postgres@localhost/db', :default_chunksize => 1000, :indexes_first => true }
24
+ @db.should_receive(:taps_client).with(:push, opts)
25
+ @db.should_receive(:confirm_command).and_return(true)
26
+ @db.push
27
+ end
28
+
29
+ describe "without PostgreSQL" do
30
+ it "defaults host to 127.0.0.1 with a username" do
31
+ @db.send(:uri_hash_to_url, {'scheme' => 'db', 'username' => 'user', 'path' => 'database'}).should == 'db://user@127.0.0.1/database'
32
+ end
33
+ end
34
+
35
+ describe "with PostgreSQL" do
36
+ it "handles lack of host as UNIX domain socket connection" do
37
+ @db.send(:uri_hash_to_url, {'scheme' => 'postgres', 'path' => 'database'}).should == 'postgres:/database'
38
+ end
39
+ end
40
+
41
+ it "handles the lack of a username properly" do
42
+ @db.send(:uri_hash_to_url, {'scheme' => 'db', 'path' => 'database'}).should == 'db://127.0.0.1/database'
43
+ end
44
+
45
+ it "handles integer port number" do
46
+ @db.send(:uri_hash_to_url, {'scheme' => 'db', 'path' => 'database', 'port' => 9000}).should == 'db://127.0.0.1:9000/database'
47
+ end
48
+
49
+ it "maps --tables to the taps table_filter option" do
50
+ @db.stub!(:args).and_return(["sqlite://local.db"])
51
+ @db.stub!(:options).and_return(:tables => "tags,countries")
52
+ opts = @db.send(:parse_taps_opts)
53
+ opts[:table_filter].should == "(^tags$|^countries$)"
54
+ end
55
+
56
+ it "handles both a url and a --confirm on the command line" do
57
+ pending("requires taps") unless taps_available?
58
+ Azuki::Command.stub!(:current_options).and_return(:confirm => "example")
59
+ @db.stub!(:args).and_return(["mysql://user:pass@host/db"])
60
+ opts = { :database_url => 'mysql://user:pass@host/db', :default_chunksize => 1000, :indexes_first => true }
61
+ @db.should_receive(:taps_client).with(:pull, opts)
62
+ @db.pull
63
+ end
64
+
65
+ it "handles no url and --confirm on the command line" do
66
+ pending("requires taps") unless taps_available?
67
+ Azuki::Command.stub!(:current_options).and_return(:confirm => "example")
68
+ opts = { :database_url => 'mysql://user:pass@host/db', :default_chunksize => 1000, :indexes_first => true }
69
+ @db.should_receive(:parse_database_yml).and_return("mysql://user:pass@host/db")
70
+ @db.should_receive(:taps_client).with(:pull, opts)
71
+ @db.pull
72
+ end
73
+
74
+ it "works with a file-based url" do
75
+ pending("requires taps") unless taps_available?
76
+ url = "sqlite://tmp/foo.db"
77
+ Azuki::Command.stub(:current_options).and_return(:confirm => "example")
78
+ @db.stub(:args).and_return([url])
79
+ @db.should_receive(:taps_client).with(:pull, hash_including(:database_url => url))
80
+ @db.pull
81
+ end
82
+
83
+ describe "with erb in the database.yml" do
84
+ before do
85
+ @rails_env = ENV["RAILS_ENV"]
86
+ ENV["RAILS_ENV"] = "development"
87
+
88
+ FakeFS.activate!
89
+ FileUtils.mkdir_p "config"
90
+ File.open("config/database.yml", "w") do |file|
91
+ file.puts <<-YAML
92
+ development:
93
+ adapter: db
94
+ host: localhost
95
+ database: <%= 'db'+'1' %>
96
+ YAML
97
+ end
98
+ end
99
+
100
+ after do
101
+ FakeFS.deactivate!
102
+ ENV["RAILS_ENV"] = @rails_env
103
+ end
104
+
105
+ it "handles ERB code in YAML" do
106
+ @db.send(:parse_database_yml).should == 'db://localhost/db1?encoding=utf8'
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+ require "azuki/command/domains"
3
+
4
+ module Azuki::Command
5
+ describe Domains do
6
+
7
+ before(:all) do
8
+ api.post_app("name" => "example", "stack" => "cedar")
9
+ api.post_addon("example", "custom_domains:basic")
10
+ end
11
+
12
+ after(:all) do
13
+ api.delete_app("example")
14
+ end
15
+
16
+ before(:each) do
17
+ stub_core
18
+ end
19
+
20
+ context("index") do
21
+
22
+ it "lists message with no domains" do
23
+ stderr, stdout = execute("domains")
24
+ stderr.should == ""
25
+ stdout.should == <<-STDOUT
26
+ example has no domain names.
27
+ STDOUT
28
+ end
29
+
30
+ it "lists domains when some exist" do
31
+ api.post_domain("example", "example.com")
32
+ stderr, stdout = execute("domains")
33
+ stderr.should == ""
34
+ stdout.should == <<-STDOUT
35
+ === example Domain Names
36
+ example.com
37
+
38
+ STDOUT
39
+ api.delete_domain("example", "example.com")
40
+ end
41
+
42
+ end
43
+
44
+ it "adds domain names" do
45
+ stderr, stdout = execute("domains:add example.com")
46
+ stderr.should == ""
47
+ stdout.should == <<-STDOUT
48
+ Adding example.com to example... done
49
+ STDOUT
50
+ api.delete_domain("example", "example.com")
51
+ end
52
+
53
+ it "shows usage if no domain specified for add" do
54
+ stderr, stdout = execute("domains:add")
55
+ stderr.should == <<-STDERR
56
+ ! Usage: azuki domains:add DOMAIN
57
+ ! Must specify DOMAIN to add.
58
+ STDERR
59
+ end
60
+
61
+ it "removes domain names" do
62
+ api.post_domain("example", "example.com")
63
+ stderr, stdout = execute("domains:remove example.com")
64
+ stderr.should == ""
65
+ stdout.should == <<-STDOUT
66
+ Removing example.com from example... done
67
+ STDOUT
68
+ end
69
+
70
+ it "shows usage if no domain specified for remove" do
71
+ stderr, stdout = execute("domains:remove")
72
+ stderr.should == <<-STDERR
73
+ ! Usage: azuki domains:remove DOMAIN
74
+ ! Must specify DOMAIN to remove.
75
+ STDERR
76
+ end
77
+
78
+ it "removes all domain names" do
79
+ stub_core.remove_domains("example")
80
+ stderr, stdout = execute("domains:clear")
81
+ stderr.should == ""
82
+ stdout.should == <<-STDOUT
83
+ Removing all domain names from example... done
84
+ STDOUT
85
+ end
86
+ end
87
+ end