af 0.3.22 → 0.5.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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
+