diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-11 09:09:11 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-11 09:09:11 +0000 |
commit | e348fb4c1b9eaf21655001dc4346ceb0c0c3d5b4 (patch) | |
tree | c11afe15edfe85d809ab0be78a6f52a539d28bec /spec | |
parent | ce567e98da6118031576d9084d3e05473746e4c6 (diff) | |
download | gitlab-ce-e348fb4c1b9eaf21655001dc4346ceb0c0c3d5b4.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
25 files changed, 657 insertions, 407 deletions
diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb index 0b940c552e5..10568d7f1cd 100644 --- a/spec/factories/integrations.rb +++ b/spec/factories/integrations.rb @@ -108,6 +108,7 @@ FactoryBot.define do api_url { '' } username { 'jira_username' } password { 'jira_password' } + jira_auth_type { 0 } jira_issue_transition_automatic { false } jira_issue_transition_id { '56-1' } issues_enabled { false } @@ -123,6 +124,7 @@ FactoryBot.define do if evaluator.create_data integration.jira_tracker_data = build(:jira_tracker_data, integration: integration, url: evaluator.url, api_url: evaluator.api_url, + jira_auth_type: evaluator.jira_auth_type, jira_issue_transition_automatic: evaluator.jira_issue_transition_automatic, jira_issue_transition_id: evaluator.jira_issue_transition_id, username: evaluator.username, password: evaluator.password, issues_enabled: evaluator.issues_enabled, @@ -224,6 +226,7 @@ FactoryBot.define do url { 'https://mysite.atlassian.net' } username { 'jira_user' } password { 'my-secret-password' } + jira_auth_type { 0 } end trait :chat_notification do diff --git a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb index e0063a9c733..9ff344bcc88 100644 --- a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb +++ b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb @@ -22,17 +22,17 @@ RSpec.describe 'User uses inherited settings', :js, feature_category: :integrati expect(page).not_to have_button('Use custom settings') expect(page).to have_field('Web URL', with: parent_settings[:url], readonly: true) - expect(page).to have_field('Enter new password or API token', with: '', readonly: true) + expect(page).to have_field('New API token, password, or Jira personal access token', with: '', readonly: true) click_on 'Use default settings' click_on 'Use custom settings' expect(page).not_to have_button('Use default settings') expect(page).to have_field('Web URL', with: project_settings[:url], readonly: false) - expect(page).to have_field('Enter new password or API token', with: '', readonly: false) + expect(page).to have_field('New API token, password, or Jira personal access token', with: '', readonly: false) fill_in 'Web URL', with: 'http://custom.com' - fill_in 'Enter new password or API token', with: 'custom' + fill_in 'New API token, password, or Jira personal access token', with: 'custom' click_save_integration @@ -53,14 +53,14 @@ RSpec.describe 'User uses inherited settings', :js, feature_category: :integrati expect(page).not_to have_button('Use default settings') expect(page).to have_field('URL', with: project_settings[:url], readonly: false) - expect(page).to have_field('Enter new password or API token', with: '', readonly: false) + expect(page).to have_field('New API token, password, or Jira personal access token', with: '', readonly: false) click_on 'Use custom settings' click_on 'Use default settings' expect(page).not_to have_button('Use custom settings') expect(page).to have_field('URL', with: parent_settings[:url], readonly: true) - expect(page).to have_field('Enter new password or API token', with: '', readonly: true) + expect(page).to have_field('New API token, password, or Jira personal access token', with: '', readonly: true) click_save_integration diff --git a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap index 17681c08dbd..c8d972b19a3 100644 --- a/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap +++ b/spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap @@ -24,7 +24,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] = </div> <div - class="js-vue-markdown-field md-area position-relative gfm-form gl-border-none! gl-shadow-none! js-expanded" + class="js-vue-markdown-field md-area position-relative gfm-form js-expanded" data-uploads-path="" > <markdown-header-stub diff --git a/spec/lib/api/entities/package_spec.rb b/spec/lib/api/entities/package_spec.rb index 9288f6fe8eb..53d9a0b4557 100644 --- a/spec/lib/api/entities/package_spec.rb +++ b/spec/lib/api/entities/package_spec.rb @@ -40,4 +40,13 @@ RSpec.describe API::Entities::Package do expect(subject[:_links]).not_to have_key(:web_path) end end + + context 'with build info' do + let_it_be(:project) { create(:project) } + let_it_be(:package) { create(:npm_package, :with_build, project: project) } + + it 'returns an empty array for pipelines' do + expect(subject[:pipelines]).to eq([]) + end + end end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index 0073d2ebe80..1ba526a6eb3 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -534,7 +534,7 @@ RSpec.describe Gitlab::GitalyClient, feature_category: :gitaly do context 'when RequestStore is enabled with empty git_env', :request_store do before do - Gitlab::SafeRequestStore[:gitlab_git_env] = {} + ::Gitlab::Instrumentation::Storage[:gitlab_git_env] = {} end it 'disables force-routing to primary' do @@ -544,7 +544,7 @@ RSpec.describe Gitlab::GitalyClient, feature_category: :gitaly do context 'when RequestStore is enabled with populated git_env', :request_store do before do - Gitlab::SafeRequestStore[:gitlab_git_env] = { + ::Gitlab::Instrumentation::Storage[:gitlab_git_env] = { "GIT_OBJECT_DIRECTORY_RELATIVE" => "foo/bar" } end diff --git a/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb b/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb index 33f49dbc8d4..1899697c78e 100644 --- a/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb +++ b/spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::Graphql::CallsGitaly::FieldExtension, :request_store do context 'when the field calls gitaly' do before do owner.define_method :value do - Gitlab::SafeRequestStore['gitaly_call_actual'] = 1 + ::Gitlab::Instrumentation::Storage['gitaly_call_actual'] = 1 'fresh-from-the-gitaly-mines!' end end @@ -64,22 +64,22 @@ RSpec.describe Gitlab::Graphql::CallsGitaly::FieldExtension, :request_store do object = :anything arguments = :any_args - ::Gitlab::SafeRequestStore['gitaly_call_actual'] = 3 - ::Gitlab::SafeRequestStore['graphql_gitaly_accounted_for'] = 0 + ::Gitlab::Instrumentation::Storage['gitaly_call_actual'] = 3 + ::Gitlab::Instrumentation::Storage['graphql_gitaly_accounted_for'] = 0 expect do |b| extension.resolve(object: object, arguments: arguments, &b) end.to yield_with_args(object, arguments, [3, 0]) - ::Gitlab::SafeRequestStore['gitaly_call_actual'] = 13 - ::Gitlab::SafeRequestStore['graphql_gitaly_accounted_for'] = 10 + ::Gitlab::Instrumentation::Storage['gitaly_call_actual'] = 13 + ::Gitlab::Instrumentation::Storage['graphql_gitaly_accounted_for'] = 10 expect { extension.after_resolve(value: 'foo', memo: [3, 0]) }.not_to raise_error end it 'is unacceptable if some of the calls are unaccounted for' do - ::Gitlab::SafeRequestStore['gitaly_call_actual'] = 10 - ::Gitlab::SafeRequestStore['graphql_gitaly_accounted_for'] = 9 + ::Gitlab::Instrumentation::Storage['gitaly_call_actual'] = 10 + ::Gitlab::Instrumentation::Storage['graphql_gitaly_accounted_for'] = 9 expect { extension.after_resolve(value: 'foo', memo: [0, 0]) }.to raise_error(include('Object.value')) end diff --git a/spec/lib/gitlab/instrumentation/storage_spec.rb b/spec/lib/gitlab/instrumentation/storage_spec.rb new file mode 100644 index 00000000000..afff8f6251b --- /dev/null +++ b/spec/lib/gitlab/instrumentation/storage_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Gitlab::Instrumentation::Storage, :request_store, feature_category: :shared do + subject(:storage) { described_class } + + describe '.active?' do + context 'when SafeRequestStore is active' do + it 'returns true' do + allow(Gitlab::SafeRequestStore).to receive(:active?).and_return(true) + + expect(storage.active?).to be(true) + end + end + + context 'when SafeRequestStore is not active' do + it 'returns false' do + allow(Gitlab::SafeRequestStore).to receive(:active?).and_return(false) + + expect(storage.active?).to be(false) + end + end + end + + it 'stores data' do + storage[:a] = 1 + storage[:b] = 'hey' + + expect(storage[:a]).to eq(1) + expect(storage[:b]).to eq('hey') + end + + describe '.clear!' do + it 'removes all values' do + storage[:a] = 1 + storage[:b] = 'hey' + + storage.clear! + + expect(storage[:a]).to be_nil + expect(storage[:b]).to be_nil + end + end + + # This is testing implementation details, but until we have a truly segregated + # instrumentation data store, we need to make sure we do not "pollute" the + # underlying RequestStore or interfere with other co-located data. + describe 'backing storage' do + it 'stores data in the instrumentation bucket' do + storage[:a] = 1 + + expect(::RequestStore[:instrumentation]).to eq({ a: 1 }) + end + + describe '.clear!' do + it 'resets only the instrumentation bucket' do + storage[:a] = 1 + storage[:b] = 'hey' + ::RequestStore[:b] = 2 + + storage.clear! + + expect(::RequestStore[:instrumentation]).to eq({}) + expect(::RequestStore[:b]).to eq(2) + end + end + end +end diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb index 8a88328e0c1..b934f2261a4 100644 --- a/spec/lib/gitlab/instrumentation_helper_spec.rb +++ b/spec/lib/gitlab/instrumentation_helper_spec.rb @@ -8,6 +8,14 @@ RSpec.describe Gitlab::InstrumentationHelper, :clean_gitlab_redis_repository_cac :use_null_store_as_repository_cache, feature_category: :scalability do using RSpec::Parameterized::TableSyntax + describe '.init_instrumentation_data' do + it 'clears instrumentation storage' do + expect(::Gitlab::Instrumentation::Storage).to receive(:clear!) + + described_class.init_instrumentation_data + end + end + describe '.add_instrumentation_data', :request_store do let(:payload) { {} } @@ -26,7 +34,7 @@ RSpec.describe Gitlab::InstrumentationHelper, :clean_gitlab_redis_repository_cac context 'when Gitaly calls are made' do it 'adds Gitaly and Redis data' do project = create(:project) - RequestStore.clear! + ::Gitlab::Instrumentation::Storage.clear! project.repository.exists? subject @@ -181,8 +189,8 @@ RSpec.describe Gitlab::InstrumentationHelper, :clean_gitlab_redis_repository_cac context 'when replica caught up search was made' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick_ok] = 2 - Gitlab::SafeRequestStore[:caught_up_replica_pick_fail] = 1 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_ok] = 2 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_fail] = 1 end it 'includes related metrics' do @@ -195,8 +203,8 @@ RSpec.describe Gitlab::InstrumentationHelper, :clean_gitlab_redis_repository_cac context 'when only a single counter was updated' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick_ok] = 1 - Gitlab::SafeRequestStore[:caught_up_replica_pick_fail] = nil + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_ok] = 1 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_fail] = nil end it 'includes only that counter into logging' do diff --git a/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb b/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb index 18a5d2c2c3f..cccfb5d8c99 100644 --- a/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/external_http_spec.rb @@ -67,7 +67,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store, featu context 'when external HTTP detail store has some values' do before do Gitlab::SafeRequestStore[:peek_enabled] = true - Gitlab::SafeRequestStore[:external_http_detail_store] = [{ + ::Gitlab::Instrumentation::Storage[:external_http_detail_store] = [{ method: 'POST', code: "200", duration: 0.321 }] end @@ -87,8 +87,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store, featu context 'when external HTTP recorded some values' do before do - Gitlab::SafeRequestStore[:external_http_count] = 7 - Gitlab::SafeRequestStore[:external_http_duration_s] = 1.2 + ::Gitlab::Instrumentation::Storage[:external_http_count] = 7 + ::Gitlab::Instrumentation::Storage[:external_http_duration_s] = 1.2 end it 'returns the external http detailed store' do @@ -133,7 +133,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store, featu ) expect(described_class.top_slowest_requests).to eq(slow_requests) - expect(Gitlab::SafeRequestStore[:external_http_slow_requests].length).to eq(3) + expect(::Gitlab::Instrumentation::Storage[:external_http_slow_requests].length).to eq(3) end end end @@ -181,8 +181,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store, featu subscriber.request(event_2) subscriber.request(event_3) - expect(Gitlab::SafeRequestStore[:external_http_count]).to eq(3) - expect(Gitlab::SafeRequestStore[:external_http_duration_s]).to eq(5.741) # 0.321 + 0.12 + 5.3 + expect(::Gitlab::Instrumentation::Storage[:external_http_count]).to eq(3) + expect(::Gitlab::Instrumentation::Storage[:external_http_duration_s]).to eq(5.741) # 0.321 + 0.12 + 5.3 end it 'stores a portion of events into the detail store' do @@ -190,22 +190,22 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store, featu subscriber.request(event_2) subscriber.request(event_3) - expect(Gitlab::SafeRequestStore[:external_http_detail_store].length).to eq(3) - expect(Gitlab::SafeRequestStore[:external_http_detail_store][0]).to match a_hash_including( + expect(::Gitlab::Instrumentation::Storage[:external_http_detail_store].length).to eq(3) + expect(::Gitlab::Instrumentation::Storage[:external_http_detail_store][0]).to match a_hash_including( start: be_like_time(Time.current), method: 'POST', code: "200", duration: 0.321, scheme: 'https', host: 'gitlab.com', port: 443, path: '/api/v4/projects', query: 'current=true', exception_object: nil, backtrace: be_a(Array) ) - expect(Gitlab::SafeRequestStore[:external_http_detail_store][1]).to match a_hash_including( + expect(::Gitlab::Instrumentation::Storage[:external_http_detail_store][1]).to match a_hash_including( start: be_like_time(Time.current), method: 'GET', code: "301", duration: 0.12, scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2', query: 'current=true', exception_object: nil, backtrace: be_a(Array) ) - expect(Gitlab::SafeRequestStore[:external_http_detail_store][2]).to match a_hash_including( + expect(::Gitlab::Instrumentation::Storage[:external_http_detail_store][2]).to match a_hash_including( start: be_like_time(Time.current), method: 'POST', duration: 5.3, scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues', @@ -225,7 +225,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store, featu subscriber.request(event_2) subscriber.request(event_3) - expect(Gitlab::SafeRequestStore[:external_http_detail_store]).to be(nil) + expect(::Gitlab::Instrumentation::Storage[:external_http_detail_store]).to be(nil) end end end diff --git a/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb b/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb index fb822c8d779..1db8659943e 100644 --- a/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/ldap_spec.rb @@ -66,7 +66,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::Ldap, :request_store, feature_categ end describe ".payload" do - context "when SafeRequestStore is empty" do + context "when instrumentation storage is empty" do it "returns an empty array" do expect(described_class.payload).to eql(net_ldap_count: 0, net_ldap_duration_s: 0.0) end @@ -74,8 +74,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::Ldap, :request_store, feature_categ context "when LDAP recorded some values" do before do - Gitlab::SafeRequestStore[:net_ldap_count] = 7 - Gitlab::SafeRequestStore[:net_ldap_duration_s] = 1.2 + ::Gitlab::Instrumentation::Storage[:net_ldap_count] = 7 + ::Gitlab::Instrumentation::Storage[:net_ldap_duration_s] = 1.2 end it "returns the populated payload" do @@ -117,8 +117,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::Ldap, :request_store, feature_categ subscriber.observe_event(event_2) subscriber.observe_event(event_3) - expect(Gitlab::SafeRequestStore[:net_ldap_count]).to eq(3) - expect(Gitlab::SafeRequestStore[:net_ldap_duration_s]).to eq(0.005741) # (0.321 + 0.12 + 5.3) / 1000 + expect(::Gitlab::Instrumentation::Storage[:net_ldap_count]).to eq(3) + expect(::Gitlab::Instrumentation::Storage[:net_ldap_duration_s]).to eq(0.005741) # (0.321 + 0.12 + 5.3) / 1000 end end end diff --git a/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb b/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb index c2c3bb29b16..be04167a588 100644 --- a/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store, feat it 'stores per-request caught up replica search result' do subject - expect(Gitlab::SafeRequestStore[counter_name]).to eq(1) + expect(::Gitlab::Instrumentation::Storage[counter_name]).to eq(1) end end @@ -50,7 +50,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store, feat context 'when no data in request store' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick] = nil + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick] = nil end it 'does not change the counters' do @@ -60,8 +60,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store, feat context 'when request store was updated' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick_ok] = 2 - Gitlab::SafeRequestStore[:caught_up_replica_pick_fail] = 1 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_ok] = 2 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_fail] = 1 end it 'increments :caught_up_replica_pick count with proper label' do @@ -78,8 +78,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store, feat context 'when no data in request store' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick_ok] = nil - Gitlab::SafeRequestStore[:caught_up_replica_pick_fail] = nil + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_ok] = nil + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_fail] = nil end it 'returns empty hash' do @@ -89,7 +89,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store, feat context 'when request store was updated for a single counter' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick_ok] = 2 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_ok] = 2 end it 'returns proper payload with only that counter' do @@ -99,8 +99,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store, feat context 'when both counters were updated' do before do - Gitlab::SafeRequestStore[:caught_up_replica_pick_ok] = 2 - Gitlab::SafeRequestStore[:caught_up_replica_pick_fail] = 1 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_ok] = 2 + ::Gitlab::Instrumentation::Storage[:caught_up_replica_pick_fail] = 1 end it 'return proper payload' do diff --git a/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb b/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb index 13965bf1244..a87b1ab4a71 100644 --- a/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do context 'when the request store already has data' do before do - Gitlab::SafeRequestStore[:rack_attack_instrumentation] = { + ::Gitlab::Instrumentation::Storage[:rack_attack_instrumentation] = { rack_attack_redis_count: 10, rack_attack_redis_duration_s: 9.0 } @@ -239,7 +239,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do it 'adds the matched name to safe request store' do subscriber.safelist(event) - expect(Gitlab::SafeRequestStore[:instrumentation_throttle_safelist]).to eql('throttle_unauthenticated') + expect(::Gitlab::Instrumentation::Storage[:instrumentation_throttle_safelist]).to eql('throttle_unauthenticated') end end end diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb index 640cf9be453..b076bb65fb5 100644 --- a/spec/lib/gitlab/project_authorizations_spec.rb +++ b/spec/lib/gitlab/project_authorizations_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::ProjectAuthorizations do +RSpec.describe Gitlab::ProjectAuthorizations, feature_category: :system_access do def map_access_levels(rows) rows.each_with_object({}) do |row, hash| hash[row.project_id] = row.access_level @@ -13,408 +13,421 @@ RSpec.describe Gitlab::ProjectAuthorizations do described_class.new(user).calculate end - context 'user added to group and project' do - let(:group) { create(:group) } - let!(:other_project) { create(:project) } - let!(:group_project) { create(:project, namespace: group) } - let!(:owned_project) { create(:project) } - let(:user) { owned_project.namespace.owner } + # Inline this shared example while cleaning up feature flag linear_project_authorization + RSpec.shared_examples 'project authorizations' do + context 'user added to group and project' do + let(:group) { create(:group) } + let!(:other_project) { create(:project) } + let!(:group_project) { create(:project, namespace: group) } + let!(:owned_project) { create(:project) } + let(:user) { owned_project.namespace.owner } - before do - other_project.add_reporter(user) - group.add_developer(user) - end + before do + other_project.add_reporter(user) + group.add_developer(user) + end - it 'returns the correct number of authorizations' do - expect(authorizations.length).to eq(3) - end + it 'returns the correct number of authorizations' do + expect(authorizations.length).to eq(3) + end - it 'includes the correct projects' do - expect(authorizations.pluck(:project_id)) - .to include(owned_project.id, other_project.id, group_project.id) - end + it 'includes the correct projects' do + expect(authorizations.pluck(:project_id)) + .to include(owned_project.id, other_project.id, group_project.id) + end - it 'includes the correct access levels' do - mapping = map_access_levels(authorizations) + it 'includes the correct access levels' do + mapping = map_access_levels(authorizations) - expect(mapping[owned_project.id]).to eq(Gitlab::Access::OWNER) - expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER) - expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER) + expect(mapping[owned_project.id]).to eq(Gitlab::Access::OWNER) + expect(mapping[other_project.id]).to eq(Gitlab::Access::REPORTER) + expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER) + end end - end - context 'unapproved access request' do - let_it_be(:group) { create(:group) } - let_it_be(:user) { create(:user) } + context 'unapproved access request' do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } - subject(:mapping) { map_access_levels(authorizations) } + subject(:mapping) { map_access_levels(authorizations) } - context 'group membership' do - let!(:group_project) { create(:project, namespace: group) } + context 'group membership' do + let!(:group_project) { create(:project, namespace: group) } - before do - create(:group_member, :developer, :access_request, user: user, group: group) - end + before do + create(:group_member, :developer, :access_request, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[group_project.id]).to be_nil + end end - end - context 'inherited group membership' do - let!(:sub_group) { create(:group, parent: group) } - let!(:sub_group_project) { create(:project, namespace: sub_group) } + context 'inherited group membership' do + let!(:sub_group) { create(:group, parent: group) } + let!(:sub_group_project) { create(:project, namespace: sub_group) } - before do - create(:group_member, :developer, :access_request, user: user, group: group) - end + before do + create(:group_member, :developer, :access_request, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[sub_group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[sub_group_project.id]).to be_nil + end end - end - context 'project membership' do - let!(:group_project) { create(:project, namespace: group) } + context 'project membership' do + let!(:group_project) { create(:project, namespace: group) } - before do - create(:project_member, :developer, :access_request, user: user, project: group_project) - end + before do + create(:project_member, :developer, :access_request, user: user, project: group_project) + end - it 'does not create authorization' do - expect(mapping[group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[group_project.id]).to be_nil + end end - end - context 'shared group' do - let!(:shared_group) { create(:group) } - let!(:shared_group_project) { create(:project, namespace: shared_group) } + context 'shared group' do + let!(:shared_group) { create(:group) } + let!(:shared_group_project) { create(:project, namespace: shared_group) } - before do - create(:group_group_link, shared_group: shared_group, shared_with_group: group) - create(:group_member, :developer, :access_request, user: user, group: group) - end + before do + create(:group_group_link, shared_group: shared_group, shared_with_group: group) + create(:group_member, :developer, :access_request, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[shared_group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[shared_group_project.id]).to be_nil + end end - end - context 'shared project' do - let!(:another_group) { create(:group) } - let!(:shared_project) { create(:project, namespace: another_group) } + context 'shared project' do + let!(:another_group) { create(:group) } + let!(:shared_project) { create(:project, namespace: another_group) } - before do - create(:project_group_link, group: group, project: shared_project) - create(:group_member, :developer, :access_request, user: user, group: group) - end + before do + create(:project_group_link, group: group, project: shared_project) + create(:group_member, :developer, :access_request, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[shared_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[shared_project.id]).to be_nil + end end end - end - context 'user with minimal access to group' do - let_it_be(:group) { create(:group) } - let_it_be(:user) { create(:user) } + context 'user with minimal access to group' do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } - subject(:mapping) { map_access_levels(authorizations) } + subject(:mapping) { map_access_levels(authorizations) } - context 'group membership' do - let!(:group_project) { create(:project, namespace: group) } + context 'group membership' do + let!(:group_project) { create(:project, namespace: group) } - before do - create(:group_member, :minimal_access, user: user, source: group) - end + before do + create(:group_member, :minimal_access, user: user, source: group) + end - it 'does not create authorization' do - expect(mapping[group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[group_project.id]).to be_nil + end end - end - context 'inherited group membership' do - let!(:sub_group) { create(:group, parent: group) } - let!(:sub_group_project) { create(:project, namespace: sub_group) } + context 'inherited group membership' do + let!(:sub_group) { create(:group, parent: group) } + let!(:sub_group_project) { create(:project, namespace: sub_group) } - before do - create(:group_member, :minimal_access, user: user, source: group) - end + before do + create(:group_member, :minimal_access, user: user, source: group) + end - it 'does not create authorization' do - expect(mapping[sub_group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[sub_group_project.id]).to be_nil + end end - end - context 'shared group' do - let!(:shared_group) { create(:group) } - let!(:shared_group_project) { create(:project, namespace: shared_group) } + context 'shared group' do + let!(:shared_group) { create(:group) } + let!(:shared_group_project) { create(:project, namespace: shared_group) } - before do - create(:group_group_link, shared_group: shared_group, shared_with_group: group) - create(:group_member, :minimal_access, user: user, source: group) - end + before do + create(:group_group_link, shared_group: shared_group, shared_with_group: group) + create(:group_member, :minimal_access, user: user, source: group) + end - it 'does not create authorization' do - expect(mapping[shared_group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[shared_group_project.id]).to be_nil + end end - end - context 'shared project' do - let!(:another_group) { create(:group) } - let!(:shared_project) { create(:project, namespace: another_group) } + context 'shared project' do + let!(:another_group) { create(:group) } + let!(:shared_project) { create(:project, namespace: another_group) } - before do - create(:project_group_link, group: group, project: shared_project) - create(:group_member, :minimal_access, user: user, source: group) - end + before do + create(:project_group_link, group: group, project: shared_project) + create(:group_member, :minimal_access, user: user, source: group) + end - it 'does not create authorization' do - expect(mapping[shared_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[shared_project.id]).to be_nil + end end end - end - context 'with nested groups' do - let(:group) { create(:group) } - let!(:nested_group) { create(:group, parent: group) } - let!(:nested_project) { create(:project, namespace: nested_group) } - let(:user) { create(:user) } + context 'with nested groups' do + let(:group) { create(:group) } + let!(:nested_group) { create(:group, parent: group) } + let!(:nested_project) { create(:project, namespace: nested_group) } + let(:user) { create(:user) } - before do - group.add_developer(user) - end + before do + group.add_developer(user) + end - it 'includes nested groups' do - expect(authorizations.pluck(:project_id)).to include(nested_project.id) - end + it 'includes nested groups' do + expect(authorizations.pluck(:project_id)).to include(nested_project.id) + end - it 'inherits access levels when the user is not a member of a nested group' do - mapping = map_access_levels(authorizations) + it 'inherits access levels when the user is not a member of a nested group' do + mapping = map_access_levels(authorizations) - expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER) - end + expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER) + end - it 'uses the greatest access level when a user is a member of a nested group' do - nested_group.add_maintainer(user) + it 'uses the greatest access level when a user is a member of a nested group' do + nested_group.add_maintainer(user) - mapping = map_access_levels(authorizations) + mapping = map_access_levels(authorizations) - expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER) + expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER) + end end - end - context 'with shared projects' do - let_it_be(:shared_with_group) { create(:group) } - let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project, group: create(:group)) } + context 'with shared projects' do + let_it_be(:shared_with_group) { create(:group) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, group: create(:group)) } - let(:mapping) { map_access_levels(authorizations) } + let(:mapping) { map_access_levels(authorizations) } - before do - create(:project_group_link, :developer, project: project, group: shared_with_group) - shared_with_group.add_maintainer(user) - end - - it 'creates proper authorizations' do - expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) - end - - context 'even when the `lock_memberships_to_ldap` setting has been turned ON' do before do - stub_application_setting(lock_memberships_to_ldap: true) + create(:project_group_link, :developer, project: project, group: shared_with_group) + shared_with_group.add_maintainer(user) end it 'creates proper authorizations' do expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) end - end - context 'when the group containing the project has forbidden group shares for any of its projects' do - before do - project.namespace.update!(share_with_group_lock: true) + context 'even when the `lock_memberships_to_ldap` setting has been turned ON' do + before do + stub_application_setting(lock_memberships_to_ldap: true) + end + + it 'creates proper authorizations' do + expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) + end end - it 'does not create authorizations' do - expect(mapping[project.id]).to be_nil + context 'when the group containing the project has forbidden group shares for any of its projects' do + before do + project.namespace.update!(share_with_group_lock: true) + end + + it 'does not create authorizations' do + expect(mapping[project.id]).to be_nil + end end end - end - context 'with shared groups' do - let(:parent_group_user) { create(:user) } - let(:group_user) { create(:user) } - let(:child_group_user) { create(:user) } + context 'with shared groups' do + let(:parent_group_user) { create(:user) } + let(:group_user) { create(:user) } + let(:child_group_user) { create(:user) } - let_it_be(:group_parent) { create(:group, :private) } - let_it_be(:group) { create(:group, :private, parent: group_parent) } - let_it_be(:group_child) { create(:group, :private, parent: group) } + let_it_be(:group_parent) { create(:group, :private) } + let_it_be(:group) { create(:group, :private, parent: group_parent) } + let_it_be(:group_child) { create(:group, :private, parent: group) } - let_it_be(:shared_group_parent) { create(:group, :private) } - let_it_be(:shared_group) { create(:group, :private, parent: shared_group_parent) } - let_it_be(:shared_group_child) { create(:group, :private, parent: shared_group) } + let_it_be(:shared_group_parent) { create(:group, :private) } + let_it_be(:shared_group) { create(:group, :private, parent: shared_group_parent) } + let_it_be(:shared_group_child) { create(:group, :private, parent: shared_group) } - let_it_be(:project_parent) { create(:project, group: shared_group_parent) } - let_it_be(:project) { create(:project, group: shared_group) } - let_it_be(:project_child) { create(:project, group: shared_group_child) } + let_it_be(:project_parent) { create(:project, group: shared_group_parent) } + let_it_be(:project) { create(:project, group: shared_group) } + let_it_be(:project_child) { create(:project, group: shared_group_child) } - before do - group_parent.add_owner(parent_group_user) - group.add_owner(group_user) - group_child.add_owner(child_group_user) + before do + group_parent.add_owner(parent_group_user) + group.add_owner(group_user) + group_child.add_owner(child_group_user) - create(:group_group_link, shared_group: shared_group, shared_with_group: group) - end + create(:group_group_link, shared_group: shared_group, shared_with_group: group) + end - context 'group user' do - let(:user) { group_user } + context 'group user' do + let(:user) { group_user } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'creates proper authorizations' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) - expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER) + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) + expect(mapping[project_child.id]).to eq(Gitlab::Access::DEVELOPER) + end end - end - context 'with lower group access level than max access level for share' do - let(:user) { create(:user) } + context 'with lower group access level than max access level for share' do + let(:user) { create(:user) } - it 'creates proper authorizations' do - group.add_reporter(user) + it 'creates proper authorizations' do + group.add_reporter(user) - mapping = map_access_levels(authorizations) + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to eq(Gitlab::Access::REPORTER) - expect(mapping[project_child.id]).to eq(Gitlab::Access::REPORTER) + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to eq(Gitlab::Access::REPORTER) + expect(mapping[project_child.id]).to eq(Gitlab::Access::REPORTER) + end end - end - context 'parent group user' do - let(:user) { parent_group_user } + context 'parent group user' do + let(:user) { parent_group_user } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'creates proper authorizations' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil + end end - end - context 'child group user' do - let(:user) { child_group_user } + context 'child group user' do + let(:user) { child_group_user } - it 'creates proper authorizations' do - mapping = map_access_levels(authorizations) + it 'creates proper authorizations' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil + end end - end - context 'user without accepted access request' do - let!(:user) { create(:user) } + context 'user without accepted access request' do + let!(:user) { create(:user) } - it 'does not have access to group and its projects' do - create(:group_member, :developer, :access_request, user: user, group: group) + it 'does not have access to group and its projects' do + create(:group_member, :developer, :access_request, user: user, group: group) - mapping = map_access_levels(authorizations) + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil + end end - end - context 'unrelated project owner' do - let(:common_id) { non_existing_record_id } - let!(:group) { create(:group, id: common_id) } - let!(:unrelated_project) { create(:project, id: common_id) } - let(:user) { unrelated_project.first_owner } + context 'unrelated project owner' do + let(:common_id) { non_existing_record_id } + let!(:group) { create(:group, id: common_id) } + let!(:unrelated_project) { create(:project, id: common_id) } + let(:user) { unrelated_project.first_owner } - it 'does not have access to group and its projects' do - mapping = map_access_levels(authorizations) + it 'does not have access to group and its projects' do + mapping = map_access_levels(authorizations) - expect(mapping[project_parent.id]).to be_nil - expect(mapping[project.id]).to be_nil - expect(mapping[project_child.id]).to be_nil + expect(mapping[project_parent.id]).to be_nil + expect(mapping[project.id]).to be_nil + expect(mapping[project_child.id]).to be_nil + end end end - end - context 'with pending memberships' do - let_it_be(:group) { create(:group) } - let_it_be(:user) { create(:user) } + context 'with pending memberships' do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } - subject(:mapping) { map_access_levels(authorizations) } + subject(:mapping) { map_access_levels(authorizations) } - context 'group membership' do - let!(:group_project) { create(:project, namespace: group) } + context 'group membership' do + let!(:group_project) { create(:project, namespace: group) } - before do - create(:group_member, :developer, :awaiting, user: user, group: group) - end + before do + create(:group_member, :developer, :awaiting, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[group_project.id]).to be_nil + end end - end - context 'inherited group membership' do - let!(:sub_group) { create(:group, parent: group) } - let!(:sub_group_project) { create(:project, namespace: sub_group) } + context 'inherited group membership' do + let!(:sub_group) { create(:group, parent: group) } + let!(:sub_group_project) { create(:project, namespace: sub_group) } - before do - create(:group_member, :developer, :awaiting, user: user, group: group) - end + before do + create(:group_member, :developer, :awaiting, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[sub_group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[sub_group_project.id]).to be_nil + end end - end - context 'project membership' do - let!(:group_project) { create(:project, namespace: group) } + context 'project membership' do + let!(:group_project) { create(:project, namespace: group) } - before do - create(:project_member, :developer, :awaiting, user: user, project: group_project) - end + before do + create(:project_member, :developer, :awaiting, user: user, project: group_project) + end - it 'does not create authorization' do - expect(mapping[group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[group_project.id]).to be_nil + end end - end - context 'shared group' do - let!(:shared_group) { create(:group) } - let!(:shared_group_project) { create(:project, namespace: shared_group) } + context 'shared group' do + let!(:shared_group) { create(:group) } + let!(:shared_group_project) { create(:project, namespace: shared_group) } - before do - create(:group_group_link, shared_group: shared_group, shared_with_group: group) - create(:group_member, :developer, :awaiting, user: user, group: group) - end + before do + create(:group_group_link, shared_group: shared_group, shared_with_group: group) + create(:group_member, :developer, :awaiting, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[shared_group_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[shared_group_project.id]).to be_nil + end end - end - context 'shared project' do - let!(:another_group) { create(:group) } - let!(:shared_project) { create(:project, namespace: another_group) } + context 'shared project' do + let!(:another_group) { create(:group) } + let!(:shared_project) { create(:project, namespace: another_group) } - before do - create(:project_group_link, group: group, project: shared_project) - create(:group_member, :developer, :awaiting, user: user, group: group) - end + before do + create(:project_group_link, group: group, project: shared_project) + create(:group_member, :developer, :awaiting, user: user, group: group) + end - it 'does not create authorization' do - expect(mapping[shared_project.id]).to be_nil + it 'does not create authorization' do + expect(mapping[shared_project.id]).to be_nil + end end end end + + context 'when feature_flag linear_project_authorization_is disabled' do + before do + stub_feature_flags(linear_project_authorization: false) + end + + it_behaves_like 'project authorizations' + end + + it_behaves_like 'project authorizations' end diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb index 4b589dc43af..459c926fa89 100644 --- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb +++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb @@ -328,7 +328,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do ApplicationRecord.connection.execute('SELECT pg_sleep(0.1);') end - Gitlab::SafeRequestStore.clear! + ::Gitlab::Instrumentation::Storage.clear! call_subject(job.dup, 'test_queue') {} end diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb index af9075f5aa0..7f22dea8528 100644 --- a/spec/lib/gitlab/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb @@ -18,8 +18,8 @@ RSpec.describe Gitlab::SidekiqMiddleware do include ApplicationWorker def perform(*args) - Gitlab::SafeRequestStore['gitaly_call_actual'] = 1 - Gitlab::SafeRequestStore[:gitaly_query_time] = 5 + ::Gitlab::Instrumentation::Storage['gitaly_call_actual'] = 1 + ::Gitlab::Instrumentation::Storage[:gitaly_query_time] = 5 end end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 87beba680d8..d62d7162497 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -2009,9 +2009,10 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ end end - describe '#deploy_freezes' do + describe '#deploy_freezes', :request_store do let(:environment) { create(:environment, project: project, name: 'staging') } let(:freeze_period) { create(:ci_freeze_period, project: project) } + let(:cache_key) { "project:#{project.id}:freeze_periods_for_environments" } subject { environment.deploy_freezes } @@ -2020,11 +2021,9 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ end it 'caches the freeze periods' do - expect(Gitlab::SafeRequestStore).to receive(:fetch) - .at_least(:once) - .and_return([freeze_period]) - - subject + expect { subject }.to( + change { Gitlab::SafeRequestStore[cache_key] }.from(nil).to([freeze_period]) + ) end end diff --git a/spec/models/integrations/jira_spec.rb b/spec/models/integrations/jira_spec.rb index ccea8748d13..d3cb386e8e0 100644 --- a/spec/models/integrations/jira_spec.rb +++ b/spec/models/integrations/jira_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Integrations::Jira do +RSpec.describe Integrations::Jira, feature_category: :integrations do include AssetsHelpers let_it_be(:project) { create(:project, :repository) } @@ -11,6 +11,7 @@ RSpec.describe Integrations::Jira do let(:url) { 'http://jira.example.com' } let(:api_url) { 'http://api-jira.example.com' } let(:username) { 'jira-username' } + let(:jira_auth_type) { 0 } let(:jira_issue_prefix) { '' } let(:jira_issue_regex) { '' } let(:password) { 'jira-password' } @@ -50,11 +51,39 @@ RSpec.describe Integrations::Jira do it { is_expected.to validate_presence_of(:url) } it { is_expected.to validate_presence_of(:username) } it { is_expected.to validate_presence_of(:password) } + it { is_expected.to validate_presence_of(:jira_auth_type) } it { is_expected.to validate_length_of(:jira_issue_regex).is_at_most(255) } it { is_expected.to validate_length_of(:jira_issue_prefix).is_at_most(255) } + it { is_expected.to validate_inclusion_of(:jira_auth_type).in_array([0, 1]) } it_behaves_like 'issue tracker integration URL attribute', :url it_behaves_like 'issue tracker integration URL attribute', :api_url + + context 'with personal_access_token_authorization' do + before do + jira_integration.jira_auth_type = 1 + end + + it { is_expected.not_to validate_presence_of(:username) } + end + + context 'when URL is for Jira Cloud' do + before do + jira_integration.url = 'https://test.atlassian.net' + end + + it 'is valid when jira_auth_type is basic' do + jira_integration.jira_auth_type = 0 + + expect(jira_integration).to be_valid + end + + it 'is invalid when jira_auth_type is PAT' do + jira_integration.jira_auth_type = 1 + + expect(jira_integration).not_to be_valid + end + end end context 'when integration is inactive' do @@ -66,8 +95,10 @@ RSpec.describe Integrations::Jira do it { is_expected.not_to validate_presence_of(:url) } it { is_expected.not_to validate_presence_of(:username) } it { is_expected.not_to validate_presence_of(:password) } + it { is_expected.not_to validate_presence_of(:jira_auth_type) } it { is_expected.not_to validate_length_of(:jira_issue_regex).is_at_most(255) } it { is_expected.not_to validate_length_of(:jira_issue_prefix).is_at_most(255) } + it { is_expected.not_to validate_inclusion_of(:jira_auth_type).in_array([0, 1]) } end describe 'jira_issue_transition_id' do @@ -173,7 +204,7 @@ RSpec.describe Integrations::Jira do subject(:fields) { integration.fields } it 'returns custom fields' do - expect(fields.pluck(:name)).to eq(%w[url api_url username password jira_issue_regex jira_issue_prefix jira_issue_transition_id]) + expect(fields.pluck(:name)).to eq(%w[url api_url jira_auth_type username password jira_issue_regex jira_issue_prefix jira_issue_transition_id]) end end @@ -323,6 +354,7 @@ RSpec.describe Integrations::Jira do project: project, url: url, api_url: api_url, + jira_auth_type: jira_auth_type, username: username, password: password, jira_issue_regex: jira_issue_regex, jira_issue_prefix: jira_issue_prefix, @@ -339,6 +371,7 @@ RSpec.describe Integrations::Jira do it 'stores data in data_fields correctly' do expect(integration.jira_tracker_data.url).to eq(url) expect(integration.jira_tracker_data.api_url).to eq(api_url) + expect(integration.jira_tracker_data.jira_auth_type).to eq(jira_auth_type) expect(integration.jira_tracker_data.username).to eq(username) expect(integration.jira_tracker_data.password).to eq(password) expect(integration.jira_tracker_data.jira_issue_regex).to eq(jira_issue_regex) @@ -545,15 +578,54 @@ RSpec.describe Integrations::Jira do end describe '#client' do + before do + stub_request(:get, 'http://jira.example.com/foo') + end + it 'uses the default GitLab::HTTP timeouts' do timeouts = Gitlab::HTTP::DEFAULT_TIMEOUT_OPTIONS - stub_request(:get, 'http://jira.example.com/foo') expect(Gitlab::HTTP).to receive(:httparty_perform_request) .with(Net::HTTP::Get, '/foo', hash_including(timeouts)).and_call_original jira_integration.client.get('/foo') end + + context 'with basic auth' do + before do + jira_integration.jira_auth_type = 0 + end + + it 'uses correct authorization options' do + expect_next_instance_of(JIRA::Client) do |instance| + expect(instance.request_client.options).to include( + additional_cookies: ['OBBasicAuth=fromDialog'], + auth_type: :basic, + use_cookies: true, + password: jira_integration.password, + username: jira_integration.username + ) + end + + jira_integration.client.get('/foo') + end + end + + context 'with personal access token auth' do + before do + jira_integration.jira_auth_type = 1 + end + + it 'uses correct authorization options' do + expect_next_instance_of(JIRA::Client) do |instance| + expect(instance.request_client.options).to include( + default_headers: { "Authorization" => "Bearer #{password}" } + ) + end + + jira_integration.client.get('/foo') + end + end end describe '#find_issue' do diff --git a/spec/requests/api/integrations_spec.rb b/spec/requests/api/integrations_spec.rb index 4e8f8810128..8d348dc0a54 100644 --- a/spec/requests/api/integrations_spec.rb +++ b/spec/requests/api/integrations_spec.rb @@ -369,7 +369,7 @@ RSpec.describe API::Integrations, feature_category: :integrations do describe 'Jira integration' do let(:integration_name) { 'jira' } let(:params) do - { url: 'https://jira.example.com', username: 'username', password: 'password' } + { url: 'https://jira.example.com', username: 'username', password: 'password', jira_auth_type: 0 } end before do diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb index b4a7782ab3f..60e91973b5d 100644 --- a/spec/requests/api/maven_packages_spec.rb +++ b/spec/requests/api/maven_packages_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe API::MavenPackages, feature_category: :package_registry do using RSpec::Parameterized::TableSyntax include WorkhorseHelpers + include HttpBasicAuthHelpers include_context 'workhorse headers' @@ -159,56 +160,149 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do end end - shared_examples 'downloads with a deploy token' do - context 'successful download' do + shared_examples 'allowing the download' do + it 'allows download' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response.media_type).to eq('application/octet-stream') + end + end + + shared_examples 'not allowing the download with' do |not_found_response| + it 'does not allow the download' do + subject + + expect(response).to have_gitlab_http_status(not_found_response) + end + end + + shared_examples 'downloads with a personal access token' do |not_found_response| + where(:valid, :sent_using) do + true | :custom_header + false | :custom_header + true | :basic_auth + false | :basic_auth + end + + with_them do + let(:token) { valid ? personal_access_token.token : 'not_valid' } + let(:headers) do + case sent_using + when :custom_header + { 'Private-Token' => token } + when :basic_auth + basic_auth_header(user.username, token) + end + end + subject do download_file( file_name: package_file.file_name, - request_headers: { Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => deploy_token.token } + request_headers: headers ) end - it 'allows download with deploy token' do - subject + if params[:valid] + it_behaves_like 'allowing the download' + else + expected_status_code = not_found_response + # invalid PAT values sent through headers are validated. + # Invalid values will trigger an :unauthorized response (and not set current_user to nil) + expected_status_code = :unauthorized if params[:sent_using] == :custom_header && !params[:valid] + it_behaves_like 'not allowing the download with', expected_status_code + end + end + end - expect(response).to have_gitlab_http_status(:ok) - expect(response.media_type).to eq('application/octet-stream') + shared_examples 'downloads with a deploy token' do |not_found_response| + where(:valid, :sent_using) do + true | :custom_header + false | :custom_header + true | :basic_auth + false | :basic_auth + end + + with_them do + let(:token) { valid ? deploy_token.token : 'not_valid' } + let(:headers) do + case sent_using + when :custom_header + { Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => token } + when :basic_auth + basic_auth_header(deploy_token.username, token) + end end - it 'allows download with deploy token with only write_package_registry scope' do - deploy_token.update!(read_package_registry: false) + subject do + download_file( + file_name: package_file.file_name, + request_headers: headers + ) + end - subject + if params[:valid] + it_behaves_like 'allowing the download' - expect(response).to have_gitlab_http_status(:ok) - expect(response.media_type).to eq('application/octet-stream') + context 'with only write_package_registry scope' do + it_behaves_like 'allowing the download' do + before do + deploy_token.update!(read_package_registry: false) + end + end + end + else + it_behaves_like 'not allowing the download with', not_found_response end end end shared_examples 'downloads with a job token' do - context 'with a running job' do - it 'allows download with job token' do - download_file(file_name: package_file.file_name, params: { job_token: job.token }) + where(:valid, :sent_using) do + true | :custom_params + false | :custom_params + true | :basic_auth + false | :basic_auth + end - expect(response).to have_gitlab_http_status(:ok) - expect(response.media_type).to eq('application/octet-stream') + with_them do + let(:token) { valid ? job.token : 'not_valid' } + let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, token) } + let(:params) { { job_token: token } } + + subject do + case sent_using + when :custom_params + download_file(file_name: package_file.file_name, params: params) + when :basic_auth + download_file(file_name: package_file.file_name, request_headers: headers) + end end - end - context 'with a finished job' do - before do - job.update!(status: :failed) + context 'with a running job' do + if params[:valid] + it_behaves_like 'allowing the download' + else + it_behaves_like 'not allowing the download with', :unauthorized + end end - it 'returns unauthorized error' do - download_file(file_name: package_file.file_name, params: { job_token: job.token }) + context 'with a finished job' do + before do + job.update!(status: :failed) + end - expect(response).to have_gitlab_http_status(:unauthorized) + it_behaves_like 'not allowing the download with', :unauthorized end end end + shared_examples 'downloads with different tokens' do |not_found_response| + it_behaves_like 'downloads with a personal access token', not_found_response + it_behaves_like 'downloads with a deploy token', not_found_response + it_behaves_like 'downloads with a job token' + end + shared_examples 'successfully returning the file' do it 'returns the file', :aggregate_failures do subject @@ -338,11 +432,10 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do it 'denies download when no private token' do download_file(file_name: package_file.file_name) - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(:unauthorized) end - it_behaves_like 'downloads with a job token' - it_behaves_like 'downloads with a deploy token' + it_behaves_like 'downloads with different tokens', :unauthorized context 'with a non existing maven path' do subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') } @@ -379,11 +472,10 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do it 'denies download when no private token' do download_file(file_name: package_file.file_name) - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(:unauthorized) end - it_behaves_like 'downloads with a job token' - it_behaves_like 'downloads with a deploy token' + it_behaves_like 'downloads with different tokens', :unauthorized it 'does not allow download by a unauthorized deploy token with same id as a user with access' do unauthorized_deploy_token = create(:deploy_token, read_package_registry: true, write_package_registry: true) @@ -413,12 +505,8 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do end context 'project name is different from a package name' do - before do - maven_metadatum.update!(path: "wrong_name/#{package.version}") - end - it 'rejects request' do - download_file(file_name: package_file.file_name) + download_file(file_name: package_file.file_name, path: "wrong_name/#{package.version}") expect(response).to have_gitlab_http_status(:forbidden) end @@ -500,8 +588,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do expect(response).to have_gitlab_http_status(not_found_response) end - it_behaves_like 'downloads with a job token' - it_behaves_like 'downloads with a deploy token' + it_behaves_like 'downloads with different tokens', not_found_response context 'with a non existing maven path' do subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') } @@ -510,7 +597,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do end end - it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { internal: :not_found, public: :redirect } + it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { internal: :unauthorized, public: :redirect } end context 'private project' do @@ -539,8 +626,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do expect(response).to have_gitlab_http_status(not_found_response) end - it_behaves_like 'downloads with a job token' - it_behaves_like 'downloads with a deploy token' + it_behaves_like 'downloads with different tokens', not_found_response context 'with a non existing maven path' do subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') } @@ -570,7 +656,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do end end - it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { private: :not_found, internal: :not_found, public: :redirect } + it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { private: :unauthorized, internal: :unauthorized, public: :redirect } context 'with a reporter from a subgroup accessing the root group' do let_it_be(:root_group) { create(:group, :private) } @@ -724,7 +810,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do it 'denies download when no private token' do download_file(file_name: package_file.file_name) - expect(response).to have_gitlab_http_status(:not_found) + expect(response).to have_gitlab_http_status(:unauthorized) end context 'with access to package registry for everyone' do @@ -737,8 +823,7 @@ RSpec.describe API::MavenPackages, feature_category: :package_registry do it_behaves_like 'successfully returning the file' end - it_behaves_like 'downloads with a job token' - it_behaves_like 'downloads with a deploy token' + it_behaves_like 'downloads with different tokens', :unauthorized context 'with a non existing maven path' do subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') } diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb index c003ae9cd48..69579323908 100644 --- a/spec/requests/api/project_packages_spec.rb +++ b/spec/requests/api/project_packages_spec.rb @@ -224,15 +224,19 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do context 'without the need for a license' do context 'with build info' do + let_it_be(:package1) { create(:npm_package, :with_build, project: project) } + + it 'returns an empty array for the pipelines attribute' do + subject + + expect(json_response['pipelines']).to be_empty + end + it 'does not result in additional queries' do control = ActiveRecord::QueryRecorder.new do get api(package_url, user) end - pipeline = create(:ci_pipeline, user: user, project: project) - create(:ci_build, user: user, pipeline: pipeline, project: project) - create(:package_build_info, package: package1, pipeline: pipeline) - expect do get api(package_url, user) end.not_to exceed_query_limit(control) diff --git a/spec/serializers/integrations/field_entity_spec.rb b/spec/serializers/integrations/field_entity_spec.rb index 1ca1545c11a..4d190b9a98e 100644 --- a/spec/serializers/integrations/field_entity_spec.rb +++ b/spec/serializers/integrations/field_entity_spec.rb @@ -23,10 +23,11 @@ RSpec.describe Integrations::FieldEntity, feature_category: :integrations do section: 'connection', type: 'text', name: 'username', - title: 'Username or email', + title: 'Email or username', placeholder: nil, - help: 'Username for the server version or an email for the cloud version', - required: true, + help: 'Only required for Basic authentication. ' \ + 'Email for Jira Cloud or username for Jira Data Center and Jira Server', + required: false, choices: nil, value: 'jira_username', checkbox_label: nil @@ -44,9 +45,9 @@ RSpec.describe Integrations::FieldEntity, feature_category: :integrations do section: 'connection', type: 'password', name: 'password', - title: 'Enter new password or API token', + title: 'New API token, password, or Jira personal access token', placeholder: nil, - help: 'Leave blank to use your current password or API token.', + help: 'Leave blank to use your current configuration', required: true, choices: nil, value: 'true', diff --git a/spec/support/helpers/jira_integration_helpers.rb b/spec/support/helpers/jira_integration_helpers.rb index 66940314589..098f2968b0b 100644 --- a/spec/support/helpers/jira_integration_helpers.rb +++ b/spec/support/helpers/jira_integration_helpers.rb @@ -11,7 +11,7 @@ module JiraIntegrationHelpers jira_issue_transition_id = '1' jira_tracker.update!( - url: url, username: username, password: password, + url: url, username: username, password: password, jira_auth_type: 0, jira_issue_transition_id: jira_issue_transition_id, active: true ) end diff --git a/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb b/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb index 958c7db28aa..c1f7dd79c08 100644 --- a/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb +++ b/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb @@ -68,6 +68,8 @@ RSpec.shared_context 'with integration' do hash.merge!(k => '1,2,3') elsif integration == 'jira' && k == :jira_issue_transition_automatic # rubocop:disable Lint/DuplicateBranch hash.merge!(k => true) + elsif integration == 'jira' && k == :jira_auth_type # rubocop:disable Lint/DuplicateBranch + hash.merge!(k => 0) elsif integration == 'emails_on_push' && k == :recipients hash.merge!(k => 'foo@bar.com') elsif (integration == 'slack' || integration == 'mattermost') && k == :labels_to_be_notified_behavior diff --git a/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb b/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb index fadd46a7e12..f16d19e5858 100644 --- a/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb +++ b/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb @@ -10,5 +10,6 @@ RSpec.shared_context 'project integration Jira context' do fill_in 'service_url', with: url fill_in 'service_username', with: 'username' fill_in 'service_password', with: 'password' + select('Basic', from: 'service_jira_auth_type') end end diff --git a/spec/workers/jira_connect/sync_project_worker_spec.rb b/spec/workers/jira_connect/sync_project_worker_spec.rb index 7a23aabfd0f..b617508bb3a 100644 --- a/spec/workers/jira_connect/sync_project_worker_spec.rb +++ b/spec/workers/jira_connect/sync_project_worker_spec.rb @@ -113,21 +113,5 @@ RSpec.describe JiraConnect::SyncProjectWorker, factory_default: :keep, feature_c perform(project.id, update_sequence_id) end end - - context 'when the feature flag is disabled' do - let!(:most_recent_merge_request) { create(:merge_request, :unique_branches, description: 'TEST-323', title: 'TEST-123') } - - before do - stub_feature_flags(jira_connect_sync_branches: false) - stub_const("#{described_class}::MAX_RECORDS_LIMIT", 1) - end - - it 'does not sync branches' do - expect(jira_connect_sync_service).to receive(:execute) - .with(merge_requests: [most_recent_merge_request], update_sequence_id: update_sequence_id) - - perform(project.id, update_sequence_id) - end - end end end |