Add more coverage on ssh connector on connect behavior. Remove subject usage.

This commit is contained in:
Chris Roberts 2016-08-11 15:39:09 -07:00
parent 9d4962c836
commit f122afeed2
1 changed files with 255 additions and 30 deletions

View File

@ -32,6 +32,8 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
ui: ui ui: ui
) )
end end
# Subject instance to test
let(:communicator){ @communicator ||= described_class.new(machine) }
# Underlying net-ssh connection mock # Underlying net-ssh connection mock
let(:connection) { double("connection") } let(:connection) { double("connection") }
# Base net-ssh connection channel mock # Base net-ssh connection channel mock
@ -43,11 +45,11 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
# Core shell command used when starting command connection # Core shell command used when starting command connection
let(:core_shell_cmd) { "bash -l" } let(:core_shell_cmd) { "bash -l" }
# Marker used for flagging start of output # Marker used for flagging start of output
let(:command_garbage_marker) { subject.class.const_get(:CMD_GARBAGE_MARKER) } let(:command_garbage_marker) { communicator.class.const_get(:CMD_GARBAGE_MARKER) }
# Start marker output when PTY is enabled # Start marker output when PTY is enabled
let(:pty_delim_start) { subject.class.const_get(:PTY_DELIM_START) } let(:pty_delim_start) { communicator.class.const_get(:PTY_DELIM_START) }
# End marker output when PTY is enabled # End marker output when PTY is enabled
let(:pty_delim_end) { subject.class.const_get(:PTY_DELIM_END) } let(:pty_delim_end) { communicator.class.const_get(:PTY_DELIM_END) }
# Command output returned on stdout # Command output returned on stdout
let(:command_stdout_data) { '' } let(:command_stdout_data) { '' }
# Command output returned on stderr # Command output returned on stderr
@ -55,13 +57,6 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
# Mock for net-ssh scp # Mock for net-ssh scp
let(:scp) { double("scp") } let(:scp) { double("scp") }
# Return mocked net-ssh connection during setup
subject do
described_class.new(machine).tap do |comm|
allow(comm).to receive(:retryable).and_return(connection)
end
end
# Setup for commands using the net-ssh connection. This can be reused where needed # Setup for commands using the net-ssh connection. This can be reused where needed
# by providing to `before` # by providing to `before`
connection_setup = proc do connection_setup = proc do
@ -81,6 +76,8 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
and_yield(command_channel, '').and_return channel and_yield(command_channel, '').and_return channel
expect(command_channel).to receive(:on_request).with('exit-status'). expect(command_channel).to receive(:on_request).with('exit-status').
and_yield(nil, exit_data) and_yield(nil, exit_data)
# Return mocked net-ssh connection during setup
allow(communicator).to receive(:retryable).and_return(connection)
end end
describe ".wait_for_ready" do describe ".wait_for_ready" do
@ -100,7 +97,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
it "retries ssh_info until ready" do it "retries ssh_info until ready" do
# retries are every 0.5 so buffer the timeout just a hair over # retries are every 0.5 so buffer the timeout just a hair over
expect(subject.wait_for_ready(0.6)).to eq(true) expect(communicator.wait_for_ready(0.6)).to eq(true)
end end
end end
end end
@ -109,7 +106,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
describe ".ready?" do describe ".ready?" do
before(&connection_setup) before(&connection_setup)
it "returns true if shell test is successful" do it "returns true if shell test is successful" do
expect(subject.ready?).to be_true expect(communicator.ready?).to be_true
end end
context "with an invalid shell test" do context "with an invalid shell test" do
@ -118,7 +115,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end end
it "returns raises SSHInvalidShell error" do it "returns raises SSHInvalidShell error" do
expect{ subject.ready? }.to raise_error Vagrant::Errors::SSHInvalidShell expect{ communicator.ready? }.to raise_error Vagrant::Errors::SSHInvalidShell
end end
end end
end end
@ -127,13 +124,13 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
before(&connection_setup) before(&connection_setup)
it "runs valid command and returns successful status code" do it "runs valid command and returns successful status code" do
expect(command_channel).to receive(:send_data).with(/ls \/\n/) expect(command_channel).to receive(:send_data).with(/ls \/\n/)
expect(subject.execute("ls /")).to eq(0) expect(communicator.execute("ls /")).to eq(0)
end end
it "prepends UUID output to command for garbage removal" do it "prepends UUID output to command for garbage removal" do
expect(command_channel).to receive(:send_data). expect(command_channel).to receive(:send_data).
with("printf '#{command_garbage_marker}'\nls /\n") with("printf '#{command_garbage_marker}'\nls /\n")
expect(subject.execute("ls /")).to eq(0) expect(communicator.execute("ls /")).to eq(0)
end end
context "with command returning an error" do context "with command returning an error" do
@ -141,12 +138,12 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
it "raises error when exit-code is non-zero" do it "raises error when exit-code is non-zero" do
expect(command_channel).to receive(:send_data).with(/ls \/\n/) expect(command_channel).to receive(:send_data).with(/ls \/\n/)
expect{ subject.execute("ls /") }.to raise_error(Vagrant::Errors::VagrantError) expect{ communicator.execute("ls /") }.to raise_error(Vagrant::Errors::VagrantError)
end end
it "returns exit-code when exit-code is non-zero and error check is disabled" do it "returns exit-code when exit-code is non-zero and error check is disabled" do
expect(command_channel).to receive(:send_data).with(/ls \/\n/) expect(command_channel).to receive(:send_data).with(/ls \/\n/)
expect(subject.execute("ls /", error_check: false)).to eq(1) expect(communicator.execute("ls /", error_check: false)).to eq(1)
end end
end end
@ -158,7 +155,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
it "removes any garbage output prepended to command output" do it "removes any garbage output prepended to command output" do
stdout = '' stdout = ''
expect( expect(
subject.execute("ls /") do |type, data| communicator.execute("ls /") do |type, data|
stdout << data stdout << data
end end
).to eq(0) ).to eq(0)
@ -179,7 +176,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end end
it "requests pty for connection" do it "requests pty for connection" do
expect(subject.execute("ls")).to eq(0) expect(communicator.execute("ls")).to eq(0)
end end
context "with sudo enabled" do context "with sudo enabled" do
@ -190,7 +187,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end end
it "wraps command in elevated shell when sudo is true" do it "wraps command in elevated shell when sudo is true" do
expect(subject.execute("ls", sudo: true)).to eq(0) expect(communicator.execute("ls", sudo: true)).to eq(0)
end end
end end
end end
@ -203,7 +200,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end end
it "wraps command in elevated shell when sudo is true" do it "wraps command in elevated shell when sudo is true" do
expect(subject.execute("ls", sudo: true)).to eq(0) expect(communicator.execute("ls", sudo: true)).to eq(0)
end end
end end
end end
@ -212,7 +209,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
before(&connection_setup) before(&connection_setup)
context "with exit code as zero" do context "with exit code as zero" do
it "returns true" do it "returns true" do
expect(subject.test("ls")).to be_true expect(communicator.test("ls")).to be_true
end end
end end
@ -222,20 +219,20 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end end
it "returns false" do it "returns false" do
expect(subject.test("/bin/false")).to be_false expect(communicator.test("/bin/false")).to be_false
end end
end end
end end
describe ".upload" do describe ".upload" do
before do before do
expect(subject).to receive(:scp_connect).and_yield(scp) expect(communicator).to receive(:scp_connect).and_yield(scp)
end end
it "uploads a directory if local path is a directory" do it "uploads a directory if local path is a directory" do
Dir.mktmpdir('vagrant-test') do |dir| Dir.mktmpdir('vagrant-test') do |dir|
expect(scp).to receive(:upload!).with(dir, '/destination', recursive: true) expect(scp).to receive(:upload!).with(dir, '/destination', recursive: true)
subject.upload(dir, '/destination') communicator.upload(dir, '/destination')
end end
end end
@ -243,7 +240,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
file = Tempfile.new('vagrant-test') file = Tempfile.new('vagrant-test')
begin begin
expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file') expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file')
subject.upload(file.path, '/destination/file') communicator.upload(file.path, '/destination/file')
ensure ensure
file.delete file.delete
end end
@ -254,7 +251,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
begin begin
expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file'). expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file').
and_raise("Permission denied") and_raise("Permission denied")
expect{ subject.upload(file.path, '/destination/file') }.to( expect{ communicator.upload(file.path, '/destination/file') }.to(
raise_error(Vagrant::Errors::SCPPermissionDenied) raise_error(Vagrant::Errors::SCPPermissionDenied)
) )
ensure ensure
@ -267,7 +264,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
begin begin
expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file'). expect(scp).to receive(:upload!).with(instance_of(File), '/destination/file').
and_raise("Some other error") and_raise("Some other error")
expect{ subject.upload(file.path, '/destination/file') }.to raise_error(RuntimeError) expect{ communicator.upload(file.path, '/destination/file') }.to raise_error(RuntimeError)
ensure ensure
file.delete file.delete
end end
@ -276,12 +273,240 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
describe ".download" do describe ".download" do
before do before do
expect(subject).to receive(:scp_connect).and_yield(scp) expect(communicator).to receive(:scp_connect).and_yield(scp)
end end
it "calls scp to download file" do it "calls scp to download file" do
expect(scp).to receive(:download!).with('/path/from', '/path/to') expect(scp).to receive(:download!).with('/path/from', '/path/to')
subject.download('/path/from', '/path/to') communicator.download('/path/from', '/path/to')
end
end
describe ".connect" do
it "cannot be called directly" do
expect{ communicator.connect }.to raise_error(NoMethodError)
end
context "with default configuration" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: nil,
port: nil,
private_key_path: nil,
username: nil,
password: nil,
keys_only: true,
paranoid: false
)
end
it "has keys_only enabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
keys_only: true
)
).and_return(true)
communicator.send(:connect)
end
it "has paranoid disabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
paranoid: false
)
).and_return(true)
communicator.send(:connect)
end
it "does not include any private key paths" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_excluding(
keys: anything
)
).and_return(true)
communicator.send(:connect)
end
it "includes `none` and `hostbased` auth methods" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
auth_methods: ["none", "hostbased"]
)
).and_return(true)
communicator.send(:connect)
end
end
context "with keys_only disabled and paranoid enabled" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: nil,
port: nil,
private_key_path: nil,
username: nil,
password: nil,
keys_only: false,
paranoid: true
)
end
it "has keys_only enabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
keys_only: false
)
).and_return(true)
communicator.send(:connect)
end
it "has paranoid disabled" do
expect(Net::SSH).to receive(:start).with(
nil, nil, hash_including(
paranoid: true
)
).and_return(true)
communicator.send(:connect)
end
end
context "with host and port configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: nil,
username: nil,
password: nil,
keys_only: true,
paranoid: false
)
end
it "specifies configured host" do
expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, anything)
communicator.send(:connect)
end
it "has port defined" do
expect(Net::SSH).to receive(:start).with("127.0.0.1", anything, hash_including(port: 2222))
communicator.send(:connect)
end
end
context "with private_key_path configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: ['/priv/key/path'],
username: nil,
password: nil,
keys_only: true,
paranoid: false
)
end
it "includes private key paths" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
keys: ["/priv/key/path"]
)
).and_return(true)
communicator.send(:connect)
end
it "includes `publickey` auth method" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
auth_methods: ["none", "hostbased", "publickey"]
)
).and_return(true)
communicator.send(:connect)
end
end
context "with username and password configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: nil,
username: 'vagrant',
password: 'vagrant',
keys_only: true,
paranoid: false
)
end
it "has username defined" do
expect(Net::SSH).to receive(:start).with(anything, 'vagrant', anything).and_return(true)
communicator.send(:connect)
end
it "has password defined" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
password: 'vagrant'
)
).and_return(true)
communicator.send(:connect)
end
it "includes `password` auth method" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
auth_methods: ["none", "hostbased", "password"]
)
).and_return(true)
communicator.send(:connect)
end
end
context "with password and private_key_path configured" do
before do
expect(machine).to receive(:ssh_info).and_return(
host: '127.0.0.1',
port: 2222,
private_key_path: ['/priv/key/path'],
username: 'vagrant',
password: 'vagrant',
keys_only: true,
paranoid: false
)
end
it "has password defined" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
password: 'vagrant'
)
).and_return(true)
communicator.send(:connect)
end
it "includes private key paths" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
keys: ["/priv/key/path"]
)
).and_return(true)
communicator.send(:connect)
end
it "includes `publickey` and `password` auth methods" do
expect(Net::SSH).to receive(:start).with(
anything, anything, hash_including(
auth_methods: ["none", "hostbased", "publickey", "password"]
)
).and_return(true)
communicator.send(:connect)
end
end end
end end
end end