cf 0.6.1.rc5 → 0.6.1.rc6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/cf/cli/populators/organization.rb +4 -0
- data/lib/cf/cli/populators/populator_methods.rb +16 -4
- data/lib/cf/cli/populators/space.rb +4 -0
- data/lib/cf/cli/space/create.rb +1 -1
- data/lib/cf/cli/space/delete.rb +18 -57
- data/lib/cf/cli/user/create.rb +13 -1
- data/lib/cf/version.rb +1 -1
- data/spec/cf/cli/populators/organization_spec.rb +30 -2
- data/spec/cf/cli/populators/space_spec.rb +19 -5
- data/spec/cf/cli/space/create_spec.rb +1 -1
- data/spec/cf/cli/space/delete_spec.rb +91 -0
- data/spec/cf/cli/space/space_spec.rb +60 -37
- data/spec/cf/cli/user/create_spec.rb +75 -29
- data/spec/features/account_lifecycle_spec.rb +4 -36
- data/spec/features/create_user_spec.rb +80 -0
- data/spec/features/login_spec.rb +2 -3
- data/spec/features/push_flow_spec.rb +2 -36
- data/spec/features/space_spec.rb +73 -0
- data/spec/features/switching_targets_spec.rb +23 -26
- data/spec/spec_helper.rb +1 -0
- data/spec/support/features_helper.rb +20 -0
- metadata +11 -3
@@ -21,15 +21,27 @@ module CF
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def get_object
|
24
|
+
previous_object = client.send(type, (info[type])) if info[type]
|
25
|
+
|
24
26
|
if input.has?(type)
|
25
|
-
|
27
|
+
if respond_to?(:finder_argument, true)
|
28
|
+
object = input[type, finder_argument]
|
29
|
+
else
|
30
|
+
object = input[type]
|
31
|
+
end
|
32
|
+
|
26
33
|
with_progress("Switching to #{type} #{c(object.name, :name)}") {}
|
27
34
|
elsif info[type]
|
28
|
-
previous_object = client.send(type, (info[type]))
|
29
35
|
object = previous_object if valid?(previous_object)
|
30
36
|
end
|
31
37
|
|
32
|
-
object
|
38
|
+
object ||= prompt_user
|
39
|
+
|
40
|
+
if (previous_object != object) && respond_to?(:changed, true)
|
41
|
+
changed
|
42
|
+
end
|
43
|
+
|
44
|
+
object
|
33
45
|
end
|
34
46
|
|
35
47
|
def prompt_user
|
@@ -49,4 +61,4 @@ module CF
|
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
52
|
-
end
|
64
|
+
end
|
data/lib/cf/cli/space/create.rb
CHANGED
@@ -37,7 +37,7 @@ module CF::Space
|
|
37
37
|
if input[:target]
|
38
38
|
invoke :target, :organization => space.organization, :space => space
|
39
39
|
else
|
40
|
-
line c("Space created! Use #{b("switch-space #{space.name}")} to target it.", :good)
|
40
|
+
line c("Space created! Use #{b("`cf switch-space #{space.name}`")} to target it.", :good)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
data/lib/cf/cli/space/delete.rb
CHANGED
@@ -5,18 +5,18 @@ module CF::Space
|
|
5
5
|
desc "Delete a space and its contents"
|
6
6
|
group :spaces
|
7
7
|
input :organization, :desc => "Space's organization",
|
8
|
-
|
9
|
-
|
8
|
+
:aliases => ["--org", "-o"], :from_given => by_name(:organization),
|
9
|
+
:default => proc { client.current_organization }
|
10
10
|
input :spaces, :desc => "Spaces to delete", :argument => :splat,
|
11
|
-
|
11
|
+
:singular => :space, :from_given => space_by_name
|
12
12
|
input :recursive, :desc => "Delete recursively", :alias => "-r",
|
13
|
-
|
13
|
+
:default => false, :forget => true
|
14
14
|
input :warn, :desc => "Show warning if it was the last space",
|
15
|
-
|
15
|
+
:default => true
|
16
16
|
input :really, :type => :boolean, :forget => true, :hidden => true,
|
17
|
-
|
17
|
+
:default => proc { force? || interact }
|
18
|
+
|
18
19
|
def delete_space
|
19
|
-
org = input[:organization]
|
20
20
|
spaces = input[:spaces, org]
|
21
21
|
|
22
22
|
deleted_current = false
|
@@ -24,66 +24,27 @@ module CF::Space
|
|
24
24
|
spaces.each do |space|
|
25
25
|
next unless input[:really, space]
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
deleted_current ||= space == client.current_space
|
27
|
+
deleted_current ||= (space == client.current_space)
|
30
28
|
|
31
|
-
|
32
|
-
space.
|
29
|
+
begin
|
30
|
+
with_progress("Deleting space #{c(space.name, :name)}") do
|
31
|
+
space.delete!
|
32
|
+
end
|
33
|
+
rescue CFoundry::APIError => boom
|
34
|
+
line
|
35
|
+
line c(boom.description, :bad)
|
36
|
+
line c("If you want to delete the space along with all dependent objects, rerun the command with the #{b("'--recursive'")} flag.", :bad)
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
if org.spaces.empty?
|
39
|
-
return unless input[:warn]
|
40
|
-
|
41
|
-
line
|
42
|
-
line c("There are no longer any spaces in #{b(org.name)}.", :warning)
|
43
|
-
line "You may want to create one with #{c("create-space", :good)}."
|
44
|
-
elsif deleted_current
|
45
|
-
invalidate_client
|
46
|
-
invoke :target, :organization => client.current_organization
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def clear_space(space)
|
51
|
-
apps = space.apps
|
52
|
-
services = space.service_instances
|
53
|
-
|
54
|
-
return true if apps.empty? && services.empty?
|
55
|
-
|
56
|
-
unless force?
|
57
|
-
line "This space is not empty!"
|
40
|
+
if deleted_current
|
58
41
|
line
|
59
|
-
line "
|
60
|
-
line "service: #{name_list(services)}"
|
61
|
-
line
|
62
|
-
|
63
|
-
return unless input[:recursive]
|
64
|
-
end
|
65
|
-
|
66
|
-
apps.each do |a|
|
67
|
-
invoke :delete, :app => a, :really => true
|
68
|
-
end
|
69
|
-
|
70
|
-
services.each do |i|
|
71
|
-
invoke :delete_service, :service => i, :really => true
|
42
|
+
line c("The space that you were targeting has now been deleted. Please use #{b("`cf target -s SPACE_NAME`")} to target a different one.", :warning)
|
72
43
|
end
|
73
|
-
|
74
|
-
true
|
75
44
|
end
|
76
45
|
|
77
46
|
private
|
78
47
|
|
79
|
-
def ask_spaces(org)
|
80
|
-
spaces = org.spaces
|
81
|
-
fail "No spaces." if spaces.empty?
|
82
|
-
|
83
|
-
[ask("Which space in #{c(org.name, :name)}?", :choices => spaces,
|
84
|
-
:display => proc(&:name))]
|
85
|
-
end
|
86
|
-
|
87
48
|
def ask_really(space)
|
88
49
|
ask("Really delete #{c(space.name, :name)}?", :default => false)
|
89
50
|
end
|
data/lib/cf/cli/user/create.rb
CHANGED
@@ -7,7 +7,13 @@ module CF::User
|
|
7
7
|
input :email, :desc => "User email", :argument => :optional
|
8
8
|
input :password, :desc => "User password"
|
9
9
|
input :verify, :desc => "Repeat password"
|
10
|
+
input :organization, :desc => "User organization",
|
11
|
+
:aliases => %w{--org -o},
|
12
|
+
:default => proc { client.current_organization },
|
13
|
+
:from_given => by_name(:organization)
|
14
|
+
|
10
15
|
def create_user
|
16
|
+
org = CF::Populators::Organization.new(input).populate_and_save!
|
11
17
|
email = input[:email]
|
12
18
|
password = input[:password]
|
13
19
|
|
@@ -15,8 +21,14 @@ module CF::User
|
|
15
21
|
fail "Passwords don't match."
|
16
22
|
end
|
17
23
|
|
24
|
+
user = nil
|
18
25
|
with_progress("Creating user") do
|
19
|
-
client.register(email, password)
|
26
|
+
user = client.register(email, password)
|
27
|
+
end
|
28
|
+
|
29
|
+
with_progress("Adding user to #{org.name}") do
|
30
|
+
user.audited_organizations = user.managed_organizations = user.organizations = [org]
|
31
|
+
user.update!
|
20
32
|
end
|
21
33
|
end
|
22
34
|
|
data/lib/cf/version.rb
CHANGED
@@ -6,7 +6,7 @@ describe CF::Populators::Organization do
|
|
6
6
|
stub_home_dir_with { "#{SPEC_ROOT}/fixtures/fake_home_dirs/new" }
|
7
7
|
|
8
8
|
describe "#populate_and_save!" do
|
9
|
-
let(:tokens_file_path) {
|
9
|
+
let(:tokens_file_path) { CF::TOKENS_FILE }
|
10
10
|
let(:user) { stub! }
|
11
11
|
let(:organizations) do
|
12
12
|
[
|
@@ -25,6 +25,7 @@ describe CF::Populators::Organization do
|
|
25
25
|
stub(client).current_user { user }
|
26
26
|
stub(client).organization { organization }
|
27
27
|
stub(client).current_organization { organization }
|
28
|
+
stub(client).target { 'https://api.some-domain.com' }
|
28
29
|
any_instance_of(described_class) do |instance|
|
29
30
|
stub(instance).client { client }
|
30
31
|
end
|
@@ -74,6 +75,33 @@ describe CF::Populators::Organization do
|
|
74
75
|
subject
|
75
76
|
expect(output).to say("Switching to organization #{organization.name}")
|
76
77
|
end
|
78
|
+
|
79
|
+
context "and a different organization and space in the token file" do
|
80
|
+
let(:input) { {:organization => organizations.last} }
|
81
|
+
|
82
|
+
before do
|
83
|
+
write_token_file({:organization => "organization-id-1", :space => "should-be-removed"})
|
84
|
+
end
|
85
|
+
|
86
|
+
it "removes the space from the token file" do
|
87
|
+
subject
|
88
|
+
refreshed_tokens = YAML.load_file(File.expand_path(tokens_file_path))
|
89
|
+
expect(refreshed_tokens["https://api.some-domain.com"][:space]).to be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
context "and the same organization and a space in the token file" do
|
95
|
+
before do
|
96
|
+
write_token_file({:organization => "organization-id-1", :space => "should-not-be-removed"})
|
97
|
+
end
|
98
|
+
|
99
|
+
it "does not remove the space from the token file" do
|
100
|
+
subject
|
101
|
+
expect(tokens_yaml["https://api.some-domain.com"][:space]).to be == "should-not-be-removed"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
77
105
|
end
|
78
106
|
|
79
107
|
context "without an organization in the input" do
|
@@ -127,4 +155,4 @@ describe CF::Populators::Organization do
|
|
127
155
|
end
|
128
156
|
end
|
129
157
|
end
|
130
|
-
end
|
158
|
+
end
|
@@ -19,9 +19,10 @@ describe CF::Populators::Space do
|
|
19
19
|
fake_client :organizations => [organization]
|
20
20
|
end
|
21
21
|
|
22
|
-
let(:
|
22
|
+
let(:input_hash) { {:space => space} }
|
23
|
+
let(:inputs) { Mothership::Inputs.new(nil, nil, input_hash) }
|
23
24
|
let(:tokens_yaml) { YAML.load_file(File.expand_path(tokens_file_path)) }
|
24
|
-
let(:populator) {
|
25
|
+
let(:populator) { CF::Populators::Space.new(inputs, organization) }
|
25
26
|
|
26
27
|
before do
|
27
28
|
stub(client).current_user { user }
|
@@ -53,8 +54,21 @@ describe CF::Populators::Space do
|
|
53
54
|
subject.should == space
|
54
55
|
end
|
55
56
|
|
57
|
+
describe "mothership input arguments" do
|
58
|
+
let(:inputs) do
|
59
|
+
Mothership::Inputs.new(nil, nil, input_hash).tap do |input|
|
60
|
+
mock(input).[](:space, organization) { space }
|
61
|
+
stub(input).[](anything) { space }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it "passes through extra arguments to the input call" do
|
66
|
+
subject
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
56
70
|
context "with a space in the input" do
|
57
|
-
let(:
|
71
|
+
let(:input_hash) { {:space => space} }
|
58
72
|
before { write_token_file({:space => "space-id-2"}) }
|
59
73
|
|
60
74
|
it "uses that space" do
|
@@ -78,7 +92,7 @@ describe CF::Populators::Space do
|
|
78
92
|
end
|
79
93
|
|
80
94
|
context "without a space in the input" do
|
81
|
-
let(:
|
95
|
+
let(:input_hash) { {} }
|
82
96
|
|
83
97
|
context "with a space in the config file" do
|
84
98
|
it "should not reprompt for space" do
|
@@ -128,4 +142,4 @@ describe CF::Populators::Space do
|
|
128
142
|
end
|
129
143
|
end
|
130
144
|
end
|
131
|
-
end
|
145
|
+
end
|
@@ -63,7 +63,7 @@ describe CF::Space::Create do
|
|
63
63
|
|
64
64
|
it "tells the user how they can switch to the new space" do
|
65
65
|
subject
|
66
|
-
expect(output).to say("Space created! Use switch-space #{new_space.name} to target it.")
|
66
|
+
expect(output).to say("Space created! Use `cf switch-space #{new_space.name}` to target it.")
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "cf/cli/space/delete"
|
3
|
+
|
4
|
+
describe CF::Space::Delete do
|
5
|
+
describe 'metadata' do
|
6
|
+
let(:command) { Mothership.commands[:delete_space] }
|
7
|
+
|
8
|
+
describe 'command' do
|
9
|
+
subject { command }
|
10
|
+
its(:description) { should eq "Delete a space and its contents" }
|
11
|
+
it { expect(Mothership::Help.group(:spaces)).to include(subject) }
|
12
|
+
end
|
13
|
+
|
14
|
+
include_examples 'inputs must have descriptions'
|
15
|
+
|
16
|
+
describe 'arguments' do
|
17
|
+
subject { command.arguments }
|
18
|
+
it 'has the correct argument order' do
|
19
|
+
should eq([
|
20
|
+
{:type => :splat, :value => nil, :name => :spaces}
|
21
|
+
])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "running the command" do
|
27
|
+
let(:space) { fake :space, :name => "some_space_name" }
|
28
|
+
let(:space_2) { fake :space, :name => "some_other_space_name" }
|
29
|
+
let(:spaces) { [space, space_2] }
|
30
|
+
let(:organization) { fake(:organization, :spaces => spaces, :name => "MyOrg") }
|
31
|
+
|
32
|
+
let(:client) { fake_client(:current_organization => organization, :spaces => spaces) }
|
33
|
+
|
34
|
+
subject { capture_output { cf %W[delete-space some_space_name some_other_space_name --quiet --force] } }
|
35
|
+
|
36
|
+
before do
|
37
|
+
any_instance_of described_class do |cli|
|
38
|
+
stub(cli).client { client }
|
39
|
+
|
40
|
+
stub(cli).check_logged_in
|
41
|
+
stub(cli).check_target
|
42
|
+
any_instance_of(CF::Populators::Organization, :populate_and_save! => organization)
|
43
|
+
end
|
44
|
+
stub(space).delete!
|
45
|
+
stub(space_2).delete!
|
46
|
+
end
|
47
|
+
|
48
|
+
it "deletes all named spaces" do
|
49
|
+
mock(space).delete!
|
50
|
+
mock(space_2).delete!
|
51
|
+
|
52
|
+
subject
|
53
|
+
end
|
54
|
+
|
55
|
+
context "without the force parameter" do
|
56
|
+
subject { cf %W[delete-space some_space_name some_other_space_name --quiet] }
|
57
|
+
it "confirms deletion of each space and deletes them" do
|
58
|
+
mock(space).delete!
|
59
|
+
dont_allow(space_2).delete!
|
60
|
+
mock_ask("Really delete #{space.name}?", {:default => false}) { true }
|
61
|
+
mock_ask("Really delete #{space_2.name}?", {:default => false}) { false }
|
62
|
+
|
63
|
+
subject
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when deleting the current space" do
|
68
|
+
it "warns the user what they've done" do
|
69
|
+
stub(client).current_space { space }
|
70
|
+
|
71
|
+
subject
|
72
|
+
expect(output).to say("The space that you were targeting has now been deleted. Please use `cf target -s SPACE_NAME` to target a different one.")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when a space fails to delete" do
|
77
|
+
before do
|
78
|
+
stub(space).delete! { raise CFoundry::APIError.new("We don't delete children.", 40005) }
|
79
|
+
subject
|
80
|
+
end
|
81
|
+
|
82
|
+
it "shows the error message" do
|
83
|
+
expect(output).to say "We don't delete children."
|
84
|
+
end
|
85
|
+
|
86
|
+
it "informs the user of how to recursively delete" do
|
87
|
+
expect(output).to say "If you want to delete the space along with all dependent objects, rerun the command with the '--recursive' flag."
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -2,56 +2,79 @@ require 'spec_helper'
|
|
2
2
|
require 'stringio'
|
3
3
|
|
4
4
|
describe CF::Space::Space do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
before do
|
14
|
-
any_instance_of described_class do |cli|
|
15
|
-
stub(cli).client { client }
|
16
|
-
stub(cli).check_logged_in
|
17
|
-
stub(cli).check_target
|
18
|
-
any_instance_of(CF::Populators::Organization, :populate_and_save! => organization)
|
19
|
-
any_instance_of(CF::Populators::Space, :populate_and_save! => space_1)
|
5
|
+
describe 'metadata' do
|
6
|
+
let(:command) { Mothership.commands[:space] }
|
7
|
+
|
8
|
+
describe 'command' do
|
9
|
+
subject { command }
|
10
|
+
its(:description) { should eq "Show space information" }
|
11
|
+
it { expect(Mothership::Help.group(:spaces)).to include(subject) }
|
20
12
|
end
|
21
|
-
end
|
22
13
|
|
23
|
-
|
24
|
-
subject { cf %W[space some_space_name --quiet] }
|
14
|
+
include_examples 'inputs must have descriptions'
|
25
15
|
|
26
|
-
|
27
|
-
subject
|
28
|
-
|
16
|
+
describe 'arguments' do
|
17
|
+
subject { command.arguments }
|
18
|
+
it 'has the correct argument order' do
|
19
|
+
should eq([
|
20
|
+
{ :type => :optional, :value => nil, :name => :space }
|
21
|
+
])
|
22
|
+
end
|
29
23
|
end
|
30
24
|
end
|
31
25
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
describe "running the command" do
|
27
|
+
let(:apps) { fake_list(:app, 2) }
|
28
|
+
let(:domains) { fake_list(:domain, 2) }
|
29
|
+
let(:services) { fake_list(:service_instance, 2) }
|
30
|
+
let!(:space_1) { fake(:space, :name => "some_space_name", :apps => apps, :service_instances => services, :domains => domains) }
|
31
|
+
let(:spaces) { [space_1] }
|
32
|
+
let(:organization) { fake(:organization, :name => "Spacey Org", :spaces => spaces) }
|
33
|
+
let(:client) { fake_client(:spaces => spaces, :current_organization => organization) }
|
36
34
|
|
37
|
-
|
38
|
-
|
35
|
+
before do
|
36
|
+
any_instance_of described_class do |cli|
|
37
|
+
stub(cli).client { client }
|
38
|
+
stub(cli).check_logged_in
|
39
|
+
stub(cli).check_target
|
40
|
+
any_instance_of(CF::Populators::Organization, :populate_and_save! => organization)
|
41
|
+
any_instance_of(CF::Populators::Space, :populate_and_save! => space_1)
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
45
|
+
context "with --quiet" do
|
46
|
+
subject { cf %W[space some_space_name --quiet] }
|
44
47
|
|
45
|
-
|
46
|
-
|
48
|
+
it "shows only the name" do
|
49
|
+
subject
|
50
|
+
expect(stdout.read).to eq("some_space_name\n")
|
51
|
+
end
|
47
52
|
end
|
48
53
|
|
49
|
-
|
50
|
-
|
51
|
-
|
54
|
+
context "with --no-quiet" do
|
55
|
+
subject { cf %W[space some_space_name --no-quiet] }
|
56
|
+
|
57
|
+
before { subject }
|
58
|
+
|
59
|
+
it "shows the space's name" do
|
60
|
+
expect(stdout.read).to include("some_space_name:")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "shows the space's org" do
|
64
|
+
expect(stdout.read).to include("organization: Spacey Org")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "shows apps" do
|
68
|
+
expect(stdout.read).to include("apps: #{apps.first.name}, #{apps.last.name}")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "shows services" do
|
72
|
+
expect(stdout.read).to include("services: #{services.first.name}, #{services.last.name}")
|
73
|
+
end
|
52
74
|
|
53
|
-
|
54
|
-
|
75
|
+
it "shows domains" do
|
76
|
+
expect(stdout.read).to include("domains: #{domains.first.name}, #{domains.last.name}")
|
77
|
+
end
|
55
78
|
end
|
56
79
|
end
|
57
80
|
end
|
@@ -1,54 +1,100 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe CF::User::Create do
|
4
|
-
|
4
|
+
describe 'metadata' do
|
5
|
+
let(:command) { Mothership.commands[:create_user] }
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
subject { cf %W[create-user --#{bool_flag(:force)}] }
|
12
|
-
|
13
|
-
context "when the user is not logged in" do
|
14
|
-
let(:force) { true }
|
15
|
-
|
16
|
-
before do
|
17
|
-
stub(client).logged_in? { false }
|
7
|
+
describe 'command' do
|
8
|
+
subject { command }
|
9
|
+
its(:description) { should eq "Create a user" }
|
10
|
+
it { expect(Mothership::Help.group(:admin, :user)).to include(subject) }
|
18
11
|
end
|
19
12
|
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
include_examples 'inputs must have descriptions'
|
14
|
+
|
15
|
+
describe 'arguments' do
|
16
|
+
subject { command.arguments }
|
17
|
+
it 'has the correct argument order' do
|
18
|
+
should eq([
|
19
|
+
{ :type => :optional, :value => nil, :name => :email }
|
20
|
+
])
|
21
|
+
end
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
let(:
|
25
|
+
describe "running the command" do
|
26
|
+
let(:client) { fake_client }
|
27
|
+
let(:org) { fake(:organization) }
|
28
|
+
let(:user) { fake(:user) }
|
28
29
|
|
29
30
|
before do
|
30
|
-
stub(
|
31
|
-
|
32
|
-
stub_ask("Password", anything) { "password1" }
|
33
|
-
stub_ask("Verify Password", anything) { confirmation }
|
31
|
+
any_instance_of(described_class) { |cli| stub(cli).client { client } }
|
32
|
+
stub(client).register
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
|
35
|
+
subject { cf %W[create-user --#{bool_flag(:force)}] }
|
36
|
+
|
37
|
+
context "when the user is not logged in" do
|
38
|
+
let(:force) { true }
|
38
39
|
|
39
|
-
|
40
|
+
before do
|
41
|
+
stub(client).logged_in? { false }
|
42
|
+
end
|
43
|
+
|
44
|
+
it "tells the user to log in" do
|
40
45
|
subject
|
41
|
-
expect(stderr.string).to include("
|
46
|
+
expect(stderr.string).to include("Please log in")
|
42
47
|
end
|
43
48
|
end
|
44
49
|
|
45
|
-
context "when the
|
50
|
+
context "when the user is logged in" do
|
51
|
+
let(:force) { false }
|
46
52
|
let(:confirmation) { "password1" }
|
47
53
|
|
48
|
-
|
49
|
-
|
54
|
+
before do
|
55
|
+
stub(client).logged_in? { true }
|
56
|
+
stub_ask("Email") { "some-angry-dude@example.com" }
|
57
|
+
stub_ask("Password", anything) { "password1" }
|
58
|
+
stub_ask("Verify Password", anything) { confirmation }
|
59
|
+
stub(CF::Populators::Organization).new(instance_of(Mothership::Inputs)) { stub!.populate_and_save! { org } }
|
60
|
+
|
61
|
+
stub(client).register("some-angry-dude@example.com", "password1") { user }
|
62
|
+
stub(user).update!
|
63
|
+
end
|
64
|
+
|
65
|
+
it "ensures that an org is present" do
|
66
|
+
mock(CF::Populators::Organization).new(instance_of(Mothership::Inputs)) { stub!.populate_and_save! { org } }
|
50
67
|
subject
|
51
68
|
end
|
69
|
+
|
70
|
+
context "when the password does not match its confirmation" do
|
71
|
+
let(:confirmation) { "wrong" }
|
72
|
+
|
73
|
+
it "displays an error message" do
|
74
|
+
subject
|
75
|
+
expect(stderr.string).to include("Passwords don't match")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when the password matches its confirmation" do
|
80
|
+
it "creates a user" do
|
81
|
+
mock(client).register("some-angry-dude@example.com", "password1") { user }
|
82
|
+
stub(user).update!
|
83
|
+
subject
|
84
|
+
end
|
85
|
+
|
86
|
+
it "adds the user to the current org" do
|
87
|
+
stub(client).register(anything, anything) { user }
|
88
|
+
mock(user).update!
|
89
|
+
|
90
|
+
subject
|
91
|
+
|
92
|
+
user.organizations.should == [org]
|
93
|
+
user.managed_organizations.should == [org]
|
94
|
+
user.audited_organizations.should == [org]
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
52
98
|
end
|
53
99
|
end
|
54
100
|
end
|
@@ -25,39 +25,16 @@ if ENV['CF_V2_RUN_INTEGRATION']
|
|
25
25
|
|
26
26
|
before do
|
27
27
|
Interact::Progress::Dots.start!
|
28
|
+
login
|
28
29
|
end
|
29
30
|
|
30
31
|
after do
|
32
|
+
logout
|
31
33
|
Interact::Progress::Dots.stop!
|
32
34
|
end
|
33
35
|
|
34
36
|
it "registers a new account and deletes it" do
|
35
37
|
email = Faker::Internet.email
|
36
|
-
BlueShell::Runner.run("#{cf_bin} logout") do |runner|
|
37
|
-
runner.wait_for_exit
|
38
|
-
end
|
39
|
-
|
40
|
-
BlueShell::Runner.run("#{cf_bin} target #{target}") do |runner|
|
41
|
-
runner.wait_for_exit
|
42
|
-
end
|
43
|
-
|
44
|
-
BlueShell::Runner.run("#{cf_bin} login #{username} --password #{password}") do |runner|
|
45
|
-
expect(runner).to say(
|
46
|
-
"Organization>" => proc {
|
47
|
-
runner.send_keys organization
|
48
|
-
expect(runner).to say /Switching to organization .*\.\.\. OK/
|
49
|
-
},
|
50
|
-
"Switching to organization" => proc {}
|
51
|
-
)
|
52
|
-
|
53
|
-
expect(runner).to say(
|
54
|
-
"Space>" => proc {
|
55
|
-
runner.send_keys "1"
|
56
|
-
expect(runner).to say /Switching to space .*\.\.\. OK/
|
57
|
-
},
|
58
|
-
"Switching to space" => proc {}
|
59
|
-
)
|
60
|
-
end
|
61
38
|
|
62
39
|
BlueShell::Runner.run("#{cf_bin} register #{email} --password p") do |runner|
|
63
40
|
expect(runner).to say "Confirm Password>"
|
@@ -67,16 +44,7 @@ if ENV['CF_V2_RUN_INTEGRATION']
|
|
67
44
|
expect(runner).to say "Authenticating... OK"
|
68
45
|
end
|
69
46
|
|
70
|
-
|
71
|
-
runner.wait_for_exit
|
72
|
-
end
|
73
|
-
|
74
|
-
BlueShell::Runner.run("#{cf_bin} login #{username} --password #{password}") do |runner|
|
75
|
-
expect(runner).to say "Organization>"
|
76
|
-
runner.send_keys "1"
|
77
|
-
expect(runner).to say "Space>"
|
78
|
-
runner.send_keys "1"
|
79
|
-
end
|
47
|
+
login
|
80
48
|
|
81
49
|
# TODO: do this when cf delete-user is implemented
|
82
50
|
#BlueShell::Runner.run("#{cf_bin} delete-user #{email}") do |runner|
|
@@ -99,7 +67,7 @@ if ENV['CF_V2_RUN_INTEGRATION']
|
|
99
67
|
expect(runner).to say "Password>"
|
100
68
|
runner.send_keys "another_password"
|
101
69
|
expect(runner).to say "Password>"
|
102
|
-
runner.send_keys "passwords_for_everyone"
|
70
|
+
runner.send_keys "passwords_for_everyone!"
|
103
71
|
expect(runner).to say "FAILED"
|
104
72
|
runner.output.should_not =~ /CFoundry::/
|
105
73
|
runner.output.should_not =~ %r{~/\.cf/crash}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "webmock/rspec"
|
3
|
+
require "ffaker"
|
4
|
+
|
5
|
+
if ENV['CF_V2_RUN_INTEGRATION']
|
6
|
+
describe 'A new user tries to use CF against v2 production', :ruby19 => true do
|
7
|
+
before(:all) do
|
8
|
+
WebMock.allow_net_connect!
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:all) do
|
12
|
+
WebMock.disable_net_connect!
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:target) { ENV['CF_V2_TEST_TARGET'] }
|
16
|
+
let(:username) { ENV['CF_V2_TEST_USER'] }
|
17
|
+
let(:password) { ENV['CF_V2_TEST_PASSWORD'] }
|
18
|
+
let(:organization) { ENV['CF_V2_TEST_ORGANIZATION'] }
|
19
|
+
|
20
|
+
let(:client) do
|
21
|
+
client = CFoundry::V2::Client.new("https://#{target}")
|
22
|
+
client.login(username, password)
|
23
|
+
client
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:new_user) { Faker::Internet.email }
|
27
|
+
|
28
|
+
before do
|
29
|
+
Interact::Progress::Dots.start!
|
30
|
+
login
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
# TODO: do this when cf delete-user is implemented
|
35
|
+
#BlueShell::Runner.run("#{cf_bin} delete-user #{email}") do |runner|
|
36
|
+
# expect(runner).to say "Really delete user #{email}?>"
|
37
|
+
# runner.send_keys "y"
|
38
|
+
# expect(runner).to say "Deleting #{email}... OK"
|
39
|
+
#end
|
40
|
+
|
41
|
+
# TODO: not this.
|
42
|
+
client.login(new_user, "p")
|
43
|
+
user = client.current_user
|
44
|
+
guid = user.guid
|
45
|
+
client.login(username, password)
|
46
|
+
user.delete!
|
47
|
+
client.base.uaa.delete_user(guid)
|
48
|
+
|
49
|
+
logout
|
50
|
+
Interact::Progress::Dots.stop!
|
51
|
+
end
|
52
|
+
|
53
|
+
it "creates a new user" do
|
54
|
+
BlueShell::Runner.run("#{cf_bin} create-user") do |runner|
|
55
|
+
expect(runner).to say "Email>"
|
56
|
+
runner.send_keys new_user
|
57
|
+
|
58
|
+
expect(runner).to say "Password>"
|
59
|
+
runner.send_keys "p"
|
60
|
+
|
61
|
+
expect(runner).to say "Verify Password>"
|
62
|
+
runner.send_keys "p"
|
63
|
+
|
64
|
+
expect(runner).to say "Creating user... OK"
|
65
|
+
expect(runner).to say "Adding user to #{organization}... OK"
|
66
|
+
end
|
67
|
+
|
68
|
+
BlueShell::Runner.run("#{cf_bin} login #{new_user} --password p") do |runner|
|
69
|
+
expect(runner).to say "Authenticating... OK"
|
70
|
+
|
71
|
+
expect(runner).to say "Space>"
|
72
|
+
runner.send_keys "1"
|
73
|
+
|
74
|
+
expect(runner).to say /Switching to space.*OK/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
$stderr.puts 'Skipping v2 integration specs; please provide environment variables'
|
80
|
+
end
|
data/spec/features/login_spec.rb
CHANGED
@@ -19,50 +19,16 @@ if ENV['CF_V2_RUN_INTEGRATION']
|
|
19
19
|
FileUtils.rm_rf File.expand_path(CF::CONFIG_DIR)
|
20
20
|
WebMock.allow_net_connect!
|
21
21
|
Interact::Progress::Dots.start!
|
22
|
+
login
|
22
23
|
end
|
23
24
|
|
24
25
|
after do
|
25
26
|
`#{cf_bin} delete #{app} -f -o --no-script`
|
27
|
+
logout
|
26
28
|
Interact::Progress::Dots.stop!
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'pushes a simple sinatra app using defaults as much as possible' do
|
30
|
-
BlueShell::Runner.run("#{cf_bin} logout") do |runner|
|
31
|
-
runner.wait_for_exit
|
32
|
-
end
|
33
|
-
|
34
|
-
BlueShell::Runner.run("#{cf_bin} target http://#{target}") do |runner|
|
35
|
-
expect(runner).to say %r{Setting target to http://#{target}... OK}
|
36
|
-
end
|
37
|
-
|
38
|
-
BlueShell::Runner.run("#{cf_bin} login") do |runner|
|
39
|
-
expect(runner).to say %r{target: https?://#{target}}
|
40
|
-
|
41
|
-
expect(runner).to say "Email>"
|
42
|
-
runner.send_keys username
|
43
|
-
|
44
|
-
expect(runner).to say "Password>"
|
45
|
-
runner.send_keys password
|
46
|
-
|
47
|
-
expect(runner).to say "Authenticating... OK"
|
48
|
-
|
49
|
-
expect(runner).to say(
|
50
|
-
"Organization>" => proc {
|
51
|
-
runner.send_keys organization
|
52
|
-
expect(runner).to say /Switching to organization .*\.\.\. OK/
|
53
|
-
},
|
54
|
-
"Switching to organization" => proc {}
|
55
|
-
)
|
56
|
-
|
57
|
-
expect(runner).to say(
|
58
|
-
"Space>" => proc {
|
59
|
-
runner.send_keys "1"
|
60
|
-
expect(runner).to say /Switching to space .*\.\.\. OK/
|
61
|
-
},
|
62
|
-
"Switching to space" => proc {}
|
63
|
-
)
|
64
|
-
end
|
65
|
-
|
66
32
|
BlueShell::Runner.run("#{cf_bin} app #{app}") do |runner|
|
67
33
|
expect(runner).to say "Unknown app '#{app}'."
|
68
34
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "webmock/rspec"
|
3
|
+
require "ffaker"
|
4
|
+
|
5
|
+
if ENV['CF_V2_RUN_INTEGRATION']
|
6
|
+
describe 'A new user tries to use CF against v2 production', :ruby19 => true do
|
7
|
+
before(:all) do
|
8
|
+
WebMock.allow_net_connect!
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:all) do
|
12
|
+
WebMock.disable_net_connect!
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:target) { ENV['CF_V2_TEST_TARGET'] }
|
16
|
+
let(:username) { ENV['CF_V2_TEST_USER'] }
|
17
|
+
let(:password) { ENV['CF_V2_TEST_PASSWORD'] }
|
18
|
+
let(:organization) { ENV['CF_V2_TEST_ORGANIZATION'] }
|
19
|
+
let(:space) { ENV['CF_V2_TEST_SPACE'] }
|
20
|
+
|
21
|
+
let(:client) do
|
22
|
+
client = CFoundry::V2::Client.new("https://#{target}")
|
23
|
+
client.login(username, password)
|
24
|
+
client
|
25
|
+
end
|
26
|
+
|
27
|
+
before do
|
28
|
+
Interact::Progress::Dots.start!
|
29
|
+
login
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
logout
|
34
|
+
Interact::Progress::Dots.stop!
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can get space info" do
|
38
|
+
BlueShell::Runner.run("#{cf_bin} space #{space} --no-quiet") do |runner|
|
39
|
+
expect(runner).to say("#{space}:\n")
|
40
|
+
expect(runner).to say("organization: #{organization}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "can create, switch, rename, and delete spaces" do
|
45
|
+
new_space = "test-space-#{rand(10000)}"
|
46
|
+
new_space_two = "test-space-renamed-#{rand(10000)}"
|
47
|
+
BlueShell::Runner.run("#{cf_bin} create-space #{new_space}") do |runner|
|
48
|
+
expect(runner).to say("Creating space #{new_space}... OK")
|
49
|
+
end
|
50
|
+
|
51
|
+
BlueShell::Runner.run("#{cf_bin} switch-space #{new_space}") do |runner|
|
52
|
+
expect(runner).to say("Switching to space #{new_space}... OK")
|
53
|
+
end
|
54
|
+
|
55
|
+
BlueShell::Runner.run("#{cf_bin} rename-space #{new_space} #{new_space_two}") do |runner|
|
56
|
+
expect(runner).to say("Renaming to #{new_space_two}... OK")
|
57
|
+
end
|
58
|
+
|
59
|
+
BlueShell::Runner.run("#{cf_bin} delete-space #{new_space_two}") do |runner|
|
60
|
+
expect(runner).to say("Really delete")
|
61
|
+
runner.send_keys "y"
|
62
|
+
|
63
|
+
expect(runner).to say("Deleting space #{new_space_two}... OK")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "shows all the spaces in the org" do
|
68
|
+
BlueShell::Runner.run("#{cf_bin} spaces") do |runner|
|
69
|
+
expect(runner).to say(space)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -9,12 +9,17 @@ if ENV['CF_V2_RUN_INTEGRATION']
|
|
9
9
|
let(:space) { ENV['CF_V2_TEST_SPACE'] }
|
10
10
|
let(:space2) { "#{ENV['CF_V2_TEST_SPACE']}-2"}
|
11
11
|
let(:organization) { ENV['CF_V2_TEST_ORGANIZATION'] }
|
12
|
+
let(:organization_two) { ENV['CF_V2_TEST_ORGANIZATION_TWO'] }
|
13
|
+
|
14
|
+
let(:created_space_1) { "space-#{rand(10000)}"}
|
15
|
+
let(:created_space_2) { "space-#{rand(10000)}"}
|
12
16
|
|
13
17
|
before do
|
14
18
|
Interact::Progress::Dots.start!
|
15
19
|
end
|
16
20
|
|
17
21
|
after do
|
22
|
+
logout
|
18
23
|
Interact::Progress::Dots.stop!
|
19
24
|
end
|
20
25
|
|
@@ -31,37 +36,29 @@ if ENV['CF_V2_RUN_INTEGRATION']
|
|
31
36
|
end
|
32
37
|
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
BlueShell::Runner.run("#{cf_bin} login") do |runner|
|
39
|
-
expect(runner).to say "Email>"
|
40
|
-
runner.send_keys username
|
41
|
-
|
42
|
-
expect(runner).to say "Password>"
|
43
|
-
runner.send_keys password
|
44
|
-
|
45
|
-
expect(runner).to say "Authenticating... OK"
|
46
|
-
end
|
39
|
+
context "with created spaces in the second org" do
|
40
|
+
it "can switch organizations and spaces" do
|
41
|
+
login
|
47
42
|
|
48
|
-
|
49
|
-
|
43
|
+
BlueShell::Runner.run("#{cf_bin} target -o #{organization_two}") do |runner|
|
44
|
+
expect(runner).to say("Switching to organization #{organization_two}")
|
45
|
+
expect(runner).to say("Space>")
|
46
|
+
runner.send_keys space2
|
50
47
|
|
51
|
-
|
52
|
-
runner.send_keys space2
|
48
|
+
expect(runner).to say(/Switching to space #{space2}/)
|
53
49
|
|
54
|
-
|
55
|
-
|
50
|
+
runner.wait_for_exit
|
51
|
+
end
|
56
52
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
BlueShell::Runner.run("#{cf_bin} target -s #{space}") do |runner|
|
54
|
+
expect(runner).to say("Switching to space #{space}")
|
55
|
+
runner.wait_for_exit
|
56
|
+
end
|
61
57
|
|
62
|
-
|
63
|
-
|
64
|
-
|
58
|
+
BlueShell::Runner.run("#{cf_bin} target -s #{space2}") do |runner|
|
59
|
+
expect(runner).to say("Switching to space #{space2}")
|
60
|
+
runner.wait_for_exit
|
61
|
+
end
|
65
62
|
end
|
66
63
|
end
|
67
64
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
module FeaturesHelper
|
2
|
+
def login
|
3
|
+
set_target
|
4
|
+
logout
|
5
|
+
|
6
|
+
cmd = "yes 1 | #{cf_bin} login #{username} --password #{password} -o #{organization}"
|
7
|
+
cmd += " -s #{space}" if respond_to?(:space)
|
8
|
+
BlueShell::Runner.run(cmd) do |runner|
|
9
|
+
runner.wait_for_exit 30
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def logout
|
14
|
+
BlueShell::Runner.run("#{cf_bin} logout")
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_target
|
18
|
+
BlueShell::Runner.run("#{cf_bin} target #{target}")
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.1.
|
4
|
+
version: 0.6.1.rc6
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-04-
|
13
|
+
date: 2013-04-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json_pure
|
@@ -335,6 +335,7 @@ files:
|
|
335
335
|
- spec/cf/cli/service/unbind_spec.rb
|
336
336
|
- spec/cf/cli/space/base_spec.rb
|
337
337
|
- spec/cf/cli/space/create_spec.rb
|
338
|
+
- spec/cf/cli/space/delete_spec.rb
|
338
339
|
- spec/cf/cli/space/rename_spec.rb
|
339
340
|
- spec/cf/cli/space/space_spec.rb
|
340
341
|
- spec/cf/cli/space/spaces_spec.rb
|
@@ -348,13 +349,16 @@ files:
|
|
348
349
|
- spec/cf/cli/user/register_spec.rb
|
349
350
|
- spec/cf/cli_spec.rb
|
350
351
|
- spec/features/account_lifecycle_spec.rb
|
352
|
+
- spec/features/create_user_spec.rb
|
351
353
|
- spec/features/login_spec.rb
|
352
354
|
- spec/features/push_flow_spec.rb
|
355
|
+
- spec/features/space_spec.rb
|
353
356
|
- spec/features/switching_targets_spec.rb
|
354
357
|
- spec/spec_helper.rb
|
355
358
|
- spec/support/cli_helper.rb
|
356
359
|
- spec/support/config_helper.rb
|
357
360
|
- spec/support/fake_home_dir.rb
|
361
|
+
- spec/support/features_helper.rb
|
358
362
|
- spec/support/interact_helper.rb
|
359
363
|
- spec/support/shared_examples/errors.rb
|
360
364
|
- spec/support/shared_examples/input.rb
|
@@ -373,7 +377,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
373
377
|
version: '0'
|
374
378
|
segments:
|
375
379
|
- 0
|
376
|
-
hash:
|
380
|
+
hash: -4051249069800079153
|
377
381
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
378
382
|
none: false
|
379
383
|
requirements:
|
@@ -418,6 +422,7 @@ test_files:
|
|
418
422
|
- spec/cf/cli/service/unbind_spec.rb
|
419
423
|
- spec/cf/cli/space/base_spec.rb
|
420
424
|
- spec/cf/cli/space/create_spec.rb
|
425
|
+
- spec/cf/cli/space/delete_spec.rb
|
421
426
|
- spec/cf/cli/space/rename_spec.rb
|
422
427
|
- spec/cf/cli/space/space_spec.rb
|
423
428
|
- spec/cf/cli/space/spaces_spec.rb
|
@@ -431,13 +436,16 @@ test_files:
|
|
431
436
|
- spec/cf/cli/user/register_spec.rb
|
432
437
|
- spec/cf/cli_spec.rb
|
433
438
|
- spec/features/account_lifecycle_spec.rb
|
439
|
+
- spec/features/create_user_spec.rb
|
434
440
|
- spec/features/login_spec.rb
|
435
441
|
- spec/features/push_flow_spec.rb
|
442
|
+
- spec/features/space_spec.rb
|
436
443
|
- spec/features/switching_targets_spec.rb
|
437
444
|
- spec/spec_helper.rb
|
438
445
|
- spec/support/cli_helper.rb
|
439
446
|
- spec/support/config_helper.rb
|
440
447
|
- spec/support/fake_home_dir.rb
|
448
|
+
- spec/support/features_helper.rb
|
441
449
|
- spec/support/interact_helper.rb
|
442
450
|
- spec/support/shared_examples/errors.rb
|
443
451
|
- spec/support/shared_examples/input.rb
|