vmc 0.5.0.rc1 → 0.5.0.rc2

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 (38) hide show
  1. data/lib/vmc/cli.rb +2 -1
  2. data/lib/vmc/cli/app/start.rb +81 -24
  3. data/lib/vmc/cli/space/create.rb +4 -0
  4. data/lib/vmc/cli/space/switch.rb +16 -0
  5. data/lib/vmc/cli/start/base.rb +21 -18
  6. data/lib/vmc/cli/user/base.rb +1 -1
  7. data/lib/vmc/cli/user/delete.rb +2 -0
  8. data/lib/vmc/version.rb +1 -1
  9. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  10. data/spec/features/{new_user_flow_spec.rb → v1/new_user_flow_spec.rb} +12 -18
  11. data/spec/features/v2/account_lifecycle_spec.rb +64 -0
  12. data/spec/features/v2/push_flow_spec.rb +126 -0
  13. data/spec/spec_helper.rb +16 -3
  14. data/{lib/vmc/test_support → spec/support}/command_helper.rb +5 -5
  15. data/spec/support/config_helper.rb +15 -0
  16. data/spec/support/console_app_specker_matchers.rb +2 -2
  17. data/spec/support/fake_home_dir.rb +42 -0
  18. data/{lib/vmc/test_support → spec/support}/interact_helper.rb +1 -1
  19. data/spec/support/shared_examples/errors.rb +40 -0
  20. data/{lib/vmc/test_support/common_input_examples.rb → spec/support/shared_examples/input.rb} +0 -0
  21. data/spec/support/specker_runner.rb +17 -61
  22. data/spec/support/tracking_expector.rb +71 -0
  23. data/spec/vmc/cli/app/instances_spec.rb +3 -3
  24. data/spec/vmc/cli/app/start_spec.rb +201 -0
  25. data/spec/vmc/cli/app/stats_spec.rb +21 -15
  26. data/spec/vmc/cli/space/create_spec.rb +73 -0
  27. data/spec/vmc/cli/space/switch_space_spec.rb +55 -0
  28. data/spec/vmc/cli/start/info_spec.rb +3 -16
  29. data/spec/vmc/cli/start/login_spec.rb +97 -37
  30. data/spec/vmc/cli/start/logout_spec.rb +3 -16
  31. data/spec/vmc/cli/start/target_spec.rb +84 -0
  32. data/spec/vmc/cli/user/delete_spec.rb +51 -0
  33. data/spec/vmc/cli/user/passwd_spec.rb +1 -1
  34. data/spec/vmc/cli/user/register_spec.rb +1 -1
  35. data/spec/vmc/cli_spec.rb +38 -36
  36. metadata +65 -39
  37. data/lib/vmc/cli/space/take.rb +0 -16
  38. data/lib/vmc/test_support/fake_home_dir.rb +0 -16
@@ -3,7 +3,7 @@ require 'stringio'
3
3
 
4
4
  describe VMC::App::Stats do
5
5
  let(:global) { { :color => false } }
6
- let(:inputs) { {:app => apps[0]} }
6
+ let(:inputs) { { :app => apps[0] } }
7
7
  let(:given) { {} }
8
8
  let(:client) { fake_client(:apps => apps) }
9
9
  let(:apps) { [fake(:app, :name => "basic_app")] }
@@ -13,21 +13,27 @@ describe VMC::App::Stats do
13
13
  stub(cli).client { client }
14
14
  stub(cli).precondition { nil }
15
15
  end
16
- stub(client).base.stub!.stats do
17
- {"0" => {
18
- :state => "RUNNING",
19
- :stats => {
20
- :name => "basic_app",
21
- :uris => ["basic_app.p01.rbconsvcs.com"],
22
- :host => "172.20.183.93",
23
- :port => 61006,
24
- :uptime => 3250,
25
- :mem_quota => 301989888,
26
- :disk_quota => 268435456,
27
- :fds_quota => 256,
28
- :usage => {:time => "2013-01-04 19:53:39 +0000", :cpu => 0.0019777013519415455, :mem => 31395840, :disk => 15638528}
16
+ stub(inputs[:app]).stats do
17
+ { "0" => {
18
+ :state => "RUNNING",
19
+ :stats => {
20
+ :name => "basic_app",
21
+ :uris => ["basic_app.p01.rbconsvcs.com"],
22
+ :host => "172.20.183.93",
23
+ :port => 61006,
24
+ :uptime => 3250,
25
+ :mem_quota => 301989888,
26
+ :disk_quota => 268435456,
27
+ :fds_quota => 256,
28
+ :usage => {
29
+ :time => "2013-01-04 19:53:39 +0000",
30
+ :cpu => 0.0019777013519415455,
31
+ :mem => 31395840,
32
+ :disk => 15638528
33
+ }
34
+ }
29
35
  }
30
- }}
36
+ }
31
37
  end
32
38
  end
33
39
 
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require "vmc/cli/space/create"
3
+
4
+ describe VMC::Space::Create do
5
+ let(:spaces) { fake_list(:space, 3) }
6
+ let(:organization) { fake(:organization, :spaces => spaces) }
7
+ let(:new_space) { stub! }
8
+ let(:client) { fake_client(:current_organization => organization, :spaces => spaces) }
9
+ let(:new_name) { "some-new-name" }
10
+
11
+ before do
12
+ %w{create! organization= name= name add_manager add_developer add_auditor organization}.each do |method|
13
+ new_space.__send__(method.to_sym)
14
+ end
15
+
16
+ stub(client).space { new_space }
17
+ any_instance_of described_class do |cli|
18
+ stub(cli).client { client }
19
+ stub(cli).precondition { nil }
20
+ end
21
+ end
22
+
23
+ describe 'metadata' do
24
+ let(:command) { Mothership.commands[:create_space] }
25
+
26
+ describe 'command' do
27
+ subject { command }
28
+ its(:description) { should eq "Create a space in an organization" }
29
+ it { expect(Mothership::Help.group(:spaces)).to include(subject) }
30
+ end
31
+
32
+ include_examples 'inputs must have descriptions'
33
+
34
+ describe 'arguments' do
35
+ subject { command.arguments }
36
+ it 'has the correct argument order' do
37
+ should eq([
38
+ { :type => :optional, :value => nil, :name => :name },
39
+ { :type => :optional, :value => nil, :name => :organization }
40
+ ])
41
+ end
42
+ end
43
+ end
44
+
45
+ context "when we don't specify an organization" do
46
+ subject { vmc %W[--no-quiet create-space new-space-name] }
47
+
48
+ context "when we have a default organization" do
49
+ it "uses that organization to create a space" do
50
+ subject
51
+
52
+ stdout.rewind
53
+ expect(stdout.readline).to include "Creating space"
54
+ end
55
+ end
56
+
57
+ context "when we don't have a default organization" do
58
+ let(:organization) { nil }
59
+
60
+ it "shows the help for the command" do
61
+ subject
62
+
63
+ stdout.rewind
64
+ expect(stdout.readline).to include "Create a space in an organization"
65
+ end
66
+
67
+ it "does not try to create the space" do
68
+ new_space.create! { raise "should not call this method" } # rr not behaving
69
+ subject
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+ require "vmc/cli/space/switch"
3
+
4
+ describe VMC::Space::Switch do
5
+ let(:space_to_switch_to) { spaces.last }
6
+ let(:spaces) { fake_list(:space, 3) }
7
+ let(:organization) { fake(:organization, :spaces => spaces) }
8
+ let(:client) { fake_client(:current_organization => organization, :spaces => spaces) }
9
+
10
+ before do
11
+ any_instance_of described_class do |cli|
12
+ stub(cli).client { client }
13
+ stub(cli).precondition { nil }
14
+ end
15
+ end
16
+
17
+ describe 'metadata' do
18
+ let(:command) { Mothership.commands[:switch_space] }
19
+
20
+ describe 'command' do
21
+ subject { command }
22
+ its(:description) { should eq "Switch to a space" }
23
+ it { expect(Mothership::Help.group(:spaces)).to include(subject) }
24
+ end
25
+
26
+ include_examples 'inputs must have descriptions'
27
+
28
+ describe 'arguments' do
29
+ subject { command.arguments }
30
+ it 'has the correct argument order' do
31
+ should eq([{ :type => :normal, :value => nil, :name => :name }])
32
+ end
33
+ end
34
+ end
35
+
36
+ subject { vmc %W[--no-quiet switch-space #{space_to_switch_to.name} --no-color] }
37
+
38
+ context "when the space exists" do
39
+ it "switches to that space" do
40
+ any_instance_of(Mothership) do |m|
41
+ mock(m).invoke(:target, {:space => space_to_switch_to})
42
+ end
43
+
44
+ subject
45
+ end
46
+ end
47
+
48
+ context "when the space does not exist" do
49
+ let(:space_to_switch_to) { fake(:space, :name => "unique-name") }
50
+
51
+ it_behaves_like "an error that gets passed through",
52
+ :with_exception => VMC::UserError,
53
+ :with_message => "The space unique-name does not exist, please create the space first."
54
+ end
55
+ end
@@ -24,18 +24,6 @@ describe VMC::Start::Info do
24
24
  any_instance_of described_class do |cli|
25
25
  stub(cli).client { client }
26
26
  end
27
-
28
- described_class.class_eval do
29
- def wrap_errors
30
- yield
31
- end
32
- end
33
- end
34
-
35
- after do
36
- described_class.class_eval do
37
- remove_method :wrap_errors
38
- end
39
27
  end
40
28
 
41
29
  describe 'metadata' do
@@ -165,9 +153,8 @@ describe VMC::Start::Info do
165
153
 
166
154
  context 'when there is no target' do
167
155
  let(:client) { nil }
168
-
169
- it "tells the user to run 'vmc target'" do
170
- expect { subject }.to raise_error(VMC::UserError)
171
- end
156
+ it_behaves_like "an error that gets passed through",
157
+ :with_exception => VMC::UserError,
158
+ :with_message => "Please select a target with 'vmc target'."
172
159
  end
173
160
  end
@@ -7,18 +7,6 @@ describe VMC::Start::Login do
7
7
  any_instance_of described_class do |cli|
8
8
  stub(cli).client { client }
9
9
  end
10
-
11
- described_class.class_eval do
12
- def wrap_errors
13
- yield
14
- end
15
- end
16
- end
17
-
18
- after do
19
- described_class.class_eval do
20
- remove_method :wrap_errors
21
- end
22
10
  end
23
11
 
24
12
  describe 'metadata' do
@@ -27,7 +15,7 @@ describe VMC::Start::Login do
27
15
  describe 'command' do
28
16
  subject { command }
29
17
  its(:description) { should eq "Authenticate with the target" }
30
- it { expect(Mothership::Help.group(:start)).to include(subject) }
18
+ specify { expect(Mothership::Help.group(:start)).to include(subject) }
31
19
  end
32
20
 
33
21
  include_examples 'inputs must have descriptions'
@@ -42,34 +30,31 @@ describe VMC::Start::Login do
42
30
  end
43
31
 
44
32
  describe 'arguments' do
45
- subject { command.arguments }
33
+ subject(:arguments) { command.arguments }
46
34
  it 'have the correct commands' do
47
- should eq [{:type=>:optional, :value=>:email, :name=>:username}]
35
+ expect(arguments).to eq [{:type => :optional, :value => :email, :name => :username}]
48
36
  end
49
37
  end
50
38
  end
51
39
 
52
40
  describe "running the command" do
53
- use_fake_home_dir { home_dir }
54
-
55
- let(:home_dir) do
56
- tmp_root = Dir.tmpdir
57
- FileUtils.cp_r(File.expand_path("#{SPEC_ROOT}/fixtures/fake_home_dirs/new"), tmp_root)
58
- "#{tmp_root}/new"
59
- end
41
+ stub_home_dir_with("new")
60
42
 
61
43
  let(:auth_token) { CFoundry::AuthToken.new("bearer some-new-access-token", "some-new-refresh-token") }
62
-
63
- after { FileUtils.rm_rf home_dir }
44
+ let(:tokens_yaml) { YAML.load_file(File.expand_path(tokens_file_path)) }
45
+ let(:tokens_file_path) { '~/.vmc/tokens.yml' }
46
+ let(:organizations) { [] }
64
47
 
65
48
  before do
66
- stub(client).login("my-username", "my-password") { auth_token }
67
-
68
- stub(client).login_prompts do
69
- {
70
- :username => ["text", "Username"],
71
- :password => ["password", "8-digit PIN"]
72
- }
49
+ any_instance_of(CFoundry::V2::Client) do |client|
50
+ stub(client).login("my-username", "my-password") { auth_token }
51
+ stub(client).login_prompts do
52
+ {
53
+ :username => ["text", "Username"],
54
+ :password => ["password", "8-digit PIN"]
55
+ }
56
+ end
57
+ stub(client).organizations { [] }
73
58
  end
74
59
  end
75
60
 
@@ -77,21 +62,96 @@ describe VMC::Start::Login do
77
62
 
78
63
  it "logs in with the provided credentials and saves the token data to the YAML file" do
79
64
  stub_ask("Username", {}) { "my-username" }
80
- stub_ask("8-digit PIN", { :echo => "*", :forget => true}) { "my-password" }
65
+ stub_ask("8-digit PIN", {:echo => "*", :forget => true}) { "my-password" }
81
66
 
82
67
  subject
83
68
 
84
- tokens_yaml = YAML.load_file(File.expand_path("~/.vmc/tokens.yml"))
85
69
  expect(tokens_yaml["https://api.some-domain.com"][:token]).to eq("bearer some-new-access-token")
86
70
  expect(tokens_yaml["https://api.some-domain.com"][:refresh_token]).to eq("some-new-refresh-token")
87
71
  end
88
72
 
89
- context 'when there is no target' do
90
- let(:client) { nil }
73
+ context "with space and org in the token file" do
74
+ before do
75
+ write_token_file({:space => "space-id-1", :organization => "organization-id-1"})
76
+ stub_ask("Username", {}) { "my-username" }
77
+ stub_ask("8-digit PIN", {:echo => "*", :forget => true}) { "my-password" }
78
+ end
79
+
80
+ context "when the user has no organizations" do
81
+ it "clears the org and space param from the token file" do
82
+ subject
83
+
84
+ expect(tokens_yaml["https://api.some-domain.com"][:space]).to be_nil
85
+ expect(tokens_yaml["https://api.some-domain.com"][:organization]).to be_nil
86
+ end
87
+ end
91
88
 
92
- it "tells the user to run 'vmc target'" do
93
- expect { subject }.to raise_error(VMC::UserError)
89
+ context "when the user has an organization, but no spaces" do
90
+ before do
91
+ stub.proxy(client).organization { organization }
92
+ any_instance_of(described_class) do |instance|
93
+ stub(instance).client { client }
94
+ end
95
+ end
96
+
97
+ let(:client) { fake_client :organizations => organizations,
98
+ :token => CFoundry::AuthToken.new("bearer some-access-token") }
99
+ let(:organization) { OpenStruct.new(:name => 'My Org', :guid => 'organization-id-1', :users => [user]) }
100
+ let(:user) { stub }
101
+
102
+ before do
103
+ stub(organization).spaces { [] }
104
+ end
105
+
106
+ shared_examples_for :method_clearing_the_token_file do
107
+ it "sets the new organization in the token file" do
108
+ subject
109
+ expect(tokens_yaml["https://api.some-domain.com"][:organization]).to eq(organizations.first.guid)
110
+ end
111
+
112
+ it "clears the space param from the token file" do
113
+ subject
114
+ expect(tokens_yaml["https://api.some-domain.com"][:space]).to be_nil
115
+ end
116
+ end
117
+
118
+ context "with one organization" do
119
+ let(:organizations) {
120
+ [ organization ]
121
+ }
122
+
123
+ it "does not prompt for an organization" do
124
+ dont_allow_ask("Organization", anything)
125
+ subject
126
+ end
127
+
128
+ it_behaves_like :method_clearing_the_token_file
129
+ end
130
+
131
+ context "with multiple organizations" do
132
+ let(:organizations) {
133
+ [ organization, OpenStruct.new(:name => 'My Org 2', :guid => 'organization-id-2') ]
134
+ }
135
+
136
+ before do
137
+ stub_ask("Organization", anything) { organizations.first }
138
+ end
139
+
140
+ it "prompts for organization" do
141
+ mock_ask("Organization", anything) { organizations.first }
142
+ subject
143
+ end
144
+
145
+ it_behaves_like :method_clearing_the_token_file
146
+ end
94
147
  end
95
148
  end
149
+
150
+ context 'when there is no target' do
151
+ let(:client) { nil }
152
+ it_behaves_like "an error that gets passed through",
153
+ :with_exception => VMC::UserError,
154
+ :with_message => "Please select a target with 'vmc target'."
155
+ end
96
156
  end
97
157
  end
@@ -7,18 +7,6 @@ describe VMC::Start::Logout do
7
7
  any_instance_of described_class do |cli|
8
8
  stub(cli).client { client }
9
9
  end
10
-
11
- described_class.class_eval do
12
- def wrap_errors
13
- yield
14
- end
15
- end
16
- end
17
-
18
- after do
19
- described_class.class_eval do
20
- remove_method :wrap_errors
21
- end
22
10
  end
23
11
 
24
12
  describe 'metadata' do
@@ -53,10 +41,9 @@ describe VMC::Start::Logout do
53
41
 
54
42
  context "when there is no target" do
55
43
  let(:client) { nil }
56
-
57
- it "tells the user to run 'vmc target'" do
58
- expect { subject }.to raise_error(VMC::UserError)
59
- end
44
+ it_behaves_like "an error that gets passed through",
45
+ :with_exception => VMC::UserError,
46
+ :with_message => "Please select a target with 'vmc target'."
60
47
  end
61
48
  end
62
49
  end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe VMC::Start::Target do
4
+ describe 'metadata' do
5
+ let(:command) { Mothership.commands[:target] }
6
+
7
+ describe 'command' do
8
+ subject { command }
9
+ its(:description) { should eq "Set or display the target cloud, organization, and space" }
10
+ specify { 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(["-s"]) { should eq :space }
21
+ end
22
+
23
+ describe 'arguments' do
24
+ subject(:arguments) { command.arguments }
25
+ it 'have the correct commands' do
26
+ expect(arguments).to eq [{:type => :optional, :value => nil, :name => :url}]
27
+ end
28
+ end
29
+ end
30
+
31
+ describe 'running the command' do
32
+ stub_home_dir_with("new")
33
+
34
+ context "when the user is authenticated and has an organization" do
35
+ let(:tokens_file_path) { '~/.vmc/tokens.yml' }
36
+ let(:organizations) {
37
+ [ fake(:organization, :name => 'My Org', :guid => 'organization-id-1', :users => [user], :spaces => spaces),
38
+ fake(:organization, :name => 'My Org 2', :guid => 'organization-id-2') ]
39
+ }
40
+ let(:spaces) {
41
+ [ fake(:space, :name => 'Development', :guid => 'space-id-1'),
42
+ fake(:space, :name => 'Staging', :guid => 'space-id-2') ]
43
+ }
44
+
45
+ let(:user) { stub! }
46
+ let(:organization) { organizations.first }
47
+ let(:client) do
48
+ fake_client :frameworks => fake_list(:framework, 3),
49
+ :organizations => organizations,
50
+ :token => CFoundry::AuthToken.new("bearer some-access-token")
51
+ end
52
+
53
+ before do
54
+ write_token_file({:space => "space-id-1", :organization => "organization-id-1"})
55
+ stub(client).current_user { user }
56
+ stub(client).organization { organization }
57
+ stub(client).current_organization { organization }
58
+ any_instance_of(described_class) do |instance|
59
+ stub(instance).client { client }
60
+ end
61
+ end
62
+
63
+ describe "switching the space" do
64
+ let(:space_name) { spaces.last.name }
65
+ let(:tokens_yaml) { YAML.load_file(File.expand_path(tokens_file_path)) }
66
+ let(:tokens_file_path) { '~/.vmc/tokens.yml' }
67
+
68
+ def run_command
69
+ vmc ["target", "-s", space_name]
70
+ end
71
+
72
+ it "should not reprompt for organization" do
73
+ dont_allow_ask("Organization", anything)
74
+ run_command
75
+ end
76
+
77
+ it "sets the space param in the token file" do
78
+ run_command
79
+ expect(tokens_yaml["https://api.some-domain.com"][:space]).to be == 'space-id-2'
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end