producer-core 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/features/{actions/echo.feature → action_echo.feature} +1 -1
  3. data/features/{actions/file_append.feature → action_file_append.feature} +1 -1
  4. data/features/{actions/file_replace_content.feature → action_file_replace_content.feature} +11 -14
  5. data/features/{actions/file_write.feature → action_file_write.feature} +7 -12
  6. data/features/{actions/mkdir.feature → action_mkdir.feature} +8 -13
  7. data/features/{actions/sh.feature → action_sh.feature} +14 -14
  8. data/features/{cli/dry_run.feature → cli_dry_run.feature} +0 -0
  9. data/features/{cli/usage.feature → cli_usage.feature} +1 -0
  10. data/features/{cli/verbose.feature → cli_verbose.feature} +12 -23
  11. data/features/{recipes/ask.feature → recipe_ask.feature} +1 -0
  12. data/features/{recipes/macro.feature → recipe_macro.feature} +4 -4
  13. data/features/{recipes/source.feature → recipe_source.feature} +3 -1
  14. data/features/{recipes/target.feature → recipe_target.feature} +3 -1
  15. data/features/recipe_test_macro.feature +52 -0
  16. data/features/{recipes/registry.feature → registry.feature} +9 -4
  17. data/features/{ssh/config.feature → ssh_config.feature} +3 -1
  18. data/features/support/env.rb +32 -0
  19. data/features/support/env_aruba_timeout.rb +3 -0
  20. data/features/{tasks/condition.feature → task_condition.feature} +1 -1
  21. data/features/{tests/dir.feature → test_dir.feature} +1 -1
  22. data/features/{tests/env.feature → test_env.feature} +24 -34
  23. data/features/{tests/executable.feature → test_executable.feature} +12 -14
  24. data/features/{tests/file.feature → test_file.feature} +1 -1
  25. data/features/{tests/file_contains.feature → test_file_contains.feature} +1 -1
  26. data/features/{tests/negated_test.feature → test_negated_test.feature} +0 -0
  27. data/features/test_shell_command_status.feature +48 -0
  28. data/lib/producer/core/cli.rb +19 -15
  29. data/lib/producer/core/condition/dsl.rb +17 -8
  30. data/lib/producer/core/condition.rb +2 -2
  31. data/lib/producer/core/env.rb +6 -10
  32. data/lib/producer/core/errors.rb +2 -1
  33. data/lib/producer/core/recipe/dsl.rb +4 -0
  34. data/lib/producer/core/remote.rb +1 -1
  35. data/lib/producer/core/tests/condition_test.rb +23 -0
  36. data/lib/producer/core/version.rb +1 -1
  37. data/lib/producer/core.rb +1 -0
  38. data/spec/producer/core/cli_spec.rb +73 -44
  39. data/spec/producer/core/condition/dsl_spec.rb +33 -5
  40. data/spec/producer/core/env_spec.rb +41 -36
  41. data/spec/producer/core/recipe/dsl_spec.rb +10 -0
  42. data/spec/producer/core/remote_spec.rb +16 -7
  43. data/spec/producer/core/tests/condition_test_spec.rb +55 -0
  44. metadata +56 -55
  45. data/features/recipes/evaluation.feature +0 -9
  46. data/features/tasks/evaluation.feature +0 -11
  47. data/features/tasks/registry.feature +0 -13
  48. data/features/tests/shell_command_status.feature +0 -58
@@ -1,5 +1,5 @@
1
1
  module Producer
2
2
  module Core
3
- VERSION = '0.2.5'
3
+ VERSION = '0.2.6'.freeze
4
4
  end
5
5
  end
data/lib/producer/core.rb CHANGED
@@ -16,6 +16,7 @@ require 'producer/core/actions/file_writer'
16
16
 
17
17
  # condition tests (need to be defined before the condition DSL)
18
18
  require 'producer/core/test'
19
+ require 'producer/core/tests/condition_test'
19
20
  require 'producer/core/tests/file_contains'
20
21
  require 'producer/core/tests/has_dir'
21
22
  require 'producer/core/tests/has_env'
@@ -8,64 +8,89 @@ module Producer::Core
8
8
  let(:recipe_file) { fixture_path_for 'recipes/some_recipe.rb' }
9
9
  let(:options) { [] }
10
10
  let(:arguments) { [*options, recipe_file] }
11
+ let(:stdin) { StringIO.new}
11
12
  let(:stdout) { StringIO.new }
13
+ let(:stderr) { StringIO.new }
12
14
 
13
- subject(:cli) { CLI.new(arguments, stdout: stdout) }
15
+ subject(:cli) { CLI.new(arguments, stdin: stdin, stdout: stdout) }
14
16
 
15
17
  describe '.run!' do
16
- let(:cli) { double('cli').as_null_object }
17
- let(:output) { StringIO.new }
18
- subject(:run) { described_class.run! arguments, output: output }
18
+ let(:cli) { double('cli').as_null_object }
19
19
 
20
- it 'builds a new CLI with given arguments' do
21
- expect(described_class)
22
- .to receive(:new).with(arguments).and_call_original
20
+ subject(:run) do
21
+ described_class.run! arguments,
22
+ stdin: stdin,
23
+ stdout: stdout,
24
+ stderr: stderr
25
+ end
26
+
27
+ before { allow(described_class).to receive(:new) { cli } }
28
+
29
+ it 'builds a new CLI with given arguments and streams' do
30
+ expect(described_class).to receive(:new).with(arguments,
31
+ stdin: stdin,
32
+ stdout: stdout,
33
+ stderr: stderr
34
+ )
23
35
  run
24
36
  end
25
37
 
26
38
  it 'runs the CLI' do
27
- allow(described_class).to receive(:new) { cli }
28
39
  expect(cli).to receive :run
29
40
  run
30
41
  end
31
42
 
32
43
  it 'parses CLI arguments' do
33
- allow(described_class).to receive(:new) { cli }
34
44
  expect(cli).to receive :parse_arguments!
35
45
  run
36
46
  end
37
47
 
38
48
  context 'when an argument error is raised' do
39
49
  before do
40
- allow(described_class).to receive(:new) { cli }
41
50
  allow(cli).to receive(:parse_arguments!)
42
51
  .and_raise described_class::ArgumentError
43
52
  end
44
53
 
45
54
  it 'exits with a return status of 64' do
46
- expect { run }.to raise_error(SystemExit) { |e|
55
+ expect { run }.to raise_error(SystemExit) do |e|
47
56
  expect(e.status).to eq 64
48
- }
57
+ end
49
58
  end
50
59
 
51
- it 'prints the usage' do
60
+ it 'prints the usage on the error stream' do
52
61
  trap_exit { run }
53
- expect(output.string).to match /\AUsage: .+/
62
+ expect(stderr.string).to match /\AUsage: .+/
63
+ end
64
+ end
65
+
66
+ context 'when a runtime error is raised' do
67
+ before do
68
+ allow(cli).to receive(:run)
69
+ .and_raise RuntimeError, 'some message'
70
+ end
71
+
72
+ it 'exits with a return status of 70' do
73
+ expect { run }.to raise_error(SystemExit) do |e|
74
+ expect(e.status).to eq 70
75
+ end
76
+ end
77
+
78
+ it 'prints exception name and message and the error stream' do
79
+ trap_exit { run }
80
+ expect(stderr.string).to match /\ARuntimeError: some message/
54
81
  end
55
82
  end
56
83
  end
57
84
 
58
85
  describe '#initialize' do
59
- it 'assigns the env with CLI output' do
60
- expect(cli.env.output).to be stdout
61
- end
86
+ subject(:cli) { described_class.new(arguments) }
62
87
 
63
- context 'without options' do
64
- subject(:cli) { described_class.new(arguments) }
88
+ it 'assigns $stdin as the default standard input' do
89
+ expect(cli.stdin).to be $stdin
90
+ end
65
91
 
66
- it 'assigns $stdout as the default standard output' do
67
- expect(cli.stdout).to be $stdout
68
- end
92
+ it 'assigns $stdout as the default standard output' do
93
+ expect(cli.stdout).to be $stdout
69
94
  end
70
95
  end
71
96
 
@@ -81,6 +106,20 @@ module Producer::Core
81
106
  end
82
107
  end
83
108
 
109
+ describe '#env' do
110
+ it 'returns the assigned env' do
111
+ expect(cli.env).to be_an Env
112
+ end
113
+
114
+ it 'assigns CLI stdin as the env input' do
115
+ expect(cli.env.input).to be stdin
116
+ end
117
+
118
+ it 'assigns CLI stdout as the env output' do
119
+ expect(cli.env.output).to be stdout
120
+ end
121
+ end
122
+
84
123
  describe '#parse_arguments!' do
85
124
  context 'with options' do
86
125
  let(:options) { %w[-v] }
@@ -92,15 +131,15 @@ module Producer::Core
92
131
  end
93
132
 
94
133
  context 'verbose' do
95
- it 'sets env logger level to INFO' do
96
- expect(cli.env.log_level).to eq Logger::INFO
134
+ it 'enables env verbose mode' do
135
+ expect(cli.env).to be_verbose
97
136
  end
98
137
  end
99
138
 
100
139
  context 'dry run' do
101
140
  let(:options) { %w[-n] }
102
141
 
103
- it 'enables env dry run' do
142
+ it 'enables env dry run mode' do
104
143
  expect(cli.env).to be_dry_run
105
144
  end
106
145
  end
@@ -116,21 +155,12 @@ module Producer::Core
116
155
  end
117
156
 
118
157
  describe '#run' do
119
- it 'loads the recipe' do
120
- cli.run
121
- expect(cli.recipe).to be_a Recipe
122
- end
123
-
124
158
  it 'process recipe tasks with given worker' do
125
159
  worker = double 'worker'
126
- expect(worker).to receive(:process).with [kind_of(Task), kind_of(Task)]
127
- cli.run worker: worker
128
- end
129
- end
130
-
131
- describe '#env' do
132
- it 'returns an env' do
133
- expect(cli.env).to be_an Env
160
+ allow(cli).to receive(:worker) { worker }
161
+ expect(worker).to receive(:process)
162
+ .with([an_instance_of(Task), an_instance_of(Task)])
163
+ cli.run
134
164
  end
135
165
  end
136
166
 
@@ -141,19 +171,18 @@ module Producer::Core
141
171
  cli.load_recipe
142
172
  end
143
173
 
144
- it 'assigns the evaluated recipe' do
145
- cli.load_recipe
146
- expect(cli.recipe.tasks.count).to be 2
174
+ it 'returns the recipe' do
175
+ expect(cli.load_recipe).to be_a Recipe
147
176
  end
148
177
  end
149
178
 
150
- describe '#build_worker' do
179
+ describe '#worker' do
151
180
  it 'returns a worker' do
152
- expect(cli.build_worker).to be_a Worker
181
+ expect(cli.worker).to be_a Worker
153
182
  end
154
183
 
155
184
  it 'assigns the CLI env' do
156
- expect(cli.build_worker.env).to eq cli.env
185
+ expect(cli.worker.env).to eq cli.env
157
186
  end
158
187
  end
159
188
  end
@@ -22,9 +22,9 @@ module Producer::Core
22
22
  end
23
23
 
24
24
  describe '.define_test' do
25
- let(:some_test_class) { Test }
25
+ let(:some_test) { Test }
26
26
 
27
- before { described_class.define_test(:some_test, some_test_class) }
27
+ before { described_class.define_test(:some_test, some_test) }
28
28
 
29
29
  it 'defines a new test keyword' do
30
30
  expect(dsl).to respond_to :some_test
@@ -41,19 +41,38 @@ module Producer::Core
41
41
 
42
42
  it 'registers the test with current env' do
43
43
  dsl.some_test
44
- expect(dsl.tests.first.env).to be env
44
+ expect(dsl.tests.last.env).to be env
45
45
  end
46
46
 
47
47
  it 'registers the test with given arguments' do
48
48
  dsl.some_test :some, :args
49
- expect(dsl.tests.first.arguments).to eq [:some, :args]
49
+ expect(dsl.tests.last.arguments).to eq [:some, :args]
50
+ end
51
+
52
+ context 'when given test is callable' do
53
+ let(:some_test) { proc {} }
54
+
55
+ before { dsl.some_test }
56
+
57
+ it 'registers a condition test' do
58
+ expect(dsl.tests.last).to be_a Tests::ConditionTest
59
+ end
60
+
61
+ it 'registers the test with given block' do
62
+ expect(dsl.tests.last.condition_block).to be some_test
63
+ end
64
+
65
+ it 'registers the test with given arguments' do
66
+ dsl.some_test :some, :args
67
+ expect(dsl.tests.last.condition_args).to eq [:some, :args]
68
+ end
50
69
  end
51
70
  end
52
71
 
53
72
  context 'when a negated test keyword is called' do
54
73
  it 'registers a negated test' do
55
74
  dsl.no_some_test
56
- expect(dsl.tests.first).to be_negated
75
+ expect(dsl.tests.last).to be_negated
57
76
  end
58
77
  end
59
78
  end
@@ -81,6 +100,15 @@ module Producer::Core
81
100
  it 'returns the value returned by the assigned block' do
82
101
  expect(dsl.evaluate).to eq block.call
83
102
  end
103
+
104
+ context 'when arguments are given' do
105
+ let(:block) { proc { |e| throw e } }
106
+
107
+ it 'passes arguments as block parameters' do
108
+ expect { dsl.evaluate :some_argument }
109
+ .to throw_symbol :some_argument
110
+ end
111
+ end
84
112
  end
85
113
  end
86
114
  end
@@ -3,10 +3,10 @@ require 'spec_helper'
3
3
  module Producer::Core
4
4
  describe Env do
5
5
  let(:output) { StringIO.new }
6
- subject(:env) { Env.new(output: output) }
6
+ subject(:env) { described_class.new(output: output) }
7
7
 
8
8
  describe '#initialize' do
9
- it 'assigns $stdin as the default output' do
9
+ it 'assigns $stdin as the default input' do
10
10
  expect(env.input).to be $stdin
11
11
  end
12
12
 
@@ -18,12 +18,16 @@ module Producer::Core
18
18
  expect(env.registry).to be_empty
19
19
  end
20
20
 
21
+ it 'assigns verbose as false' do
22
+ expect(env.verbose).to be false
23
+ end
24
+
21
25
  it 'assigns dry run as false' do
22
26
  expect(env.dry_run).to be false
23
27
  end
24
28
 
25
29
  context 'when output is not given as argument' do
26
- subject(:env) { Env.new }
30
+ subject(:env) { described_class.new }
27
31
 
28
32
  it 'assigns $stdout as the default output' do
29
33
  expect(env.output).to be $stdout
@@ -60,31 +64,12 @@ module Producer::Core
60
64
  describe '#target' do
61
65
  let(:target) { double 'target' }
62
66
 
63
- it 'returns the defined target' do
67
+ it 'returns the assigned target' do
64
68
  env.target = target
65
69
  expect(env.target).to be target
66
70
  end
67
71
  end
68
72
 
69
- describe '#logger' do
70
- it 'returns a logger' do
71
- expect(env.logger).to be_a Logger
72
- end
73
-
74
- it 'uses env output' do
75
- env.logger.error 'some message'
76
- expect(output.string).to include 'some message'
77
- end
78
-
79
- it 'has a log level of ERROR' do
80
- expect(env.log_level).to eq Logger::ERROR
81
- end
82
-
83
- it 'uses our formatter' do
84
- expect(env.logger.formatter).to be_a LoggerFormatter
85
- end
86
- end
87
-
88
73
  describe '#remote' do
89
74
  it 'builds a Remote with the current target' do
90
75
  env.target = 'some_hostname.example'
@@ -118,6 +103,33 @@ module Producer::Core
118
103
  end
119
104
  end
120
105
 
106
+ describe '#logger' do
107
+ it 'returns a logger' do
108
+ expect(env.logger).to be_a Logger
109
+ end
110
+
111
+ it 'uses env output' do
112
+ env.logger.error 'some message'
113
+ expect(output.string).to include 'some message'
114
+ end
115
+
116
+ it 'has a log level of ERROR' do
117
+ expect(env.logger.level).to eq Logger::ERROR
118
+ end
119
+
120
+ it 'uses our formatter' do
121
+ expect(env.logger.formatter).to be_a LoggerFormatter
122
+ end
123
+
124
+ context 'when verbose mode is enabled' do
125
+ before { env.verbose = true }
126
+
127
+ it 'has a log level of INFO' do
128
+ expect(env.logger.level).to eq Logger::INFO
129
+ end
130
+ end
131
+ end
132
+
121
133
  describe '#log' do
122
134
  it 'logs an info message through the assigned logger' do
123
135
  expect(env.logger).to receive(:info).with 'message'
@@ -125,24 +137,17 @@ module Producer::Core
125
137
  end
126
138
  end
127
139
 
128
- describe '#log_level' do
129
- it 'returns the logger level' do
130
- expect(env.log_level).to eq env.logger.level
131
- end
132
- end
133
-
134
- describe '#log_level=' do
135
- it 'sets the logger level' do
136
- env.log_level = Logger::DEBUG
137
- expect(env.logger.level).to eq Logger::DEBUG
140
+ describe '#verbose?' do
141
+ it 'returns true when verbose is enabled' do
142
+ env.verbose = true
143
+ expect(env).to be_verbose
138
144
  end
139
145
  end
140
146
 
141
147
  describe '#dry_run?' do
142
- before { env.dry_run = true }
143
-
144
148
  it 'returns true when dry run is enabled' do
145
- expect(env.dry_run?).to be true
149
+ env.dry_run = true
150
+ expect(env).to be_dry_run
146
151
  end
147
152
  end
148
153
  end
@@ -102,6 +102,16 @@ module Producer::Core
102
102
  end
103
103
  end
104
104
 
105
+ describe '#test_macro' do
106
+ it 'defines the new test' do
107
+ condition_dsl = double 'condition dsl'
108
+ test_block = proc {}
109
+ expect(condition_dsl)
110
+ .to receive(:define_test).with(:some_test, test_block)
111
+ dsl.test_macro(:some_test, dsl: condition_dsl, &test_block)
112
+ end
113
+ end
114
+
105
115
  describe '#set' do
106
116
  it 'registers a key/value pair in env registry' do
107
117
  dsl.set :some_key, :some_value
@@ -106,14 +106,23 @@ module Producer::Core
106
106
  expect(output.string).to eq arguments
107
107
  end
108
108
 
109
- it 'raises an exception when the exit status code is not 0' do
110
- story_with_new_channel do |ch|
111
- ch.sends_exec command
112
- ch.gets_data arguments
113
- ch.gets_exit_status 1
109
+ context 'when command execution fails' do
110
+ before do
111
+ story_with_new_channel do |ch|
112
+ ch.sends_exec command
113
+ ch.gets_data arguments
114
+ ch.gets_exit_status 1
115
+ end
116
+ end
117
+
118
+ it 'raises an exception' do
119
+ expect { remote.execute command }
120
+ .to raise_error(RemoteCommandExecutionError)
121
+ end
122
+
123
+ it 'includes the command in the exception message' do
124
+ expect { remote.execute command }.to raise_error /#{command}/
114
125
  end
115
- expect { remote.execute command }
116
- .to raise_error(RemoteCommandExecutionError)
117
126
  end
118
127
  end
119
128