2013-04-11 16:54:20 -04:00
require 'rspec/mocks'
2018-02-21 06:39:15 -05:00
require 'toml-rb'
2013-04-11 16:54:20 -04:00
2013-04-01 07:39:19 -04:00
module TestEnv
extend self
2017-09-14 11:40:41 -04:00
ComponentFailedToInstallError = Class . new ( StandardError )
2014-09-29 10:50:20 -04:00
# When developing the seed repository, comment out the branch you will modify.
BRANCH_SHA = {
2017-09-04 04:34:39 -04:00
'signed-commits' = > '2d1096e' ,
2016-09-27 11:39:29 -04:00
'not-merged-branch' = > 'b83d6e3' ,
'branch-merged' = > '498214d' ,
2016-08-05 07:15:06 -04:00
'empty-branch' = > '7efb185' ,
2016-08-29 07:43:09 -04:00
'ends-with.json' = > '98b0d8b' ,
2016-08-05 07:15:06 -04:00
'flatten-dir' = > 'e56497b' ,
'feature' = > '0b4bc9a' ,
'feature_conflict' = > 'bb5206f' ,
'fix' = > '48f0be4' ,
'improve/awesome' = > '5937ac0' ,
2017-09-20 01:52:43 -04:00
'merged-target' = > '21751bf' ,
2016-08-05 07:15:06 -04:00
'markdown' = > '0ed8c6c' ,
2018-01-11 18:12:34 -05:00
'lfs' = > '55bc176' ,
2016-09-27 11:39:29 -04:00
'master' = > 'b83d6e3' ,
2016-10-15 17:49:20 -04:00
'merge-test' = > '5937ac0' ,
2016-08-05 07:15:06 -04:00
" 'test' " = > 'e56497b' ,
'orphaned-branch' = > '45127a9' ,
'binary-encoding' = > '7b1cf43' ,
'gitattributes' = > '5a62481' ,
'expand-collapse-diffs' = > '4842455' ,
2016-11-10 12:24:12 -05:00
'symlink-expand-diff' = > '81e6355' ,
2016-08-05 07:15:06 -04:00
'expand-collapse-files' = > '025db92' ,
'expand-collapse-lines' = > '238e82d' ,
'video' = > '8879059' ,
2017-04-11 08:06:45 -04:00
'add-balsamiq-file' = > 'b89b56d' ,
2016-08-05 07:15:06 -04:00
'crlf-diff' = > '5938907' ,
2016-09-01 11:20:29 -04:00
'conflict-start' = > '824be60' ,
2016-08-05 07:15:06 -04:00
'conflict-resolvable' = > '1450cd6' ,
'conflict-binary-file' = > '259a6fb' ,
2016-09-01 11:20:29 -04:00
'conflict-contains-conflict-markers' = > '78a3086' ,
2016-08-05 07:15:06 -04:00
'conflict-missing-side' = > 'eb227b3' ,
2016-08-23 11:37:14 -04:00
'conflict-non-utf8' = > 'd0a293c' ,
2016-08-05 07:15:06 -04:00
'conflict-too-large' = > '39fa04f' ,
2016-12-15 15:48:26 -05:00
'deleted-image-test' = > '6c17798' ,
2017-02-01 09:28:04 -05:00
'wip' = > 'b9238ee' ,
2017-02-24 07:13:42 -05:00
'csv' = > '3dd0896' ,
2017-04-11 16:25:10 -04:00
'v1.1.0' = > 'b83d6e3' ,
2017-05-30 11:00:36 -04:00
'add-ipython-files' = > '93ee732' ,
2017-07-25 12:57:02 -04:00
'add-pdf-file' = > 'e774ebd' ,
2018-05-29 05:51:43 -04:00
'squash-large-files' = > '54cec52' ,
2017-10-07 00:25:17 -04:00
'add-pdf-text-binary' = > '79faa7b' ,
'add_images_and_changes' = > '010d106'
2017-02-21 18:32:18 -05:00
} . freeze
2017-03-30 12:21:19 -04:00
2015-07-20 19:55:45 -04:00
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
# need to keep all the branches in sync.
# We currently only need a subset of the branches
FORKED_BRANCH_SHA = {
2016-08-29 07:43:09 -04:00
'add-submodule-version-bump' = > '3f547c0' ,
'master' = > '5937ac0' ,
'remove-submodule' = > '2a33e0c' ,
'conflict-resolvable-fork' = > '404fa3f'
2017-02-21 18:32:18 -05:00
} . freeze
2015-04-13 01:27:45 -04:00
2017-06-06 10:23:40 -04:00
TMP_TEST_PATH = Rails . root . join ( 'tmp' , 'tests' , '**' )
2018-03-26 14:21:49 -04:00
REPOS_STORAGE = 'default' . freeze
2017-06-06 10:23:40 -04:00
2013-04-01 07:39:19 -04:00
# Test environment
#
2014-07-31 08:39:01 -04:00
# See gitlab.yml.example test section for paths
2013-04-01 07:39:19 -04:00
#
2013-04-10 16:28:42 -04:00
def init ( opts = { } )
2017-09-14 11:23:10 -04:00
unless Rails . env . test?
puts " \n TestEnv.init can only be run if `RAILS_ENV` is set to 'test' not ' #{ Rails . env } '! \n "
exit 1
end
2013-04-11 04:50:58 -04:00
# Disable mailer for spinach tests
disable_mailer if opts [ :mailer ] == false
2015-02-03 20:50:30 -05:00
clean_test_path
2014-07-31 14:01:42 -04:00
2014-07-31 12:35:07 -04:00
# Setup GitLab shell for test instance
setup_gitlab_shell
2017-07-06 08:45:29 -04:00
setup_gitaly
2017-03-28 21:23:45 -04:00
2017-12-13 19:13:44 -05:00
# Create repository for FactoryBot.create(:project)
2014-07-31 08:39:01 -04:00
setup_factory_repo
2015-05-02 16:01:33 -04:00
2017-12-13 19:13:44 -05:00
# Create repository for FactoryBot.create(:forked_project_with_submodules)
2015-04-13 01:27:45 -04:00
setup_forked_repo
2013-04-25 10:15:33 -04:00
end
2013-06-07 16:39:32 -04:00
def disable_mailer
2017-06-21 09:48:12 -04:00
allow_any_instance_of ( NotificationService ) . to receive ( :mailer )
. and_return ( double . as_null_object )
2013-04-25 10:15:33 -04:00
end
2013-11-07 11:53:35 -05:00
2013-06-07 16:39:32 -04:00
def enable_mailer
2017-06-21 09:48:12 -04:00
allow_any_instance_of ( NotificationService ) . to receive ( :mailer )
. and_call_original
2013-04-25 10:15:33 -04:00
end
2015-08-14 07:27:51 -04:00
def disable_pre_receive
2016-06-17 01:51:17 -04:00
allow_any_instance_of ( Gitlab :: Git :: Hook ) . to receive ( :trigger ) . and_return ( [ true , nil ] )
2015-08-14 07:27:51 -04:00
end
2015-02-03 20:50:30 -05:00
# Clean /tmp/tests
#
# Keeps gitlab-shell and gitlab-test
def clean_test_path
2017-06-06 10:23:40 -04:00
Dir [ TMP_TEST_PATH ] . each do | entry |
2017-03-28 21:23:45 -04:00
unless File . basename ( entry ) =~ / \ A(gitaly|gitlab-(shell|test|test_bare|test-fork|test-fork_bare)) \ z /
2015-02-03 20:50:30 -05:00
FileUtils . rm_rf ( entry )
end
end
2017-03-28 13:27:44 -04:00
FileUtils . mkdir_p ( repos_path )
FileUtils . mkdir_p ( backup_path )
FileUtils . mkdir_p ( pages_path )
2017-11-08 04:46:47 -05:00
FileUtils . mkdir_p ( artifacts_path )
2015-02-03 20:50:30 -05:00
end
2017-06-06 10:23:40 -04:00
def clean_gitlab_test_path
Dir [ TMP_TEST_PATH ] . each do | entry |
if File . basename ( entry ) =~ / \ A(gitlab-(test|test_bare|test-fork|test-fork_bare)) \ z /
FileUtils . rm_rf ( entry )
end
end
end
2014-07-31 08:39:01 -04:00
def setup_gitlab_shell
2017-09-14 11:40:41 -04:00
component_timed_setup ( 'GitLab Shell' ,
install_dir : Gitlab . config . gitlab_shell . path ,
version : Gitlab :: Shell . version_required ,
task : 'gitlab:shell:install' )
2018-06-05 11:58:28 -04:00
create_fake_git_hooks
end
def create_fake_git_hooks
# gitlab-shell hooks don't work in our test environment because they try to make internal API calls
hooks_dir = File . join ( Gitlab . config . gitlab_shell . path , 'hooks' )
%w[ pre-receive post-receive update ] . each do | hook |
File . open ( File . join ( hooks_dir , hook ) , 'w' , 0755 ) { | f | f . puts '#!/bin/sh' }
end
2013-04-25 10:15:33 -04:00
end
2013-04-01 07:39:19 -04:00
2017-03-28 21:23:45 -04:00
def setup_gitaly
2017-05-10 08:18:59 -04:00
socket_path = Gitlab :: GitalyClient . address ( 'default' ) . sub ( / \ Aunix: / , '' )
2017-03-28 21:23:45 -04:00
gitaly_dir = File . dirname ( socket_path )
2017-08-03 06:15:08 -04:00
2017-09-14 11:40:41 -04:00
component_timed_setup ( 'Gitaly' ,
install_dir : gitaly_dir ,
version : Gitlab :: GitalyClient . expected_server_version ,
task : " gitlab:gitaly:install[ #{ gitaly_dir } ] " ) do
2017-08-03 06:15:08 -04:00
2018-01-05 06:31:12 -05:00
# Always re-create config, in case it's outdated. This is fast anyway.
Gitlab :: SetupHelper . create_gitaly_configuration ( gitaly_dir , force : true )
2017-09-14 11:40:41 -04:00
start_gitaly ( gitaly_dir )
2017-03-28 21:23:45 -04:00
end
2017-08-03 06:15:08 -04:00
end
2017-04-10 17:33:41 -04:00
def start_gitaly ( gitaly_dir )
2017-07-31 09:17:14 -04:00
if ENV [ 'CI' ] . present?
# Gitaly has been spawned outside this process already
return
end
spawn_script = Rails . root . join ( 'scripts/gitaly-test-spawn' ) . to_s
2018-05-14 04:10:29 -04:00
Bundler . with_original_env do
raise " gitaly spawn failed " unless system ( spawn_script )
end
@gitaly_pid = Integer ( File . read ( 'tmp/tests/gitaly.pid' ) )
2018-01-12 11:41:37 -05:00
Kernel . at_exit { stop_gitaly }
2017-09-05 09:37:48 -04:00
wait_gitaly
end
def wait_gitaly
sleep_time = 10
sleep_interval = 0 . 1
socket = Gitlab :: GitalyClient . address ( 'default' ) . sub ( 'unix:' , '' )
Integer ( sleep_time / sleep_interval ) . times do
begin
Socket . unix ( socket )
return
rescue
sleep sleep_interval
end
end
raise " could not connect to gitaly at #{ socket . inspect } after #{ sleep_time } seconds "
2017-03-28 21:23:45 -04:00
end
def stop_gitaly
return unless @gitaly_pid
Process . kill ( 'KILL' , @gitaly_pid )
2017-10-17 11:42:43 -04:00
rescue Errno :: ESRCH
# The process can already be gone if the test run was INTerrupted.
2017-03-28 21:23:45 -04:00
end
2014-07-31 08:39:01 -04:00
def setup_factory_repo
2015-04-13 01:27:45 -04:00
setup_repo ( factory_repo_path , factory_repo_path_bare , factory_repo_name ,
BRANCH_SHA )
end
# This repo has a submodule commit that is not present in the main test
# repository.
def setup_forked_repo
setup_repo ( forked_repo_path , forked_repo_path_bare , forked_repo_name ,
FORKED_BRANCH_SHA )
end
2017-05-10 11:31:50 -04:00
def setup_repo ( repo_path , repo_path_bare , repo_name , refs )
2015-04-13 01:27:45 -04:00
clone_url = " https://gitlab.com/gitlab-org/ #{ repo_name } .git "
2013-04-25 10:15:33 -04:00
2015-04-13 01:27:45 -04:00
unless File . directory? ( repo_path )
2015-11-03 17:10:38 -05:00
system ( * %W( #{ Gitlab . config . git . bin_path } clone -q #{ clone_url } #{ repo_path } ) )
2014-09-29 10:50:20 -04:00
end
2017-05-10 11:31:50 -04:00
set_repo_refs ( repo_path , refs )
2014-09-29 10:50:20 -04:00
2017-03-31 02:46:51 -04:00
unless File . directory? ( repo_path_bare )
# We must copy bare repositories because we will push to them.
system ( git_env , * %W( #{ Gitlab . config . git . bin_path } clone -q --bare #{ repo_path } #{ repo_path_bare } ) )
end
2013-11-12 09:11:46 -05:00
end
2017-05-10 11:31:50 -04:00
def copy_repo ( project , bare_repo : , refs : )
2018-04-13 06:57:19 -04:00
target_repo_path = File . expand_path ( repos_path + " / #{ project . disk_path } .git " )
2014-07-31 14:30:35 -04:00
FileUtils . mkdir_p ( target_repo_path )
2017-05-10 11:31:50 -04:00
FileUtils . cp_r ( " #{ File . expand_path ( bare_repo ) } /. " , target_repo_path )
2014-07-31 14:30:35 -04:00
FileUtils . chmod_R 0755 , target_repo_path
2017-05-10 11:31:50 -04:00
set_repo_refs ( target_repo_path , refs )
2013-04-01 07:39:19 -04:00
end
2014-07-31 08:39:01 -04:00
def repos_path
2018-04-13 06:57:19 -04:00
@repos_path || = Gitlab . config . repositories . storages [ REPOS_STORAGE ] . legacy_disk_path
2013-04-01 07:39:19 -04:00
end
2014-09-27 03:56:19 -04:00
2015-07-24 12:54:06 -04:00
def backup_path
Gitlab . config . backup . path
end
2017-03-28 13:27:44 -04:00
def pages_path
Gitlab . config . pages . path
end
2017-11-08 04:46:47 -05:00
def artifacts_path
2018-01-29 12:57:34 -05:00
Gitlab . config . artifacts . storage_path
2017-11-08 04:46:47 -05:00
end
2016-01-28 20:45:03 -05:00
# When no cached assets exist, manually hit the root path to create them
#
# Otherwise they'd be created by the first test, often timing out and
# causing a transient test failure
2017-03-30 10:30:05 -04:00
def eager_load_driver_server
2016-01-28 20:45:03 -05:00
return unless defined? ( Capybara )
2017-04-01 23:23:18 -04:00
puts " Starting the Capybara driver server... "
2017-03-31 03:55:46 -04:00
Capybara . current_session . visit '/'
2016-01-28 20:45:03 -05:00
end
2017-05-10 11:31:50 -04:00
def factory_repo_path_bare
" #{ factory_repo_path } _bare "
end
def forked_repo_path_bare
" #{ forked_repo_path } _bare "
end
2017-08-08 07:05:40 -04:00
def with_empty_bare_repository ( name = nil )
path = Rails . root . join ( 'tmp/tests' , name || 'empty-bare-repository' ) . to_s
yield ( Rugged :: Repository . init_at ( path , :bare ) )
ensure
FileUtils . rm_rf ( path )
end
2014-09-27 03:56:19 -04:00
private
def factory_repo_path
2014-09-29 10:50:20 -04:00
@factory_repo_path || = Rails . root . join ( 'tmp' , 'tests' , factory_repo_name )
end
2014-09-27 03:56:19 -04:00
def factory_repo_name
'gitlab-test'
end
2015-03-27 16:39:41 -04:00
2015-04-13 01:27:45 -04:00
def forked_repo_path
@forked_repo_path || = Rails . root . join ( 'tmp' , 'tests' , forked_repo_name )
end
def forked_repo_name
'gitlab-test-fork'
end
2015-03-27 16:39:41 -04:00
# Prevent developer git configurations from being persisted to test
# repositories
def git_env
2015-04-13 01:27:45 -04:00
{ 'GIT_TEMPLATE_DIR' = > '' }
2015-03-27 16:39:41 -04:00
end
2016-08-29 07:43:09 -04:00
def set_repo_refs ( repo_path , branch_sha )
2017-05-10 11:31:50 -04:00
instructions = branch_sha . map { | branch , sha | " update refs/heads/ #{ branch } \x00 #{ sha } \x00 " } . join ( " \x00 " ) << " \x00 "
2016-11-04 00:47:39 -04:00
update_refs = %W( #{ Gitlab . config . git . bin_path } update-ref --stdin -z )
reset = proc do
2017-05-10 11:31:50 -04:00
Dir . chdir ( repo_path ) do
IO . popen ( update_refs , " w " ) { | io | io . write ( instructions ) }
$? . success?
end
2016-11-04 00:47:39 -04:00
end
2017-05-10 11:31:50 -04:00
# Try to reset without fetching to avoid using the network.
unless reset . call
raise 'Could not fetch test seed repository.' unless system ( * %W( #{ Gitlab . config . git . bin_path } -C #{ repo_path } fetch origin ) )
# Before we used Git clone's --mirror option, bare repos could end up
# with missing refs, clearing them and retrying should fix the issue.
2018-01-12 11:41:37 -05:00
clean_gitlab_test_path && init unless reset . call
2016-08-29 07:43:09 -04:00
end
end
2017-05-23 15:27:39 -04:00
2017-09-14 11:40:41 -04:00
def component_timed_setup ( component , install_dir : , version : , task : )
puts " \n ==> Setting up #{ component } ... "
start = Time . now
ensure_component_dir_name_is_correct! ( component , install_dir )
2017-09-23 17:22:15 -04:00
# On CI, once installed, components never need update
return if File . exist? ( install_dir ) && ENV [ 'CI' ]
2017-09-14 11:40:41 -04:00
if component_needs_update? ( install_dir , version )
# Cleanup the component entirely to ensure we start fresh
FileUtils . rm_rf ( install_dir )
2018-01-11 11:34:01 -05:00
2017-09-14 11:40:41 -04:00
unless system ( 'rake' , task )
raise ComponentFailedToInstallError
end
end
yield if block_given?
rescue ComponentFailedToInstallError
puts " \n #{ component } failed to install, cleaning up #{ install_dir } ! \n "
FileUtils . rm_rf ( install_dir )
exit 1
ensure
puts " #{ component } setup in #{ Time . now - start } seconds... \n "
end
def ensure_component_dir_name_is_correct! ( component , path )
actual_component_dir_name = File . basename ( path )
expected_component_dir_name = component . parameterize
unless actual_component_dir_name == expected_component_dir_name
puts " #{ component } install dir should be named ' #{ expected_component_dir_name } ', not ' #{ actual_component_dir_name } ' (full install path given was ' #{ path } ')! \n "
exit 1
end
end
2017-06-19 17:06:17 -04:00
def component_needs_update? ( component_folder , expected_version )
2018-01-05 06:31:12 -05:00
# Allow local overrides of the component for tests during development
return false if Rails . env . test? && File . symlink? ( component_folder )
2017-06-19 17:06:17 -04:00
version = File . read ( File . join ( component_folder , 'VERSION' ) ) . strip
2017-05-23 15:27:39 -04:00
# Notice that this will always yield true when using branch versions
# (`=branch_name`), but that actually makes sure the server is always based
# on the latest branch revision.
2017-06-19 17:06:17 -04:00
version != expected_version
2017-05-23 15:27:39 -04:00
rescue Errno :: ENOENT
true
end
2013-04-01 07:39:19 -04:00
end