producer-core 0.2.5 → 0.2.6

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 (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