diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-20 21:08:30 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-04-20 21:08:30 +0300 |
commit | 4faa270685797bd689d2035efe7c7e724950eb82 (patch) | |
tree | 880f35cfcb4ef5dad3c82f701937d27d5cc85389 /spec | |
parent | da23c5d563d68bfa5271b216209a7715c7ce3073 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
19 files changed, 512 insertions, 213 deletions
diff --git a/spec/frontend/super_sidebar/components/nav_item_spec.js b/spec/frontend/super_sidebar/components/nav_item_spec.js index d96d2b77d21..ffbdaa326f9 100644 --- a/spec/frontend/super_sidebar/components/nav_item_spec.js +++ b/spec/frontend/super_sidebar/components/nav_item_spec.js @@ -53,6 +53,13 @@ describe('NavItem component', () => { expect(findLink().attributes('class')).toContain(customClass); }); + it('applies custom classes set in the backend', () => { + const customClass = 'customBackendClass'; + createWrapper({ title: 'Foo', link_classes: customClass }); + + expect(findLink().attributes('class')).toContain(customClass); + }); + describe('Data Tracking Attributes', () => { it('adds no labels on sections', () => { const id = 'my-id'; diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index c4ab9fb115f..2d3bb06df41 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -212,6 +212,80 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do end end + describe '#last_pipeline_from_status_cache' do + before do + # clear cross-example caches + project_with_repo.pipeline_status.delete_from_cache + project_with_repo.instance_variable_set(:@pipeline_status, nil) + end + + context 'without a pipeline' do + it 'returns nil', :aggregate_failures do + expect(::Gitlab::GitalyClient).to receive(:call).at_least(:once).and_call_original + actual_pipeline = last_pipeline_from_status_cache(project_with_repo) + expect(actual_pipeline).to be_nil + end + + context 'when pipeline_status is loaded' do + before do + project_with_repo.pipeline_status # this loads the status + end + + it 'returns nil without calling gitaly when there is no pipeline', :aggregate_failures do + expect(::Gitlab::GitalyClient).not_to receive(:call) + actual_pipeline = last_pipeline_from_status_cache(project_with_repo) + expect(actual_pipeline).to be_nil + end + end + + context 'when FF load_last_pipeline_from_pipeline_status is disabled' do + before do + stub_feature_flags(last_pipeline_from_pipeline_status: false) + end + + it 'returns nil', :aggregate_failures do + expect(project_with_repo).not_to receive(:pipeline_status) + actual_pipeline = last_pipeline_from_status_cache(project_with_repo) + expect(actual_pipeline).to be_nil + end + end + end + + context 'with a pipeline' do + let_it_be(:pipeline) { create(:ci_pipeline, project: project_with_repo) } + + it 'returns the latest pipeline', :aggregate_failures do + expect(::Gitlab::GitalyClient).to receive(:call).at_least(:once).and_call_original + actual_pipeline = last_pipeline_from_status_cache(project_with_repo) + expect(actual_pipeline).to eq pipeline + end + + context 'when pipeline_status is loaded' do + before do + project_with_repo.pipeline_status # this loads the status + end + + it 'returns the latest pipeline without calling gitaly' do + expect(::Gitlab::GitalyClient).not_to receive(:call) + actual_pipeline = last_pipeline_from_status_cache(project_with_repo) + expect(actual_pipeline).to eq pipeline + end + + context 'when FF load_last_pipeline_from_pipeline_status is disabled' do + before do + stub_feature_flags(last_pipeline_from_pipeline_status: false) + end + + it 'returns the latest pipeline', :aggregate_failures do + expect(project_with_repo).not_to receive(:pipeline_status) + actual_pipeline = last_pipeline_from_status_cache(project_with_repo) + expect(actual_pipeline).to eq pipeline + end + end + end + end + end + describe '#show_no_ssh_key_message?' do before do allow(helper).to receive(:current_user).and_return(user) diff --git a/spec/helpers/sidebars_helper_spec.rb b/spec/helpers/sidebars_helper_spec.rb index 5323b041a9e..6c0ac024944 100644 --- a/spec/helpers/sidebars_helper_spec.rb +++ b/spec/helpers/sidebars_helper_spec.rb @@ -66,9 +66,10 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do let_it_be(:group) { build(:group) } let_it_be(:panel) { {} } let_it_be(:panel_type) { 'project' } + let(:project) { nil } subject do - helper.super_sidebar_context(user, group: group, project: nil, panel: panel, panel_type: panel_type) + helper.super_sidebar_context(user, group: group, project: project, panel: panel, panel_type: panel_type) end before do @@ -160,6 +161,47 @@ RSpec.describe SidebarsHelper, feature_category: :navigation do }) end + describe "shortcut links" do + let(:global_shortcut_links) do + [ + { + title: _('Milestones'), + href: dashboard_milestones_path, + css_class: 'dashboard-shortcuts-milestones' + }, + { + title: _('Snippets'), + href: dashboard_snippets_path, + css_class: 'dashboard-shortcuts-snippets' + }, + { + title: _('Activity'), + href: activity_dashboard_path, + css_class: 'dashboard-shortcuts-activity' + } + ] + end + + it 'returns global shortcut links' do + expect(subject[:shortcut_links]).to eq(global_shortcut_links) + end + + context 'in a project' do + let(:project) { build(:project) } + + it 'returns project-specific shortcut links' do + expect(subject[:shortcut_links]).to eq([ + *global_shortcut_links, + { + title: _('Create a new issue'), + href: new_project_issue_path(project), + css_class: 'shortcuts-new-issue' + } + ]) + end + end + end + it 'returns "Merge requests" menu', :use_clean_rails_memory_store_caching do expect(subject[:merge_request_menu]).to eq([ { diff --git a/spec/lib/gitlab/metrics/sidekiq_slis_spec.rb b/spec/lib/gitlab/metrics/sidekiq_slis_spec.rb new file mode 100644 index 00000000000..eef9a9c79e6 --- /dev/null +++ b/spec/lib/gitlab/metrics/sidekiq_slis_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::SidekiqSlis, feature_category: :error_budgets do + using RSpec::Parameterized::TableSyntax + + describe ".initialize_slis!" do + let(:possible_labels) do + [ + { + worker: "Projects::RecordTargetPlatformsWorker", + feature_category: "projects", + urgency: "low" + } + ] + end + + it "initializes the apdex and error rate SLIs" do + expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:sidekiq_execution, possible_labels) + expect(Gitlab::Metrics::Sli::ErrorRate).to receive(:initialize_sli).with(:sidekiq_execution, possible_labels) + + described_class.initialize_slis!(possible_labels) + end + end + + describe ".record_execution_apdex" do + where(:urgency, :duration, :success) do + "high" | 5 | true + "high" | 11 | false + "low" | 295 | true + "low" | 400 | false + "throttled" | 295 | true + "throttled" | 400 | false + "not_found" | 295 | true + "not_found" | 400 | false + end + + with_them do + it "increments the apdex SLI with success based on urgency requirement" do + labels = { urgency: urgency } + expect(Gitlab::Metrics::Sli::Apdex[:sidekiq_execution]).to receive(:increment).with( + labels: labels, + success: success + ) + + described_class.record_execution_apdex(labels, duration) + end + end + end + + describe ".record_execution_error" do + it "increments the error rate SLI with the given labels and error" do + labels = { urgency: :throttled } + error = StandardError.new("something went wrong") + + expect(Gitlab::Metrics::Sli::ErrorRate[:sidekiq_execution]).to receive(:increment).with( + labels: labels, + error: error + ) + + described_class.record_execution_error(labels, error) + end + end +end diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb index 80c1af1b913..965ca612b3f 100644 --- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb @@ -59,6 +59,45 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do described_class.initialize_process_metrics end + context 'when sidekiq_execution_application_slis FF is turned on' do + it 'initializes sidekiq SLIs for the workers in the current Sidekiq process' do + allow(Gitlab::SidekiqConfig) + .to receive(:current_worker_queue_mappings) + .and_return('MergeWorker' => 'merge', 'Ci::BuildFinishedWorker' => 'default') + + allow(completion_seconds_metric).to receive(:get) + + expect(Gitlab::Metrics::SidekiqSlis) + .to receive(:initialize_slis!).with([ + { + worker: 'MergeWorker', + urgency: 'high', + feature_category: 'source_code_management' + }, + { + worker: 'Ci::BuildFinishedWorker', + urgency: 'high', + feature_category: 'continuous_integration' + } + ]) + + described_class.initialize_process_metrics + end + end + + context 'when sidekiq_execution_application_slis FF is turned off' do + before do + stub_feature_flags(sidekiq_execution_application_slis: false) + end + + it 'does not initialize sidekiq SLIs' do + expect(Gitlab::Metrics::SidekiqSlis) + .not_to receive(:initialize_slis!) + + described_class.initialize_process_metrics + end + end + context 'when the sidekiq_job_completion_metric_initialize feature flag is disabled' do before do stub_feature_flags(sidekiq_job_completion_metric_initialize: false) @@ -79,6 +118,17 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do described_class.initialize_process_metrics end + + it 'does not initializes sidekiq SLIs' do + allow(Gitlab::SidekiqConfig) + .to receive(:current_worker_queue_mappings) + .and_return('MergeWorker' => 'merge', 'Ci::BuildFinishedWorker' => 'default') + + expect(Gitlab::Metrics::SidekiqSlis) + .not_to receive(:initialize_slis!) + + described_class.initialize_process_metrics + end end end @@ -110,6 +160,12 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do expect(redis_requests_total).to receive(:increment).with(labels_with_job_status, redis_calls) expect(elasticsearch_requests_total).to receive(:increment).with(labels_with_job_status, elasticsearch_calls) expect(sidekiq_mem_total_bytes).to receive(:set).with(labels_with_job_status, mem_total_bytes) + expect(Gitlab::Metrics::SidekiqSlis).to receive(:record_execution_apdex).with(labels.slice(:worker, + :feature_category, + :urgency), monotonic_time_duration) + expect(Gitlab::Metrics::SidekiqSlis).to receive(:record_execution_error).with(labels.slice(:worker, + :feature_category, + :urgency), false) subject.call(worker, job, :test) { nil } end @@ -159,6 +215,16 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do expect { subject.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed") end + + it 'records sidekiq SLI error but does not record sidekiq SLI apdex' do + expect(failed_total_metric).to receive(:increment) + expect(Gitlab::Metrics::SidekiqSlis).not_to receive(:record_execution_apdex) + expect(Gitlab::Metrics::SidekiqSlis).to receive(:record_execution_error).with(labels.slice(:worker, + :feature_category, + :urgency), true) + + expect { subject.call(worker, job, :test) { raise StandardError, "Failed" } }.to raise_error(StandardError, "Failed") + end end context 'when job is retried' do @@ -180,6 +246,19 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do subject.call(worker, job, :test) { nil } end end + + context 'when sidekiq_execution_application_slis FF is turned off' do + before do + stub_feature_flags(sidekiq_execution_application_slis: false) + end + + it 'does not call record_execution_apdex nor record_execution_error' do + expect(Gitlab::Metrics::SidekiqSlis).not_to receive(:record_execution_apdex) + expect(Gitlab::Metrics::SidekiqSlis).not_to receive(:record_execution_error) + + subject.call(worker, job, :test) { nil } + end + end end end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 3c7542ea5f9..a1c2f7d667f 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -458,18 +458,42 @@ RSpec.describe Gitlab::Workhorse do describe '.send_url' do let(:url) { 'http://example.com' } - subject { described_class.send_url(url) } - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) + key, command, params = decode_workhorse_header( + described_class.send_url(url) + ) expect(key).to eq("Gitlab-Workhorse-Send-Data") expect(command).to eq("send-url") expect(params).to eq({ 'URL' => url, - 'AllowRedirects' => false + 'AllowRedirects' => false, + 'Body' => '', + 'Method' => 'GET' }.deep_stringify_keys) end + + context 'when body, headers and method are specified' do + let(:body) { 'body' } + let(:headers) { { Authorization: ['Bearer token'] } } + let(:method) { 'POST' } + + it 'sets the header correctly' do + key, command, params = decode_workhorse_header( + described_class.send_url(url, body: body, headers: headers, method: method) + ) + + expect(key).to eq("Gitlab-Workhorse-Send-Data") + expect(command).to eq("send-url") + expect(params).to eq({ + 'URL' => url, + 'AllowRedirects' => false, + 'Body' => body, + 'Header' => headers, + 'Method' => method + }.deep_stringify_keys) + end + end end describe '.send_scaled_image' do diff --git a/spec/lib/sidebars/menu_item_spec.rb b/spec/lib/sidebars/menu_item_spec.rb index 15804f51934..84bc3430260 100644 --- a/spec/lib/sidebars/menu_item_spec.rb +++ b/spec/lib/sidebars/menu_item_spec.rb @@ -18,4 +18,14 @@ RSpec.describe Sidebars::MenuItem do expect(menu_item.container_html_options).to eq html_options end end + + describe "#serialize_for_super_sidebar" do + let(:html_options) { { class: 'custom-class' } } + + subject { menu_item.serialize_for_super_sidebar } + + it 'includes custom CSS classes' do + expect(subject[:link_classes]).to be('custom-class') + end + end end diff --git a/spec/lib/sidebars/menu_spec.rb b/spec/lib/sidebars/menu_spec.rb index 74ed344dd24..21aec10ec27 100644 --- a/spec/lib/sidebars/menu_spec.rb +++ b/spec/lib/sidebars/menu_spec.rb @@ -61,7 +61,8 @@ RSpec.describe Sidebars::Menu, feature_category: :navigation do icon: nil, link: "foo2", is_active: true, - pill_count: nil + pill_count: nil, + link_classes: nil }, { id: 'id2', @@ -69,7 +70,8 @@ RSpec.describe Sidebars::Menu, feature_category: :navigation do icon: nil, link: "foo3", is_active: false, - pill_count: 10 + pill_count: 10, + link_classes: nil } ] }) diff --git a/spec/lib/sidebars/static_menu_spec.rb b/spec/lib/sidebars/static_menu_spec.rb index b336b457302..3d9feee0494 100644 --- a/spec/lib/sidebars/static_menu_spec.rb +++ b/spec/lib/sidebars/static_menu_spec.rb @@ -25,7 +25,8 @@ RSpec.describe Sidebars::StaticMenu, feature_category: :navigation do icon: nil, link: "foo2", is_active: true, - pill_count: nil + pill_count: nil, + link_classes: nil }, { id: 'id2', @@ -33,7 +34,8 @@ RSpec.describe Sidebars::StaticMenu, feature_category: :navigation do icon: nil, link: "foo3", is_active: false, - pill_count: nil + pill_count: nil, + link_classes: nil } ] ) diff --git a/spec/mailers/emails/merge_requests_spec.rb b/spec/mailers/emails/merge_requests_spec.rb index 7682cf39450..9aece9538dc 100644 --- a/spec/mailers/emails/merge_requests_spec.rb +++ b/spec/mailers/emails/merge_requests_spec.rb @@ -13,12 +13,15 @@ RSpec.describe Emails::MergeRequests do let_it_be(:reviewer, reload: true) { create(:user, email: 'reviewer@example.com', name: 'Jane Doe') } let_it_be(:project) { create(:project, :repository) } let_it_be(:merge_request) do - create(:merge_request, source_project: project, - target_project: project, - author: current_user, - assignees: [assignee], - reviewers: [reviewer], - description: 'Awesome description') + create( + :merge_request, + source_project: project, + target_project: project, + author: current_user, + assignees: [assignee], + reviewers: [reviewer], + description: 'Awesome description' + ) end let(:recipient) { assignee } diff --git a/spec/mailers/emails/pipelines_spec.rb b/spec/mailers/emails/pipelines_spec.rb index 1ac989cc46b..ae0876338f5 100644 --- a/spec/mailers/emails/pipelines_spec.rb +++ b/spec/mailers/emails/pipelines_spec.rb @@ -25,8 +25,13 @@ RSpec.describe Emails::Pipelines do let(:pipeline) { create(:ci_pipeline, ref: 'master', sha: sha, project: project) } let!(:merge_request) do - create(:merge_request, source_branch: 'master', target_branch: 'feature', - source_project: project, target_project: project) + create( + :merge_request, + source_branch: 'master', + target_branch: 'feature', + source_project: project, + target_project: project + ) end it 'has correct information that there is no merge request link' do @@ -55,8 +60,13 @@ RSpec.describe Emails::Pipelines do context 'when branch pipeline is set to a merge request as a head pipeline' do let(:pipeline) do - create(:ci_pipeline, project: project, ref: ref, sha: sha, - merge_requests_as_head_pipeline: [merge_request]) + create( + :ci_pipeline, + project: project, + ref: ref, + sha: sha, + merge_requests_as_head_pipeline: [merge_request] + ) end let(:merge_request) do diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index eb681846e82..c2c32abbdc4 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -21,19 +21,25 @@ RSpec.describe Notify do let_it_be(:reviewer, reload: true) { create(:user, email: 'reviewer@example.com', name: 'Jane Doe') } let_it_be(:merge_request) do - create(:merge_request, source_project: project, - target_project: project, - author: current_user, - assignees: [assignee], - reviewers: [reviewer], - description: 'Awesome description') + create( + :merge_request, + source_project: project, + target_project: project, + author: current_user, + assignees: [assignee], + reviewers: [reviewer], + description: 'Awesome description' + ) end let_it_be(:issue, reload: true) do - create(:issue, author: current_user, - assignees: [assignee], - project: project, - description: 'My awesome description!') + create( + :issue, + author: current_user, + assignees: [assignee], + project: project, + description: 'My awesome description!' + ) end describe 'with HTML-encoded entities' do @@ -824,9 +830,11 @@ RSpec.describe Notify do end it 'has References header including the notes and issue of the discussion' do - expect(subject.header['References'].message_ids).to include("issue_#{first_note.noteable.id}@#{host}", - "note_#{first_note.id}@#{host}", - "note_#{second_note.id}@#{host}") + expect(subject.header['References'].message_ids).to include( + "issue_#{first_note.noteable.id}@#{host}", + "note_#{first_note.id}@#{host}", + "note_#{second_note.id}@#{host}" + ) end it 'has X-GitLab-Discussion-ID header' do @@ -899,9 +907,11 @@ RSpec.describe Notify do end it 'links to the project snippet' do - target_url = project_snippet_url(project, - project_snippet_note.noteable, - { anchor: "note_#{project_snippet_note.id}" }) + target_url = project_snippet_url( + project, + project_snippet_note.noteable, + { anchor: "note_#{project_snippet_note.id}" } + ) is_expected.to have_body_text target_url end end @@ -910,9 +920,7 @@ RSpec.describe Notify do let_it_be(:design) { create(:design, :with_file) } let_it_be(:recipient) { create(:user) } let_it_be(:note) do - create(:diff_note_on_design, - noteable: design, - note: "Hello #{recipient.to_reference}") + create(:diff_note_on_design, noteable: design, note: "Hello #{recipient.to_reference}") end let(:header_name) { 'X-Gitlab-DesignManagement-Design-ID' } @@ -1078,9 +1086,10 @@ RSpec.describe Notify do is_expected.to have_body_text project.full_name is_expected.to have_body_text project_member.human_access.downcase is_expected.to have_body_text project_member.invite_token - is_expected.to have_link('Join now', - href: invite_url(project_member.invite_token, - invite_type: Emails::Members::INITIAL_INVITE)) + is_expected.to have_link( + 'Join now', + href: invite_url(project_member.invite_token, invite_type: Emails::Members::INITIAL_INVITE) + ) is_expected.to have_content("#{inviter.name} invited you to join the") is_expected.to have_content('Project details') is_expected.to have_content("What's it about?") @@ -1096,9 +1105,10 @@ RSpec.describe Notify do is_expected.to have_body_text project.full_name is_expected.to have_body_text project_member.human_access.downcase is_expected.to have_body_text project_member.invite_token - is_expected.to have_link('Join now', - href: invite_url(project_member.invite_token, - invite_type: Emails::Members::INITIAL_INVITE)) + is_expected.to have_link( + 'Join now', + href: invite_url(project_member.invite_token, invite_type: Emails::Members::INITIAL_INVITE) + ) is_expected.to have_content('Project details') is_expected.to have_content("What's it about?") end diff --git a/spec/models/ci/commit_with_pipeline_spec.rb b/spec/models/ci/commit_with_pipeline_spec.rb index 320143535e2..766e99288c0 100644 --- a/spec/models/ci/commit_with_pipeline_spec.rb +++ b/spec/models/ci/commit_with_pipeline_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' -RSpec.describe Ci::CommitWithPipeline do - let(:project) { create(:project, :public, :repository) } - let(:commit) { described_class.new(project.commit) } +RSpec.describe Ci::CommitWithPipeline, feature_category: :continuous_integration do + let_it_be(:project) { create(:project, :public, :repository) } + let(:commit) { described_class.new(project.commit) } describe '#last_pipeline' do let!(:first_pipeline) do @@ -27,28 +27,43 @@ RSpec.describe Ci::CommitWithPipeline do end describe '#lazy_latest_pipeline' do - let(:commit_1) do - described_class.new(Commit.new(RepoHelpers.sample_commit, project)) + let_it_be(:other_project) { create(:project, :repository) } + + let_it_be(:commits_with_pipelines) do + [ + described_class.new(Commit.new(RepoHelpers.sample_commit, project)), + described_class.new(Commit.new(RepoHelpers.another_sample_commit, project)), + described_class.new(Commit.new(RepoHelpers.sample_big_commit, project)), + described_class.new(Commit.new(RepoHelpers.sample_commit, other_project)) + ] end - let(:commit_2) do - described_class.new(Commit.new(RepoHelpers.another_sample_commit, project)) + let_it_be(:commits) do + commits_with_pipelines + [ + described_class.new(Commit.new(RepoHelpers.another_sample_commit, other_project)) + ] end - let!(:commits) { [commit_1, commit_2] } + before(:all) do + commits_with_pipelines.each do |commit| + create(:ci_empty_pipeline, project: commit.project, sha: commit.sha) + end + end - it 'executes only 1 SQL query' do + it 'returns the correct pipelines with only 1 SQL query per project', :aggregate_failures do recorder = ActiveRecord::QueryRecorder.new do - # Running this first ensures we don't run one query for every - # commit. + # batch commits commits.each(&:lazy_latest_pipeline) - # This forces the execution of the SQL queries necessary to load the - # data. - commits.each { |c| c.latest_pipeline.try(:id) } + # assert result correctness + commits_with_pipelines.each do |commit| + expect(commit.lazy_latest_pipeline.project).to eq(commit.project) + end + + expect(commits.last.lazy_latest_pipeline&.itself).to be_nil end - expect(recorder.count).to eq(1) + expect(recorder.count).to eq(2) end end diff --git a/spec/models/concerns/require_email_verification_spec.rb b/spec/models/concerns/require_email_verification_spec.rb index 0a6293f852e..1fb54e4276f 100644 --- a/spec/models/concerns/require_email_verification_spec.rb +++ b/spec/models/concerns/require_email_verification_spec.rb @@ -15,24 +15,20 @@ RSpec.describe RequireEmailVerification, feature_category: :insider_threat do using RSpec::Parameterized::TableSyntax - where(:feature_flag_enabled, :two_factor_enabled, :skipped, :overridden) do - false | false | false | false - false | false | true | false - false | true | false | false - false | true | true | false - true | false | false | true - true | false | true | false - true | true | false | false - true | true | true | false - end + where(feature_flag_enabled: [true, false], + two_factor_enabled: [true, false], + oauth_user: [true, false], + skipped: [true, false]) with_them do let(:instance) { model.new(id: 1) } let(:another_instance) { model.new(id: 2) } + let(:overridden) { feature_flag_enabled && !two_factor_enabled && !oauth_user && !skipped } before do stub_feature_flags(require_email_verification: feature_flag_enabled ? instance : another_instance) allow(instance).to receive(:two_factor_enabled?).and_return(two_factor_enabled) + allow(instance).to receive(:identities).and_return(oauth_user ? [:google] : []) stub_feature_flags(skip_require_email_verification: skipped ? instance : another_instance) end diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb index 4c9f930df2f..bce004dcd48 100644 --- a/spec/requests/api/terraform/state_spec.rb +++ b/spec/requests/api/terraform/state_spec.rb @@ -114,6 +114,17 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu end end + context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do + let(:state_name) { 'state-name-with-dot' } + let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}.tfstate" } + + before do + stub_feature_flags(allow_dots_on_tf_state_names: false) + end + + it_behaves_like 'can access terraform state' + end + context 'for a project that does not exist' do let(:project_id) { '0000' } @@ -266,6 +277,21 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu expect(Gitlab::Json.parse(response.body)).to be_empty end end + + context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do + let(:non_existing_state_name) { 'state-name-with-dot.tfstate' } + + before do + stub_feature_flags(allow_dots_on_tf_state_names: false) + end + + it 'strips characters after the dot' do + expect { request }.to change { Terraform::State.count }.by(1) + + expect(response).to have_gitlab_http_status(:ok) + expect(Terraform::State.last.name).to eq('state-name-with-dot') + end + end end context 'without body' do @@ -373,6 +399,18 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu it_behaves_like 'schedules the state for deletion' end + context 'allow_dots_on_tf_state_names is disabled, and the state name contains a dot' do + let(:state_name) { 'state-name-with-dot' } + let(:state_name_with_dot) { "#{state_name}.tfstate" } + let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name_with_dot}" } + + before do + stub_feature_flags(allow_dots_on_tf_state_names: false) + end + + it_behaves_like 'schedules the state for deletion' + end + context 'with invalid state name' do let(:state_name) { 'foo/bar' } @@ -462,10 +500,30 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu context 'with a dot in the state name' do let(:state_name) { 'test.state' } - it 'locks the terraform state' do - request + context 'with allow_dots_on_tf_state_names ff enabled' do + before do + stub_feature_flags(allow_dots_on_tf_state_names: true) + end - expect(response).to have_gitlab_http_status(:ok) + let(:state_name) { 'test.state' } + + it 'locks the terraform state' do + request + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'with allow_dots_on_tf_state_names ff disabled' do + before do + stub_feature_flags(allow_dots_on_tf_state_names: false) + end + + it 'returns 404' do + request + + expect(response).to have_gitlab_http_status(:not_found) + end end end end @@ -486,6 +544,7 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu before do state.lock_xid = '123.456' state.save! + stub_feature_flags(allow_dots_on_tf_state_names: true) end subject(:request) { delete api("#{state_path}/lock"), headers: auth_header, params: params } @@ -516,6 +575,23 @@ RSpec.describe API::Terraform::State, :snowplow, feature_category: :infrastructu end end + context 'with allow_dots_on_tf_state_names ff disabled' do + before do + stub_feature_flags(allow_dots_on_tf_state_names: false) + end + + context 'with dots in the state name' do + let(:lock_id) { '123.456' } + let(:state_name) { 'test.state' } + + it 'returns 404' do + request + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + context 'with no lock id (force-unlock)' do let(:params) { {} } diff --git a/spec/services/projects/android_target_platform_detector_service_spec.rb b/spec/services/projects/android_target_platform_detector_service_spec.rb deleted file mode 100644 index d5feef4ec3d..00000000000 --- a/spec/services/projects/android_target_platform_detector_service_spec.rb +++ /dev/null @@ -1,30 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Projects::AndroidTargetPlatformDetectorService, feature_category: :projects do - let_it_be(:project) { build(:project) } - - subject { described_class.new(project).execute } - - before do - allow(Gitlab::FileFinder).to receive(:new) { finder } - end - - context 'when project is not an Android project' do - let(:finder) { instance_double(Gitlab::FileFinder, find: []) } - - it { is_expected.to be_nil } - end - - context 'when project is an Android project' do - let(:finder) { instance_double(Gitlab::FileFinder) } - - before do - query = described_class::MANIFEST_FILE_SEARCH_QUERY - allow(finder).to receive(:find).with(query) { [instance_double(Gitlab::Search::FoundBlob)] } - end - - it { is_expected.to eq :android } - end -end diff --git a/spec/services/projects/record_target_platforms_service_spec.rb b/spec/services/projects/record_target_platforms_service_spec.rb index dd4e9fe19e0..17aa7fd7009 100644 --- a/spec/services/projects/record_target_platforms_service_spec.rb +++ b/spec/services/projects/record_target_platforms_service_spec.rb @@ -9,34 +9,23 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute', feature_categ subject(:execute) { described_class.new(project, detector_service).execute } - context 'when detector returns target platform values' do - let(:detector_result) { [:ios, :osx] } - let(:service_result) { detector_result.map(&:to_s) } + context 'when project is an XCode project' do + def project_setting + ProjectSetting.find_by_project_id(project.id) + end before do - double = instance_double(detector_service, execute: detector_result) - allow(detector_service).to receive(:new) { double } + double = instance_double(detector_service, execute: [:ios, :osx]) + allow(Projects::AppleTargetPlatformDetectorService).to receive(:new) { double } end - shared_examples 'saves and returns detected target platforms' do - it 'creates a new setting record for the project', :aggregate_failures do - expect { execute }.to change { ProjectSetting.count }.from(0).to(1) - expect(ProjectSetting.last.target_platforms).to match_array(service_result) - end - - it 'returns the array of stored target platforms' do - expect(execute).to match_array service_result - end + it 'creates a new setting record for the project', :aggregate_failures do + expect { execute }.to change { ProjectSetting.count }.from(0).to(1) + expect(ProjectSetting.last.target_platforms).to match_array(%w(ios osx)) end - it_behaves_like 'saves and returns detected target platforms' - - context 'when detector returns a non-array value' do - let(:detector_service) { Projects::AndroidTargetPlatformDetectorService } - let(:detector_result) { :android } - let(:service_result) { [detector_result.to_s] } - - it_behaves_like 'saves and returns detected target platforms' + it 'returns array of detected target platforms' do + expect(execute).to match_array %w(ios osx) end context 'when a project has an existing setting record' do @@ -44,10 +33,6 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute', feature_categ create(:project_setting, project: project, target_platforms: saved_target_platforms) end - def project_setting - ProjectSetting.find_by_project_id(project.id) - end - context 'when target platforms changed' do let(:saved_target_platforms) { %w(tvos) } @@ -98,44 +83,23 @@ RSpec.describe Projects::RecordTargetPlatformsService, '#execute', feature_categ it_behaves_like 'tracks experiment assignment event' end - shared_examples 'does not send email' do - it 'does not execute a Projects::InProductMarketingCampaignEmailsService' do - expect(Projects::InProductMarketingCampaignEmailsService).not_to receive(:new) - - execute - end - end - context 'experiment control' do before do stub_experiments(build_ios_app_guide_email: :control) end - it_behaves_like 'does not send email' - it_behaves_like 'tracks experiment assignment event' - end - - context 'when project is not an iOS project' do - let(:detector_service) { Projects::AppleTargetPlatformDetectorService } - let(:detector_result) { :android } - - before do - stub_experiments(build_ios_app_guide_email: :candidate) - end - - it_behaves_like 'does not send email' - - it 'does not track experiment assignment event', :experiment do - expect(experiment(:build_ios_app_guide_email)) - .not_to track(:assignment) + it 'does not execute a Projects::InProductMarketingCampaignEmailsService' do + expect(Projects::InProductMarketingCampaignEmailsService).not_to receive(:new) execute end + + it_behaves_like 'tracks experiment assignment event' end end end - context 'when detector does not return any target platform values' do + context 'when project is not an XCode project' do before do double = instance_double(Projects::AppleTargetPlatformDetectorService, execute: []) allow(Projects::AppleTargetPlatformDetectorService).to receive(:new).with(project) { double } diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index e488ca19903..a6b069bc93b 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -9495,7 +9495,6 @@ - './spec/services/projects/alerting/notify_service_spec.rb' - './spec/services/projects/all_issues_count_service_spec.rb' - './spec/services/projects/all_merge_requests_count_service_spec.rb' -- './spec/services/projects/android_target_platform_detector_service_spec.rb' - './spec/services/projects/apple_target_platform_detector_service_spec.rb' - './spec/services/projects/autocomplete_service_spec.rb' - './spec/services/projects/auto_devops/disable_service_spec.rb' diff --git a/spec/workers/projects/record_target_platforms_worker_spec.rb b/spec/workers/projects/record_target_platforms_worker_spec.rb index c1e99d52473..0e106fe32f9 100644 --- a/spec/workers/projects/record_target_platforms_worker_spec.rb +++ b/spec/workers/projects/record_target_platforms_worker_spec.rb @@ -7,11 +7,11 @@ RSpec.describe Projects::RecordTargetPlatformsWorker, feature_category: :project let_it_be(:swift) { create(:programming_language, name: 'Swift') } let_it_be(:objective_c) { create(:programming_language, name: 'Objective-C') } - let_it_be(:java) { create(:programming_language, name: 'Java') } - let_it_be(:kotlin) { create(:programming_language, name: 'Kotlin') } let_it_be(:project) { create(:project, :repository, detected_repository_languages: true) } let(:worker) { described_class.new } + let(:service_result) { %w(ios osx watchos) } + let(:service_double) { instance_double(Projects::RecordTargetPlatformsService, execute: service_result) } let(:lease_key) { "#{described_class.name.underscore}:#{project.id}" } let(:lease_timeout) { described_class::LEASE_TIMEOUT } @@ -49,68 +49,19 @@ RSpec.describe Projects::RecordTargetPlatformsWorker, feature_category: :project end end - def create_language(language) - create(:repository_language, project: project, programming_language: language) - end - - context 'when project uses programming language for Apple platform' do - let(:service_result) { %w(ios osx watchos) } - - context 'when project uses Swift programming language' do - before do - create_language(swift) - end - - it_behaves_like 'performs detection', Projects::AppleTargetPlatformDetectorService - end + context 'when project uses Swift programming language' do + let!(:repository_language) { create(:repository_language, project: project, programming_language: swift) } - context 'when project uses Objective-C programming language' do - before do - create_language(objective_c) - end - - it_behaves_like 'performs detection', Projects::AppleTargetPlatformDetectorService - end + include_examples 'performs detection', Projects::AppleTargetPlatformDetectorService end - context 'when project uses programming language for Android platform' do - let(:feature_enabled) { true } - let(:service_result) { %w(android) } - - before do - stub_feature_flags(detect_android_projects: feature_enabled) - end + context 'when project uses Objective-C programming language' do + let!(:repository_language) { create(:repository_language, project: project, programming_language: objective_c) } - context 'when project uses Java' do - before do - create_language(java) - end - - it_behaves_like 'performs detection', Projects::AndroidTargetPlatformDetectorService - - context 'when feature flag is disabled' do - let(:feature_enabled) { false } - - it_behaves_like 'does nothing' - end - end - - context 'when project uses Kotlin' do - before do - create_language(kotlin) - end - - it_behaves_like 'performs detection', Projects::AndroidTargetPlatformDetectorService - - context 'when feature flag is disabled' do - let(:feature_enabled) { false } - - it_behaves_like 'does nothing' - end - end + include_examples 'performs detection', Projects::AppleTargetPlatformDetectorService end - context 'when the project does not use programming languages for Apple or Android platforms' do + context 'when the project does not contain programming languages for Apple platforms' do it_behaves_like 'does nothing' end |