summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/bin/sidekiq_cluster_spec.rb45
-rw-r--r--spec/controllers/concerns/confirm_email_warning_spec.rb4
-rw-r--r--spec/controllers/registrations_controller_spec.rb18
-rw-r--r--spec/features/invites_spec.rb51
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb4
-rw-r--r--spec/features/users/login_spec.rb1
-rw-r--r--spec/features/users/signup_spec.rb68
-rw-r--r--spec/lib/gitlab/sidekiq_cluster/cli_spec.rb278
-rw-r--r--spec/lib/gitlab/sidekiq_cluster_spec.rb196
-rw-r--r--spec/models/commit_spec.rb81
-rw-r--r--spec/models/project_import_state_spec.rb3
-rw-r--r--spec/models/project_spec.rb5
-rw-r--r--spec/models/repository_spec.rb26
-rw-r--r--spec/services/issues/close_service_spec.rb4
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb2
-rw-r--r--spec/support/helpers/project_forks_helper.rb2
16 files changed, 639 insertions, 149 deletions
diff --git a/spec/bin/sidekiq_cluster_spec.rb b/spec/bin/sidekiq_cluster_spec.rb
new file mode 100644
index 00000000000..67de55ad9f5
--- /dev/null
+++ b/spec/bin/sidekiq_cluster_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'bin/sidekiq-cluster' do
+ using RSpec::Parameterized::TableSyntax
+
+ context 'when selecting some queues and excluding others' do
+ where(:args, :included, :excluded) do
+ %w[--negate cronjob] | '-qdefault,1' | '-qcronjob,1'
+ %w[--experimental-queue-selector resource_boundary=cpu] | '-qupdate_merge_requests,1' | '-qdefault,1'
+ end
+
+ with_them do
+ it 'runs successfully', :aggregate_failures do
+ cmd = %w[bin/sidekiq-cluster --dryrun] + args
+
+ output, status = Gitlab::Popen.popen(cmd, Rails.root.to_s)
+
+ expect(status).to be(0)
+ expect(output).to include('"bundle", "exec", "sidekiq"')
+ expect(output).to include(included)
+ expect(output).not_to include(excluded)
+ end
+ end
+ end
+
+ context 'when selecting all queues' do
+ [
+ %w[*],
+ %w[--experimental-queue-selector *]
+ ].each do |args|
+ it "runs successfully with `#{args}`", :aggregate_failures do
+ cmd = %w[bin/sidekiq-cluster --dryrun] + args
+
+ output, status = Gitlab::Popen.popen(cmd, Rails.root.to_s)
+
+ expect(status).to be(0)
+ expect(output).to include('"bundle", "exec", "sidekiq"')
+ expect(output).to include('-qdefault,1')
+ expect(output).to include('-qcronjob:ci_archive_traces_cron,1')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/concerns/confirm_email_warning_spec.rb b/spec/controllers/concerns/confirm_email_warning_spec.rb
index 2aad380203b..93e3423261c 100644
--- a/spec/controllers/concerns/confirm_email_warning_spec.rb
+++ b/spec/controllers/concerns/confirm_email_warning_spec.rb
@@ -3,6 +3,10 @@
require 'spec_helper'
describe ConfirmEmailWarning do
+ before do
+ stub_feature_flags(soft_email_confirmation: true)
+ end
+
controller(ApplicationController) do
# `described_class` is not available in this context
include ConfirmEmailWarning
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 8d79e505e5d..d7fe3e87056 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -79,29 +79,31 @@ describe RegistrationsController do
stub_application_setting(send_user_confirmation_email: true)
end
- context 'when a grace period is active for confirming the email address' do
+ context 'when soft email confirmation is not enabled' do
before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
+ stub_feature_flags(soft_email_confirmation: false)
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
end
- it 'sends a confirmation email and redirects to the dashboard' do
+ it 'does not authenticate the user and sends a confirmation email' do
post(:create, params: user_params)
expect(ActionMailer::Base.deliveries.last.to.first).to eq(user_params[:user][:email])
- expect(response).to redirect_to(dashboard_projects_path)
+ expect(subject.current_user).to be_nil
end
end
- context 'when no grace period is active for confirming the email address' do
+ context 'when soft email confirmation is enabled' do
before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
+ stub_feature_flags(soft_email_confirmation: true)
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
end
- it 'sends a confirmation email and redirects to the almost there page' do
+ it 'authenticates the user and sends a confirmation email' do
post(:create, params: user_params)
expect(ActionMailer::Base.deliveries.last.to.first).to eq(user_params[:user][:email])
- expect(response).to redirect_to(users_almost_there_path)
+ expect(response).to redirect_to(dashboard_projects_path)
end
end
end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index 7884a16c118..9cd01894575 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -135,7 +135,9 @@ describe 'Invites' do
expect(current_path).to eq(dashboard_projects_path)
expect(page).to have_content(project.full_name)
+
visit group_path(group)
+
expect(page).to have_content(group.full_name)
end
@@ -153,6 +155,25 @@ describe 'Invites' do
context 'email confirmation enabled' do
let(:send_email_confirmation) { true }
+ context 'when soft email confirmation is not enabled' do
+ before do
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
+ end
+
+ it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do
+ fill_in_sign_up_form(new_user)
+ confirm_email(new_user)
+ fill_in_sign_in_form(new_user)
+
+ expect(current_path).to eq(root_path)
+ expect(page).to have_content(project.full_name)
+
+ visit group_path(group)
+
+ expect(page).to have_content(group.full_name)
+ end
+ end
+
context 'when soft email confirmation is enabled' do
before do
allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
@@ -164,7 +185,9 @@ describe 'Invites' do
expect(current_path).to eq(root_path)
expect(page).to have_content(project.full_name)
+
visit group_path(group)
+
expect(page).to have_content(group.full_name)
end
end
@@ -180,14 +203,32 @@ describe 'Invites' do
context 'the user sign-up using a different email address' do
let(:invite_email) { build_stubbed(:user).email }
- before do
- allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
+ context 'when soft email confirmation is not enabled' do
+ before do
+ stub_feature_flags(soft_email_confirmation: false)
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
+ end
+
+ it 'signs up and redirects to the invitation page' do
+ fill_in_sign_up_form(new_user)
+ confirm_email(new_user)
+ fill_in_sign_in_form(new_user)
+
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ end
end
- it 'signs up and redirects to the invitation page' do
- fill_in_sign_up_form(new_user)
+ context 'when soft email confirmation is enabled' do
+ before do
+ stub_feature_flags(soft_email_confirmation: true)
+ allow(User).to receive(:allow_unconfirmed_access_for).and_return 2.days
+ end
- expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ it 'signs up and redirects to the invitation page' do
+ fill_in_sign_up_form(new_user)
+
+ expect(current_path).to eq(invite_path(group_invite.raw_invite_token))
+ end
end
end
end
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 2ef4a18f78d..868451883d8 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -61,10 +61,6 @@ describe 'Merge request > User sees diff', :js do
let(:merge_request) { create(:merge_request_with_diffs, source_project: forked_project, target_project: project, author: author_user) }
let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") }
- before do
- forked_project.repository.after_import
- end
-
context 'as author' do
it 'shows direct edit link', :sidekiq_might_not_need_inline do
sign_in(author_user)
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 0bef61a4854..5a8db3c070d 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -797,6 +797,7 @@ describe 'Login' do
before do
stub_application_setting(send_user_confirmation_email: true)
+ stub_feature_flags(soft_email_confirmation: true)
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index 8d5c0657fa5..daa987ea389 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -129,29 +129,63 @@ shared_examples 'Signup' do
stub_application_setting(send_user_confirmation_email: true)
end
- it 'creates the user account and sends a confirmation email' do
- visit new_user_registration_path
+ context 'when soft email confirmation is not enabled' do
+ before do
+ stub_feature_flags(soft_email_confirmation: false)
+ end
- fill_in 'new_user_username', with: new_user.username
- fill_in 'new_user_email', with: new_user.email
+ it 'creates the user account and sends a confirmation email' do
+ visit new_user_registration_path
- if Gitlab::Experimentation.enabled?(:signup_flow)
- fill_in 'new_user_first_name', with: new_user.first_name
- fill_in 'new_user_last_name', with: new_user.last_name
- else
- fill_in 'new_user_name', with: new_user.name
- fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+
+ if Gitlab::Experimentation.enabled?(:signup_flow)
+ fill_in 'new_user_first_name', with: new_user.first_name
+ fill_in 'new_user_last_name', with: new_user.last_name
+ else
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ end
+
+ fill_in 'new_user_password', with: new_user.password
+
+ expect { click_button 'Register' }.to change { User.count }.by(1)
+
+ expect(current_path).to eq users_almost_there_path
+ expect(page).to have_content('Please check your email to confirm your account')
end
+ end
- fill_in 'new_user_password', with: new_user.password
+ context 'when soft email confirmation is enabled' do
+ before do
+ stub_feature_flags(soft_email_confirmation: true)
+ end
- expect { click_button 'Register' }.to change { User.count }.by(1)
+ it 'creates the user account and sends a confirmation email' do
+ visit new_user_registration_path
- if Gitlab::Experimentation.enabled?(:signup_flow)
- expect(current_path).to eq users_sign_up_welcome_path
- else
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Please check your email (#{new_user.email}) to verify that you own this address and unlock the power of CI/CD.")
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+
+ if Gitlab::Experimentation.enabled?(:signup_flow)
+ fill_in 'new_user_first_name', with: new_user.first_name
+ fill_in 'new_user_last_name', with: new_user.last_name
+ else
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ end
+
+ fill_in 'new_user_password', with: new_user.password
+
+ expect { click_button 'Register' }.to change { User.count }.by(1)
+
+ if Gitlab::Experimentation.enabled?(:signup_flow)
+ expect(current_path).to eq users_sign_up_welcome_path
+ else
+ expect(current_path).to eq dashboard_projects_path
+ expect(page).to have_content("Please check your email (#{new_user.email}) to verify that you own this address and unlock the power of CI/CD.")
+ end
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_cluster/cli_spec.rb b/spec/lib/gitlab/sidekiq_cluster/cli_spec.rb
new file mode 100644
index 00000000000..8b31b8ade68
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_cluster/cli_spec.rb
@@ -0,0 +1,278 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+describe Gitlab::SidekiqCluster::CLI do
+ let(:cli) { described_class.new('/dev/null') }
+ let(:default_options) do
+ { env: 'test', directory: Dir.pwd, max_concurrency: 50, min_concurrency: 0, dryrun: false }
+ end
+
+ before do
+ stub_env('RAILS_ENV', 'test')
+ end
+
+ describe '#run' do
+ context 'without any arguments' do
+ it 'raises CommandError' do
+ expect { cli.run([]) }.to raise_error(described_class::CommandError)
+ end
+ end
+
+ context 'with arguments' do
+ before do
+ allow(cli).to receive(:write_pid)
+ allow(cli).to receive(:trap_signals)
+ allow(cli).to receive(:start_loop)
+ end
+
+ it 'starts the Sidekiq workers' do
+ expect(Gitlab::SidekiqCluster).to receive(:start)
+ .with([['foo']], default_options)
+ .and_return([])
+
+ cli.run(%w(foo))
+ end
+
+ it 'allows the special * selector' do
+ expect(Gitlab::SidekiqCluster)
+ .to receive(:start).with(
+ [Gitlab::SidekiqConfig::CliMethods.worker_queues],
+ default_options
+ )
+
+ cli.run(%w(*))
+ end
+
+ context 'with --negate flag' do
+ it 'starts Sidekiq workers for all queues in all_queues.yml except the ones in argv' do
+ expect(Gitlab::SidekiqConfig::CliMethods).to receive(:worker_queues).and_return(['baz'])
+ expect(Gitlab::SidekiqCluster).to receive(:start)
+ .with([['baz']], default_options)
+ .and_return([])
+
+ cli.run(%w(foo -n))
+ end
+ end
+
+ context 'with --max-concurrency flag' do
+ it 'starts Sidekiq workers for specified queues with a max concurrency' do
+ expect(Gitlab::SidekiqConfig::CliMethods).to receive(:worker_queues).and_return(%w(foo bar baz))
+ expect(Gitlab::SidekiqCluster).to receive(:start)
+ .with([%w(foo bar baz), %w(solo)], default_options.merge(max_concurrency: 2))
+ .and_return([])
+
+ cli.run(%w(foo,bar,baz solo -m 2))
+ end
+ end
+
+ context 'with --min-concurrency flag' do
+ it 'starts Sidekiq workers for specified queues with a min concurrency' do
+ expect(Gitlab::SidekiqConfig::CliMethods).to receive(:worker_queues).and_return(%w(foo bar baz))
+ expect(Gitlab::SidekiqCluster).to receive(:start)
+ .with([%w(foo bar baz), %w(solo)], default_options.merge(min_concurrency: 2))
+ .and_return([])
+
+ cli.run(%w(foo,bar,baz solo --min-concurrency 2))
+ end
+ end
+
+ context 'queue namespace expansion' do
+ it 'starts Sidekiq workers for all queues in all_queues.yml with a namespace in argv' do
+ expect(Gitlab::SidekiqConfig::CliMethods).to receive(:worker_queues).and_return(['cronjob:foo', 'cronjob:bar'])
+ expect(Gitlab::SidekiqCluster).to receive(:start)
+ .with([['cronjob', 'cronjob:foo', 'cronjob:bar']], default_options)
+ .and_return([])
+
+ cli.run(%w(cronjob))
+ end
+ end
+
+ context 'with --experimental-queue-selector' do
+ where do
+ {
+ 'memory-bound queues' => {
+ query: 'resource_boundary=memory',
+ included_queues: %w(project_export),
+ excluded_queues: %w(merge)
+ },
+ 'memory- or CPU-bound queues' => {
+ query: 'resource_boundary=memory,cpu',
+ included_queues: %w(auto_merge:auto_merge_process project_export),
+ excluded_queues: %w(merge)
+ },
+ 'high urgency CI queues' => {
+ query: 'feature_category=continuous_integration&urgency=high',
+ included_queues: %w(pipeline_cache:expire_job_cache pipeline_cache:expire_pipeline_cache),
+ excluded_queues: %w(merge)
+ },
+ 'CPU-bound high urgency CI queues' => {
+ query: 'feature_category=continuous_integration&urgency=high&resource_boundary=cpu',
+ included_queues: %w(pipeline_cache:expire_pipeline_cache),
+ excluded_queues: %w(pipeline_cache:expire_job_cache merge)
+ },
+ 'CPU-bound high urgency non-CI queues' => {
+ query: 'feature_category!=continuous_integration&urgency=high&resource_boundary=cpu',
+ included_queues: %w(new_issue),
+ excluded_queues: %w(pipeline_cache:expire_pipeline_cache)
+ },
+ 'CI and SCM queues' => {
+ query: 'feature_category=continuous_integration|feature_category=source_code_management',
+ included_queues: %w(pipeline_cache:expire_job_cache merge),
+ excluded_queues: %w(mailers)
+ }
+ }
+ end
+
+ with_them do
+ it 'expands queues by attributes' do
+ expect(Gitlab::SidekiqCluster).to receive(:start) do |queues, opts|
+ expect(opts).to eq(default_options)
+ expect(queues.first).to include(*included_queues)
+ expect(queues.first).not_to include(*excluded_queues)
+
+ []
+ end
+
+ cli.run(%W(--experimental-queue-selector #{query}))
+ end
+
+ it 'works when negated' do
+ expect(Gitlab::SidekiqCluster).to receive(:start) do |queues, opts|
+ expect(opts).to eq(default_options)
+ expect(queues.first).not_to include(*included_queues)
+ expect(queues.first).to include(*excluded_queues)
+
+ []
+ end
+
+ cli.run(%W(--negate --experimental-queue-selector #{query}))
+ end
+ end
+
+ it 'expands multiple queue groups correctly' do
+ expect(Gitlab::SidekiqCluster)
+ .to receive(:start)
+ .with([['chat_notification'], ['project_export']], default_options)
+ .and_return([])
+
+ cli.run(%w(--experimental-queue-selector feature_category=chatops&urgency=high resource_boundary=memory&feature_category=importers))
+ end
+
+ it 'allows the special * selector' do
+ expect(Gitlab::SidekiqCluster)
+ .to receive(:start).with(
+ [Gitlab::SidekiqConfig::CliMethods.worker_queues],
+ default_options
+ )
+
+ cli.run(%w(--experimental-queue-selector *))
+ end
+
+ it 'errors when the selector matches no queues' do
+ expect(Gitlab::SidekiqCluster).not_to receive(:start)
+
+ expect { cli.run(%w(--experimental-queue-selector has_external_dependencies=true&has_external_dependencies=false)) }
+ .to raise_error(described_class::CommandError)
+ end
+
+ it 'errors on an invalid query multiple queue groups correctly' do
+ expect(Gitlab::SidekiqCluster).not_to receive(:start)
+
+ expect { cli.run(%w(--experimental-queue-selector unknown_field=chatops)) }
+ .to raise_error(Gitlab::SidekiqConfig::CliMethods::QueryError)
+ end
+ end
+ end
+ end
+
+ describe '#write_pid' do
+ context 'when a PID is specified' do
+ it 'writes the PID to a file' do
+ expect(Gitlab::SidekiqCluster).to receive(:write_pid).with('/dev/null')
+
+ cli.option_parser.parse!(%w(-P /dev/null))
+ cli.write_pid
+ end
+ end
+
+ context 'when no PID is specified' do
+ it 'does not write a PID' do
+ expect(Gitlab::SidekiqCluster).not_to receive(:write_pid)
+
+ cli.write_pid
+ end
+ end
+ end
+
+ describe '#wait_for_termination' do
+ it 'waits for termination of all sub-processes and succeeds after 3 checks' do
+ expect(Gitlab::SidekiqCluster).to receive(:any_alive?)
+ .with(an_instance_of(Array)).and_return(true, true, true, false)
+
+ expect(Gitlab::SidekiqCluster).to receive(:pids_alive)
+ .with([]).and_return([])
+
+ expect(Gitlab::SidekiqCluster).to receive(:signal_processes)
+ .with([], :KILL)
+
+ stub_const("Gitlab::SidekiqCluster::CLI::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1)
+ stub_const("Gitlab::SidekiqCluster::CLI::TERMINATE_TIMEOUT_SECONDS", 1)
+ cli.wait_for_termination
+ end
+
+ context 'with hanging workers' do
+ before do
+ expect(cli).to receive(:write_pid)
+ expect(cli).to receive(:trap_signals)
+ expect(cli).to receive(:start_loop)
+ end
+
+ it 'hard kills workers after timeout expires' do
+ worker_pids = [101, 102, 103]
+ expect(Gitlab::SidekiqCluster).to receive(:start)
+ .with([['foo']], default_options)
+ .and_return(worker_pids)
+
+ expect(Gitlab::SidekiqCluster).to receive(:any_alive?)
+ .with(worker_pids).and_return(true).at_least(10).times
+
+ expect(Gitlab::SidekiqCluster).to receive(:pids_alive)
+ .with(worker_pids).and_return([102])
+
+ expect(Gitlab::SidekiqCluster).to receive(:signal_processes)
+ .with([102], :KILL)
+
+ cli.run(%w(foo))
+
+ stub_const("Gitlab::SidekiqCluster::CLI::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1)
+ stub_const("Gitlab::SidekiqCluster::CLI::TERMINATE_TIMEOUT_SECONDS", 1)
+ cli.wait_for_termination
+ end
+ end
+ end
+
+ describe '#trap_signals' do
+ it 'traps the termination and forwarding signals' do
+ expect(Gitlab::SidekiqCluster).to receive(:trap_terminate)
+ expect(Gitlab::SidekiqCluster).to receive(:trap_forward)
+
+ cli.trap_signals
+ end
+ end
+
+ describe '#start_loop' do
+ it 'runs until one of the processes has been terminated' do
+ allow(cli).to receive(:sleep).with(a_kind_of(Numeric))
+
+ expect(Gitlab::SidekiqCluster).to receive(:all_alive?)
+ .with(an_instance_of(Array)).and_return(false)
+
+ expect(Gitlab::SidekiqCluster).to receive(:signal_processes)
+ .with(an_instance_of(Array), :TERM)
+
+ cli.start_loop
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_cluster_spec.rb b/spec/lib/gitlab/sidekiq_cluster_spec.rb
new file mode 100644
index 00000000000..fa5de04f2f3
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_cluster_spec.rb
@@ -0,0 +1,196 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+describe Gitlab::SidekiqCluster do
+ describe '.trap_signals' do
+ it 'traps the given signals' do
+ expect(described_class).to receive(:trap).ordered.with(:INT)
+ expect(described_class).to receive(:trap).ordered.with(:HUP)
+
+ described_class.trap_signals(%i(INT HUP))
+ end
+ end
+
+ describe '.trap_terminate' do
+ it 'traps the termination signals' do
+ expect(described_class).to receive(:trap_signals)
+ .with(described_class::TERMINATE_SIGNALS)
+
+ described_class.trap_terminate { }
+ end
+ end
+
+ describe '.trap_forward' do
+ it 'traps the signals to forward' do
+ expect(described_class).to receive(:trap_signals)
+ .with(described_class::FORWARD_SIGNALS)
+
+ described_class.trap_forward { }
+ end
+ end
+
+ describe '.signal' do
+ it 'sends a signal to the given process' do
+ allow(Process).to receive(:kill).with(:INT, 4)
+ expect(described_class.signal(4, :INT)).to eq(true)
+ end
+
+ it 'returns false when the process does not exist' do
+ allow(Process).to receive(:kill).with(:INT, 4).and_raise(Errno::ESRCH)
+ expect(described_class.signal(4, :INT)).to eq(false)
+ end
+ end
+
+ describe '.signal_processes' do
+ it 'sends a signal to every thread' do
+ expect(described_class).to receive(:signal).with(1, :INT)
+
+ described_class.signal_processes([1], :INT)
+ end
+ end
+
+ describe '.start' do
+ it 'starts Sidekiq with the given queues, environment and options' do
+ expected_options = {
+ env: :production,
+ directory: 'foo/bar',
+ max_concurrency: 20,
+ min_concurrency: 10,
+ dryrun: true
+ }
+
+ expect(described_class).to receive(:start_sidekiq).ordered.with(%w(foo), expected_options.merge(worker_id: 0))
+ expect(described_class).to receive(:start_sidekiq).ordered.with(%w(bar baz), expected_options.merge(worker_id: 1))
+
+ described_class.start([%w(foo), %w(bar baz)], env: :production, directory: 'foo/bar', max_concurrency: 20, min_concurrency: 10, dryrun: true)
+ end
+
+ it 'starts Sidekiq with the given queues and sensible default options' do
+ expected_options = {
+ env: :development,
+ directory: an_instance_of(String),
+ max_concurrency: 50,
+ min_concurrency: 0,
+ worker_id: an_instance_of(Integer),
+ dryrun: false
+ }
+
+ expect(described_class).to receive(:start_sidekiq).ordered.with(%w(foo bar baz), expected_options)
+ expect(described_class).to receive(:start_sidekiq).ordered.with(%w(solo), expected_options)
+
+ described_class.start([%w(foo bar baz), %w(solo)])
+ end
+ end
+
+ describe '.start_sidekiq' do
+ let(:first_worker_id) { 0 }
+ let(:options) do
+ { env: :production, directory: 'foo/bar', max_concurrency: 20, min_concurrency: 0, worker_id: first_worker_id, dryrun: false }
+ end
+ let(:env) { { "ENABLE_SIDEKIQ_CLUSTER" => "1", "SIDEKIQ_WORKER_ID" => first_worker_id.to_s } }
+ let(:args) { ['bundle', 'exec', 'sidekiq', anything, '-eproduction', *([anything] * 5)] }
+
+ it 'starts a Sidekiq process' do
+ allow(Process).to receive(:spawn).and_return(1)
+
+ expect(described_class).to receive(:wait_async).with(1)
+ expect(described_class.start_sidekiq(%w(foo), options)).to eq(1)
+ end
+
+ it 'handles duplicate queue names' do
+ allow(Process)
+ .to receive(:spawn)
+ .with(env, *args, anything)
+ .and_return(1)
+
+ expect(described_class).to receive(:wait_async).with(1)
+ expect(described_class.start_sidekiq(%w(foo foo bar baz), options)).to eq(1)
+ end
+
+ it 'runs the sidekiq process in a new process group' do
+ expect(Process)
+ .to receive(:spawn)
+ .with(anything, *args, a_hash_including(pgroup: true))
+ .and_return(1)
+
+ allow(described_class).to receive(:wait_async)
+ expect(described_class.start_sidekiq(%w(foo bar baz), options)).to eq(1)
+ end
+ end
+
+ describe '.concurrency' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:queue_count, :min, :max, :expected) do
+ 2 | 0 | 0 | 3 # No min or max specified
+ 2 | 0 | 9 | 3 # No min specified, value < max
+ 2 | 1 | 4 | 3 # Value between min and max
+ 2 | 4 | 5 | 4 # Value below range
+ 5 | 2 | 3 | 3 # Value above range
+ 2 | 1 | 1 | 1 # Value above explicit setting (min == max)
+ 0 | 3 | 3 | 3 # Value below explicit setting (min == max)
+ 1 | 4 | 3 | 3 # Min greater than max
+ end
+
+ with_them do
+ let(:queues) { Array.new(queue_count) }
+
+ it { expect(described_class.concurrency(queues, min, max)).to eq(expected) }
+ end
+ end
+
+ describe '.wait_async' do
+ it 'waits for a process in a separate thread' do
+ thread = described_class.wait_async(Process.spawn('true'))
+
+ # Upon success Process.wait just returns the PID.
+ expect(thread.value).to be_a_kind_of(Numeric)
+ end
+ end
+
+ # In the X_alive? checks, we check negative PIDs sometimes as a simple way
+ # to be sure the pids are definitely for non-existent processes.
+ # Note that -1 is special, and sends the signal to every process we have permission
+ # for, so we use -2, -3 etc
+ describe '.all_alive?' do
+ it 'returns true if all processes are alive' do
+ processes = [Process.pid]
+
+ expect(described_class.all_alive?(processes)).to eq(true)
+ end
+
+ it 'returns false when a thread was not alive' do
+ processes = [-2]
+
+ expect(described_class.all_alive?(processes)).to eq(false)
+ end
+ end
+
+ describe '.any_alive?' do
+ it 'returns true if at least one process is alive' do
+ processes = [Process.pid, -2]
+
+ expect(described_class.any_alive?(processes)).to eq(true)
+ end
+
+ it 'returns false when all threads are dead' do
+ processes = [-2, -3]
+
+ expect(described_class.any_alive?(processes)).to eq(false)
+ end
+ end
+
+ describe '.write_pid' do
+ it 'writes the PID of the current process to the given file' do
+ handle = double(:handle)
+
+ allow(File).to receive(:open).with('/dev/null', 'w').and_yield(handle)
+
+ expect(handle).to receive(:write).with(Process.pid.to_s)
+
+ described_class.write_pid('/dev/null')
+ end
+ end
+end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 005d6bae2db..ddda04faaf1 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -444,61 +444,6 @@ eos
it { is_expected.to respond_to(:id) }
end
- describe '#closes_issues' do
- let(:issue) { create :issue, project: project }
- let(:other_project) { create(:project, :public) }
- let(:other_issue) { create :issue, project: other_project }
- let(:committer) { create :user }
-
- before do
- project.add_developer(committer)
- other_project.add_developer(committer)
- end
-
- it 'detects issues that this commit is marked as closing' do
- ext_ref = "#{other_project.full_path}##{other_issue.iid}"
-
- allow(commit).to receive_messages(
- safe_message: "Fixes ##{issue.iid} and #{ext_ref}",
- committer_email: committer.email
- )
-
- expect(commit.closes_issues).to include(issue)
- expect(commit.closes_issues).to include(other_issue)
- end
-
- it 'ignores referenced issues when auto-close is disabled' do
- project.update!(autoclose_referenced_issues: false)
-
- allow(commit).to receive_messages(
- safe_message: "Fixes ##{issue.iid}",
- committer_email: committer.email
- )
-
- expect(commit.closes_issues).to be_empty
- end
-
- context 'with personal snippet' do
- let(:commit) { personal_snippet.commit }
-
- it 'does not call Gitlab::ClosingIssueExtractor' do
- expect(Gitlab::ClosingIssueExtractor).not_to receive(:new)
-
- commit.closes_issues
- end
- end
-
- context 'with project snippet' do
- let(:commit) { project_snippet.commit }
-
- it 'does not call Gitlab::ClosingIssueExtractor' do
- expect(Gitlab::ClosingIssueExtractor).not_to receive(:new)
-
- commit.closes_issues
- end
- end
- end
-
it_behaves_like 'a mentionable' do
subject { create(:project, :repository).commit }
@@ -775,32 +720,6 @@ eos
end
end
- describe '#merge_requests' do
- let!(:project) { create(:project, :repository) }
- let!(:merge_request1) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'feature') }
- let!(:merge_request2) { create(:merge_request, source_project: project, source_branch: 'merged-target', target_branch: 'feature') }
- let(:commit1) { merge_request1.merge_request_diff.commits.last }
- let(:commit2) { merge_request1.merge_request_diff.commits.first }
-
- it 'returns merge_requests that introduced that commit' do
- expect(commit1.merge_requests).to contain_exactly(merge_request1, merge_request2)
- expect(commit2.merge_requests).to contain_exactly(merge_request1)
- end
-
- context 'with personal snippet' do
- it 'returns empty relation' do
- expect(personal_snippet.repository.commit.merge_requests).to eq MergeRequest.none
- end
- end
-
- context 'with project snippet' do
- it 'returns empty relation' do
- expect(project_snippet.project).not_to receive(:merge_requests)
- expect(project_snippet.repository.commit.merge_requests).to eq MergeRequest.none
- end
- end
- end
-
describe 'signed commits' do
let(:gpg_signed_commit) { project.commit_by(oid: '0b4bc9a49b562e85de7cc9e834518ea6828729b9') }
let(:x509_signed_commit) { project.commit_by(oid: '189a6c924013fc3fe40d6f1ec1dc20214183bc97') }
diff --git a/spec/models/project_import_state_spec.rb b/spec/models/project_import_state_spec.rb
index babde7b0670..157477767af 100644
--- a/spec/models/project_import_state_spec.rb
+++ b/spec/models/project_import_state_spec.rb
@@ -23,8 +23,7 @@ describe ProjectImportState, type: :model do
# Works around https://github.com/rspec/rspec-mocks/issues/910
allow(Project).to receive(:find).with(project.id).and_return(project)
- expect(project.repository).to receive(:after_import).and_call_original
- expect(project.wiki.repository).to receive(:after_import).and_call_original
+ expect(project).to receive(:after_import).and_call_original
end
it 'imports a project', :sidekiq_might_not_need_inline do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index ca6ff8606f5..782e526b69d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4618,13 +4618,14 @@ describe Project do
let(:import_state) { create(:import_state, project: project) }
it 'runs the correct hooks' do
- expect(project.repository).to receive(:after_import)
- expect(project.wiki.repository).to receive(:after_import)
+ expect(project.repository).to receive(:expire_content_cache)
+ expect(project.wiki.repository).to receive(:expire_content_cache)
expect(import_state).to receive(:finish)
expect(project).to receive(:update_project_counter_caches)
expect(project).to receive(:after_create_default_branch)
expect(project).to receive(:refresh_markdown_cache!)
expect(InternalId).to receive(:flush_records!).with(project: project)
+ expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id)
project.after_import
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 58f17a3661f..ca04bd7a28a 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1929,32 +1929,6 @@ describe Repository do
end
end
- describe '#after_import' do
- subject { repository.after_import }
-
- it 'flushes and builds the cache' do
- expect(repository).to receive(:expire_content_cache)
-
- subject
- end
-
- it 'calls DetectRepositoryLanguagesWorker' do
- expect(DetectRepositoryLanguagesWorker).to receive(:perform_async)
-
- subject
- end
-
- context 'with a wiki repository' do
- let(:repository) { project.wiki.repository }
-
- it 'does not call DetectRepositoryLanguagesWorker' do
- expect(DetectRepositoryLanguagesWorker).not_to receive(:perform_async)
-
- subject
- end
- end
- end
-
describe '#after_push_commit' do
it 'expires statistics caches' do
expect(repository).to receive(:expire_statistics_caches)
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index 141567045a2..fa4e6ddb088 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -103,8 +103,8 @@ describe Issues::CloseService do
subject { described_class.new(project, user).close_issue(issue, closed_via: closing_merge_request) }
context 'when `metrics.first_mentioned_in_commit_at` is not set' do
- it 'uses the first commit timestamp' do
- expected = closing_merge_request.commits.first.date
+ it 'uses the first commit authored timestamp' do
+ expected = closing_merge_request.commits.first.authored_date
subject
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 6cc042e9a97..4d72decb632 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -118,7 +118,7 @@ describe MergeRequests::MergeService do
it 'closes GitLab issue tracker issues' do
issue = create :issue, project: project
- commit = double('commit', safe_message: "Fixes #{issue.to_reference}", date: Time.now)
+ commit = instance_double('commit', safe_message: "Fixes #{issue.to_reference}", date: Time.now, authored_date: Time.now)
allow(merge_request).to receive(:commits).and_return([commit])
merge_request.cache_merge_request_closes_issues!
diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb
index 90d0d1845fc..a32e39e52c8 100644
--- a/spec/support/helpers/project_forks_helper.rb
+++ b/spec/support/helpers/project_forks_helper.rb
@@ -59,7 +59,7 @@ module ProjectForksHelper
bare_repo: TestEnv.forked_repo_path_bare,
refs: TestEnv::FORKED_BRANCH_SHA
)
- forked_project.repository.after_import
+ forked_project.repository.expire_content_cache
forked_project
end