summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb40
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb2
-rw-r--r--spec/lib/gitlab/external_authorization/access_spec.rb142
-rw-r--r--spec/lib/gitlab/external_authorization/cache_spec.rb48
-rw-r--r--spec/lib/gitlab/external_authorization/client_spec.rb97
-rw-r--r--spec/lib/gitlab/external_authorization/logger_spec.rb45
-rw-r--r--spec/lib/gitlab/external_authorization/response_spec.rb52
-rw-r--r--spec/lib/gitlab/external_authorization_spec.rb54
-rw-r--r--spec/lib/gitlab/hook_data/issuable_builder_spec.rb6
-rw-r--r--spec/lib/gitlab/hook_data/merge_request_builder_spec.rb1
-rw-r--r--spec/lib/gitlab/import/merge_request_helpers_spec.rb73
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml4
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml5
-rw-r--r--spec/lib/gitlab/issuable_metadata_spec.rb4
-rw-r--r--spec/lib/gitlab/kubernetes/namespace_spec.rb27
-rw-r--r--spec/lib/gitlab/legacy_github_import/project_creator_spec.rb16
-rw-r--r--spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb1
-rw-r--r--spec/lib/gitlab/push_options_spec.rb103
20 files changed, 752 insertions, 10 deletions
diff --git a/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
index f8107dd40b9..4db829b1e7b 100644
--- a/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
@@ -30,6 +30,6 @@ describe Gitlab::BackgroundMigration::MigrateStageIndex, :migration, schema: 201
described_class.new.perform(100, 101)
- expect(stages.all.pluck(:position)).to eq [2, 3]
+ expect(stages.all.order(:id).pluck(:position)).to eq [2, 3]
end
end
diff --git a/spec/lib/gitlab/background_migration_spec.rb b/spec/lib/gitlab/background_migration_spec.rb
index 7d3d8a949ef..1d0ffb5e9df 100644
--- a/spec/lib/gitlab/background_migration_spec.rb
+++ b/spec/lib/gitlab/background_migration_spec.rb
@@ -195,4 +195,44 @@ describe Gitlab::BackgroundMigration do
end
end
end
+
+ describe '.dead_jobs?' do
+ let(:queue) do
+ [double(args: ['Foo', [10, 20]], queue: described_class.queue)]
+ end
+
+ context 'when there are dead jobs present' do
+ before do
+ allow(Sidekiq::DeadSet).to receive(:new).and_return(queue)
+ end
+
+ it 'returns true if specific job exists' do
+ expect(described_class.dead_jobs?('Foo')).to eq(true)
+ end
+
+ it 'returns false if specific job does not exist' do
+ expect(described_class.dead_jobs?('Bar')).to eq(false)
+ end
+ end
+ end
+
+ describe '.retrying_jobs?' do
+ let(:queue) do
+ [double(args: ['Foo', [10, 20]], queue: described_class.queue)]
+ end
+
+ context 'when there are dead jobs present' do
+ before do
+ allow(Sidekiq::RetrySet).to receive(:new).and_return(queue)
+ end
+
+ it 'returns true if specific job exists' do
+ expect(described_class.retrying_jobs?('Foo')).to eq(true)
+ end
+
+ it 'returns false if specific job does not exist' do
+ expect(described_class.retrying_jobs?('Bar')).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
index 3c0007f4d57..0bc9e8bd3cd 100644
--- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -100,6 +100,26 @@ describe Gitlab::Ci::Config::Entry::Environment do
end
end
+ context 'when wrong action type is used' do
+ let(:config) do
+ { name: 'production',
+ action: ['stop'] }
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'contains error about wrong action type' do
+ expect(entry.errors)
+ .to include 'environment action should be a string'
+ end
+ end
+ end
+
context 'when invalid action is used' do
let(:config) do
{ name: 'production',
@@ -151,6 +171,26 @@ describe Gitlab::Ci::Config::Entry::Environment do
end
end
+ context 'when wrong url type is used' do
+ let(:config) do
+ { name: 'production',
+ url: ['https://meow.meow'] }
+ end
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'contains error about wrong url type' do
+ expect(entry.errors)
+ .to include 'environment url should be a string'
+ end
+ end
+ end
+
context 'when variables are used for environment' do
let(:config) do
{ name: 'review/$CI_COMMIT_REF_NAME',
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 8b39c4e4dd0..b7b30e60d44 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -615,7 +615,7 @@ module Gitlab
subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config), opts) }
context "when validating a ci config file with no project context" do
- context "when a single string is provided" do
+ context "when a single string is provided", :quarantine do
let(:include_content) { "/local.gitlab-ci.yml" }
it "does not return any error" do
diff --git a/spec/lib/gitlab/external_authorization/access_spec.rb b/spec/lib/gitlab/external_authorization/access_spec.rb
new file mode 100644
index 00000000000..5dc2521b310
--- /dev/null
+++ b/spec/lib/gitlab/external_authorization/access_spec.rb
@@ -0,0 +1,142 @@
+require 'spec_helper'
+
+describe Gitlab::ExternalAuthorization::Access, :clean_gitlab_redis_cache do
+ subject(:access) { described_class.new(build(:user), 'dummy_label') }
+
+ describe '#loaded?' do
+ it 'is `true` when it was loaded recently' do
+ Timecop.freeze do
+ allow(access).to receive(:loaded_at).and_return(5.minutes.ago)
+
+ expect(access).to be_loaded
+ end
+ end
+
+ it 'is `false` when there is no loading time' do
+ expect(access).not_to be_loaded
+ end
+
+ it 'is `false` when there the result was loaded a long time ago' do
+ Timecop.freeze do
+ allow(access).to receive(:loaded_at).and_return(2.weeks.ago)
+
+ expect(access).not_to be_loaded
+ end
+ end
+ end
+
+ describe 'load!' do
+ let(:fake_client) { double('ExternalAuthorization::Client') }
+ let(:fake_response) do
+ double(
+ 'Response',
+ 'successful?' => true,
+ 'valid?' => true,
+ 'reason' => nil
+ )
+ end
+
+ before do
+ allow(access).to receive(:load_from_cache)
+ allow(fake_client).to receive(:request_access).and_return(fake_response)
+ allow(Gitlab::ExternalAuthorization::Client).to receive(:new) { fake_client }
+ end
+
+ context 'when loading from the webservice' do
+ it 'loads from the webservice it the cache was empty' do
+ expect(access).to receive(:load_from_cache)
+ expect(access).to receive(:load_from_service).and_call_original
+
+ access.load!
+
+ expect(access).to be_loaded
+ end
+
+ it 'assigns the accessibility, reason and loaded_at' do
+ allow(fake_response).to receive(:successful?).and_return(false)
+ allow(fake_response).to receive(:reason).and_return('Inaccessible label')
+
+ access.load!
+
+ expect(access.reason).to eq('Inaccessible label')
+ expect(access).not_to have_access
+ expect(access.loaded_at).not_to be_nil
+ end
+
+ it 'returns itself' do
+ expect(access.load!).to eq(access)
+ end
+
+ it 'stores the result in redis' do
+ Timecop.freeze do
+ fake_cache = double
+ expect(fake_cache).to receive(:store).with(true, nil, Time.now)
+ expect(access).to receive(:cache).and_return(fake_cache)
+
+ access.load!
+ end
+ end
+
+ context 'when the request fails' do
+ before do
+ allow(fake_client).to receive(:request_access) do
+ raise ::Gitlab::ExternalAuthorization::RequestFailed.new('Service unavailable')
+ end
+ end
+
+ it 'is loaded' do
+ access.load!
+
+ expect(access).to be_loaded
+ end
+
+ it 'assigns the correct accessibility, reason and loaded_at' do
+ access.load!
+
+ expect(access.reason).to eq('Service unavailable')
+ expect(access).not_to have_access
+ expect(access.loaded_at).not_to be_nil
+ end
+
+ it 'does not store the result in redis' do
+ fake_cache = double
+ expect(fake_cache).not_to receive(:store)
+ allow(access).to receive(:cache).and_return(fake_cache)
+
+ access.load!
+ end
+ end
+ end
+
+ context 'When loading from cache' do
+ let(:fake_cache) { double('ExternalAuthorization::Cache') }
+
+ before do
+ allow(access).to receive(:cache).and_return(fake_cache)
+ end
+
+ it 'does not load from the webservice' do
+ Timecop.freeze do
+ expect(fake_cache).to receive(:load).and_return([true, nil, Time.now])
+
+ expect(access).to receive(:load_from_cache).and_call_original
+ expect(access).not_to receive(:load_from_service)
+
+ access.load!
+ end
+ end
+
+ it 'loads from the webservice when the cached result was too old' do
+ Timecop.freeze do
+ expect(fake_cache).to receive(:load).and_return([true, nil, 2.days.ago])
+
+ expect(access).to receive(:load_from_cache).and_call_original
+ expect(access).to receive(:load_from_service).and_call_original
+ allow(fake_cache).to receive(:store)
+
+ access.load!
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/external_authorization/cache_spec.rb b/spec/lib/gitlab/external_authorization/cache_spec.rb
new file mode 100644
index 00000000000..58e7d626707
--- /dev/null
+++ b/spec/lib/gitlab/external_authorization/cache_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Gitlab::ExternalAuthorization::Cache, :clean_gitlab_redis_cache do
+ let(:user) { build_stubbed(:user) }
+ let(:cache_key) { "external_authorization:user-#{user.id}:label-dummy_label" }
+
+ subject(:cache) { described_class.new(user, 'dummy_label') }
+
+ def read_from_redis(key)
+ Gitlab::Redis::Cache.with do |redis|
+ redis.hget(cache_key, key)
+ end
+ end
+
+ def set_in_redis(key, value)
+ Gitlab::Redis::Cache.with do |redis|
+ redis.hmset(cache_key, key, value)
+ end
+ end
+
+ describe '#load' do
+ it 'reads stored info from redis' do
+ Timecop.freeze do
+ set_in_redis(:access, false)
+ set_in_redis(:reason, 'Access denied for now')
+ set_in_redis(:refreshed_at, Time.now)
+
+ access, reason, refreshed_at = cache.load
+
+ expect(access).to eq(false)
+ expect(reason).to eq('Access denied for now')
+ expect(refreshed_at).to be_within(1.second).of(Time.now)
+ end
+ end
+ end
+
+ describe '#store' do
+ it 'sets the values in redis' do
+ Timecop.freeze do
+ cache.store(true, 'the reason', Time.now)
+
+ expect(read_from_redis(:access)).to eq('true')
+ expect(read_from_redis(:reason)).to eq('the reason')
+ expect(read_from_redis(:refreshed_at)).to eq(Time.now.to_s)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/external_authorization/client_spec.rb b/spec/lib/gitlab/external_authorization/client_spec.rb
new file mode 100644
index 00000000000..fa18c1e56e8
--- /dev/null
+++ b/spec/lib/gitlab/external_authorization/client_spec.rb
@@ -0,0 +1,97 @@
+require 'spec_helper'
+
+describe Gitlab::ExternalAuthorization::Client do
+ let(:user) { build(:user, email: 'dummy_user@example.com') }
+ let(:dummy_url) { 'https://dummy.net/' }
+ subject(:client) { described_class.new(user, 'dummy_label') }
+
+ before do
+ stub_application_setting(external_authorization_service_url: dummy_url)
+ end
+
+ describe '#request_access' do
+ it 'performs requests to the configured endpoint' do
+ expect(Excon).to receive(:post).with(dummy_url, any_args)
+
+ client.request_access
+ end
+
+ it 'adds the correct params for the user to the body of the request' do
+ expected_body = {
+ user_identifier: 'dummy_user@example.com',
+ project_classification_label: 'dummy_label'
+ }.to_json
+ expect(Excon).to receive(:post)
+ .with(dummy_url, hash_including(body: expected_body))
+
+ client.request_access
+ end
+
+ it 'respects the the timeout' do
+ stub_application_setting(
+ external_authorization_service_timeout: 3
+ )
+
+ expect(Excon).to receive(:post).with(dummy_url,
+ hash_including(
+ connect_timeout: 3,
+ read_timeout: 3,
+ write_timeout: 3
+ ))
+
+ client.request_access
+ end
+
+ it 'adds the mutual tls params when they are present' do
+ stub_application_setting(
+ external_auth_client_cert: 'the certificate data',
+ external_auth_client_key: 'the key data',
+ external_auth_client_key_pass: 'open sesame'
+ )
+ expected_params = {
+ client_cert_data: 'the certificate data',
+ client_key_data: 'the key data',
+ client_key_pass: 'open sesame'
+ }
+
+ expect(Excon).to receive(:post).with(dummy_url, hash_including(expected_params))
+
+ client.request_access
+ end
+
+ it 'returns an expected response' do
+ expect(Excon).to receive(:post)
+
+ expect(client.request_access)
+ .to be_kind_of(::Gitlab::ExternalAuthorization::Response)
+ end
+
+ it 'wraps exceptions if the request fails' do
+ expect(Excon).to receive(:post) { raise Excon::Error.new('the request broke') }
+
+ expect { client.request_access }
+ .to raise_error(::Gitlab::ExternalAuthorization::RequestFailed)
+ end
+
+ describe 'for ldap users' do
+ let(:user) do
+ create(:omniauth_user,
+ email: 'dummy_user@example.com',
+ extern_uid: 'external id',
+ provider: 'ldapprovider')
+ end
+
+ it 'includes the ldap dn for ldap users' do
+ expected_body = {
+ user_identifier: 'dummy_user@example.com',
+ project_classification_label: 'dummy_label',
+ user_ldap_dn: 'external id'
+ }.to_json
+ expect(Excon).to receive(:post)
+ .with(dummy_url, hash_including(body: expected_body))
+
+ client.request_access
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/external_authorization/logger_spec.rb b/spec/lib/gitlab/external_authorization/logger_spec.rb
new file mode 100644
index 00000000000..81f1b2390e6
--- /dev/null
+++ b/spec/lib/gitlab/external_authorization/logger_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+describe Gitlab::ExternalAuthorization::Logger do
+ let(:request_time) { Time.parse('2018-03-26 20:22:15') }
+
+ def fake_access(has_access, user, load_type = :request)
+ access = double('access')
+ allow(access).to receive_messages(user: user,
+ has_access?: has_access,
+ loaded_at: request_time,
+ label: 'dummy_label',
+ load_type: load_type)
+
+ access
+ end
+
+ describe '.log_access' do
+ it 'logs a nice message for an access request' do
+ expected_message = "GRANTED admin@example.com access to 'dummy_label' (the/project/path)"
+ fake_access = fake_access(true, build(:user, email: 'admin@example.com'))
+
+ expect(described_class).to receive(:info).with(expected_message)
+
+ described_class.log_access(fake_access, 'the/project/path')
+ end
+
+ it 'does not trip without a project path' do
+ expected_message = "DENIED admin@example.com access to 'dummy_label'"
+ fake_access = fake_access(false, build(:user, email: 'admin@example.com'))
+
+ expect(described_class).to receive(:info).with(expected_message)
+
+ described_class.log_access(fake_access, nil)
+ end
+
+ it 'adds the load time for cached accesses' do
+ expected_message = "DENIED admin@example.com access to 'dummy_label' - cache #{request_time}"
+ fake_access = fake_access(false, build(:user, email: 'admin@example.com'), :cache)
+
+ expect(described_class).to receive(:info).with(expected_message)
+
+ described_class.log_access(fake_access, nil)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/external_authorization/response_spec.rb b/spec/lib/gitlab/external_authorization/response_spec.rb
new file mode 100644
index 00000000000..43211043eca
--- /dev/null
+++ b/spec/lib/gitlab/external_authorization/response_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+describe Gitlab::ExternalAuthorization::Response do
+ let(:excon_response) { double }
+ subject(:response) { described_class.new(excon_response) }
+
+ describe '#valid?' do
+ it 'is valid for 200, 401, and 403 responses' do
+ [200, 401, 403].each do |status|
+ allow(excon_response).to receive(:status).and_return(status)
+
+ expect(response).to be_valid
+ end
+ end
+
+ it "is invalid for other statuses" do
+ expect(excon_response).to receive(:status).and_return(500)
+
+ expect(response).not_to be_valid
+ end
+ end
+
+ describe '#reason' do
+ it 'returns a reason if it was included in the response body' do
+ expect(excon_response).to receive(:body).and_return({ reason: 'Not authorized' }.to_json)
+
+ expect(response.reason).to eq('Not authorized')
+ end
+
+ it 'returns nil when there was no body' do
+ expect(excon_response).to receive(:body).and_return('')
+
+ expect(response.reason).to eq(nil)
+ end
+ end
+
+ describe '#successful?' do
+ it 'is `true` if the status is 200' do
+ allow(excon_response).to receive(:status).and_return(200)
+
+ expect(response).to be_successful
+ end
+
+ it 'is `false` if the status is 401 or 403' do
+ [401, 403].each do |status|
+ allow(excon_response).to receive(:status).and_return(status)
+
+ expect(response).not_to be_successful
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/external_authorization_spec.rb b/spec/lib/gitlab/external_authorization_spec.rb
new file mode 100644
index 00000000000..7394fbfe0ce
--- /dev/null
+++ b/spec/lib/gitlab/external_authorization_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe Gitlab::ExternalAuthorization, :request_store do
+ include ExternalAuthorizationServiceHelpers
+
+ let(:user) { build(:user) }
+ let(:label) { 'dummy_label' }
+
+ describe '#access_allowed?' do
+ it 'is always true when the feature is disabled' do
+ # Not using `stub_application_setting` because the method is prepended in
+ # `EE::ApplicationSetting` which breaks when using `any_instance`
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/33587
+ expect(::Gitlab::CurrentSettings.current_application_settings)
+ .to receive(:external_authorization_service_enabled) { false }
+
+ expect(described_class).not_to receive(:access_for_user_to_label)
+
+ expect(described_class.access_allowed?(user, label)).to be_truthy
+ end
+ end
+
+ describe '#rejection_reason' do
+ it 'is always nil when the feature is disabled' do
+ expect(::Gitlab::CurrentSettings.current_application_settings)
+ .to receive(:external_authorization_service_enabled) { false }
+
+ expect(described_class).not_to receive(:access_for_user_to_label)
+
+ expect(described_class.rejection_reason(user, label)).to be_nil
+ end
+ end
+
+ describe '#access_for_user_to_label' do
+ it 'only loads the access once per request' do
+ enable_external_authorization_service_check
+
+ expect(::Gitlab::ExternalAuthorization::Access)
+ .to receive(:new).with(user, label).once.and_call_original
+
+ 2.times { described_class.access_for_user_to_label(user, label, nil) }
+ end
+
+ it 'logs the access request once per request' do
+ expect(::Gitlab::ExternalAuthorization::Logger)
+ .to receive(:log_access)
+ .with(an_instance_of(::Gitlab::ExternalAuthorization::Access),
+ 'the/project/path')
+ .once
+
+ 2.times { described_class.access_for_user_to_label(user, label, 'the/project/path') }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb b/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
index 26529c4759d..569d5dcc757 100644
--- a/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/issuable_builder_spec.rb
@@ -97,13 +97,13 @@ describe Gitlab::HookData::IssuableBuilder do
end
context 'merge_request is assigned' do
- let(:merge_request) { create(:merge_request, assignee: user) }
+ let(:merge_request) { create(:merge_request, assignees: [user]) }
let(:data) { described_class.new(merge_request).build(user: user) }
it 'returns correct hook data' do
expect(data[:object_attributes]['assignee_id']).to eq(user.id)
- expect(data[:assignee]).to eq(user.hook_attrs)
- expect(data).not_to have_key(:assignees)
+ expect(data[:assignees].first).to eq(user.hook_attrs)
+ expect(data).not_to have_key(:assignee)
end
end
end
diff --git a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
index 9ce697adbba..39f80f92fa6 100644
--- a/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
+++ b/spec/lib/gitlab/hook_data/merge_request_builder_spec.rb
@@ -10,6 +10,7 @@ describe Gitlab::HookData::MergeRequestBuilder do
it 'includes safe attribute' do
%w[
assignee_id
+ assignee_ids
author_id
created_at
description
diff --git a/spec/lib/gitlab/import/merge_request_helpers_spec.rb b/spec/lib/gitlab/import/merge_request_helpers_spec.rb
new file mode 100644
index 00000000000..cc0f2baf905
--- /dev/null
+++ b/spec/lib/gitlab/import/merge_request_helpers_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Import::MergeRequestHelpers, type: :helper do
+ set(:project) { create(:project, :repository) }
+ set(:user) { create(:user) }
+
+ describe '.create_merge_request_without_hooks' do
+ let(:iid) { 42 }
+
+ let(:attributes) do
+ {
+ iid: iid,
+ title: 'My Pull Request',
+ description: 'This is my pull request',
+ source_project_id: project.id,
+ target_project_id: project.id,
+ source_branch: 'master-42',
+ target_branch: 'master',
+ state: :merged,
+ author_id: user.id,
+ assignee_id: user.id
+ }
+ end
+
+ subject { helper.create_merge_request_without_hooks(project, attributes, iid) }
+
+ context 'when merge request does not exist' do
+ it 'returns a new object' do
+ expect(subject.first).not_to be_nil
+ expect(subject.second).to eq(false)
+ end
+
+ it 'does load all existing objects' do
+ 5.times do |iid|
+ MergeRequest.create!(
+ attributes.merge(iid: iid, source_branch: iid.to_s))
+ end
+
+ # does ensure that we only load object twice
+ # 1. by #insert_and_return_id
+ # 2. by project.merge_requests.find
+ expect_any_instance_of(MergeRequest).to receive(:attributes)
+ .twice.times.and_call_original
+
+ expect(subject.first).not_to be_nil
+ expect(subject.second).to eq(false)
+ end
+ end
+
+ context 'when merge request does exist' do
+ before do
+ MergeRequest.create!(attributes)
+ end
+
+ it 'returns an existing object' do
+ expect(subject.first).not_to be_nil
+ expect(subject.second).to eq(true)
+ end
+ end
+
+ context 'when project is deleted' do
+ before do
+ project.delete
+ end
+
+ it 'returns an existing object' do
+ expect(subject.first).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index e418516569a..ed557ffd4e3 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -102,6 +102,7 @@ merge_requests:
- merge_request_pipelines
- merge_request_assignees
- suggestions
+- assignees
merge_request_diff:
- merge_request
- merge_request_diff_commits
@@ -336,6 +337,9 @@ push_event_payload:
issue_assignees:
- issue
- assignee
+merge_request_assignees:
+- merge_request
+- assignee
lfs_file_locks:
- user
project_badges:
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index d0ed588f05f..ebb62124cb1 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -496,6 +496,7 @@ Project:
- merge_requests_ff_only_enabled
- merge_requests_rebase_enabled
- jobs_cache_index
+- external_authorization_classification_label
- pages_https_only
Author:
- name
@@ -621,3 +622,7 @@ Suggestion:
- outdated
- lines_above
- lines_below
+MergeRequestAssignee:
+- id
+- user_id
+- merge_request_id
diff --git a/spec/lib/gitlab/issuable_metadata_spec.rb b/spec/lib/gitlab/issuable_metadata_spec.rb
index 6ec86163233..916f3876a8e 100644
--- a/spec/lib/gitlab/issuable_metadata_spec.rb
+++ b/spec/lib/gitlab/issuable_metadata_spec.rb
@@ -19,7 +19,7 @@ describe Gitlab::IssuableMetadata do
let!(:closed_issue) { create(:issue, state: :closed, author: user, project: project) }
let!(:downvote) { create(:award_emoji, :downvote, awardable: closed_issue) }
let!(:upvote) { create(:award_emoji, :upvote, awardable: issue) }
- let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
+ let!(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, target_project: project, title: "Test") }
let!(:closing_issues) { create(:merge_requests_closing_issues, issue: issue, merge_request: merge_request) }
it 'aggregates stats on issues' do
@@ -39,7 +39,7 @@ describe Gitlab::IssuableMetadata do
end
context 'merge requests' do
- let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test") }
+ let!(:merge_request) { create(:merge_request, :simple, author: user, assignees: [user], source_project: project, target_project: project, title: "Test") }
let!(:merge_request_closed) { create(:merge_request, state: "closed", source_project: project, target_project: project, title: "Closed Test") }
let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request) }
let!(:upvote) { create(:award_emoji, :upvote, awardable: merge_request) }
diff --git a/spec/lib/gitlab/kubernetes/namespace_spec.rb b/spec/lib/gitlab/kubernetes/namespace_spec.rb
index e1c35c355f4..e91a755aa03 100644
--- a/spec/lib/gitlab/kubernetes/namespace_spec.rb
+++ b/spec/lib/gitlab/kubernetes/namespace_spec.rb
@@ -62,5 +62,32 @@ describe Gitlab::Kubernetes::Namespace do
subject.ensure_exists!
end
+
+ context 'when client errors' do
+ let(:exception) { Kubeclient::HttpError.new(500, 'system failure', nil) }
+
+ before do
+ allow(client).to receive(:get_namespace).with(name).once.and_raise(exception)
+ end
+
+ it 'raises the exception' do
+ expect { subject.ensure_exists! }.to raise_error(exception)
+ end
+
+ it 'logs the error' do
+ expect(subject.send(:logger)).to receive(:error).with(
+ hash_including(
+ exception: 'Kubeclient::HttpError',
+ status_code: 500,
+ namespace: 'a_namespace',
+ class_name: 'Gitlab::Kubernetes::Namespace',
+ event: :failed_to_create_namespace,
+ message: 'system failure'
+ )
+ )
+
+ expect { subject.ensure_exists! }.to raise_error(exception)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
index 3d4240fa4ba..8675d8691c8 100644
--- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
@@ -47,12 +47,22 @@ describe Gitlab::LegacyGithubImport::ProjectCreator do
end
context 'when GitHub project is public' do
- it 'sets project visibility to public' do
+ it 'sets project visibility to namespace visibility level' do
repo.private = false
-
project = service.execute
- expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
+ expect(project.visibility_level).to eq(namespace.visibility_level)
+ end
+
+ context 'when importing into a user namespace' do
+ subject(:service) { described_class.new(repo, repo.name, user.namespace, user, github_access_token: 'asdffg') }
+
+ it 'sets project visibility to user namespace visibility level' do
+ repo.private = false
+ project = service.execute
+
+ expect(project.visibility_level).to eq(user.namespace.visibility_level)
+ end
end
end
diff --git a/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
index 082e3b36dd0..c57b96fb00d 100644
--- a/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/release_formatter_spec.rb
@@ -25,6 +25,7 @@ describe Gitlab::LegacyGithubImport::ReleaseFormatter do
expected = {
project: project,
tag: 'v1.0.0',
+ name: 'First release',
description: 'Release v1.0.0',
created_at: created_at,
updated_at: created_at
diff --git a/spec/lib/gitlab/push_options_spec.rb b/spec/lib/gitlab/push_options_spec.rb
new file mode 100644
index 00000000000..fc9e421bea6
--- /dev/null
+++ b/spec/lib/gitlab/push_options_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::PushOptions do
+ describe 'namespace and key validation' do
+ it 'ignores unrecognised namespaces' do
+ options = described_class.new(['invalid.key=value'])
+
+ expect(options.get(:invalid)).to eq(nil)
+ end
+
+ it 'ignores unrecognised keys' do
+ options = described_class.new(['merge_request.key=value'])
+
+ expect(options.get(:merge_request)).to eq(nil)
+ end
+
+ it 'ignores blank keys' do
+ options = described_class.new(['merge_request'])
+
+ expect(options.get(:merge_request)).to eq(nil)
+ end
+
+ it 'parses recognised namespace and key pairs' do
+ options = described_class.new(['merge_request.target=value'])
+
+ expect(options.get(:merge_request)).to include({
+ target: 'value'
+ })
+ end
+ end
+
+ describe '#get' do
+ it 'can emulate Hash#dig' do
+ options = described_class.new(['merge_request.target=value'])
+
+ expect(options.get(:merge_request, :target)).to eq('value')
+ end
+ end
+
+ describe '#as_json' do
+ it 'returns all options' do
+ options = described_class.new(['merge_request.target=value'])
+
+ expect(options.as_json).to include(
+ merge_request: {
+ target: 'value'
+ }
+ )
+ end
+ end
+
+ it 'can parse multiple push options' do
+ options = described_class.new([
+ 'merge_request.create',
+ 'merge_request.target=value'
+ ])
+
+ expect(options.get(:merge_request)).to include({
+ create: true,
+ target: 'value'
+ })
+ expect(options.get(:merge_request, :create)).to eq(true)
+ expect(options.get(:merge_request, :target)).to eq('value')
+ end
+
+ it 'stores options internally as a HashWithIndifferentAccess' do
+ options = described_class.new([
+ 'merge_request.create'
+ ])
+
+ expect(options.get('merge_request', 'create')).to eq(true)
+ expect(options.get(:merge_request, :create)).to eq(true)
+ end
+
+ it 'selects the last option when options contain duplicate namespace and key pairs' do
+ options = described_class.new([
+ 'merge_request.target=value1',
+ 'merge_request.target=value2'
+ ])
+
+ expect(options.get(:merge_request, :target)).to eq('value2')
+ end
+
+ it 'defaults values to true' do
+ options = described_class.new(['merge_request.create'])
+
+ expect(options.get(:merge_request, :create)).to eq(true)
+ end
+
+ it 'expands aliases' do
+ options = described_class.new(['mr.target=value'])
+
+ expect(options.get(:merge_request, :target)).to eq('value')
+ end
+
+ it 'forgives broken push options' do
+ options = described_class.new(['merge_request . target = value'])
+
+ expect(options.get(:merge_request, :target)).to eq('value')
+ end
+end