cfndk 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +23 -14
  3. data/.gitignore +0 -1
  4. data/.rspec_parallel +6 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +811 -0
  7. data/README.md +122 -10
  8. data/cfndk.gemspec +1 -0
  9. data/lib/cfndk/change_set_command.rb +97 -0
  10. data/lib/cfndk/command.rb +15 -181
  11. data/lib/cfndk/config_file_loadable.rb +13 -0
  12. data/lib/cfndk/global_config.rb +15 -0
  13. data/lib/cfndk/key_pair.rb +7 -4
  14. data/lib/cfndk/key_pair_command.rb +53 -0
  15. data/lib/cfndk/key_pairs.rb +2 -1
  16. data/lib/cfndk/logger.rb +1 -1
  17. data/lib/cfndk/stack.rb +382 -103
  18. data/lib/cfndk/stack_command.rb +110 -0
  19. data/lib/cfndk/stacks.rb +40 -14
  20. data/lib/cfndk/subcommand_help_returnable.rb +16 -0
  21. data/lib/cfndk/version.rb +1 -1
  22. data/lib/cfndk.rb +6 -0
  23. data/skel/cfndk.yml +4 -0
  24. data/spec/cfndk_change_set_create_spec.rb +436 -0
  25. data/spec/cfndk_change_set_destroy_spec.rb +160 -0
  26. data/spec/cfndk_change_set_execute_spec.rb +179 -0
  27. data/spec/cfndk_change_set_report_spec.rb +107 -0
  28. data/spec/cfndk_change_set_spec.rb +37 -0
  29. data/spec/cfndk_create_spec.rb +56 -141
  30. data/spec/cfndk_destroy_spec.rb +4 -2
  31. data/spec/cfndk_keypiar_spec.rb +11 -9
  32. data/spec/cfndk_report_spec.rb +3 -1
  33. data/spec/cfndk_spec.rb +5 -3
  34. data/spec/cfndk_stack_create_spec.rb +454 -0
  35. data/spec/cfndk_stack_destroy_spec.rb +161 -0
  36. data/spec/cfndk_stack_report_spec.rb +181 -0
  37. data/spec/cfndk_stack_spec.rb +6 -1146
  38. data/spec/cfndk_stack_update_spec.rb +467 -0
  39. data/spec/spec_helper.rb +4 -1
  40. data/spec/support/aruba.rb +1 -0
  41. metadata +42 -2
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'CFnDK', type: :aruba do
4
+ before(:each) { set_environment_variable('AWS_REGION', ENV['AWS_REGION']) }
5
+ before(:each) { set_environment_variable('AWS_PROFILE', ENV['AWS_PROFILE']) }
6
+ before(:each) { set_environment_variable('AWS_ACCESS_KEY_ID', ENV["AWS_ACCESS_KEY_ID#{ENV['TEST_ENV_NUMBER']}"]) }
7
+ before(:each) { set_environment_variable('AWS_SECRET_ACCESS_KEY', ENV["AWS_SECRET_ACCESS_KEY#{ENV['TEST_ENV_NUMBER']}"]) }
8
+ describe 'bin/cfndk' do
9
+ before(:each) { setup_aruba }
10
+ let(:file) { 'cfndk.yml' }
11
+ let(:file2) { 'cfndk2.yml' }
12
+ let(:uuid) { '38437346-c75c-47c5-83b4-d504f85e275b' }
13
+ let(:change_set_uuid) { '38437346-c75c-47c5-83b4-d504f85e27ca' }
14
+
15
+ describe 'changeset' do
16
+ describe 'destroy', destroy: true do
17
+ context 'when -f without cfndk.yml' do
18
+ before(:each) { run_command('cfndk changeset destroy -f') }
19
+ it 'displays file does not exist error and status code = 1' do
20
+ aggregate_failures do
21
+ expect(last_command_started).to have_exit_status(1)
22
+ expect(last_command_started).to have_output(/ERROR RuntimeError: File does not exist./)
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'with cfndk2.yml' do
28
+ yaml = <<-"YAML"
29
+ stacks:
30
+ YAML
31
+ before(:each) { write_file(file2, yaml) }
32
+ context 'when -c cfndk2.yml -f and empty stacks' do
33
+ before(:each) { run_command("cfndk changeset destroy -c=#{file2} -f") }
34
+ it 'displays empty stack log' do
35
+ aggregate_failures do
36
+ expect(last_command_started).to be_successfully_executed
37
+ expect(last_command_started).to have_output(/INFO destroy.../)
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'when --config-path cfndk2.yml -f and empty stacks' do
43
+ before(:each) { run_command("cfndk changeset destroy --config-path=#{file2} -f") }
44
+ it 'displays empty stack log' do
45
+ aggregate_failures do
46
+ expect(last_command_started).to be_successfully_executed
47
+ expect(last_command_started).to have_output(/INFO destroy.../)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'with cfndk.yml' do
54
+ context 'when cfndk.yml is empty' do
55
+ before(:each) { touch(file) }
56
+ before(:each) { run_command('cfndk changeset destroy -f') }
57
+ it 'displays File is empty error and status code = 1' do
58
+ aggregate_failures do
59
+ expect(last_command_started).to have_exit_status(1)
60
+ expect(last_command_started).to have_output(/ERROR File is empty./)
61
+ end
62
+ end
63
+ end
64
+ context 'when enter no' do
65
+ yaml = <<-"YAML"
66
+ keypairs:
67
+ Test1:
68
+ stacks:
69
+ Test:
70
+ template_file: vpc.yaml
71
+ parameter_input: vpc.json
72
+ parameters:
73
+ VpcName: sample<%= append_uuid%>
74
+ timeout_in_minutes: 2
75
+ YAML
76
+ before(:each) { write_file(file, yaml) }
77
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
78
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
79
+ before(:each) { run_command('cfndk changeset destroy') }
80
+ before(:each) { type('no') }
81
+ it 'displays confirm message and cancel message and status code = 2' do
82
+ aggregate_failures do
83
+ expect(last_command_started).to have_exit_status(2)
84
+ expect(last_command_started).to have_output(/INFO destroy../)
85
+ expect(last_command_started).to have_output(%r{Are you sure you want to destroy\? \(y/n\)})
86
+ expect(last_command_started).to have_output(/INFO destroy command was canceled/)
87
+ expect(last_command_started).not_to have_output(/INFO deleting change set:/)
88
+ expect(last_command_started).not_to have_output(/INFO deleted change set:/)
89
+ expect(last_command_started).not_to have_output(/INFO do not delete keypair: Test1$/)
90
+ expect(last_command_started).not_to have_output(/INFO do not delete stack: Test$/)
91
+ end
92
+ end
93
+ end
94
+ context 'when enter yes' do
95
+ context 'when keyparis and stacks do not exist' do
96
+ yaml = <<-"YAML"
97
+ keypairs:
98
+ Test1:
99
+ stacks:
100
+ Test:
101
+ template_file: vpc.yaml
102
+ parameter_input: vpc.json
103
+ parameters:
104
+ VpcName: sample<%= append_uuid%>
105
+ timeout_in_minutes: 2
106
+ YAML
107
+ before(:each) { write_file(file, yaml) }
108
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
109
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
110
+ before(:each) { run_command('cfndk destroy -f') }
111
+ before(:each) { stop_all_commands }
112
+ before(:each) { run_command('cfndk changeset destroy') }
113
+ before(:each) { type('yes') }
114
+ before(:each) { stop_all_commands }
115
+ it 'displays confirm message and do not delete message' do
116
+ aggregate_failures do
117
+ expect(last_command_started).to have_exit_status(1)
118
+ expect(last_command_started).to have_output(/INFO destroy../)
119
+ expect(last_command_started).to have_output(%r{Are you sure you want to destroy\? \(y/n\)})
120
+ expect(last_command_started).to have_output(/INFO deleting change set: Test$/)
121
+ expect(last_command_started).to have_output(/ERROR Aws::CloudFormation::Errors::ValidationError: Stack \[Test\] does not exist$/)
122
+ end
123
+ end
124
+ end
125
+ context 'when keyparis and stacks exist' do
126
+ yaml = <<-"YAML"
127
+ keypairs:
128
+ Test1:
129
+ stacks:
130
+ Test:
131
+ template_file: vpc.yaml
132
+ parameter_input: vpc.json
133
+ parameters:
134
+ VpcName: sample<%= append_uuid%>
135
+ timeout_in_minutes: 2
136
+ YAML
137
+ before(:each) { write_file(file, yaml) }
138
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
139
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
140
+ before(:each) { run_command_and_stop('cfndk changeset create') }
141
+ before(:each) { run_command('cfndk changeset destroy') }
142
+ before(:each) { type('yes') }
143
+ before(:each) { stop_all_commands }
144
+ it 'displays confirm message and delete message' do
145
+ aggregate_failures do
146
+ expect(last_command_started).to be_successfully_executed
147
+ expect(last_command_started).to have_output(/INFO destroy../)
148
+ expect(last_command_started).to have_output(%r{Are you sure you want to destroy\? \(y/n\)})
149
+ expect(last_command_started).not_to have_output(/INFO deleted keypair: Test1$/)
150
+ expect(last_command_started).to have_output(/INFO deleted change set: Test$/)
151
+ end
152
+ end
153
+ after(:each) { run_command('cfndk destroy -f') }
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'CFnDK', type: :aruba do
4
+ before(:each) { set_environment_variable('AWS_REGION', ENV['AWS_REGION']) }
5
+ before(:each) { set_environment_variable('AWS_PROFILE', ENV['AWS_PROFILE']) }
6
+ before(:each) { set_environment_variable('AWS_ACCESS_KEY_ID', ENV["AWS_ACCESS_KEY_ID#{ENV['TEST_ENV_NUMBER']}"]) }
7
+ before(:each) { set_environment_variable('AWS_SECRET_ACCESS_KEY', ENV["AWS_SECRET_ACCESS_KEY#{ENV['TEST_ENV_NUMBER']}"]) }
8
+ describe 'bin/cfndk' do
9
+ before(:each) { setup_aruba }
10
+ let(:file) { 'cfndk.yml' }
11
+ let(:file2) { 'cfndk2.yml' }
12
+ let(:uuid) { '38437346-c75c-47c5-83b4-d504f85e275b' }
13
+ let(:change_set_uuid) { '38437346-c75c-47c5-83b4-d504f85e27ca' }
14
+
15
+ describe 'changeset' do
16
+ describe 'execute', execute: true do
17
+ context 'without cfndk.yml' do
18
+ before(:each) { run_command('cfndk changeset execute') }
19
+ it 'displays file does not exist error and status code = 1' do
20
+ aggregate_failures do
21
+ expect(last_command_started).to have_exit_status(1)
22
+ expect(last_command_started).to have_output(/ERROR RuntimeError: File does not exist./)
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'with cfndk2.yml' do
28
+ yaml = <<-"YAML"
29
+ keypairs:
30
+ YAML
31
+ before(:each) { write_file(file2, yaml) }
32
+ context 'when -c cfndk2.yml and empty stacks' do
33
+ before(:each) { run_command("cfndk changeset execute -c=#{file2}") }
34
+ it 'displays empty stack log' do
35
+ aggregate_failures do
36
+ expect(last_command_started).to be_successfully_executed
37
+ expect(last_command_started).to have_output(/INFO execute.../)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'with cfndk.yml' do
44
+ context 'when cfndk.yml is empty' do
45
+ before(:each) { touch(file) }
46
+ before(:each) { run_command('cfndk changeset execute') }
47
+ it 'displays File is empty error and status code = 1' do
48
+ aggregate_failures do
49
+ expect(last_command_started).to have_exit_status(1)
50
+ expect(last_command_started).to have_output(/ERROR File is empty./)
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'with stacks:' do
56
+ context 'without stack' do
57
+ before(:each) { write_file(file, 'stacks:') }
58
+ before(:each) { run_command('cfndk changeset execute') }
59
+ it 'displays create log' do
60
+ aggregate_failures do
61
+ expect(last_command_started).to be_successfully_executed
62
+ expect(last_command_started).to have_output(/INFO execute.../)
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'with a stack' do
68
+ yaml = <<-"YAML"
69
+ stacks:
70
+ Test:
71
+ template_file: vpc.yaml
72
+ parameter_input: vpc.json
73
+ timeout_in_minutes: 3
74
+ YAML
75
+ before(:each) { write_file(file, yaml) }
76
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
77
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
78
+ before(:each) { run_command_and_stop('cfndk changeset create') }
79
+ before(:each) { run_command('cfndk changeset execute') }
80
+ it 'displays executed log' do
81
+ aggregate_failures do
82
+ expect(last_command_started).to be_successfully_executed
83
+ expect(last_command_started).to have_output(/INFO created stack: Test$/)
84
+ end
85
+ end
86
+ after(:each) { run_command('cfndk destroy -f') }
87
+ end
88
+ context 'with two stacks' do
89
+ yaml = <<-"YAML"
90
+ stacks:
91
+ Test:
92
+ template_file: vpc.yaml
93
+ parameter_input: vpc.json
94
+ timeout_in_minutes: 2
95
+ Test2:
96
+ template_file: sg.yaml
97
+ parameter_input: sg.json
98
+ depends:
99
+ - Test
100
+ YAML
101
+
102
+ before(:each) { write_file(file, yaml) }
103
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
104
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
105
+ before(:each) { copy('%/sg.yaml', 'sg.yaml') }
106
+ before(:each) { copy('%/sg.json', 'sg.json') }
107
+ before(:each) { run_command_and_stop('cfndk changeset create') }
108
+ before(:each) { run_command('cfndk changeset execute') }
109
+ it 'displays executed logs' do
110
+ aggregate_failures do
111
+ expect(last_command_started).to be_successfully_executed
112
+ expect(last_command_started).to have_output(/INFO execute change set: Test$/)
113
+ expect(last_command_started).to have_output(/INFO execute change set: Test2$/)
114
+ expect(last_command_started).to have_output(/INFO created stack: Test$/)
115
+ expect(last_command_started).to have_output(/INFO created stack: Test2$/)
116
+ end
117
+ end
118
+ after(:each) { run_command('cfndk destroy -f') }
119
+ end
120
+ context 'when invalid dependency', dependency: true do
121
+ yaml = <<-"YAML"
122
+ stacks:
123
+ Test:
124
+ template_file: vpc.yaml
125
+ parameter_input: vpc.json
126
+ timeout_in_minutes: 2
127
+ depends:
128
+ - Test2
129
+ Test2:
130
+ template_file: sg.yaml
131
+ parameter_input: sg.json
132
+ YAML
133
+
134
+ before(:each) { write_file(file, yaml) }
135
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
136
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
137
+ before(:each) { copy('%/sg.yaml', 'sg.yaml') }
138
+ before(:each) { copy('%/sg.json', 'sg.json') }
139
+ before(:each) { run_command_and_stop('cfndk changeset create') }
140
+ before(:each) { run_command('cfndk changeset execute') }
141
+ it 'displays executed logs' do
142
+ aggregate_failures do
143
+ expect(last_command_started).to have_exit_status(1)
144
+ expect(last_command_started).to have_output(/INFO execute change set: Test2$/)
145
+ expect(last_command_started).to have_output(/ERROR Aws::Waiters::Errors::FailureStateError: stopped waiting, encountered a failure state$/)
146
+ end
147
+ end
148
+ after(:each) { run_command('cfndk destroy -f') }
149
+ end
150
+ context 'when success with capabilities', capabilities: true do
151
+ yaml = <<-"YAML"
152
+ stacks:
153
+ Test:
154
+ template_file: iam.yaml
155
+ parameter_input: iam.json
156
+ capabilities:
157
+ - CAPABILITY_NAMED_IAM
158
+ timeout_in_minutes: 3
159
+ YAML
160
+
161
+ before(:each) { write_file(file, yaml) }
162
+ before(:each) { copy('%/iam.yaml', 'iam.yaml') }
163
+ before(:each) { copy('%/iam.json', 'iam.json') }
164
+ before(:each) { run_command_and_stop('cfndk changeset create') }
165
+ before(:each) { run_command('cfndk changeset execute') }
166
+ it do
167
+ aggregate_failures do
168
+ expect(last_command_started).to be_successfully_executed
169
+ expect(last_command_started).to have_output(/INFO created stack: Test$/)
170
+ end
171
+ end
172
+ after(:each) { run_command('cfndk destroy -f') }
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'CFnDK', type: :aruba do
4
+ before(:each) { set_environment_variable('AWS_REGION', ENV['AWS_REGION']) }
5
+ before(:each) { set_environment_variable('AWS_PROFILE', ENV['AWS_PROFILE']) }
6
+ before(:each) { set_environment_variable('AWS_ACCESS_KEY_ID', ENV["AWS_ACCESS_KEY_ID#{ENV['TEST_ENV_NUMBER']}"]) }
7
+ before(:each) { set_environment_variable('AWS_SECRET_ACCESS_KEY', ENV["AWS_SECRET_ACCESS_KEY#{ENV['TEST_ENV_NUMBER']}"]) }
8
+ describe 'bin/cfndk' do
9
+ before(:each) { setup_aruba }
10
+ let(:file) { 'cfndk.yml' }
11
+ let(:file2) { 'cfndk2.yml' }
12
+ let(:uuid) { '38437346-c75c-47c5-83b4-d504f85e275b' }
13
+ let(:change_set_uuid) { '38437346-c75c-47c5-83b4-d504f85e27ca' }
14
+
15
+ describe 'changeset' do
16
+ describe 'report', report: true do
17
+ context 'without cfndk.yml' do
18
+ before(:each) { run_command('cfndk changeset report') }
19
+ it 'displays file does not exist error and status code = 1' do
20
+ aggregate_failures do
21
+ expect(last_command_started).to have_exit_status(1)
22
+ expect(last_command_started).to have_output(/ERROR RuntimeError: File does not exist./)
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'with cfndk.yml' do
28
+ context 'when cfndk.yml is empty' do
29
+ before(:each) { touch(file) }
30
+ before(:each) { run_command('cfndk changeset report') }
31
+ it 'displays File is empty error and status code = 1' do
32
+ aggregate_failures do
33
+ expect(last_command_started).to have_exit_status(1)
34
+ expect(last_command_started).to have_output(/ERROR File is empty./)
35
+ end
36
+ end
37
+ end
38
+
39
+ context 'with stacks:' do
40
+ context 'without stack' do
41
+ before(:each) { write_file(file, 'stacks:') }
42
+ before(:each) { run_command('cfndk changeset report') }
43
+ it 'displays report log' do
44
+ aggregate_failures do
45
+ expect(last_command_started).to be_successfully_executed
46
+ expect(last_command_started).to have_output(/INFO report.../)
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'with a stack' do
52
+ yaml = <<-"YAML"
53
+ stacks:
54
+ Test:
55
+ template_file: vpc.yaml
56
+ parameter_input: vpc.json
57
+ timeout_in_minutes: 3
58
+ YAML
59
+ before(:each) { write_file(file, yaml) }
60
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
61
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
62
+ before(:each) { run_command_and_stop('cfndk changeset create') }
63
+ before(:each) { run_command('cfndk changeset report') }
64
+ it 'displays report log' do
65
+ aggregate_failures do
66
+ expect(last_command_started).to be_successfully_executed
67
+ expect(last_command_started).to have_output(/INFO change set: Test$/)
68
+ end
69
+ end
70
+ after(:each) { run_command('cfndk destroy -f') }
71
+ end
72
+ context 'with two stacks' do
73
+ yaml = <<-"YAML"
74
+ stacks:
75
+ Test:
76
+ template_file: vpc.yaml
77
+ parameter_input: vpc.json
78
+ timeout_in_minutes: 2
79
+ Test2:
80
+ template_file: sg.yaml
81
+ parameter_input: sg.json
82
+ depends:
83
+ - Test
84
+ YAML
85
+
86
+ before(:each) { write_file(file, yaml) }
87
+ before(:each) { copy('%/vpc.yaml', 'vpc.yaml') }
88
+ before(:each) { copy('%/vpc.json', 'vpc.json') }
89
+ before(:each) { copy('%/sg.yaml', 'sg.yaml') }
90
+ before(:each) { copy('%/sg.json', 'sg.json') }
91
+ before(:each) { run_command_and_stop('cfndk changeset create') }
92
+ before(:each) { run_command('cfndk changeset report') }
93
+ it 'displays report logs' do
94
+ aggregate_failures do
95
+ expect(last_command_started).to be_successfully_executed
96
+ expect(last_command_started).to have_output(/INFO change set: Test$/)
97
+ expect(last_command_started).to have_output(/INFO change set: Test2$/)
98
+ end
99
+ end
100
+ after(:each) { run_command('cfndk destroy -f') }
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'CFnDK', type: :aruba do
4
+ before(:each) { set_environment_variable('AWS_REGION', ENV['AWS_REGION']) }
5
+ before(:each) { set_environment_variable('AWS_PROFILE', ENV['AWS_PROFILE']) }
6
+ before(:each) { set_environment_variable('AWS_ACCESS_KEY_ID', ENV["AWS_ACCESS_KEY_ID#{ENV['TEST_ENV_NUMBER']}"]) }
7
+ before(:each) { set_environment_variable('AWS_SECRET_ACCESS_KEY', ENV["AWS_SECRET_ACCESS_KEY#{ENV['TEST_ENV_NUMBER']}"]) }
8
+ describe 'bin/cfndk' do
9
+ before(:each) { setup_aruba }
10
+ let(:file) { 'cfndk.yml' }
11
+ let(:file2) { 'cfndk2.yml' }
12
+ let(:uuid) { '38437346-c75c-47c5-83b4-d504f85e275b' }
13
+ let(:change_set_uuid) { '38437346-c75c-47c5-83b4-d504f85e27ca' }
14
+
15
+ describe 'changeset' do
16
+ context 'without subcommand', help: true do
17
+ before(:each) { run_command('cfndk changeset') }
18
+ it 'displays help and status code = 2' do
19
+ aggregate_failures do
20
+ expect(last_command_started).to have_exit_status(2)
21
+ end
22
+ end
23
+ end
24
+
25
+ describe 'help', help: true do
26
+ context 'without subsubcommand' do
27
+ before(:each) { run_command('cfndk changeset help') }
28
+ it 'displays help and status code = 2' do
29
+ aggregate_failures do
30
+ expect(last_command_started).to have_exit_status(2)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end