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.
- checksums.yaml +14 -6
- data/LICENSE +1277 -24
- data/Rakefile +24 -87
- data/bin/af +7 -2
- data/lib/af/version.rb +3 -0
- data/lib/vmc.rb +7 -2
- data/lib/vmc/cli.rb +475 -0
- data/lib/vmc/cli/app/app.rb +45 -0
- data/lib/vmc/cli/app/apps.rb +105 -0
- data/lib/vmc/cli/app/base.rb +82 -0
- data/lib/vmc/cli/app/crashes.rb +46 -0
- data/lib/vmc/cli/app/delete.rb +95 -0
- data/lib/vmc/cli/app/deprecated.rb +11 -0
- data/lib/vmc/cli/app/env.rb +78 -0
- data/lib/vmc/cli/app/files.rb +137 -0
- data/lib/vmc/cli/app/health.rb +26 -0
- data/lib/vmc/cli/app/instances.rb +53 -0
- data/lib/vmc/cli/app/logs.rb +76 -0
- data/lib/vmc/cli/app/push.rb +107 -0
- data/lib/vmc/cli/app/push/create.rb +150 -0
- data/lib/vmc/cli/app/push/interactions.rb +100 -0
- data/lib/vmc/cli/app/push/sync.rb +64 -0
- data/lib/vmc/cli/app/rename.rb +39 -0
- data/lib/vmc/cli/app/restart.rb +20 -0
- data/lib/vmc/cli/app/scale.rb +71 -0
- data/lib/vmc/cli/app/start.rb +93 -0
- data/lib/vmc/cli/app/stats.rb +67 -0
- data/lib/vmc/cli/app/stop.rb +27 -0
- data/lib/vmc/cli/domain/base.rb +12 -0
- data/lib/vmc/cli/domain/domains.rb +40 -0
- data/lib/vmc/cli/domain/map.rb +55 -0
- data/lib/vmc/cli/domain/unmap.rb +56 -0
- data/lib/vmc/cli/help.rb +16 -0
- data/lib/vmc/cli/interactive.rb +105 -0
- data/lib/vmc/cli/organization/base.rb +14 -0
- data/lib/vmc/cli/organization/create.rb +32 -0
- data/lib/vmc/cli/organization/delete.rb +73 -0
- data/lib/vmc/cli/organization/org.rb +45 -0
- data/lib/vmc/cli/organization/orgs.rb +35 -0
- data/lib/vmc/cli/organization/rename.rb +36 -0
- data/lib/vmc/cli/route/base.rb +12 -0
- data/lib/vmc/cli/route/map.rb +80 -0
- data/lib/vmc/cli/route/routes.rb +26 -0
- data/lib/vmc/cli/route/unmap.rb +94 -0
- data/lib/vmc/cli/service/base.rb +8 -0
- data/lib/vmc/cli/service/bind.rb +44 -0
- data/lib/vmc/cli/service/create.rb +126 -0
- data/lib/vmc/cli/service/delete.rb +86 -0
- data/lib/vmc/cli/service/rename.rb +35 -0
- data/lib/vmc/cli/service/service.rb +42 -0
- data/lib/vmc/cli/service/services.rb +115 -0
- data/lib/vmc/cli/service/unbind.rb +38 -0
- data/lib/vmc/cli/space/base.rb +21 -0
- data/lib/vmc/cli/space/create.rb +56 -0
- data/lib/vmc/cli/space/delete.rb +95 -0
- data/lib/vmc/cli/space/rename.rb +39 -0
- data/lib/vmc/cli/space/space.rb +64 -0
- data/lib/vmc/cli/space/spaces.rb +55 -0
- data/lib/vmc/cli/space/take.rb +16 -0
- data/lib/vmc/cli/start/base.rb +80 -0
- data/lib/vmc/cli/start/colors.rb +13 -0
- data/lib/vmc/cli/start/info.rb +122 -0
- data/lib/vmc/cli/start/login.rb +92 -0
- data/lib/vmc/cli/start/logout.rb +13 -0
- data/lib/vmc/cli/start/target.rb +64 -0
- data/lib/vmc/cli/start/target_interactions.rb +37 -0
- data/lib/vmc/cli/start/targets.rb +16 -0
- data/lib/vmc/cli/user/base.rb +29 -0
- data/lib/vmc/cli/user/create.rb +39 -0
- data/lib/vmc/cli/user/delete.rb +25 -0
- data/lib/vmc/cli/user/passwd.rb +50 -0
- data/lib/vmc/cli/user/register.rb +42 -0
- data/lib/vmc/cli/user/users.rb +32 -0
- data/lib/vmc/constants.rb +13 -0
- data/lib/vmc/detect.rb +134 -0
- data/lib/vmc/errors.rb +17 -0
- data/lib/vmc/plugin.rb +56 -0
- data/lib/vmc/spacing.rb +89 -0
- data/lib/vmc/spec_helper.rb +1 -0
- data/lib/vmc/test_support.rb +4 -0
- data/lib/vmc/test_support/command_helper.rb +32 -0
- data/lib/vmc/test_support/common_input_examples.rb +14 -0
- data/lib/vmc/test_support/fake_home_dir.rb +16 -0
- data/lib/vmc/test_support/interact_helper.rb +29 -0
- data/lib/vmc/version.rb +3 -0
- data/spec/assets/hello-sinatra/Gemfile +3 -0
- data/spec/assets/hello-sinatra/main.rb +6 -0
- data/spec/features/new_user_flow_spec.rb +71 -0
- data/spec/spec_helper.rb +63 -0
- data/spec/vmc/cli/app/base_spec.rb +17 -0
- data/spec/vmc/cli/app/delete_spec.rb +188 -0
- data/spec/vmc/cli/app/instances_spec.rb +65 -0
- data/spec/vmc/cli/app/push/create_spec.rb +571 -0
- data/spec/vmc/cli/app/push_spec.rb +369 -0
- data/spec/vmc/cli/app/rename_spec.rb +104 -0
- data/spec/vmc/cli/app/scale_spec.rb +81 -0
- data/spec/vmc/cli/app/stats_spec.rb +62 -0
- data/spec/vmc/cli/domain/map_spec.rb +140 -0
- data/spec/vmc/cli/domain/unmap_spec.rb +73 -0
- data/spec/vmc/cli/organization/orgs_spec.rb +108 -0
- data/spec/vmc/cli/organization/rename_spec.rb +113 -0
- data/spec/vmc/cli/route/map_spec.rb +138 -0
- data/spec/vmc/cli/route/unmap_spec.rb +215 -0
- data/spec/vmc/cli/service/bind_spec.rb +25 -0
- data/spec/vmc/cli/service/delete_spec.rb +22 -0
- data/spec/vmc/cli/service/rename_spec.rb +105 -0
- data/spec/vmc/cli/service/service_spec.rb +23 -0
- data/spec/vmc/cli/service/unbind_spec.rb +25 -0
- data/spec/vmc/cli/space/rename_spec.rb +102 -0
- data/spec/vmc/cli/space/spaces_spec.rb +104 -0
- data/spec/vmc/cli/start/info_spec.rb +153 -0
- data/spec/vmc/cli/start/login_spec.rb +71 -0
- data/spec/vmc/cli/user/create_spec.rb +54 -0
- data/spec/vmc/cli/user/passwd_spec.rb +102 -0
- data/spec/vmc/cli/user/register_spec.rb +148 -0
- data/spec/vmc/cli_spec.rb +448 -0
- data/spec/vmc/detect_spec.rb +54 -0
- metadata +231 -124
- data/README.md +0 -155
- data/caldecott_helper/Gemfile +0 -10
- data/caldecott_helper/Gemfile.lock +0 -48
- data/caldecott_helper/server.rb +0 -43
- data/config/clients.yml +0 -17
- data/config/micro/offline.conf +0 -2
- data/config/micro/paths.yml +0 -22
- data/config/micro/refresh_ip.rb +0 -20
- data/lib/cli.rb +0 -48
- data/lib/cli/commands/admin.rb +0 -81
- data/lib/cli/commands/apps.rb +0 -1358
- data/lib/cli/commands/base.rb +0 -233
- data/lib/cli/commands/manifest.rb +0 -56
- data/lib/cli/commands/micro.rb +0 -115
- data/lib/cli/commands/misc.rb +0 -147
- data/lib/cli/commands/services.rb +0 -217
- data/lib/cli/commands/user.rb +0 -70
- data/lib/cli/config.rb +0 -176
- data/lib/cli/console_helper.rb +0 -163
- data/lib/cli/core_ext.rb +0 -122
- data/lib/cli/errors.rb +0 -19
- data/lib/cli/file_helper.rb +0 -123
- data/lib/cli/frameworks.rb +0 -265
- data/lib/cli/manifest_helper.rb +0 -316
- data/lib/cli/runner.rb +0 -633
- data/lib/cli/services_helper.rb +0 -104
- data/lib/cli/tunnel_helper.rb +0 -336
- data/lib/cli/usage.rb +0 -129
- data/lib/cli/version.rb +0 -7
- data/lib/cli/zip_util.rb +0 -102
- data/lib/vmc/client.rb +0 -574
- data/lib/vmc/const.rb +0 -27
- data/lib/vmc/micro.rb +0 -56
- data/lib/vmc/micro/switcher/base.rb +0 -97
- data/lib/vmc/micro/switcher/darwin.rb +0 -19
- data/lib/vmc/micro/switcher/dummy.rb +0 -15
- data/lib/vmc/micro/switcher/linux.rb +0 -16
- data/lib/vmc/micro/switcher/windows.rb +0 -31
- 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
|
+
|