af 0.3.22 → 0.5.0.beta.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 (157) hide show
  1. checksums.yaml +14 -6
  2. data/LICENSE +1277 -24
  3. data/Rakefile +24 -87
  4. data/bin/af +7 -2
  5. data/lib/af/version.rb +3 -0
  6. data/lib/vmc.rb +7 -2
  7. data/lib/vmc/cli.rb +475 -0
  8. data/lib/vmc/cli/app/app.rb +45 -0
  9. data/lib/vmc/cli/app/apps.rb +105 -0
  10. data/lib/vmc/cli/app/base.rb +82 -0
  11. data/lib/vmc/cli/app/crashes.rb +46 -0
  12. data/lib/vmc/cli/app/delete.rb +95 -0
  13. data/lib/vmc/cli/app/deprecated.rb +11 -0
  14. data/lib/vmc/cli/app/env.rb +78 -0
  15. data/lib/vmc/cli/app/files.rb +137 -0
  16. data/lib/vmc/cli/app/health.rb +26 -0
  17. data/lib/vmc/cli/app/instances.rb +53 -0
  18. data/lib/vmc/cli/app/logs.rb +76 -0
  19. data/lib/vmc/cli/app/push.rb +107 -0
  20. data/lib/vmc/cli/app/push/create.rb +150 -0
  21. data/lib/vmc/cli/app/push/interactions.rb +100 -0
  22. data/lib/vmc/cli/app/push/sync.rb +64 -0
  23. data/lib/vmc/cli/app/rename.rb +39 -0
  24. data/lib/vmc/cli/app/restart.rb +20 -0
  25. data/lib/vmc/cli/app/scale.rb +71 -0
  26. data/lib/vmc/cli/app/start.rb +93 -0
  27. data/lib/vmc/cli/app/stats.rb +67 -0
  28. data/lib/vmc/cli/app/stop.rb +27 -0
  29. data/lib/vmc/cli/domain/base.rb +12 -0
  30. data/lib/vmc/cli/domain/domains.rb +40 -0
  31. data/lib/vmc/cli/domain/map.rb +55 -0
  32. data/lib/vmc/cli/domain/unmap.rb +56 -0
  33. data/lib/vmc/cli/help.rb +16 -0
  34. data/lib/vmc/cli/interactive.rb +105 -0
  35. data/lib/vmc/cli/organization/base.rb +14 -0
  36. data/lib/vmc/cli/organization/create.rb +32 -0
  37. data/lib/vmc/cli/organization/delete.rb +73 -0
  38. data/lib/vmc/cli/organization/org.rb +45 -0
  39. data/lib/vmc/cli/organization/orgs.rb +35 -0
  40. data/lib/vmc/cli/organization/rename.rb +36 -0
  41. data/lib/vmc/cli/route/base.rb +12 -0
  42. data/lib/vmc/cli/route/map.rb +80 -0
  43. data/lib/vmc/cli/route/routes.rb +26 -0
  44. data/lib/vmc/cli/route/unmap.rb +94 -0
  45. data/lib/vmc/cli/service/base.rb +8 -0
  46. data/lib/vmc/cli/service/bind.rb +44 -0
  47. data/lib/vmc/cli/service/create.rb +126 -0
  48. data/lib/vmc/cli/service/delete.rb +86 -0
  49. data/lib/vmc/cli/service/rename.rb +35 -0
  50. data/lib/vmc/cli/service/service.rb +42 -0
  51. data/lib/vmc/cli/service/services.rb +115 -0
  52. data/lib/vmc/cli/service/unbind.rb +38 -0
  53. data/lib/vmc/cli/space/base.rb +21 -0
  54. data/lib/vmc/cli/space/create.rb +56 -0
  55. data/lib/vmc/cli/space/delete.rb +95 -0
  56. data/lib/vmc/cli/space/rename.rb +39 -0
  57. data/lib/vmc/cli/space/space.rb +64 -0
  58. data/lib/vmc/cli/space/spaces.rb +55 -0
  59. data/lib/vmc/cli/space/take.rb +16 -0
  60. data/lib/vmc/cli/start/base.rb +80 -0
  61. data/lib/vmc/cli/start/colors.rb +13 -0
  62. data/lib/vmc/cli/start/info.rb +122 -0
  63. data/lib/vmc/cli/start/login.rb +92 -0
  64. data/lib/vmc/cli/start/logout.rb +13 -0
  65. data/lib/vmc/cli/start/target.rb +64 -0
  66. data/lib/vmc/cli/start/target_interactions.rb +37 -0
  67. data/lib/vmc/cli/start/targets.rb +16 -0
  68. data/lib/vmc/cli/user/base.rb +29 -0
  69. data/lib/vmc/cli/user/create.rb +39 -0
  70. data/lib/vmc/cli/user/delete.rb +25 -0
  71. data/lib/vmc/cli/user/passwd.rb +50 -0
  72. data/lib/vmc/cli/user/register.rb +42 -0
  73. data/lib/vmc/cli/user/users.rb +32 -0
  74. data/lib/vmc/constants.rb +13 -0
  75. data/lib/vmc/detect.rb +134 -0
  76. data/lib/vmc/errors.rb +17 -0
  77. data/lib/vmc/plugin.rb +56 -0
  78. data/lib/vmc/spacing.rb +89 -0
  79. data/lib/vmc/spec_helper.rb +1 -0
  80. data/lib/vmc/test_support.rb +4 -0
  81. data/lib/vmc/test_support/command_helper.rb +32 -0
  82. data/lib/vmc/test_support/common_input_examples.rb +14 -0
  83. data/lib/vmc/test_support/fake_home_dir.rb +16 -0
  84. data/lib/vmc/test_support/interact_helper.rb +29 -0
  85. data/lib/vmc/version.rb +3 -0
  86. data/spec/assets/hello-sinatra/Gemfile +3 -0
  87. data/spec/assets/hello-sinatra/main.rb +6 -0
  88. data/spec/features/new_user_flow_spec.rb +71 -0
  89. data/spec/spec_helper.rb +63 -0
  90. data/spec/vmc/cli/app/base_spec.rb +17 -0
  91. data/spec/vmc/cli/app/delete_spec.rb +188 -0
  92. data/spec/vmc/cli/app/instances_spec.rb +65 -0
  93. data/spec/vmc/cli/app/push/create_spec.rb +571 -0
  94. data/spec/vmc/cli/app/push_spec.rb +369 -0
  95. data/spec/vmc/cli/app/rename_spec.rb +104 -0
  96. data/spec/vmc/cli/app/scale_spec.rb +81 -0
  97. data/spec/vmc/cli/app/stats_spec.rb +62 -0
  98. data/spec/vmc/cli/domain/map_spec.rb +140 -0
  99. data/spec/vmc/cli/domain/unmap_spec.rb +73 -0
  100. data/spec/vmc/cli/organization/orgs_spec.rb +108 -0
  101. data/spec/vmc/cli/organization/rename_spec.rb +113 -0
  102. data/spec/vmc/cli/route/map_spec.rb +138 -0
  103. data/spec/vmc/cli/route/unmap_spec.rb +215 -0
  104. data/spec/vmc/cli/service/bind_spec.rb +25 -0
  105. data/spec/vmc/cli/service/delete_spec.rb +22 -0
  106. data/spec/vmc/cli/service/rename_spec.rb +105 -0
  107. data/spec/vmc/cli/service/service_spec.rb +23 -0
  108. data/spec/vmc/cli/service/unbind_spec.rb +25 -0
  109. data/spec/vmc/cli/space/rename_spec.rb +102 -0
  110. data/spec/vmc/cli/space/spaces_spec.rb +104 -0
  111. data/spec/vmc/cli/start/info_spec.rb +153 -0
  112. data/spec/vmc/cli/start/login_spec.rb +71 -0
  113. data/spec/vmc/cli/user/create_spec.rb +54 -0
  114. data/spec/vmc/cli/user/passwd_spec.rb +102 -0
  115. data/spec/vmc/cli/user/register_spec.rb +148 -0
  116. data/spec/vmc/cli_spec.rb +448 -0
  117. data/spec/vmc/detect_spec.rb +54 -0
  118. metadata +231 -124
  119. data/README.md +0 -155
  120. data/caldecott_helper/Gemfile +0 -10
  121. data/caldecott_helper/Gemfile.lock +0 -48
  122. data/caldecott_helper/server.rb +0 -43
  123. data/config/clients.yml +0 -17
  124. data/config/micro/offline.conf +0 -2
  125. data/config/micro/paths.yml +0 -22
  126. data/config/micro/refresh_ip.rb +0 -20
  127. data/lib/cli.rb +0 -48
  128. data/lib/cli/commands/admin.rb +0 -81
  129. data/lib/cli/commands/apps.rb +0 -1358
  130. data/lib/cli/commands/base.rb +0 -233
  131. data/lib/cli/commands/manifest.rb +0 -56
  132. data/lib/cli/commands/micro.rb +0 -115
  133. data/lib/cli/commands/misc.rb +0 -147
  134. data/lib/cli/commands/services.rb +0 -217
  135. data/lib/cli/commands/user.rb +0 -70
  136. data/lib/cli/config.rb +0 -176
  137. data/lib/cli/console_helper.rb +0 -163
  138. data/lib/cli/core_ext.rb +0 -122
  139. data/lib/cli/errors.rb +0 -19
  140. data/lib/cli/file_helper.rb +0 -123
  141. data/lib/cli/frameworks.rb +0 -265
  142. data/lib/cli/manifest_helper.rb +0 -316
  143. data/lib/cli/runner.rb +0 -633
  144. data/lib/cli/services_helper.rb +0 -104
  145. data/lib/cli/tunnel_helper.rb +0 -336
  146. data/lib/cli/usage.rb +0 -129
  147. data/lib/cli/version.rb +0 -7
  148. data/lib/cli/zip_util.rb +0 -102
  149. data/lib/vmc/client.rb +0 -574
  150. data/lib/vmc/const.rb +0 -27
  151. data/lib/vmc/micro.rb +0 -56
  152. data/lib/vmc/micro/switcher/base.rb +0 -97
  153. data/lib/vmc/micro/switcher/darwin.rb +0 -19
  154. data/lib/vmc/micro/switcher/dummy.rb +0 -15
  155. data/lib/vmc/micro/switcher/linux.rb +0 -16
  156. data/lib/vmc/micro/switcher/windows.rb +0 -31
  157. data/lib/vmc/micro/vmrun.rb +0 -158
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::Start::Login do
4
+ describe 'metadata' do
5
+ let(:command) { Mothership.commands[:login] }
6
+
7
+ describe 'command' do
8
+ subject { command }
9
+ its(:description) { should eq "Authenticate with the target" }
10
+ it { expect(Mothership::Help.group(:start)).to include(subject) }
11
+ end
12
+
13
+ include_examples 'inputs must have descriptions'
14
+
15
+ describe 'flags' do
16
+ subject { command.flags }
17
+
18
+ its(["-o"]) { should eq :organization }
19
+ its(["--org"]) { should eq :organization }
20
+ its(["--email"]) { should eq :username }
21
+ its(["-s"]) { should eq :space }
22
+ end
23
+
24
+ describe 'arguments' do
25
+ subject { command.arguments }
26
+ it 'have the correct commands' do
27
+ should eq [{:type=>:optional, :value=>:email, :name=>:username}]
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "running the command" do
33
+ use_fake_home_dir { home_dir }
34
+
35
+ let(:home_dir) do
36
+ tmp_root = Dir.tmpdir
37
+ FileUtils.cp_r(File.expand_path("#{SPEC_ROOT}/fixtures/fake_home_dirs/new"), tmp_root)
38
+ "#{tmp_root}/new"
39
+ end
40
+
41
+ let(:auth_token) { CFoundry::AuthToken.new("bearer some-new-access-token", "some-new-refresh-token") }
42
+
43
+ after { FileUtils.rm_rf home_dir }
44
+
45
+ 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 { [] }
55
+ end
56
+ end
57
+
58
+ subject { vmc ["login", "--no-force"] }
59
+
60
+ it "logs in with the provided credentials and saves the token data to the YAML file" do
61
+ stub_ask("Username", {}) { "my-username" }
62
+ stub_ask("8-digit PIN", { :echo => "*", :forget => true}) { "my-password" }
63
+
64
+ subject
65
+
66
+ tokens_yaml = YAML.load_file(File.expand_path("~/.af/tokens.yml"))
67
+ expect(tokens_yaml["https://api.some-domain.com"][:token]).to eq("bearer some-new-access-token")
68
+ expect(tokens_yaml["https://api.some-domain.com"][:refresh_token]).to eq("some-new-refresh-token")
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::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 { vmc %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 VMC::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!.uaa.stub!.password_score(new_password) { score }
43
+ end
44
+
45
+ subject { vmc %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,148 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::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
+ any_instance_of described_class do |cli|
36
+ stub(cli).client { client }
37
+ stub(cli).precondition { nil }
38
+ end
39
+ stub(client).register
40
+ stub(client).base.stub!.uaa.stub!.password_score(password) { score }
41
+ end
42
+
43
+ subject { vmc %W[register --email #{email} --password #{password} --verify #{verify_password} --#{bool_flag(:login)} --#{bool_flag(:force)}] }
44
+
45
+ context 'when the passwords dont match' do
46
+ let(:verify_password) { "other_password" }
47
+
48
+ it { should eq 1 }
49
+
50
+ it 'fails' do
51
+ subject
52
+ expect(stderr.string).to include "Passwords do not match."
53
+ end
54
+
55
+ it "doesn't print out the score" do
56
+ subject
57
+ expect(stdout.string).not_to include "strength"
58
+ end
59
+
60
+ it "doesn't log in or register" do
61
+ dont_allow(client).register
62
+ any_instance_of(described_class) do |register|
63
+ dont_allow(register).invoke
64
+ end
65
+ subject
66
+ end
67
+
68
+ context 'and the force flag is passed' do
69
+ let(:force) { true }
70
+
71
+ it "doesn't verify the password" do
72
+ mock(client).register(email, password)
73
+ subject
74
+ expect(stderr.string).not_to include "Passwords do not match."
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'when the password is good or strong' do
80
+ it { should eq 0 }
81
+
82
+ it 'prints out the password score' do
83
+ subject
84
+ expect(stdout.string).to include "Your password strength is: strong"
85
+ end
86
+
87
+ it 'registers the user' do
88
+ mock(client).register(email, password)
89
+ subject
90
+ end
91
+
92
+ context 'and the login flag is true' do
93
+ let(:login) { true }
94
+
95
+ it 'logs in' do
96
+ any_instance_of(described_class) do |register|
97
+ mock(register).invoke(:login, :username => email, :password => password)
98
+ end
99
+ subject
100
+ end
101
+ end
102
+
103
+ context 'and the login flag is false' do
104
+ it "doesn't log in" do
105
+ any_instance_of(described_class) do |register|
106
+ dont_allow(register).invoke(:login, :username => email, :password => password)
107
+ end
108
+ subject
109
+ end
110
+ end
111
+ end
112
+
113
+ context 'when the password is weak' do
114
+ let(:score) { :weak }
115
+ let(:login) { true }
116
+
117
+ it { should eq 1 }
118
+
119
+ it 'prints out the password score' do
120
+ subject
121
+ expect(stderr.string).to include "Your password strength is: weak"
122
+ end
123
+
124
+ it "doesn't register" do
125
+ dont_allow(client).register(email, password)
126
+ subject
127
+ end
128
+
129
+ it "doesn't log in" do
130
+ any_instance_of(described_class) do |register|
131
+ dont_allow(register).invoke(:login, :username => email, :password => password)
132
+ end
133
+ subject
134
+ end
135
+ end
136
+
137
+ context 'when arguments are not passed in the command line' do
138
+ subject { vmc %W[register --no-force --no-login] }
139
+
140
+ it 'asks for the email, password and confirm password' do
141
+ mock_ask("Email") { email }
142
+ mock_ask("Password", anything) { password }
143
+ mock_ask("Confirm Password", anything) { verify_password }
144
+ subject
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,448 @@
1
+ require 'spec_helper'
2
+
3
+ class NoWrapErrorsDummy < VMC::CLI
4
+ def wrap_errors
5
+ yield
6
+ end
7
+ end
8
+
9
+ describe VMC::CLI do
10
+ let(:context) { NoWrapErrorsDummy.new }
11
+ let(:command) { nil }
12
+
13
+ describe "#wrap_errors" do
14
+ let(:context) { VMC::CLI.new }
15
+ let(:inputs) { {} }
16
+
17
+ subject do
18
+ capture_output do
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
67
+ end
68
+ end
69
+
70
+ context "with a SystemExit" do
71
+ let(:action) { proc { exit 1 } }
72
+
73
+ it_behaves_like "an error that gets passed through",
74
+ :with_exception => SystemExit
75
+ end
76
+
77
+ context "with a Mothership::Error" do
78
+ let(:action) { proc { raise Mothership::Error } }
79
+
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
90
+ end
91
+ end
92
+
93
+ context "with a CFoundry authentication error" do
94
+ let(:action) { proc { raise CFoundry::InvalidAuthToken.new("foo bar") } }
95
+ let(:asked) { false }
96
+
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
241
+ end
242
+ end
243
+ end
244
+
245
+ describe '#log_error' do
246
+ subject do
247
+ context.log_error(exception)
248
+ File.read(File.expand_path(VMC::CRASH_FILE))
249
+ end
250
+
251
+ context 'when the exception is a normal error' do
252
+ let(:exception) do
253
+ error = StandardError.new("gemfiles are kinda hard")
254
+ error.set_backtrace(["fo/gems/bar", "baz quick"])
255
+ error
256
+ end
257
+
258
+ it { should include "Time of crash:"}
259
+ it { should include "gemfiles are kinda hard" }
260
+ it { should include "bar" }
261
+ it { should_not include "fo/gems/bar" }
262
+ it { should include "baz quick" }
263
+ end
264
+
265
+ context 'when the exception is an APIError' do
266
+ let(:request) { { :method => "GET", :url => "http://api.cloudfoundry.com/foo", :headers => {}, :body => nil } }
267
+ let(:response) { { :status => 404, :body => "bar", :headers => {} } }
268
+ let(:exception) do
269
+ error = CFoundry::APIError.new(nil, nil, request, response)
270
+ error.set_backtrace(["fo/gems/bar", "baz quick"])
271
+ error
272
+ end
273
+
274
+ before do
275
+ stub(response).body {"Response Body"}
276
+ end
277
+
278
+ it { should include "REQUEST: " }
279
+ it { should include "RESPONSE: " }
280
+ end
281
+ end
282
+
283
+ describe "#client_target" do
284
+ subject { context.client_target }
285
+
286
+ context "when a ~/.vmc/target exists" do
287
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
288
+
289
+ it "returns the target in that file" do
290
+ expect(subject).to eq "https://api.some-domain.com"
291
+ end
292
+ end
293
+
294
+ context "when a ~/.vmc_target exists" do
295
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/old" }
296
+
297
+ it "returns the target in that file" do
298
+ expect(subject).to eq "https://api.some-domain.com"
299
+ end
300
+ end
301
+
302
+ context "when no target file exists" do
303
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
304
+
305
+ it "returns nil" do
306
+ expect(subject).to eq nil
307
+ end
308
+ end
309
+ end
310
+
311
+ describe "#targets_info" do
312
+ subject { context.targets_info }
313
+
314
+ context "when a ~/.vmc/tokens.yml exists" do
315
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
316
+
317
+ it "returns the file's contents as a hash" do
318
+ expect(subject).to eq({
319
+ "https://api.some-domain.com" => {
320
+ :token => "bearer some-token",
321
+ :version => 2
322
+ }
323
+ })
324
+ end
325
+ end
326
+
327
+ context "when a ~/.vmc_token file exists" do
328
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/old" }
329
+
330
+ it "returns the target in that file" do
331
+ expect(subject).to eq({
332
+ "https://api.some-domain.com" => {
333
+ :token => "bearer some-token"
334
+ }
335
+ })
336
+ end
337
+ end
338
+
339
+ context "when no token file exists" do
340
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
341
+
342
+ it "returns an empty hash" do
343
+ expect(subject).to eq({})
344
+ end
345
+ end
346
+ end
347
+
348
+ describe "#target_info" do
349
+ subject { VMC::CLI.new.target_info("https://api.some-domain.com") }
350
+
351
+ context "when a ~/.vmc/tokens.yml exists" do
352
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
353
+
354
+ it "returns the info for the given url" do
355
+ expect(subject).to eq({
356
+ :token => "bearer some-token",
357
+ :version => 2
358
+ })
359
+ end
360
+ end
361
+
362
+ context "when a ~/.vmc_token file exists" do
363
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/old" }
364
+
365
+ it "returns the info for the given url" do
366
+ expect(subject).to eq({
367
+ :token => "bearer some-token"
368
+ })
369
+ end
370
+ end
371
+
372
+ context "when no token file exists" do
373
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/no_config" }
374
+
375
+ it "returns an empty hash" do
376
+ expect(subject).to eq({})
377
+ end
378
+ end
379
+ end
380
+
381
+ describe "methods that update the token info" do
382
+ let!(:tmpdir) { Dir.mktmpdir }
383
+ use_fake_home_dir { tmpdir }
384
+
385
+ before do
386
+ stub(context).targets_info do
387
+ {
388
+ "https://api.some-domain.com" => { :token => "bearer token1" },
389
+ "https://api.some-other-domain.com" => { :token => "bearer token2" }
390
+ }
391
+ end
392
+ end
393
+
394
+ after { FileUtils.rm_rf tmpdir }
395
+
396
+ describe "#save_target_info" do
397
+ it "adds the given target info, and writes the result to ~/.vmc/tokens.yml" do
398
+ context.save_target_info({ :token => "bearer token3" }, "https://api.some-domain.com")
399
+ YAML.load_file(File.expand_path("~/.vmc/tokens.yml")).should == {
400
+ "https://api.some-domain.com" => { :token => "bearer token3" },
401
+ "https://api.some-other-domain.com" => { :token => "bearer token2" }
402
+ }
403
+ end
404
+ end
405
+
406
+ describe "#remove_target_info" do
407
+ it "removes the given target, and writes the result to ~/.vmc/tokens.yml" do
408
+ context.remove_target_info("https://api.some-domain.com")
409
+ YAML.load_file(File.expand_path("~/.vmc/tokens.yml")).should == {
410
+ "https://api.some-other-domain.com" => { :token => "bearer token2" }
411
+ }
412
+ end
413
+ end
414
+ end
415
+
416
+ describe "#client" do
417
+ use_fake_home_dir { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
418
+ before { stub(context).input { {} } }
419
+
420
+ describe "the client's token" do
421
+ it "constructs an AuthToken object with the data from the tokens.yml file" do
422
+ expect(context.client.token).to be_a(CFoundry::AuthToken)
423
+ expect(context.client.token.auth_header).to eq("bearer some-token")
424
+ end
425
+
426
+ it "does not assign an AuthToken on the client if there is no token stored" do
427
+ mock(context).target_info("some-fake-target") { { :version => 2 } }
428
+ expect(context.client("some-fake-target").token).to be_nil
429
+ end
430
+ end
431
+
432
+ describe "the client's version" do
433
+ it "uses the version stored in the yml file" do
434
+
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)
444
+ end
445
+ end
446
+ end
447
+ end
448
+