vmc 0.5.0.beta.12 → 0.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -57,6 +57,6 @@ describe VMC::App::Stats do
57
57
  it 'prints out the stats' do
58
58
  subject
59
59
  stdout.rewind
60
- expect(stdout.readlines.last).to match /.*0\s+0\.0% of\s+cores\s+29\.9M of 288M\s+14\.9M of 256M.*/
60
+ expect(stdout.readlines.last).to match /.*0\s+0\.0%\s+29\.9M of 288M\s+14\.9M of 256M.*/
61
61
  end
62
62
  end
@@ -1,6 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe VMC::Route::Map do
4
+ let(:inputs) { {} }
4
5
  let(:global) { { :color => false } }
5
6
  let(:given) { {} }
6
7
  let(:client) { fake_client }
@@ -16,7 +17,6 @@ describe VMC::Route::Map do
16
17
  let(:domain) { fake(:domain, :name => domain_name ) }
17
18
  let(:domain_name) { "some-domain.com" }
18
19
  let(:host_name) { "some-host" }
19
- let(:url) { "#{host_name}.#{domain_name}" }
20
20
  let(:space_domains) { [] }
21
21
 
22
22
  subject { invoke_cli(cli, :map, inputs, given, global) }
@@ -72,55 +72,23 @@ describe VMC::Route::Map do
72
72
  end
73
73
 
74
74
  context 'when an app is specified' do
75
- let(:inputs) { { :url => url, :app => app } }
75
+ let(:inputs) { { :domain => domain, :host => host_name, :app => app } }
76
76
 
77
77
  context 'and the domain is not already mapped to the space' do
78
- it 'indicates that the domain is invalid' do
79
- expect { subject }.to raise_error(VMC::UserError, /Invalid domain/)
80
- end
81
- end
82
-
83
- include_examples "mapping the route to the app"
84
- end
85
-
86
- context 'when a space is specified' do
87
- let(:inputs) { { :url => url, :space => space } }
78
+ let(:space_domains) { [] }
79
+ let(:inputs) { { :app => app } }
80
+ let(:given) { { :domain => "some-bad-domain" }}
88
81
 
89
- context 'and the domain is not mapped to the space' do
90
82
  it 'indicates that the domain is invalid' do
91
- expect { subject }.to raise_error(VMC::UserError, /Invalid domain/)
83
+ expect { subject }.to raise_error(VMC::UserError, /Unknown domain/)
92
84
  end
93
85
  end
94
86
 
95
- context 'and the domain is mapped to the space' do
96
- let(:domain) { fake(:domain, :client => client, :name => domain_name ) }
97
- let(:new_route) { fake(:route, :host => "new-route-host") }
98
-
99
- before do
100
- stub(client).route { new_route }
101
- stub(new_route).create!
102
- stub(space).domain_by_name(domain_name, anything) { domain }
103
- end
104
-
105
- context 'and the route does not exist' do
106
- it 'indicates that it is creating a route' do
107
- mock(cli).print("Creating route #{host_name}.#{domain_name}")
108
- subject
109
- end
110
-
111
- it 'creates the route in the given space' do
112
- mock(new_route).create!
113
- subject
114
- expect(new_route.host).to eq host_name
115
- expect(new_route.domain).to eq domain
116
- expect(new_route.space).to eq space
117
- end
118
- end
119
- end
87
+ include_examples "mapping the route to the app"
120
88
  end
121
89
 
122
- context 'when neither an app nor a space is specified' do
123
- let(:inputs) { { :url => url } }
90
+ context 'when an app is not specified' do
91
+ let(:inputs) { { :domain => domain, :host => host_name } }
124
92
  let(:space_domains) { [domain] }
125
93
  let(:new_route) { fake(:route) }
126
94
 
@@ -136,7 +104,35 @@ describe VMC::Route::Map do
136
104
 
137
105
  include_examples "mapping the route to the app"
138
106
  end
107
+
108
+ context "when a host is not specified" do
109
+ let(:inputs) { { :domain => domain, :app => app } }
110
+ let(:new_route) { fake(:route) }
111
+
112
+ before do
113
+ stub(client).route { new_route }
114
+ stub(app).add_route
115
+ stub(new_route).create!
116
+ end
117
+
118
+ it "creates a route with an empty string as its host" do
119
+ mock(new_route).create!
120
+ subject
121
+ expect(new_route.host).to eq ""
122
+ end
123
+ end
139
124
  end
140
125
 
141
- context 'when targeting v1'
126
+ context 'when targeting v1' do
127
+ let(:given) { { :domain => "foo.bar.com" } }
128
+ let(:app) { v1_fake :app, :name => "foo" }
129
+ let(:client) { v1_fake_client }
130
+ let(:inputs) { { :app => app } }
131
+
132
+ it "adds the domain to the app's urls" do
133
+ stub(app).update!
134
+ subject
135
+ expect(app.urls).to include "foo.bar.com"
136
+ end
137
+ end
142
138
  end
@@ -14,7 +14,7 @@ describe VMC::Start::Info do
14
14
  end
15
15
 
16
16
  let(:target_info) do
17
- {:description => "Some description",
17
+ { :description => "Some description",
18
18
  :version => 2,
19
19
  :support => "http://example.com"
20
20
  }
@@ -24,6 +24,18 @@ describe VMC::Start::Info do
24
24
  any_instance_of described_class do |cli|
25
25
  stub(cli).client { client }
26
26
  end
27
+
28
+ described_class.class_eval do
29
+ def wrap_errors
30
+ yield
31
+ end
32
+ end
33
+ end
34
+
35
+ after do
36
+ described_class.class_eval do
37
+ remove_method :wrap_errors
38
+ end
27
39
  end
28
40
 
29
41
  describe 'metadata' do
@@ -150,4 +162,12 @@ describe VMC::Start::Info do
150
162
  expect(stdout.readline).to match /Getting services.*OK/
151
163
  end
152
164
  end
165
+
166
+ context 'when there is no target' do
167
+ let(:client) { nil }
168
+
169
+ it "tells the user to run 'vmc target'" do
170
+ expect { subject }.to raise_error(VMC::UserError)
171
+ end
172
+ end
153
173
  end
@@ -1,6 +1,26 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe VMC::Start::Login do
4
+ let(:client) { fake_client :organizations => [] }
5
+
6
+ before do
7
+ any_instance_of described_class do |cli|
8
+ stub(cli).client { client }
9
+ end
10
+
11
+ described_class.class_eval do
12
+ def wrap_errors
13
+ yield
14
+ end
15
+ end
16
+ end
17
+
18
+ after do
19
+ described_class.class_eval do
20
+ remove_method :wrap_errors
21
+ end
22
+ end
23
+
4
24
  describe 'metadata' do
5
25
  let(:command) { Mothership.commands[:login] }
6
26
 
@@ -43,15 +63,13 @@ describe VMC::Start::Login do
43
63
  after { FileUtils.rm_rf home_dir }
44
64
 
45
65
  before do
46
- any_instance_of(CFoundry::V2::Client) do |client|
47
- stub(client).login("my-username", "my-password") { auth_token }
48
- stub(client).login_prompts do
49
- {
50
- :username => ["text", "Username"],
51
- :password => ["password", "8-digit PIN"]
52
- }
53
- end
54
- stub(client).organizations { [] }
66
+ stub(client).login("my-username", "my-password") { auth_token }
67
+
68
+ stub(client).login_prompts do
69
+ {
70
+ :username => ["text", "Username"],
71
+ :password => ["password", "8-digit PIN"]
72
+ }
55
73
  end
56
74
  end
57
75
 
@@ -67,5 +85,13 @@ describe VMC::Start::Login do
67
85
  expect(tokens_yaml["https://api.some-domain.com"][:token]).to eq("bearer some-new-access-token")
68
86
  expect(tokens_yaml["https://api.some-domain.com"][:refresh_token]).to eq("some-new-refresh-token")
69
87
  end
88
+
89
+ context 'when there is no target' do
90
+ let(:client) { nil }
91
+
92
+ it "tells the user to run 'vmc target'" do
93
+ expect { subject }.to raise_error(VMC::UserError)
94
+ end
95
+ end
70
96
  end
71
- end
97
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::Start::Logout do
4
+ let(:client) { fake_client }
5
+
6
+ before do
7
+ any_instance_of described_class do |cli|
8
+ stub(cli).client { client }
9
+ end
10
+
11
+ described_class.class_eval do
12
+ def wrap_errors
13
+ yield
14
+ end
15
+ end
16
+ end
17
+
18
+ after do
19
+ described_class.class_eval do
20
+ remove_method :wrap_errors
21
+ end
22
+ end
23
+
24
+ describe 'metadata' do
25
+ let(:command) { Mothership.commands[:logout] }
26
+
27
+ describe 'command' do
28
+ subject { command }
29
+ its(:description) { should eq "Log out from the target" }
30
+ it { expect(Mothership::Help.group(:start)).to include(subject) }
31
+ end
32
+ end
33
+
34
+ describe "running the command" do
35
+ subject { vmc ["logout"] }
36
+
37
+ context "when there is a target" do
38
+ let(:info) { { client.target => "x", "abc" => "x" } }
39
+
40
+ before do
41
+ any_instance_of VMC::CLI do |cli|
42
+ stub(cli).targets_info { info }
43
+ stub(cli).client_target { client.target }
44
+ end
45
+ end
46
+
47
+ it "removes the target info from the tokens file" do
48
+ expect {
49
+ subject
50
+ }.to change { info }.to("abc" => "x")
51
+ end
52
+ end
53
+
54
+ context "when there is no target" do
55
+ let(:client) { nil }
56
+
57
+ it "tells the user to run 'vmc target'" do
58
+ expect { subject }.to raise_error(VMC::UserError)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
@@ -39,7 +39,7 @@ describe VMC::User::Passwd do
39
39
  stub(client).logged_in? { true }
40
40
  stub(client).current_user { user }
41
41
  stub(client).register
42
- stub(client).base.stub!.uaa.stub!.password_score(new_password) { score }
42
+ stub(client).password_score(new_password) { score }
43
43
  end
44
44
 
45
45
  subject { vmc %W[passwd --password #{old_password} --new-password #{new_password} --verify #{verify_password} --no-force --debug] }
@@ -37,7 +37,7 @@ describe VMC::User::Register do
37
37
  stub(cli).precondition { nil }
38
38
  end
39
39
  stub(client).register
40
- stub(client).base.stub!.uaa.stub!.password_score(password) { score }
40
+ stub(client).password_score(password) { score }
41
41
  end
42
42
 
43
43
  subject { vmc %W[register --email #{email} --password #{password} --verify #{verify_password} --#{bool_flag(:login)} --#{bool_flag(:force)}] }
@@ -1,48 +1,250 @@
1
1
  require 'spec_helper'
2
2
 
3
+ class NoWrapErrorsDummy < VMC::CLI
4
+ def wrap_errors
5
+ yield
6
+ end
7
+ end
8
+
3
9
  describe VMC::CLI do
4
- let(:cmd) { Class.new(VMC::CLI).new }
5
- let(:cli) { VMC::CLI.new }
10
+ let(:context) { NoWrapErrorsDummy.new }
11
+ let(:command) { nil }
6
12
 
7
- describe '#execute' do
13
+ describe "#wrap_errors" do
14
+ let(:context) { VMC::CLI.new }
8
15
  let(:inputs) { {} }
9
16
 
10
17
  subject do
11
18
  capture_output do
12
- stub(cmd).input { inputs }
13
- cmd.execute(nil, [])
19
+ stub(context).input { inputs }
20
+ context.wrap_errors { action.call }
21
+ end
22
+ end
23
+
24
+ shared_examples_for "an error that's obvious to the user" do |options|
25
+ message = options[:with_message]
26
+
27
+ it "prints the message" do
28
+ subject
29
+ expect(stderr.string).to include message
30
+ end
31
+
32
+ it "sets the exit code to 1" do
33
+ mock(context).exit_status(1)
34
+ subject
35
+ end
36
+
37
+ it "does not mention ~/.vmc/crash" do
38
+ subject
39
+ expect(stderr.string).to_not include VMC::CRASH_FILE
40
+ end
41
+ end
42
+
43
+ shared_examples_for "an error that gets passed through" do |options|
44
+ exception = options[:with_exception]
45
+
46
+ it "reraises the error" do
47
+ expect { subject }.to raise_error(exception)
48
+ end
49
+ end
50
+
51
+ context "with a CFoundry::Timeout" do
52
+ let(:action) { proc { raise CFoundry::Timeout.new(123, "fizzbuzz") } }
53
+
54
+ it_behaves_like "an error that's obvious to the user",
55
+ :with_message => "fizzbuzz"
56
+ end
57
+
58
+ context "with a UserError" do
59
+ let(:action) { proc { context.fail "foo bar" } }
60
+
61
+ it_behaves_like "an error that's obvious to the user",
62
+ :with_message => "foo bar"
63
+
64
+ it "saves it in the crashlog" do
65
+ mock(context).log_error(anything)
66
+ subject
14
67
  end
15
68
  end
16
69
 
17
- it 'wraps Timeout::Error with a more friendly message' do
18
- stub(cmd).precondition { raise CFoundry::Timeout.new("GET", "/foo") }
70
+ context "with a SystemExit" do
71
+ let(:action) { proc { exit 1 } }
19
72
 
20
- mock(cmd).err 'GET /foo timed out'
21
- subject
73
+ it_behaves_like "an error that gets passed through",
74
+ :with_exception => SystemExit
22
75
  end
23
76
 
24
- context 'when the debug flag is on' do
25
- let(:inputs) { {:debug => true} }
77
+ context "with a Mothership::Error" do
78
+ let(:action) { proc { raise Mothership::Error } }
26
79
 
27
- it 'reraises' do
28
- stub(cmd).precondition { raise StandardError.new }
29
- expect { subject }.to raise_error(StandardError)
80
+ it_behaves_like "an error that gets passed through",
81
+ :with_exception => Mothership::Error
82
+ end
83
+
84
+ context "with an Interrupt" do
85
+ let(:action) { proc { raise Interrupt } }
86
+
87
+ it "sets the exit code to 130" do
88
+ mock(context).exit_status(130)
89
+ subject
30
90
  end
31
91
  end
32
92
 
33
- context 'when the debug flag is off' do
34
- it 'outputs the crash log message' do
35
- stub(cmd).precondition { raise StandardError.new }
36
- mock(cmd).err /StandardError: StandardError\nFor more information, see .+\.vmc\/crash/
93
+ context "with a CFoundry authentication error" do
94
+ let(:action) { proc { raise CFoundry::InvalidAuthToken.new("foo bar") } }
95
+ let(:asked) { false }
37
96
 
38
- expect { subject }.not_to raise_error(StandardError)
97
+ before do
98
+ $vmc_asked_auth = asked
99
+ end
100
+
101
+ it "tells the user they are not authenticated" do
102
+ stub(context).invoke(:login)
103
+ subject
104
+ expect(stdout.string).to include "Not authenticated! Try logging in:"
105
+ end
106
+
107
+ it "asks the user to log in" do
108
+ mock(context).invoke(:login)
109
+ subject
110
+ end
111
+
112
+ context "and after logging in they got another authentication error" do
113
+ let(:asked) { true }
114
+
115
+ it "does not ask them to log in" do
116
+ dont_allow(context).invoke(:login)
117
+ subject
118
+ end
119
+
120
+ it_behaves_like "an error that's obvious to the user",
121
+ :with_message => "Denied: foo bar"
122
+ end
123
+ end
124
+
125
+ context "with an arbitrary exception" do
126
+ let(:action) { proc { raise "foo bar" } }
127
+
128
+ it "logs the error" do
129
+ mock(context).log_error(anything)
130
+ subject
131
+ end
132
+
133
+ it "prints the message" do
134
+ subject
135
+ expect(stderr.string).to include "RuntimeError: foo bar"
136
+ end
137
+
138
+ it "sets the exit code to 1" do
139
+ mock(context).exit_status(1)
140
+ subject
141
+ end
142
+
143
+ it "tells the user to check ~/.vmc/crash" do
144
+ subject
145
+ expect(stderr.string).to include VMC::CRASH_FILE
146
+ end
147
+
148
+ context "when we are debugging" do
149
+ let(:inputs) { { :debug => true } }
150
+
151
+ it_behaves_like "an error that gets passed through",
152
+ :with_exception => RuntimeError
153
+ end
154
+ end
155
+ end
156
+
157
+ describe '#execute' do
158
+ let(:inputs) { {} }
159
+ let(:client) { fake_client }
160
+
161
+ before do
162
+ any_instance_of(VMC::CLI) do |cli|
163
+ stub(cli).client { client }
164
+ end
165
+ end
166
+
167
+ subject do
168
+ capture_output do
169
+ stub(context).input { inputs }
170
+ context.execute(command, [])
171
+ end
172
+ end
173
+
174
+ describe "token refreshing" do
175
+ let(:context) { TokenRefreshDummy.new }
176
+ let(:command) { Mothership.commands[:refresh_token] }
177
+ let(:auth_token) { CFoundry::AuthToken.new("old-header") }
178
+ let(:new_auth_token) { CFoundry::AuthToken.new("new-header") }
179
+
180
+ class TokenRefreshDummy < NoWrapErrorsDummy
181
+ class << self
182
+ attr_accessor :new_token
183
+ end
184
+
185
+ def precondition; end
186
+
187
+ desc "XXX"
188
+ def refresh_token
189
+ if client
190
+ client.token = self.class.new_token
191
+ end
192
+ end
193
+ end
194
+
195
+ context "when there is a target" do
196
+ before do
197
+ TokenRefreshDummy.new_token = nil
198
+ client.token = auth_token
199
+ end
200
+
201
+ context "when the token refreshes" do
202
+ it "saves to the target file" do
203
+ any_instance_of(TokenRefreshDummy) do |trd|
204
+ trd.new_token = new_auth_token
205
+
206
+ stub(trd).target_info { {} }
207
+ mock(trd).save_target_info(anything) do |info|
208
+ expect(info[:token]).to eq new_auth_token.auth_header
209
+ end
210
+ end
211
+
212
+ subject
213
+ end
214
+ end
215
+
216
+ context "but there is no token initially" do
217
+ let(:auth_token) { nil }
218
+
219
+ it "doesn't save the new token because something else probably did" do
220
+ dont_allow(context).save_target_info(anything)
221
+ subject
222
+ end
223
+ end
224
+
225
+ context "and the token becomes nil" do
226
+ let(:new_auth_token) { nil }
227
+
228
+ it "doesn't save the nil token" do
229
+ dont_allow(context).save_target_info(anything)
230
+ subject
231
+ end
232
+ end
233
+ end
234
+
235
+ context "when there is no target" do
236
+ let(:client) { nil }
237
+
238
+ it "doesn't try to compare the tokens" do
239
+ expect { subject }.to_not raise_error
240
+ end
39
241
  end
40
242
  end
41
243
  end
42
244
 
43
245
  describe '#log_error' do
44
246
  subject do
45
- cmd.log_error(exception)
247
+ context.log_error(exception)
46
248
  File.read(File.expand_path(VMC::CRASH_FILE))
47
249
  end
48
250
 
@@ -79,7 +281,7 @@ describe VMC::CLI do
79
281
  end
80
282
 
81
283
  describe "#client_target" do
82
- subject { cli.client_target }
284
+ subject { context.client_target }
83
285
 
84
286
  context "when a ~/.vmc/target exists" do
85
287
  use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
@@ -100,14 +302,14 @@ describe VMC::CLI do
100
302
  context "when no target file exists" do
101
303
  use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
102
304
 
103
- it "displays an error to the user" do
104
- expect{ subject }.to raise_error(VMC::UserError, /Please select a target/)
305
+ it "returns nil" do
306
+ expect(subject).to eq nil
105
307
  end
106
308
  end
107
309
  end
108
310
 
109
311
  describe "#targets_info" do
110
- subject { cli.targets_info }
312
+ subject { context.targets_info }
111
313
 
112
314
  context "when a ~/.vmc/tokens.yml exists" do
113
315
  use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
@@ -181,7 +383,7 @@ describe VMC::CLI do
181
383
  use_fake_home_dir { tmpdir }
182
384
 
183
385
  before do
184
- stub(cli).targets_info do
386
+ stub(context).targets_info do
185
387
  {
186
388
  "https://api.some-domain.com" => { :token => "bearer token1" },
187
389
  "https://api.some-other-domain.com" => { :token => "bearer token2" }
@@ -193,7 +395,7 @@ describe VMC::CLI do
193
395
 
194
396
  describe "#save_target_info" do
195
397
  it "adds the given target info, and writes the result to ~/.vmc/tokens.yml" do
196
- cli.save_target_info({ :token => "bearer token3" }, "https://api.some-domain.com")
398
+ context.save_target_info({ :token => "bearer token3" }, "https://api.some-domain.com")
197
399
  YAML.load_file(File.expand_path("~/.vmc/tokens.yml")).should == {
198
400
  "https://api.some-domain.com" => { :token => "bearer token3" },
199
401
  "https://api.some-other-domain.com" => { :token => "bearer token2" }
@@ -203,7 +405,7 @@ describe VMC::CLI do
203
405
 
204
406
  describe "#remove_target_info" do
205
407
  it "removes the given target, and writes the result to ~/.vmc/tokens.yml" do
206
- cli.remove_target_info("https://api.some-domain.com")
408
+ context.remove_target_info("https://api.some-domain.com")
207
409
  YAML.load_file(File.expand_path("~/.vmc/tokens.yml")).should == {
208
410
  "https://api.some-other-domain.com" => { :token => "bearer token2" }
209
411
  }
@@ -213,24 +415,32 @@ describe VMC::CLI do
213
415
 
214
416
  describe "#client" do
215
417
  use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
216
- before { stub(cli).input { {} } }
418
+ before { stub(context).input { {} } }
217
419
 
218
420
  describe "the client's token" do
219
421
  it "constructs an AuthToken object with the data from the tokens.yml file" do
220
- expect(cli.client.token).to be_a(CFoundry::AuthToken)
221
- expect(cli.client.token.auth_header).to eq("bearer some-token")
422
+ expect(context.client.token).to be_a(CFoundry::AuthToken)
423
+ expect(context.client.token.auth_header).to eq("bearer some-token")
222
424
  end
223
425
 
224
426
  it "does not assign an AuthToken on the client if there is no token stored" do
225
- mock(cli).target_info("some-fake-target") { { :version => 2 } }
226
- expect(cli.client("some-fake-target").token).to be_nil
427
+ mock(context).target_info("some-fake-target") { { :version => 2 } }
428
+ expect(context.client("some-fake-target").token).to be_nil
227
429
  end
228
430
  end
229
431
 
230
432
  describe "the client's version" do
231
433
  it "uses the version stored in the yml file" do
232
434
 
233
- expect(cli.client.version).to eq(2)
435
+ expect(context.client.version).to eq(2)
436
+ end
437
+ end
438
+
439
+ context "when there is no target" do
440
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
441
+
442
+ it "returns nil" do
443
+ expect(context.client).to eq(nil)
234
444
  end
235
445
  end
236
446
  end