Merge branch 'ml-qa-git-protocol-v2-spec' into 'master'
Add e2e test of push over SSH using Git protocol v2 See merge request gitlab-org/gitlab-ce!22548
This commit is contained in:
commit
068e75f660
|
@ -109,6 +109,28 @@ module QA
|
||||||
known_hosts_file.close(true)
|
known_hosts_file.close(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def push_with_git_protocol(version, file_name, file_content, commit_message = 'Initial commit')
|
||||||
|
self.git_protocol = version
|
||||||
|
add_file(file_name, file_content)
|
||||||
|
commit(commit_message)
|
||||||
|
push_changes
|
||||||
|
|
||||||
|
fetch_supported_git_protocol
|
||||||
|
end
|
||||||
|
|
||||||
|
def git_protocol=(value)
|
||||||
|
raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2" unless %w[0 1 2].include?(value.to_s)
|
||||||
|
|
||||||
|
run("git config protocol.version #{value}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_supported_git_protocol
|
||||||
|
# ls-remote is one command known to respond to Git protocol v2 so we use
|
||||||
|
# it to get output including the version reported via Git tracing
|
||||||
|
output = run("git ls-remote #{uri}", "GIT_TRACE_PACKET=1")
|
||||||
|
output[/git< version (\d+)/, 1] || 'unknown'
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
|
attr_reader :uri, :username, :password, :known_hosts_file, :private_key_file
|
||||||
|
@ -117,8 +139,8 @@ module QA
|
||||||
!private_key_file.nil?
|
!private_key_file.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(command_str)
|
def run(command_str, *extra_env)
|
||||||
command = [env_vars, command_str, '2>&1'].compact.join(' ')
|
command = [env_vars, *extra_env, command_str, '2>&1'].compact.join(' ')
|
||||||
Runtime::Logger.debug "Git: command=[#{command}]"
|
Runtime::Logger.debug "Git: command=[#{command}]"
|
||||||
|
|
||||||
output, _ = Open3.capture2(command)
|
output, _ = Open3.capture2(command)
|
||||||
|
|
|
@ -7,6 +7,16 @@ module QA
|
||||||
|
|
||||||
attr_writer :personal_access_token
|
attr_writer :personal_access_token
|
||||||
|
|
||||||
|
# The environment variables used to indicate if the environment under test
|
||||||
|
# supports the given feature
|
||||||
|
SUPPORTED_FEATURES = {
|
||||||
|
git_protocol_v2: 'QA_CAN_TEST_GIT_PROTOCOL_V2'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
def supported_features
|
||||||
|
SUPPORTED_FEATURES
|
||||||
|
end
|
||||||
|
|
||||||
def debug?
|
def debug?
|
||||||
enabled?(ENV['QA_DEBUG'], default: false)
|
enabled?(ENV['QA_DEBUG'], default: false)
|
||||||
end
|
end
|
||||||
|
@ -104,6 +114,15 @@ module QA
|
||||||
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
|
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns true if there is an environment variable that indicates that
|
||||||
|
# the feature is supported in the environment under test.
|
||||||
|
# All features are supported by default.
|
||||||
|
def can_test?(feature)
|
||||||
|
raise ArgumentError, %Q(Unknown feature "#{feature}") unless SUPPORTED_FEATURES.include? feature
|
||||||
|
|
||||||
|
enabled?(ENV[SUPPORTED_FEATURES[feature]], default: true)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def enabled?(value, default: true)
|
def enabled?(value, default: true)
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module QA
|
||||||
|
context 'Create' do
|
||||||
|
describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
|
||||||
|
# Note: If you run this test against GDK make sure you've enabled sshd and
|
||||||
|
# enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
|
||||||
|
# `sshd_config`
|
||||||
|
# See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
|
||||||
|
|
||||||
|
let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
|
||||||
|
let(:ssh_key) do
|
||||||
|
Factory::Resource::SSHKey.fabricate! do |resource|
|
||||||
|
resource.title = key_title
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def login
|
||||||
|
Runtime::Browser.visit(:gitlab, Page::Main::Login)
|
||||||
|
Page::Main::Login.perform(&:sign_in_using_credentials)
|
||||||
|
end
|
||||||
|
|
||||||
|
around do |example|
|
||||||
|
# Create an SSH key to be used with Git
|
||||||
|
login
|
||||||
|
ssh_key
|
||||||
|
|
||||||
|
example.run
|
||||||
|
|
||||||
|
# Remove the SSH key
|
||||||
|
login
|
||||||
|
Page::Main::Menu.perform(&:go_to_profile_settings)
|
||||||
|
Page::Profile::Menu.perform(&:click_ssh_keys)
|
||||||
|
Page::Profile::SSHKeys.perform do |ssh_keys|
|
||||||
|
ssh_keys.remove_key(key_title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'user pushes to the repository' do
|
||||||
|
# Create a project to push to
|
||||||
|
project = Factory::Resource::Project.fabricate! do |project|
|
||||||
|
project.name = 'git-protocol-project'
|
||||||
|
end
|
||||||
|
|
||||||
|
file_name = 'README.md'
|
||||||
|
file_content = 'Test Git protocol v2'
|
||||||
|
git_protocol = '2'
|
||||||
|
git_protocol_reported = nil
|
||||||
|
|
||||||
|
# Use Git to clone the project, push a file to it, and then check the
|
||||||
|
# supported Git protocol
|
||||||
|
Git::Repository.perform do |repository|
|
||||||
|
username = 'GitLab QA'
|
||||||
|
email = 'root@gitlab.com'
|
||||||
|
|
||||||
|
repository.uri = project.repository_ssh_location.uri
|
||||||
|
|
||||||
|
begin
|
||||||
|
repository.use_ssh_key(ssh_key)
|
||||||
|
repository.clone
|
||||||
|
repository.configure_identity(username, email)
|
||||||
|
|
||||||
|
git_protocol_reported = repository.push_with_git_protocol(
|
||||||
|
git_protocol,
|
||||||
|
file_name,
|
||||||
|
file_content)
|
||||||
|
ensure
|
||||||
|
repository.delete_ssh_key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
project.visit!
|
||||||
|
Page::Project::Show.perform(&:wait_for_push)
|
||||||
|
|
||||||
|
# Check that the push worked
|
||||||
|
expect(page).to have_content(file_name)
|
||||||
|
expect(page).to have_content(file_content)
|
||||||
|
|
||||||
|
# And check that the correct Git protocol was used
|
||||||
|
expect(git_protocol_reported).to eq(git_protocol)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,6 +25,10 @@ module QA
|
||||||
|
|
||||||
args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
|
args.push(%w[--tag ~skip_signup_disabled]) if QA::Runtime::Env.signup_disabled?
|
||||||
|
|
||||||
|
QA::Runtime::Env.supported_features.each_key do |key|
|
||||||
|
args.push(["--tag", "~requires_#{key}"]) unless QA::Runtime::Env.can_test? key
|
||||||
|
end
|
||||||
|
|
||||||
args.push(options)
|
args.push(options)
|
||||||
args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
|
args.push(DEFAULT_TEST_PATH_ARGS) unless options.any? { |opt| opt =~ %r{/features/} }
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,36 @@ describe QA::Git::Repository do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#git_protocol=' do
|
||||||
|
[0, 1, 2].each do |version|
|
||||||
|
it "configures git to use protocol version #{version}" do
|
||||||
|
expect(repository).to receive(:run).with("git config protocol.version #{version}")
|
||||||
|
repository.git_protocol = version
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error if the version is unsupported' do
|
||||||
|
expect { repository.git_protocol = 'foo' }.to raise_error(ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#fetch_supported_git_protocol' do
|
||||||
|
it "reports the detected version" do
|
||||||
|
expect(repository).to receive(:run).and_return("packet: git< version 2")
|
||||||
|
expect(repository.fetch_supported_git_protocol).to eq('2')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reports unknown if version is unknown' do
|
||||||
|
expect(repository).to receive(:run).and_return("packet: git< version -1")
|
||||||
|
expect(repository.fetch_supported_git_protocol).to eq('unknown')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'reports unknown if content does not identify a version' do
|
||||||
|
expect(repository).to receive(:run).and_return("foo")
|
||||||
|
expect(repository.fetch_supported_git_protocol).to eq('unknown')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cd_empty_temp_directory
|
def cd_empty_temp_directory
|
||||||
tmp_dir = 'tmp/git-repository-spec/'
|
tmp_dir = 'tmp/git-repository-spec/'
|
||||||
FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir)
|
FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir)
|
||||||
|
|
|
@ -3,49 +3,62 @@
|
||||||
describe QA::Runtime::Env do
|
describe QA::Runtime::Env do
|
||||||
include Support::StubENV
|
include Support::StubENV
|
||||||
|
|
||||||
shared_examples 'boolean method' do |method, env_key, default|
|
shared_examples 'boolean method' do |**kwargs|
|
||||||
|
it_behaves_like 'boolean method with parameter', kwargs
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'boolean method with parameter' do |method:, param: nil, env_key:, default:|
|
||||||
context 'when there is an env variable set' do
|
context 'when there is an env variable set' do
|
||||||
it 'returns false when falsey values specified' do
|
it 'returns false when falsey values specified' do
|
||||||
stub_env(env_key, 'false')
|
stub_env(env_key, 'false')
|
||||||
expect(described_class.public_send(method)).to be_falsey
|
expect(described_class.public_send(method, *param)).to be_falsey
|
||||||
|
|
||||||
stub_env(env_key, 'no')
|
stub_env(env_key, 'no')
|
||||||
expect(described_class.public_send(method)).to be_falsey
|
expect(described_class.public_send(method, *param)).to be_falsey
|
||||||
|
|
||||||
stub_env(env_key, '0')
|
stub_env(env_key, '0')
|
||||||
expect(described_class.public_send(method)).to be_falsey
|
expect(described_class.public_send(method, *param)).to be_falsey
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns true when anything else specified' do
|
it 'returns true when anything else specified' do
|
||||||
stub_env(env_key, 'true')
|
stub_env(env_key, 'true')
|
||||||
expect(described_class.public_send(method)).to be_truthy
|
expect(described_class.public_send(method, *param)).to be_truthy
|
||||||
|
|
||||||
stub_env(env_key, '1')
|
stub_env(env_key, '1')
|
||||||
expect(described_class.public_send(method)).to be_truthy
|
expect(described_class.public_send(method, *param)).to be_truthy
|
||||||
|
|
||||||
stub_env(env_key, 'anything')
|
stub_env(env_key, 'anything')
|
||||||
expect(described_class.public_send(method)).to be_truthy
|
expect(described_class.public_send(method, *param)).to be_truthy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is no env variable set' do
|
context 'when there is no env variable set' do
|
||||||
it "returns the default, #{default}" do
|
it "returns the default, #{default}" do
|
||||||
stub_env(env_key, nil)
|
stub_env(env_key, nil)
|
||||||
expect(described_class.public_send(method)).to be(default)
|
expect(described_class.public_send(method, *param)).to be(default)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.signup_disabled?' do
|
describe '.signup_disabled?' do
|
||||||
it_behaves_like 'boolean method', :signup_disabled?, 'SIGNUP_DISABLED', false
|
it_behaves_like 'boolean method',
|
||||||
|
method: :signup_disabled?,
|
||||||
|
env_key: 'SIGNUP_DISABLED',
|
||||||
|
default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.debug?' do
|
describe '.debug?' do
|
||||||
it_behaves_like 'boolean method', :debug?, 'QA_DEBUG', false
|
it_behaves_like 'boolean method',
|
||||||
|
method: :debug?,
|
||||||
|
env_key: 'QA_DEBUG',
|
||||||
|
default: false
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.chrome_headless?' do
|
describe '.chrome_headless?' do
|
||||||
it_behaves_like 'boolean method', :chrome_headless?, 'CHROME_HEADLESS', true
|
it_behaves_like 'boolean method',
|
||||||
|
method: :chrome_headless?,
|
||||||
|
env_key: 'CHROME_HEADLESS',
|
||||||
|
default: true
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.running_in_ci?' do
|
describe '.running_in_ci?' do
|
||||||
|
@ -182,4 +195,16 @@ describe QA::Runtime::Env do
|
||||||
expect(described_class.log_destination).to eq('path/to_file')
|
expect(described_class.log_destination).to eq('path/to_file')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.can_test?' do
|
||||||
|
it_behaves_like 'boolean method with parameter',
|
||||||
|
method: :can_test?,
|
||||||
|
param: :git_protocol_v2,
|
||||||
|
env_key: 'QA_CAN_TEST_GIT_PROTOCOL_V2',
|
||||||
|
default: true
|
||||||
|
|
||||||
|
it 'raises ArgumentError if feature is unknown' do
|
||||||
|
expect { described_class.can_test? :foo }.to raise_error(ArgumentError, 'Unknown feature "foo"')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -76,6 +76,20 @@ describe QA::Specs::Runner do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when git protocol v2 is not supported' do
|
||||||
|
before do
|
||||||
|
allow(QA::Runtime::Env).to receive(:can_test?).with(:git_protocol_v2).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { described_class.new }
|
||||||
|
|
||||||
|
it 'it includes default args and excludes the requires_git_protocol_v2 tag' do
|
||||||
|
expect_rspec_runner_arguments(['--tag', '~orchestrated', '--tag', '~requires_git_protocol_v2', *described_class::DEFAULT_TEST_PATH_ARGS])
|
||||||
|
|
||||||
|
subject.perform
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def expect_rspec_runner_arguments(arguments)
|
def expect_rspec_runner_arguments(arguments)
|
||||||
expect(RSpec::Core::Runner).to receive(:run)
|
expect(RSpec::Core::Runner).to receive(:run)
|
||||||
.with(arguments, $stderr, $stdout)
|
.with(arguments, $stderr, $stdout)
|
||||||
|
|
Loading…
Reference in New Issue