Improve CLI usage:

* Rewrite arguments parsing with OptionParser;
* Allow processing of multiple recipes.
This commit is contained in:
Thibault Jouan
2014-09-26 14:16:26 +00:00
parent 85ee79ab88
commit 7a7c8379ff
4 changed files with 124 additions and 90 deletions

View File

@@ -5,28 +5,42 @@ module Producer::Core
include ExitHelpers
include FixturesHelpers
let(:recipe_file) { fixture_path_for 'recipes/some_recipe.rb' }
let(:options) { [] }
let(:recipe_file) { fixture_path_for 'recipes/some_recipe.rb' }
let(:arguments) { [*options, recipe_file] }
subject(:cli) { described_class.new(arguments) }
describe '.run!' do
let(:stderr) { StringIO.new }
subject(:run!) { described_class.run! arguments, stderr: stderr }
subject(:run!) { described_class.run! arguments }
context 'when given arguments are invalid' do
it 'builds a new CLI instance with given arguments' do
expect(described_class)
.to receive(:new).with(arguments, anything).and_call_original
run!
end
it 'parses new CLI instance arguments' do
expect_any_instance_of(described_class).to receive :parse_arguments!
run!
end
it 'runs the new CLI instance' do
expect_any_instance_of(described_class).to receive :run
run!
end
context 'when no recipe is given' do
let(:arguments) { [] }
it 'exits with a return status of 64' do
expect { run! }.to raise_error(SystemExit) do |e|
expect(e.status).to eq 64
end
expect { described_class.run! arguments, stderr: StringIO.new }
.to raise_error(SystemExit) { |e| expect(e.status).to eq 64 }
end
it 'prints the usage on the error stream' do
trap_exit { run! }
expect(stderr.string).to match /\AUsage: .+/
expect { trap_exit { run! } }
.to output(/\AUsage: .+/).to_stderr
end
end
@@ -34,92 +48,95 @@ module Producer::Core
let(:recipe_file) { fixture_path_for 'recipes/raise.rb' }
it 'exits with a return status of 70' do
expect { run! }.to raise_error(SystemExit) do |e|
expect(e.status).to eq 70
end
expect { described_class.run! arguments, stderr: StringIO.new }
.to raise_error(SystemExit) { |e| expect(e.status).to eq 70 }
end
it 'prints exception name and message and the error stream' do
trap_exit { run! }
expect(stderr.string).to eq "RemoteCommandExecutionError: false\n"
expect { trap_exit { run! } }
.to output("RemoteCommandExecutionError: false\n").to_stderr
end
end
end
describe '#env' do
let(:stdin) { StringIO.new }
let(:stdout) { StringIO.new }
let(:stderr) { StringIO.new }
subject(:cli) { described_class.new(arguments,
stdin: stdin, stdout: stdout, stderr: stderr) }
it 'returns an env' do
describe '#initialize' do
it 'assigns an env' do
expect(cli.env).to be_an Env
end
it 'assigns CLI stdin as the env input' do
expect(cli.env.input).to be stdin
expect(cli.env.input).to be cli.stdin
end
it 'assigns CLI stdout as the env output' do
expect(cli.env.output).to be stdout
expect(cli.env.output).to be cli.stdout
end
it 'assigns CLI stderr as the env error output' do
expect(cli.env.error_output).to be stderr
expect(cli.env.error_output).to be cli.stderr
end
end
describe '#parse_arguments!' do
context 'with options' do
let(:options) { %w[-v -t some_host.example] }
let(:options) { %w[-v -t some_host.example] }
before { cli.parse_arguments! }
it 'removes options from arguments' do
cli.parse_arguments!
expect(cli.arguments).to eq [recipe_file]
end
it 'removes options from arguments' do
expect(cli.arguments).to eq [recipe_file]
end
context 'with verbose option' do
let(:options) { %w[-v] }
context 'verbose' do
let(:options) { %w[-v] }
it 'enables env verbose mode' do
expect(cli.env).to be_verbose
end
end
context 'dry run' do
let(:options) { %w[-n] }
it 'enables env dry run mode' do
expect(cli.env).to be_dry_run
end
end
context 'target' do
let(:options) { %w[-t some_host.example] }
it 'assigns the given target to the env' do
expect(cli.env.target).to eq 'some_host.example'
end
it 'enables env verbose mode' do
cli.parse_arguments!
expect(cli.env).to be_verbose
end
end
context 'without arguments' do
context 'with dry run option' do
let(:options) { %w[-n] }
it 'enables env dry run mode' do
cli.parse_arguments!
expect(cli.env).to be_dry_run
end
end
context 'with target option' do
let(:options) { %w[-t some_host.example] }
it 'assigns the given target to the env' do
cli.parse_arguments!
expect(cli.env.target).to eq 'some_host.example'
end
end
context 'with combined options' do
let(:options) { %w[-vn]}
it 'handles combined options' do
cli.parse_arguments!
expect(cli.env).to be_verbose.and be_dry_run
end
end
context 'when no arguments remains after parsing' do
let(:arguments) { [] }
it 'raises the argument error exception' do
expect { cli.parse_arguments! }.to raise_error described_class::ArgumentError
it 'raises an error' do
expect { cli.parse_arguments! }
.to raise_error described_class::ArgumentError
end
end
end
describe '#run' do
it 'processes recipe tasks with a worker' do
it 'processes recipes tasks with a worker' do
worker = instance_spy Worker
cli.run worker: worker
expect(worker).to have_received(:process).with cli.recipe.tasks
expect(worker).to have_received(:process)
.with all be_an_instance_of Task
end
it 'cleans up the env' do
@@ -128,12 +145,9 @@ module Producer::Core
end
end
describe '#recipe' do
it 'returns the evaluated recipe' do
expect(cli.recipe.tasks).to match [
an_object_having_attributes(name: :some_task),
an_object_having_attributes(name: :another_task)
]
describe '#evaluate_recipes' do
it 'returns the evaluated recipes' do
expect(cli.evaluate_recipes).to all be_an_instance_of Recipe
end
end
end