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