cf 0.1.5 → 0.6.0.rc1

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 (140) hide show
  1. data/LICENSE +1277 -30
  2. data/Rakefile +12 -1
  3. data/bin/cf +0 -3
  4. data/lib/cf.rb +6 -0
  5. data/lib/cf/cli.rb +389 -190
  6. data/lib/cf/cli/app/app.rb +45 -0
  7. data/lib/cf/cli/app/apps.rb +99 -0
  8. data/lib/cf/cli/app/base.rb +90 -0
  9. data/lib/cf/cli/app/crashes.rb +42 -0
  10. data/lib/cf/cli/app/delete.rb +95 -0
  11. data/lib/cf/cli/app/deprecated.rb +11 -0
  12. data/lib/cf/cli/app/env.rb +78 -0
  13. data/lib/cf/cli/app/files.rb +137 -0
  14. data/lib/cf/cli/app/health.rb +26 -0
  15. data/lib/cf/cli/app/instances.rb +53 -0
  16. data/lib/cf/cli/app/logs.rb +76 -0
  17. data/lib/cf/cli/app/push.rb +105 -0
  18. data/lib/cf/cli/app/push/create.rb +149 -0
  19. data/lib/cf/cli/app/push/interactions.rb +94 -0
  20. data/lib/cf/cli/app/push/sync.rb +64 -0
  21. data/lib/cf/cli/app/rename.rb +35 -0
  22. data/lib/cf/cli/app/restart.rb +20 -0
  23. data/lib/cf/cli/app/scale.rb +69 -0
  24. data/lib/cf/cli/app/start.rb +143 -0
  25. data/lib/cf/cli/app/stats.rb +67 -0
  26. data/lib/cf/cli/app/stop.rb +27 -0
  27. data/lib/cf/cli/domain/base.rb +8 -0
  28. data/lib/cf/cli/domain/domains.rb +40 -0
  29. data/lib/cf/cli/domain/map.rb +55 -0
  30. data/lib/cf/cli/domain/unmap.rb +56 -0
  31. data/lib/cf/cli/help.rb +15 -0
  32. data/lib/cf/cli/interactive.rb +105 -0
  33. data/lib/cf/cli/organization/base.rb +12 -0
  34. data/lib/cf/cli/organization/create.rb +32 -0
  35. data/lib/cf/cli/organization/delete.rb +73 -0
  36. data/lib/cf/cli/organization/org.rb +45 -0
  37. data/lib/cf/cli/organization/orgs.rb +35 -0
  38. data/lib/cf/cli/organization/rename.rb +36 -0
  39. data/lib/cf/cli/route/base.rb +8 -0
  40. data/lib/cf/cli/route/map.rb +70 -0
  41. data/lib/cf/cli/route/routes.rb +26 -0
  42. data/lib/cf/cli/route/unmap.rb +62 -0
  43. data/lib/cf/cli/service/base.rb +8 -0
  44. data/lib/cf/cli/service/bind.rb +44 -0
  45. data/lib/cf/cli/service/create.rb +107 -0
  46. data/lib/cf/cli/service/delete.rb +82 -0
  47. data/lib/cf/cli/service/rename.rb +35 -0
  48. data/lib/cf/cli/service/service.rb +40 -0
  49. data/lib/cf/cli/service/services.rb +99 -0
  50. data/lib/cf/cli/service/unbind.rb +38 -0
  51. data/lib/cf/cli/space/base.rb +19 -0
  52. data/lib/cf/cli/space/create.rb +63 -0
  53. data/lib/cf/cli/space/delete.rb +95 -0
  54. data/lib/cf/cli/space/rename.rb +39 -0
  55. data/lib/cf/cli/space/space.rb +64 -0
  56. data/lib/cf/cli/space/spaces.rb +55 -0
  57. data/lib/cf/cli/space/switch.rb +16 -0
  58. data/lib/cf/cli/start/base.rb +93 -0
  59. data/lib/cf/cli/start/colors.rb +13 -0
  60. data/lib/cf/cli/start/info.rb +124 -0
  61. data/lib/cf/cli/start/login.rb +94 -0
  62. data/lib/cf/cli/start/logout.rb +17 -0
  63. data/lib/cf/cli/start/target.rb +69 -0
  64. data/lib/cf/cli/start/target_interactions.rb +37 -0
  65. data/lib/cf/cli/start/targets.rb +16 -0
  66. data/lib/cf/cli/user/base.rb +29 -0
  67. data/lib/cf/cli/user/create.rb +39 -0
  68. data/lib/cf/cli/user/passwd.rb +43 -0
  69. data/lib/cf/cli/user/register.rb +42 -0
  70. data/lib/cf/cli/user/users.rb +32 -0
  71. data/lib/cf/constants.rb +10 -7
  72. data/lib/cf/detect.rb +113 -48
  73. data/lib/cf/errors.rb +17 -0
  74. data/lib/cf/plugin.rb +28 -12
  75. data/lib/cf/spacing.rb +89 -0
  76. data/lib/cf/spec_helper.rb +1 -0
  77. data/lib/cf/test_support.rb +6 -0
  78. data/lib/cf/version.rb +1 -1
  79. data/spec/assets/hello-sinatra/Gemfile +3 -0
  80. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  81. data/spec/assets/hello-sinatra/config.ru +3 -0
  82. data/spec/assets/hello-sinatra/fat-cat-makes-app-larger.png +0 -0
  83. data/spec/assets/hello-sinatra/main.rb +6 -0
  84. data/spec/assets/specker_runner/specker_runner_input.rb +6 -0
  85. data/spec/assets/specker_runner/specker_runner_pause.rb +5 -0
  86. data/spec/cf/cli/app/base_spec.rb +17 -0
  87. data/spec/cf/cli/app/delete_spec.rb +188 -0
  88. data/spec/cf/cli/app/instances_spec.rb +65 -0
  89. data/spec/cf/cli/app/push/create_spec.rb +661 -0
  90. data/spec/cf/cli/app/push_spec.rb +369 -0
  91. data/spec/cf/cli/app/rename_spec.rb +104 -0
  92. data/spec/cf/cli/app/scale_spec.rb +75 -0
  93. data/spec/cf/cli/app/start_spec.rb +208 -0
  94. data/spec/cf/cli/app/stats_spec.rb +68 -0
  95. data/spec/cf/cli/domain/map_spec.rb +130 -0
  96. data/spec/cf/cli/domain/unmap_spec.rb +69 -0
  97. data/spec/cf/cli/organization/orgs_spec.rb +108 -0
  98. data/spec/cf/cli/organization/rename_spec.rb +113 -0
  99. data/spec/cf/cli/route/map_spec.rb +121 -0
  100. data/spec/cf/cli/route/unmap_spec.rb +155 -0
  101. data/spec/cf/cli/service/bind_spec.rb +25 -0
  102. data/spec/cf/cli/service/delete_spec.rb +22 -0
  103. data/spec/cf/cli/service/rename_spec.rb +105 -0
  104. data/spec/cf/cli/service/service_spec.rb +23 -0
  105. data/spec/cf/cli/service/unbind_spec.rb +25 -0
  106. data/spec/cf/cli/space/create_spec.rb +93 -0
  107. data/spec/cf/cli/space/rename_spec.rb +102 -0
  108. data/spec/cf/cli/space/spaces_spec.rb +104 -0
  109. data/spec/cf/cli/space/switch_space_spec.rb +55 -0
  110. data/spec/cf/cli/start/info_spec.rb +160 -0
  111. data/spec/cf/cli/start/login_spec.rb +142 -0
  112. data/spec/cf/cli/start/logout_spec.rb +50 -0
  113. data/spec/cf/cli/start/target_spec.rb +123 -0
  114. data/spec/cf/cli/user/create_spec.rb +54 -0
  115. data/spec/cf/cli/user/passwd_spec.rb +102 -0
  116. data/spec/cf/cli/user/register_spec.rb +140 -0
  117. data/spec/cf/cli_spec.rb +442 -0
  118. data/spec/cf/detect_spec.rb +54 -0
  119. data/spec/console_app_specker/console_app_specker_matchers_spec.rb +173 -0
  120. data/spec/console_app_specker/specker_runner_spec.rb +167 -0
  121. data/spec/features/account_lifecycle_spec.rb +85 -0
  122. data/spec/features/login_spec.rb +66 -0
  123. data/spec/features/push_flow_spec.rb +125 -0
  124. data/spec/features/switching_targets_spec.rb +32 -0
  125. data/spec/spec_helper.rb +72 -0
  126. data/spec/support/command_helper.rb +81 -0
  127. data/spec/support/config_helper.rb +15 -0
  128. data/spec/support/console_app_specker_matchers.rb +86 -0
  129. data/spec/support/fake_home_dir.rb +55 -0
  130. data/spec/support/interact_helper.rb +29 -0
  131. data/spec/support/shared_examples/errors.rb +40 -0
  132. data/spec/support/shared_examples/input.rb +14 -0
  133. data/spec/support/specker_runner.rb +80 -0
  134. data/spec/support/tracking_expector.rb +71 -0
  135. metadata +427 -66
  136. data/lib/cf/cli/app.rb +0 -595
  137. data/lib/cf/cli/command.rb +0 -444
  138. data/lib/cf/cli/dots.rb +0 -133
  139. data/lib/cf/cli/service.rb +0 -112
  140. data/lib/cf/cli/user.rb +0 -71
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe CF::User::Create do
4
+ let(:client) { fake_client }
5
+
6
+ before do
7
+ any_instance_of(described_class) { |cli| stub(cli).client { client } }
8
+ stub(client).register
9
+ end
10
+
11
+ subject { cf %W[create-user --#{bool_flag(:force)}] }
12
+
13
+ context "when the user is not logged in" do
14
+ let(:force) { true }
15
+
16
+ before do
17
+ stub(client).logged_in? { false }
18
+ end
19
+
20
+ it "tells the user to log in" do
21
+ subject
22
+ expect(stderr.string).to include("Please log in")
23
+ end
24
+ end
25
+
26
+ context "when the user is logged in" do
27
+ let(:force) { false }
28
+
29
+ before do
30
+ stub(client).logged_in? { true }
31
+ stub_ask("Email") { "some-angry-dude@example.com" }
32
+ stub_ask("Password", anything) { "password1" }
33
+ stub_ask("Verify Password", anything) { confirmation }
34
+ end
35
+
36
+ context "when the password does not match its confirmation" do
37
+ let(:confirmation) { "wrong" }
38
+
39
+ it "displays an error message" do
40
+ subject
41
+ expect(stderr.string).to include("Passwords don't match")
42
+ end
43
+ end
44
+
45
+ context "when the password matches its confirmation" do
46
+ let(:confirmation) { "password1" }
47
+
48
+ it "creates a user" do
49
+ mock(client).register("some-angry-dude@example.com", "password1")
50
+ subject
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe CF::User::Passwd do
4
+ describe 'metadata' do
5
+ let(:command) { Mothership.commands[:passwd] }
6
+
7
+ describe 'command' do
8
+ subject { command }
9
+ its(:description) { should eq "Update a user's password" }
10
+ it { expect(Mothership::Help.group(:admin, :user)).to include(subject) }
11
+ end
12
+
13
+ include_examples 'inputs must have descriptions'
14
+
15
+ describe 'arguments' do
16
+ subject { command.arguments }
17
+ it 'have the correct commands (with inconsistent user instead of email)' do
18
+ should eq [{:type => :optional, :value => nil, :name => :user}]
19
+ end
20
+ end
21
+ end
22
+
23
+ describe '#passwd' do
24
+ let(:client) { fake_client }
25
+ let(:old_password) { "old" }
26
+ let(:new_password) { "password" }
27
+ let(:verify_password) { new_password }
28
+ let(:score) { :strong }
29
+ let(:guid) { random_string("my-object-guid") }
30
+ let(:user_model) { fake_model { attribute :password, :object } }
31
+ let(:user_object) { user_model.new(guid, client) }
32
+ let(:user) { user_object.fake(:password => 'foo') }
33
+
34
+ before do
35
+ any_instance_of described_class do |cli|
36
+ stub(cli).client { client }
37
+ stub(cli).precondition { nil }
38
+ end
39
+ stub(client).logged_in? { true }
40
+ stub(client).current_user { user }
41
+ stub(client).register
42
+ stub(client).base.stub!.password_score(new_password) { score }
43
+ end
44
+
45
+ subject { cf %W[passwd --password #{old_password} --new-password #{new_password} --verify #{verify_password} --no-force --debug] }
46
+
47
+ context 'when the passwords dont match' do
48
+ let(:verify_password) { "other_password" }
49
+
50
+ it { should eq 1 }
51
+
52
+ it 'fails' do
53
+ subject
54
+ expect(stderr.string).to include "Passwords do not match."
55
+ end
56
+
57
+ it "doesn't print out the score" do
58
+ subject
59
+ expect(stdout.string).not_to include "strength"
60
+ end
61
+
62
+ it "doesn't log in or register" do
63
+ dont_allow(user).change_password!
64
+ subject
65
+ end
66
+ end
67
+
68
+ context 'when the password is good or strong' do
69
+ before do
70
+ stub(user).change_password!
71
+ end
72
+
73
+ it { should eq 0 }
74
+
75
+ it 'prints out the password score' do
76
+ subject
77
+ expect(stdout.string).to include "Your password strength is: strong"
78
+ end
79
+
80
+ it 'changes the password' do
81
+ mock(user).change_password!(new_password, old_password)
82
+ subject
83
+ end
84
+ end
85
+
86
+ context 'when the password is weak' do
87
+ let(:score) { :weak }
88
+
89
+ it { should eq 1 }
90
+
91
+ it 'prints out the password score' do
92
+ subject
93
+ expect(stderr.string).to include "Your password strength is: weak"
94
+ end
95
+
96
+ it "doesn't change the password" do
97
+ dont_allow(user).change_password!
98
+ subject
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,140 @@
1
+ require 'spec_helper'
2
+
3
+ command CF::User::Register do
4
+ describe 'metadata' do
5
+ let(:command) { Mothership.commands[:register] }
6
+
7
+ describe 'command' do
8
+ subject { command }
9
+ its(:description) { should eq "Create a user and log in" }
10
+ it { expect(Mothership::Help.group(:admin, :user)).to include(subject) }
11
+ end
12
+
13
+ include_examples 'inputs must have descriptions'
14
+
15
+ describe 'arguments' do
16
+ subject { command.arguments }
17
+ it 'have the correct commands' do
18
+ should eq [
19
+ {:type => :optional, :value => nil, :name => :email}
20
+ ]
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#register' do
26
+ let(:client) { fake_client }
27
+ let(:email) { 'a@b.com' }
28
+ let(:password) { 'password' }
29
+ let(:verify_password) { password }
30
+ let(:force) { false }
31
+ let(:login) { false }
32
+ let(:score) { :strong }
33
+
34
+ before do
35
+ stub(client).register
36
+ stub(client).base.stub!.password_score(password) { score }
37
+ end
38
+
39
+ subject { cf %W[register --email #{email} --password #{password} --verify #{verify_password} --#{bool_flag(:login)} --#{bool_flag(:force)}] }
40
+
41
+ context 'when the passwords dont match' do
42
+ let(:verify_password) { "other_password" }
43
+
44
+ it { should eq 1 }
45
+
46
+ it 'fails' do
47
+ subject
48
+ expect(error_output).to say("Passwords do not match.")
49
+ end
50
+
51
+ it "doesn't print out the score" do
52
+ subject
53
+ expect(output).to_not say("strength")
54
+ end
55
+
56
+ it "doesn't log in or register" do
57
+ dont_allow(client).register
58
+ dont_allow_invoke
59
+ subject
60
+ end
61
+
62
+ context 'and the force flag is passed' do
63
+ let(:force) { true }
64
+
65
+ it "doesn't verify the password" do
66
+ mock(client).register(email, password)
67
+ subject
68
+ expect(error_output).to_not say("Passwords do not match.")
69
+ end
70
+ end
71
+ end
72
+
73
+ context 'when the password is good or strong' do
74
+ it { should eq 0 }
75
+
76
+ it 'prints out the password score' do
77
+ subject
78
+ expect(stdout.string).to include "Your password strength is: strong"
79
+ end
80
+
81
+ it 'registers the user' do
82
+ mock(client).register(email, password)
83
+ subject
84
+ end
85
+
86
+ context 'and the login flag is true' do
87
+ let(:login) { true }
88
+
89
+ it 'logs in' do
90
+ any_instance_of(described_class) do |register|
91
+ mock(register).invoke(:login, :username => email, :password => password)
92
+ end
93
+ subject
94
+ end
95
+ end
96
+
97
+ context 'and the login flag is false' do
98
+ it "doesn't log in" do
99
+ any_instance_of(described_class) do |register|
100
+ dont_allow(register).invoke(:login, :username => email, :password => password)
101
+ end
102
+ subject
103
+ end
104
+ end
105
+ end
106
+
107
+ context 'when the password is weak' do
108
+ let(:score) { :weak }
109
+ let(:login) { true }
110
+
111
+ it { should eq 1 }
112
+
113
+ it 'prints out the password score' do
114
+ subject
115
+ expect(error_output).to say("Your password strength is: weak")
116
+ end
117
+
118
+ it "doesn't register" do
119
+ dont_allow(client).register(email, password)
120
+ subject
121
+ end
122
+
123
+ it "doesn't log in" do
124
+ dont_allow_invoke :login
125
+ subject
126
+ end
127
+ end
128
+
129
+ context 'when arguments are not passed in the command line' do
130
+ subject { cf %W[register --no-force --no-login] }
131
+
132
+ it 'asks for the email, password and confirm password' do
133
+ mock_ask("Email") { email }
134
+ mock_ask("Password", anything) { password }
135
+ mock_ask("Confirm Password", anything) { verify_password }
136
+ subject
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,442 @@
1
+ require 'spec_helper'
2
+
3
+ describe CF::CLI do
4
+ let(:context) { CF::CLI.new }
5
+ let(:command) { nil }
6
+
7
+ let(:fake_home_dir) { nil }
8
+ stub_home_dir_with { fake_home_dir }
9
+
10
+ describe "#wrap_errors" do
11
+ let(:inputs) { {} }
12
+
13
+ subject do
14
+ capture_output do
15
+ stub(context).input { inputs }
16
+ context.wrap_errors { action.call }
17
+ end
18
+ end
19
+
20
+ context "with a CFoundry::Timeout" do
21
+ let(:action) { proc { raise CFoundry::Timeout.new(123, "fizzbuzz") } }
22
+
23
+ it_behaves_like "an error that's obvious to the user",
24
+ :with_message => "fizzbuzz"
25
+ end
26
+
27
+ context "with a UserError" do
28
+ let(:action) { proc { context.fail "foo bar" } }
29
+
30
+ it_behaves_like "an error that's obvious to the user",
31
+ :with_message => "foo bar"
32
+
33
+ it "saves it in the crashlog" do
34
+ mock(context).log_error(anything)
35
+ subject
36
+ end
37
+ end
38
+
39
+ context "with a SystemExit" do
40
+ let(:action) { proc { exit 1 } }
41
+
42
+ it_behaves_like "an error that gets passed through",
43
+ :with_exception => SystemExit
44
+ end
45
+
46
+ context "with a Mothership::Error" do
47
+ let(:action) { proc { raise Mothership::Error } }
48
+
49
+ it_behaves_like "an error that gets passed through",
50
+ :with_exception => Mothership::Error
51
+ end
52
+
53
+ context "with an Interrupt" do
54
+ let(:action) { proc { raise Interrupt } }
55
+
56
+ it "sets the exit code to 130" do
57
+ mock(context).exit_status(130)
58
+ subject
59
+ end
60
+ end
61
+
62
+ context "with a CFoundry authentication error" do
63
+ let(:action) { proc { raise CFoundry::InvalidAuthToken.new("foo bar") } }
64
+ let(:asked) { false }
65
+
66
+ before do
67
+ $cf_asked_auth = asked
68
+ end
69
+
70
+ it "tells the user they are not authenticated" do
71
+ stub(context).invoke(:login)
72
+ subject
73
+ expect(stdout.string).to include "Not authenticated! Try logging in:"
74
+ end
75
+
76
+ it "asks the user to log in" do
77
+ mock(context).invoke(:login)
78
+ subject
79
+ end
80
+
81
+ context "and after logging in they got another authentication error" do
82
+ let(:asked) { true }
83
+
84
+ it "does not ask them to log in" do
85
+ dont_allow(context).invoke(:login)
86
+ subject
87
+ end
88
+
89
+ it_behaves_like "an error that's obvious to the user",
90
+ :with_message => "Denied: foo bar"
91
+ end
92
+ end
93
+
94
+ context "with an arbitrary exception" do
95
+ let(:action) { proc { raise "foo bar" } }
96
+
97
+ it "logs the error" do
98
+ mock(context).log_error(anything)
99
+ subject
100
+ end
101
+
102
+ it "prints the message" do
103
+ subject
104
+ expect(stderr.string).to include "RuntimeError: foo bar"
105
+ end
106
+
107
+ it "sets the exit code to 1" do
108
+ mock(context).exit_status(1)
109
+ subject
110
+ end
111
+
112
+ it "tells the user to check ~/.cf/crash" do
113
+ subject
114
+ expect(stderr.string).to include CF::CRASH_FILE
115
+ end
116
+
117
+ context "when we are debugging" do
118
+ let(:inputs) { { :debug => true } }
119
+
120
+ it_behaves_like "an error that gets passed through",
121
+ :with_exception => RuntimeError
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '#execute' do
127
+ let(:inputs) { {} }
128
+ let(:client) { fake_client }
129
+
130
+ before do
131
+ any_instance_of(CF::CLI) do |cli|
132
+ stub(cli).client { client }
133
+ end
134
+ end
135
+
136
+ subject do
137
+ capture_output do
138
+ stub(context).input { inputs }
139
+ context.execute(command, [])
140
+ end
141
+ end
142
+
143
+ describe "token refreshing" do
144
+ let(:context) { TokenRefreshDummy.new }
145
+ let(:command) { Mothership.commands[:refresh_token] }
146
+ let(:auth_token) { CFoundry::AuthToken.new("old-header") }
147
+ let(:new_auth_token) { CFoundry::AuthToken.new("new-header") }
148
+
149
+ class TokenRefreshDummy < CF::CLI
150
+ class << self
151
+ attr_accessor :new_token
152
+ end
153
+
154
+ def precondition; end
155
+
156
+ desc "XXX"
157
+ def refresh_token
158
+ if client
159
+ client.token = self.class.new_token
160
+ end
161
+ end
162
+ end
163
+
164
+ context "when there is a target" do
165
+ before do
166
+ TokenRefreshDummy.new_token = nil
167
+ client.token = auth_token
168
+ end
169
+
170
+ context "when the token refreshes" do
171
+ it "saves to the target file" do
172
+ any_instance_of(TokenRefreshDummy) do |trd|
173
+ trd.new_token = new_auth_token
174
+
175
+ stub(trd).target_info { {} }
176
+ mock(trd).save_target_info(anything) do |info|
177
+ expect(info[:token]).to eq new_auth_token.auth_header
178
+ end
179
+ end
180
+
181
+ subject
182
+ end
183
+ end
184
+
185
+ context "but there is no token initially" do
186
+ let(:auth_token) { nil }
187
+
188
+ it "doesn't save the new token because something else probably did" do
189
+ dont_allow(context).save_target_info(anything)
190
+ subject
191
+ end
192
+ end
193
+
194
+ context "and the token becomes nil" do
195
+ let(:new_auth_token) { nil }
196
+
197
+ it "doesn't save the nil token" do
198
+ dont_allow(context).save_target_info(anything)
199
+ subject
200
+ end
201
+ end
202
+ end
203
+
204
+ context "when there is no target" do
205
+ let(:client) { nil }
206
+
207
+ it "doesn't try to compare the tokens" do
208
+ expect { subject }.to_not raise_error
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ describe '#log_error' do
215
+ subject do
216
+ context.log_error(exception)
217
+ File.read(File.expand_path(CF::CRASH_FILE))
218
+ end
219
+
220
+ context 'when the exception is a normal error' do
221
+ let(:exception) do
222
+ error = StandardError.new("gemfiles are kinda hard")
223
+ error.set_backtrace(["fo/gems/bar", "baz quick"])
224
+ error
225
+ end
226
+
227
+ it { should include "Time of crash:"}
228
+ it { should include "gemfiles are kinda hard" }
229
+ it { should include "bar" }
230
+ it { should_not include "fo/gems/bar" }
231
+ it { should include "baz quick" }
232
+ end
233
+
234
+ context 'when the exception is an APIError' do
235
+ let(:request) { { :method => "GET", :url => "http://api.cloudfoundry.com/foo", :headers => {}, :body => nil } }
236
+ let(:response) { { :status => 404, :body => "bar", :headers => {} } }
237
+ let(:exception) do
238
+ error = CFoundry::APIError.new(nil, nil, request, response)
239
+ error.set_backtrace(["fo/gems/bar", "baz quick"])
240
+ error
241
+ end
242
+
243
+ before do
244
+ stub(response).body {"Response Body"}
245
+ end
246
+
247
+ it { should include "REQUEST: " }
248
+ it { should include "RESPONSE: " }
249
+ end
250
+ end
251
+
252
+ describe "#client_target" do
253
+ subject { context.client_target }
254
+
255
+ context "when a ~/.cf/target exists" do
256
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
257
+
258
+ it "returns the target in that file" do
259
+ expect(subject).to eq "https://api.some-domain.com"
260
+ end
261
+ end
262
+
263
+ context "when a ~/.cf_target exists" do
264
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/old" }
265
+
266
+ it "returns the target in that file" do
267
+ expect(subject).to eq "https://api.some-domain.com"
268
+ end
269
+ end
270
+
271
+ context "when no target file exists" do
272
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
273
+
274
+ it "returns nil" do
275
+ expect(subject).to eq nil
276
+ end
277
+ end
278
+ end
279
+
280
+ describe "#targets_info" do
281
+ subject { context.targets_info }
282
+
283
+ context "when a ~/.cf/tokens.yml exists" do
284
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
285
+
286
+ it "returns the file's contents as a hash" do
287
+ expect(subject).to eq({
288
+ "https://api.some-domain.com" => {
289
+ :token => "bearer some-token",
290
+ :version => 2
291
+ }
292
+ })
293
+ end
294
+ end
295
+
296
+ context "when a ~/.cf_token file exists" do
297
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/old" }
298
+
299
+ it "returns the target in that file" do
300
+ expect(subject).to eq({
301
+ "https://api.some-domain.com" => {
302
+ :token => "bearer some-token"
303
+ }
304
+ })
305
+ end
306
+ end
307
+
308
+ context "when no token file exists" do
309
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
310
+
311
+ it "returns an empty hash" do
312
+ expect(subject).to eq({})
313
+ end
314
+ end
315
+ end
316
+
317
+ describe "#target_info" do
318
+ subject { CF::CLI.new.target_info("https://api.some-domain.com") }
319
+
320
+ context "when a ~/.cf/tokens.yml exists" do
321
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
322
+
323
+ it "returns the info for the given url" do
324
+ expect(subject).to eq({
325
+ :token => "bearer some-token",
326
+ :version => 2
327
+ })
328
+ end
329
+ end
330
+
331
+ context "when a ~/.cf_token file exists" do
332
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/old" }
333
+
334
+ it "returns the info for the given url" do
335
+ expect(subject).to eq({
336
+ :token => "bearer some-token"
337
+ })
338
+ end
339
+ end
340
+
341
+ context "when no token file exists" do
342
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
343
+
344
+ it "returns an empty hash" do
345
+ expect(subject).to eq({})
346
+ end
347
+ end
348
+ end
349
+
350
+ describe "methods that update the token info" do
351
+ before do
352
+ stub(context).targets_info do
353
+ {
354
+ "https://api.some-domain.com" => { :token => "bearer token1" },
355
+ "https://api.some-other-domain.com" => { :token => "bearer token2" }
356
+ }
357
+ end
358
+ end
359
+
360
+ describe "#save_target_info" do
361
+ it "adds the given target info, and writes the result to ~/.cf/tokens.yml" do
362
+ context.save_target_info({ :token => "bearer token3" }, "https://api.some-domain.com")
363
+ YAML.load_file(File.expand_path("~/.cf/tokens.yml")).should == {
364
+ "https://api.some-domain.com" => { :token => "bearer token3" },
365
+ "https://api.some-other-domain.com" => { :token => "bearer token2" }
366
+ }
367
+ end
368
+ end
369
+
370
+ describe "#remove_target_info" do
371
+ it "removes the given target, and writes the result to ~/.cf/tokens.yml" do
372
+ context.remove_target_info("https://api.some-domain.com")
373
+ YAML.load_file(File.expand_path("~/.cf/tokens.yml")).should == {
374
+ "https://api.some-other-domain.com" => { :token => "bearer token2" }
375
+ }
376
+ end
377
+ end
378
+ end
379
+
380
+ describe "#client" do
381
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
382
+
383
+ before { stub(context).input { {} } }
384
+
385
+ describe "the client's token" do
386
+ it "constructs an AuthToken object with the data from the tokens.yml file" do
387
+ expect(context.client.token).to be_a(CFoundry::AuthToken)
388
+ expect(context.client.token.auth_header).to eq("bearer some-token")
389
+ end
390
+
391
+ it "does not assign an AuthToken on the client if there is no token stored" do
392
+ mock(context).target_info("some-fake-target") { { :version => 2 } }
393
+ expect(context.client("some-fake-target").token).to be_nil
394
+ end
395
+ end
396
+
397
+ describe "the client's version" do
398
+ it "uses the version stored in the yml file" do
399
+ expect(context.client.version).to eq(2)
400
+ end
401
+ end
402
+
403
+ context "when there is no target" do
404
+ let(:fake_home_dir) { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
405
+
406
+ it "returns nil" do
407
+ expect(context.client).to eq(nil)
408
+ end
409
+ end
410
+
411
+ context "with a v1 cloud controller" do
412
+ before do
413
+ stub(context).target_info { { :version => 1 } }
414
+ end
415
+
416
+ it "fails" do
417
+ expect {
418
+ context.client
419
+ }.to raise_error(CF::UserError, /no longer supported/)
420
+ end
421
+ end
422
+
423
+ context "with a v2 cloud controller" do
424
+ before do
425
+ stub(context).target_info { { :version => 2} }
426
+ end
427
+
428
+ it "connects using the v2 api" do
429
+ expect(context.client).to be_a(CFoundry::V2::Client)
430
+ end
431
+
432
+ context "with a proxy user" do
433
+ before { stub(context).input { { :proxy => 'foo@example.com' } } }
434
+
435
+ it "fails with the right error message" do
436
+ expect {context.client}.to raise_error(CF::UserError, "User switching not implemented.")
437
+ end
438
+ end
439
+ end
440
+ end
441
+ end
442
+