producer-core 0.5.7 → 0.5.8

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/Rakefile +6 -3
  4. data/config/cucumber.yaml +2 -1
  5. data/features/cli/debug.feature +7 -1
  6. data/features/cli/dry_run.feature +5 -0
  7. data/features/cli/error_reporting.feature +1 -1
  8. data/features/cli/usage.feature +6 -10
  9. data/features/cli/verbose.feature +5 -0
  10. data/features/recipe/errors.feature +9 -0
  11. data/features/ssh/config.feature +1 -1
  12. data/features/steps/environment_steps.rb +3 -0
  13. data/lib/producer/core/action.rb +5 -7
  14. data/lib/producer/core/actions/file_append.rb +1 -1
  15. data/lib/producer/core/actions/file_writer.rb +1 -2
  16. data/lib/producer/core/actions/mkdir.rb +4 -4
  17. data/lib/producer/core/actions/shell_command.rb +1 -1
  18. data/lib/producer/core/actions/yaml_writer.rb +1 -2
  19. data/lib/producer/core/cli.rb +48 -37
  20. data/lib/producer/core/condition.rb +3 -3
  21. data/lib/producer/core/env.rb +5 -4
  22. data/lib/producer/core/error_formatter.rb +13 -14
  23. data/lib/producer/core/errors.rb +1 -0
  24. data/lib/producer/core/logger_formatter.rb +3 -4
  25. data/lib/producer/core/prompter.rb +4 -4
  26. data/lib/producer/core/recipe/file_evaluator.rb +3 -3
  27. data/lib/producer/core/recipe.rb +10 -10
  28. data/lib/producer/core/remote/environment.rb +1 -1
  29. data/lib/producer/core/remote/fs.rb +10 -10
  30. data/lib/producer/core/remote.rb +13 -7
  31. data/lib/producer/core/task.rb +15 -9
  32. data/lib/producer/core/template.rb +9 -10
  33. data/lib/producer/core/test.rb +1 -1
  34. data/lib/producer/core/testing/aruba_program_wrapper.rb +2 -2
  35. data/lib/producer/core/testing/cucumber/output_steps.rb +12 -0
  36. data/lib/producer/core/testing/cucumber/recipe_steps.rb +5 -5
  37. data/lib/producer/core/testing/cucumber/remote_steps.rb +8 -8
  38. data/lib/producer/core/testing/cucumber.rb +3 -3
  39. data/lib/producer/core/testing/mock_remote.rb +27 -25
  40. data/lib/producer/core/tests/condition_test.rb +1 -1
  41. data/lib/producer/core/tests/file_contains.rb +2 -3
  42. data/lib/producer/core/tests/file_eq.rb +1 -2
  43. data/lib/producer/core/tests/file_match.rb +1 -2
  44. data/lib/producer/core/tests/has_env.rb +3 -6
  45. data/lib/producer/core/tests/has_executable.rb +1 -1
  46. data/lib/producer/core/tests/shell_command_status.rb +1 -1
  47. data/lib/producer/core/version.rb +1 -1
  48. data/lib/producer/core/worker.rb +7 -7
  49. data/lib/producer/core.rb +0 -1
  50. data/producer-core.gemspec +2 -2
  51. data/spec/producer/core/cli_spec.rb +29 -4
  52. data/spec/producer/core/remote_spec.rb +9 -1
  53. data/spec/producer/core/testing/mock_remote_spec.rb +5 -4
  54. metadata +10 -6
@@ -4,41 +4,41 @@ module Producer
4
4
  class FS
5
5
  attr_reader :sftp
6
6
 
7
- def initialize(sftp)
7
+ def initialize sftp
8
8
  @sftp = sftp
9
9
  end
10
10
 
11
- def dir?(path)
11
+ def dir? path
12
12
  sftp.stat!(path).directory?
13
13
  rescue Net::SFTP::StatusException
14
14
  false
15
15
  end
16
16
 
17
- def file?(path)
17
+ def file? path
18
18
  sftp.stat!(path).file?
19
19
  rescue Net::SFTP::StatusException
20
20
  false
21
21
  end
22
22
 
23
- def setstat(path, attributes)
23
+ def setstat path, attributes
24
24
  sftp.setstat! path, attributes
25
25
  end
26
26
 
27
- def chmod(path, mode)
27
+ def chmod path, mode
28
28
  setstat path, permissions: mode
29
29
  end
30
30
 
31
- def mkdir(path, attributes = {})
32
- ret = sftp.mkdir! path, attributes
31
+ def mkdir path, attributes = {}
32
+ sftp.mkdir! path, attributes
33
33
  end
34
34
 
35
- def file_read(path)
36
- sftp.file.open(path) { |f| content = f.read }
35
+ def file_read path
36
+ sftp.file.open(path) { |f| f.read }
37
37
  rescue Net::SFTP::StatusException
38
38
  nil
39
39
  end
40
40
 
41
- def file_write(path, content)
41
+ def file_write path, content
42
42
  sftp.file.open path, 'w' do |f|
43
43
  f.write content
44
44
  end
@@ -4,12 +4,18 @@ module Producer
4
4
  attr_reader :hostname
5
5
  attr_writer :session
6
6
 
7
- def initialize(hostname)
7
+ def initialize hostname
8
8
  @hostname = hostname
9
9
  end
10
10
 
11
11
  def session
12
- @session ||= Net::SSH.start(@hostname, user_name)
12
+ @session ||= begin
13
+ if !@hostname
14
+ fail RemoteInvalidError,
15
+ "remote target is invalid: `#{@hostname.inspect}'"
16
+ end
17
+ Net::SSH.start(@hostname, user_name)
18
+ end
13
19
  end
14
20
 
15
21
  def config
@@ -24,18 +30,18 @@ module Producer
24
30
  @fs ||= Remote::FS.new(session.sftp.connect)
25
31
  end
26
32
 
27
- def execute(command, output = '', error_output = '')
33
+ def execute command, output = '', error_output = ''
28
34
  session.open_channel do |channel|
29
- channel.exec command do |ch, success|
30
- ch.on_data do |c, data|
35
+ channel.exec command do |ch, _success|
36
+ ch.on_data do |_c, data|
31
37
  output << data
32
38
  end
33
39
 
34
- ch.on_extended_data do |c, type, data|
40
+ ch.on_extended_data do |_c, _type, data|
35
41
  error_output << data
36
42
  end
37
43
 
38
- ch.on_request 'exit-status' do |c, data|
44
+ ch.on_request 'exit-status' do |_c, data|
39
45
  exit_status = data.read_long
40
46
  fail RemoteCommandExecutionError, command if exit_status != 0
41
47
  end
@@ -2,13 +2,13 @@ module Producer
2
2
  module Core
3
3
  class Task
4
4
  class << self
5
- def define_action(keyword, klass)
6
- define_method(keyword) do |*args|
5
+ def define_action keyword, klass
6
+ define_method keyword do |*args|
7
7
  @actions << klass.new(@env, *args)
8
8
  end
9
9
  end
10
10
 
11
- def evaluate(env, name, *args, &block)
11
+ def evaluate env, name, *args, &block
12
12
  new(env, name).tap { |o| o.instance_exec *args, &block }
13
13
  end
14
14
  end
@@ -32,7 +32,7 @@ module Producer
32
32
 
33
33
  attr_reader :name, :actions, :condition
34
34
 
35
- def initialize(env, name, actions = [], condition = true)
35
+ def initialize env, name, actions = [], condition = true
36
36
  @env = env
37
37
  @name = name
38
38
  @actions = actions
@@ -43,22 +43,28 @@ module Producer
43
43
  !!@condition
44
44
  end
45
45
 
46
- def condition(&block)
46
+ def condition &block
47
47
  @condition = Condition.evaluate(@env, &block) if block
48
48
  @condition
49
49
  end
50
50
 
51
- def task(name, *args, &block)
51
+ def task name, *args, &block
52
52
  @actions << self.class.evaluate(@env, name, *args, &block)
53
53
  end
54
54
 
55
- def ask(question, choices, prompter: Prompter.new(@env.input, @env.output))
56
- prompter.prompt(question, choices)
55
+ def ask question, choices, prompter: build_prompter
56
+ prompter.prompt question, choices
57
57
  end
58
58
 
59
- def template(path, variables = {})
59
+ def template path, variables = {}
60
60
  Template.new(path).render variables
61
61
  end
62
+
63
+ private
64
+
65
+ def build_prompter
66
+ Prompter.new(@env.input, @env.output)
67
+ end
62
68
  end
63
69
  end
64
70
  end
@@ -3,28 +3,27 @@ module Producer
3
3
  class Template
4
4
  SEARCH_PATH = 'templates'.freeze
5
5
 
6
- def initialize(path, search_path: SEARCH_PATH)
6
+ def initialize path, search_path: SEARCH_PATH
7
7
  @path = Pathname.new(path)
8
8
  @search_path = Pathname.new(search_path)
9
9
  end
10
10
 
11
- def render(variables = {})
11
+ def render variables = {}
12
12
  case (file_path = resolve_path).extname
13
- when '.yaml' then render_yaml file_path
14
- when '.erb' then render_erb file_path, variables
13
+ when '.yaml' then render_yaml file_path
14
+ when '.erb' then render_erb file_path, variables
15
15
  end
16
16
  end
17
17
 
18
+ private
18
19
 
19
- private
20
-
21
- def render_erb(file_path, variables = {})
20
+ def render_erb file_path, variables = {}
22
21
  tpl = ERB.new(File.read(file_path), nil, '-')
23
22
  tpl.filename = file_path.to_s
24
23
  tpl.result build_erb_binding variables
25
24
  end
26
25
 
27
- def render_yaml(file_path)
26
+ def render_yaml file_path
28
27
  YAML.load(File.read(file_path))
29
28
  end
30
29
 
@@ -36,11 +35,11 @@ module Producer
36
35
  end
37
36
  end
38
37
 
39
- def resolve_suffix(path)
38
+ def resolve_suffix path
40
39
  Pathname.glob("#{path}.{erb,yaml}").first
41
40
  end
42
41
 
43
- def build_erb_binding(variables)
42
+ def build_erb_binding variables
44
43
  Object.new.instance_eval do |o|
45
44
  variables.each do |k, v|
46
45
  o.instance_variable_set "@#{k}", v
@@ -7,7 +7,7 @@ module Producer
7
7
 
8
8
  attr_reader :env, :arguments
9
9
 
10
- def initialize(env, *arguments, negated: false)
10
+ def initialize env, *arguments, negated: false
11
11
  @env = env
12
12
  @arguments = arguments
13
13
  @negated = negated
@@ -2,8 +2,8 @@ module Producer
2
2
  module Core
3
3
  module Testing
4
4
  class ArubaProgramWrapper
5
- def initialize(argv, stdin = $stdin, stdout = $stdout, stderr = $stderr,
6
- kernel = Kernel)
5
+ def initialize argv, stdin = $stdin, stdout = $stdout, stderr = $stderr,
6
+ kernel = Kernel
7
7
  @argv = argv
8
8
  @stdin = stdin
9
9
  @stdout = stdout
@@ -1,3 +1,15 @@
1
+ Then /^the output must contain exactly the usage$/ do
2
+ assert_exact_output <<-eoh, all_output
3
+ Usage: producer [options] [recipes] [-- recipe_argument...]
4
+
5
+ options:
6
+ -v, --verbose enable verbose mode
7
+ -d, --debug enable debug mode
8
+ -n, --dry-run enable dry run mode
9
+ -t, --target HOST target host
10
+ eoh
11
+ end
12
+
1
13
  Then /^the output must match \/([^\/]+)\/$/ do |pattern|
2
14
  assert_matching_output pattern, all_output
3
15
  end
@@ -1,13 +1,13 @@
1
- def run_recipe(remote: false, options: nil, check: false, rargv: nil)
1
+ def run_recipe remote: false, options: nil, check: false, rargv: nil
2
2
  command = %w[producer recipe.rb]
3
3
  case remote
4
- when :unknown then command += %w[-t unknown_host.test]
5
- when true then command += %w[-t some_host.test]
4
+ when :unknown then command += %w[-t unknown_host.test]
5
+ when true then command += %w[-t some_host.test]
6
6
  end
7
7
  command << options if options
8
8
  command << ['--', *rargv] if rargv
9
9
 
10
- with_env 'HOME' => File.expand_path(current_dir) do
10
+ with_environment 'HOME' => expand_path('.') do
11
11
  run_simple command.join(' '), false
12
12
  end
13
13
 
@@ -72,5 +72,5 @@ When /^I successfully execute the recipe with arguments "([^"]+)"$/ do |rargv|
72
72
  end
73
73
 
74
74
  When /^I execute the recipe interactively$/ do
75
- run_interactive 'producer recipe.rb'
75
+ run 'producer recipe.rb'
76
76
  end
@@ -1,11 +1,11 @@
1
- def stat_mode(path)
2
- in_current_dir do
1
+ def stat_mode path
2
+ cd '.' do
3
3
  ('%o' % [File::Stat.new(path).mode])[-4, 4]
4
4
  end
5
5
  end
6
6
 
7
7
  Given /^a remote directory named "([^"]+)"$/ do |path|
8
- create_dir path
8
+ create_directory path
9
9
  end
10
10
 
11
11
  Given /^a remote file named "([^"]+)"$/ do |file_name|
@@ -17,7 +17,7 @@ Given /^a remote file named "([^"]+)" with "([^"]+)"$/ do |file_name, content|
17
17
  end
18
18
 
19
19
  Then /^the remote directory "([^"]+)" must exist$/ do |path|
20
- check_directory_presence [path], true
20
+ expect(path).to be_an_existing_directory
21
21
  end
22
22
 
23
23
  Then /^the remote file "([^"]+)" must exist$/ do |path|
@@ -25,19 +25,19 @@ Then /^the remote file "([^"]+)" must exist$/ do |path|
25
25
  end
26
26
 
27
27
  Then /^the remote file "([^"]+)" must contain "([^"]+)"$/ do |path, content|
28
- check_file_content path, content, true
28
+ expect(path).to have_file_content Regexp.new(content)
29
29
  end
30
30
 
31
31
  Then /^the remote file "([^"]+)" must contain exactly "([^"]+)"$/ do |path, content|
32
- check_file_content path, content
32
+ expect(path).to have_file_content content
33
33
  end
34
34
 
35
35
  Then /^the remote file "([^"]+)" must contain exactly:$/ do |path, content|
36
- check_file_content path, content
36
+ expect(path).to have_file_content content
37
37
  end
38
38
 
39
39
  Then /^the remote file "([^"]+)" must match \/([^\/]+)\/$/ do |path, pattern|
40
- check_file_content path, /#{pattern}/, true
40
+ expect(path).to have_file_content /#{pattern}/
41
41
  end
42
42
 
43
43
  Then /^the remote file "([^"]+)" must have (\d+) mode$/ do |path, mode|
@@ -18,12 +18,12 @@ end
18
18
  # Use aruba "in process" optimization only for scenarios not tagged @exec.
19
19
  # We need a real process in a few cases: real program name, interactive usage…
20
20
  Before('@exec') do
21
- Aruba.process = Aruba::SpawnProcess
21
+ aruba.config.command_launcher = :spawn
22
22
  end
23
23
 
24
24
  Before('~@exec') do
25
- Aruba::InProcess.main_class = Producer::Core::Testing::ArubaProgramWrapper
26
- Aruba.process = Aruba::InProcess
25
+ aruba.config.command_launcher = :in_process
26
+ aruba.config.main_class = Producer::Core::Testing::ArubaProgramWrapper
27
27
  end
28
28
 
29
29
  # Enable cucumber-sshd "fast" mode (persists sshd across scenarios), and
@@ -3,35 +3,37 @@ module Producer
3
3
  module Testing
4
4
  class MockRemote < Remote
5
5
  def session
6
- fail 'no session for mock remote!'
6
+ fail RuntimeError, 'no session for mock remote!'
7
7
  end
8
8
 
9
- def execute(command, output = '', error_output = '')
10
- tokens = command.gsub(/\d?>.*/, '').split
11
- program = tokens.shift
9
+ def execute command, output = '', error_output = ''
10
+ program, *args = command.gsub(/\d?>.*/, '').split
11
+ program_output = command =~ />&2\z/ ? error_output : output
12
+ send "handle_program_#{program}", args, program_output
13
+ output
14
+ end
12
15
 
13
- case program
14
- when 'echo'
15
- out = tokens.join(' ') << "\n"
16
- if command =~ />&2\z/
17
- error_output << out
18
- else
19
- output << out
20
- end
21
- when 'true'
22
- output << ''
23
- when 'false'
24
- fail RemoteCommandExecutionError
25
- when 'type'
26
- fail RemoteCommandExecutionError unless %w[
27
- echo
28
- true
29
- false
30
- type
31
- ].include? tokens.first
32
- end
16
+ private
33
17
 
34
- output
18
+ def handle_program_echo args, output
19
+ output << args.join(' ') << "\n"
20
+ end
21
+
22
+ def handle_program_true _, output
23
+ output << ''
24
+ end
25
+
26
+ def handle_program_false *_
27
+ fail RemoteCommandExecutionError
28
+ end
29
+
30
+ def handle_program_type args, _
31
+ fail RemoteCommandExecutionError unless %w[
32
+ echo
33
+ true
34
+ false
35
+ type
36
+ ].include? args.first
35
37
  end
36
38
  end
37
39
  end
@@ -7,7 +7,7 @@ module Producer
7
7
  def_delegator :condition, :met?, :verify
8
8
 
9
9
  def condition
10
- Condition.evaluate(env, *condition_args, &condition_block)
10
+ Condition.evaluate env, *condition_args, &condition_block
11
11
  end
12
12
 
13
13
  def condition_args
@@ -7,11 +7,10 @@ module Producer
7
7
  content ? content.include?(arguments[1]) : false
8
8
  end
9
9
 
10
-
11
- private
10
+ private
12
11
 
13
12
  def file_content
14
- fs.file_read(arguments[0])
13
+ fs.file_read arguments[0]
15
14
  end
16
15
  end
17
16
  end
@@ -6,8 +6,7 @@ module Producer
6
6
  file_content ? file_content == expected_content : false
7
7
  end
8
8
 
9
-
10
- private
9
+ private
11
10
 
12
11
  def file_content
13
12
  @file_content ||= fs.file_read(arguments[0])
@@ -6,8 +6,7 @@ module Producer
6
6
  !!(file_content =~ arguments[1])
7
7
  end
8
8
 
9
-
10
- private
9
+ private
11
10
 
12
11
  def file_content
13
12
  fs.file_read(arguments[0]) or ''
@@ -4,15 +4,12 @@ module Producer
4
4
  class HasEnv < Test
5
5
  def verify
6
6
  case arguments.size
7
- when 1
8
- remote.environment.key? key
9
- when 2
10
- remote.environment[key] == arguments.last
7
+ when 1 then remote.environment.key? key
8
+ when 2 then remote.environment[key] == arguments.last
11
9
  end
12
10
  end
13
11
 
14
-
15
- private
12
+ private
16
13
 
17
14
  def key
18
15
  arguments.first.to_s.upcase
@@ -3,7 +3,7 @@ module Producer
3
3
  module Tests
4
4
  class HasExecutable < Test
5
5
  def verify
6
- remote.execute("type #{arguments.first}")
6
+ remote.execute "type #{arguments.first}"
7
7
  true
8
8
  rescue RemoteCommandExecutionError
9
9
  false
@@ -6,7 +6,7 @@ module Producer
6
6
  def_delegator :@arguments, :first, :command
7
7
 
8
8
  def verify
9
- remote.execute(command)
9
+ remote.execute command
10
10
  true
11
11
  rescue RemoteCommandExecutionError
12
12
  false
@@ -1,5 +1,5 @@
1
1
  module Producer
2
2
  module Core
3
- VERSION = '0.5.7'.freeze
3
+ VERSION = '0.5.8'.freeze
4
4
  end
5
5
  end
@@ -4,22 +4,23 @@ module Producer
4
4
  DRY_RUN_WARNING =
5
5
  'running in dry run mode, actions will NOT be applied'.freeze
6
6
 
7
- def initialize(env)
7
+ def initialize env
8
8
  @env = env
9
9
  end
10
10
 
11
- def process(tasks)
11
+ def process tasks
12
12
  @env.log DRY_RUN_WARNING, :warn if @env.dry_run?
13
13
 
14
14
  tasks.each { |t| process_task t }
15
15
  end
16
16
 
17
- def process_task(task, indent_level = 0)
17
+ def process_task task, indent_level = 0
18
18
  if task.condition_met?
19
19
  log "Task: `#{task}' applying...", indent_level
20
20
  task.actions.each do |e|
21
21
  case e
22
- when Task then process_task e, indent_level + 2
22
+ when Task
23
+ process_task e, indent_level + 2
23
24
  else
24
25
  log " action: #{e}", indent_level
25
26
  e.apply unless @env.dry_run?
@@ -30,10 +31,9 @@ module Producer
30
31
  end
31
32
  end
32
33
 
34
+ private
33
35
 
34
- private
35
-
36
- def log(message, indent_level)
36
+ def log message, indent_level
37
37
  message = [' ' * indent_level, message].join
38
38
  @env.log message
39
39
  end
data/lib/producer/core.rb CHANGED
@@ -8,7 +8,6 @@ require 'yaml'
8
8
  require 'net/ssh'
9
9
  require 'net/sftp'
10
10
 
11
-
12
11
  # task actions
13
12
  require 'producer/core/action'
14
13
  require 'producer/core/actions/echo'
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.add_development_dependency 'rspec', '~> 3.1'
24
24
  s.add_development_dependency 'cucumber', '~> 2.0'
25
- s.add_development_dependency 'aruba', '~> 0.5'
26
- s.add_development_dependency 'cucumber-sshd', '~> 1.1'
25
+ s.add_development_dependency 'aruba', '~> 0.8'
26
+ s.add_development_dependency 'cucumber-sshd', '~> 1.1.1'
27
27
  s.add_development_dependency 'rake', '~> 10.1'
28
28
  end
@@ -8,15 +8,16 @@ module Producer::Core
8
8
  let(:options) { [] }
9
9
  let(:recipe_file) { fixture_path_for 'recipes/some_recipe.rb' }
10
10
  let(:arguments) { [*options, recipe_file] }
11
+ let(:environment) { {} }
11
12
 
12
- subject(:cli) { described_class.new(arguments) }
13
+ subject(:cli) { described_class.new(arguments, environment) }
13
14
 
14
15
  describe '.run!' do
15
16
  subject(:run!) { described_class.run! arguments }
16
17
 
17
- it 'builds a new CLI instance with given arguments' do
18
+ it 'builds a new CLI instance with given arguments and environment' do
18
19
  expect(described_class)
19
- .to receive(:new).with(arguments, anything).and_call_original
20
+ .to receive(:new).with(arguments, ENV, anything).and_call_original
20
21
  run!
21
22
  end
22
23
 
@@ -54,7 +55,7 @@ module Producer::Core
54
55
 
55
56
  it 'prints a report to the error stream' do
56
57
  expect { trap_exit { run! } }
57
- .to output(/\ARemoteCommandExecutionError: false$/).to_stderr
58
+ .to output(/\ARecipeEvaluationError: false$/).to_stderr
58
59
  end
59
60
  end
60
61
  end
@@ -75,6 +76,30 @@ module Producer::Core
75
76
  it 'assigns CLI stderr as the env error output' do
76
77
  expect(cli.env.error_output).to be cli.stderr
77
78
  end
79
+
80
+ context 'when PRODUCER_VERBOSE environment variable is set' do
81
+ before { environment['PRODUCER_VERBOSE'] = 'yes' }
82
+
83
+ it 'enables env verbose mode' do
84
+ expect(cli.env).to be_verbose
85
+ end
86
+ end
87
+
88
+ context 'when PRODUCER_DEBUG environment variable is set' do
89
+ before { environment['PRODUCER_DEBUG'] = 'yes' }
90
+
91
+ it 'enables env debug mode' do
92
+ expect(cli.env).to be_debug
93
+ end
94
+ end
95
+
96
+ context 'when PRODUCER_DRYRUN environment variable is set' do
97
+ before { environment['PRODUCER_DRYRUN'] = 'yes' }
98
+
99
+ it 'enables env dry run mode' do
100
+ expect(cli.env).to be_dry_run
101
+ end
102
+ end
78
103
  end
79
104
 
80
105
  describe '#parse_arguments!' do
@@ -27,6 +27,14 @@ module Producer::Core
27
27
  allow(Net::SSH).to receive(:start) { Object.new }
28
28
  expect(remote.session).to be remote.session
29
29
  end
30
+
31
+ context 'when hostname is invalid' do
32
+ let(:hostname) { nil }
33
+
34
+ it 'raises RemoteInvalidError' do
35
+ expect { remote.session }.to raise_error RemoteInvalidError
36
+ end
37
+ end
30
38
  end
31
39
 
32
40
  describe '#config' do
@@ -128,7 +136,7 @@ module Producer::Core
128
136
 
129
137
  it 'raises an exception' do
130
138
  expect { remote.execute command }
131
- .to raise_error(RemoteCommandExecutionError)
139
+ .to raise_error RemoteCommandExecutionError
132
140
  end
133
141
 
134
142
  it 'includes the command in the exception message' do