diff options
Diffstat (limited to 'spec/support/shared_examples')
35 files changed, 657 insertions, 143 deletions
diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index 62b35923bcd..5ed8dc7ce98 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -205,10 +205,30 @@ RSpec.shared_examples 'handle uploads' do allow_any_instance_of(FileUploader).to receive(:image?).and_return(true) end - it "responds with status 200" do - show_upload + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end - expect(response).to have_gitlab_http_status(:ok) + it "responds with status 302" do + show_upload + + expect(response).to have_gitlab_http_status(:redirect) + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload + + expect(response).to have_gitlab_http_status(:ok) + end + end end end @@ -276,10 +296,30 @@ RSpec.shared_examples 'handle uploads' do allow_any_instance_of(FileUploader).to receive(:image?).and_return(true) end - it "responds with status 200" do - show_upload + context "enforce_auth_checks_on_uploads feature flag" do + context "with flag enabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: true) + end + + it "responds with status 404" do + show_upload + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context "with flag disabled" do + before do + stub_feature_flags(enforce_auth_checks_on_uploads: false) + end + + it "responds with status 200" do + show_upload - expect(response).to have_gitlab_http_status(:ok) + expect(response).to have_gitlab_http_status(:ok) + end + end end end diff --git a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb index e0a032b1a43..8a07e52019c 100644 --- a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb +++ b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb @@ -2,7 +2,7 @@ RSpec.shared_examples 'comment on merge request file' do it 'adds a comment' do - click_diff_line(find("[id='#{sample_commit.line_code}']")) + click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']")) page.within('.js-discussion-note-form') do fill_in(:note_note, with: 'Line is wrong') diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb index 1c816ee4b0a..456175e7113 100644 --- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb +++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb @@ -62,7 +62,7 @@ RSpec.shared_examples 'a creatable merge request' do end it 'updates the branches when selecting a new target project', :js do - target_project_member = target_project.owner + target_project_member = target_project.first_owner ::Branches::CreateService.new(target_project, target_project_member) .execute('a-brand-new-branch-to-test', 'master') diff --git a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb index a9dac7a391f..281a70e46c4 100644 --- a/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb +++ b/spec/support/shared_examples/features/sidebar/sidebar_labels_shared_examples.rb @@ -54,7 +54,10 @@ RSpec.shared_examples 'labels sidebar widget' do end fill_in 'Search', with: 'Devel' - sleep 1 + expect(page).to have_css('.labels-fetch-loading') + wait_for_all_requests + + expect(page).to have_css('[data-testid="dropdown-content"] .gl-new-dropdown-item') expect(page.all(:css, '[data-testid="dropdown-content"] .gl-new-dropdown-item').length).to eq(1) find_field('Search').native.send_keys(:enter) diff --git a/spec/support/shared_examples/features/variable_list_shared_examples.rb b/spec/support/shared_examples/features/variable_list_shared_examples.rb index 52451839281..c63faace6b2 100644 --- a/spec/support/shared_examples/features/variable_list_shared_examples.rb +++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb @@ -166,7 +166,7 @@ RSpec.shared_examples 'variable list' do wait_for_requests expect(find('.flash-container')).to be_present - expect(find('.flash-text').text).to have_content('Variables key (key) has already been taken') + expect(find('[data-testid="alert-danger"]').text).to have_content('Variables key (key) has already been taken') end it 'prevents a variable to be added if no values are provided when a variable is set to masked' do diff --git a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb index 1a981f42086..2285d9a17e2 100644 --- a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb @@ -85,7 +85,9 @@ RSpec.shared_examples 'User previews wiki changes' do end it 'renders content with CommonMark' do - fill_in :wiki_content, with: "1. one\n - sublist\n" + # using two `\n` ensures we're sublist to it's own line due + # to list auto-continue + fill_in :wiki_content, with: "1. one\n\n - sublist\n" click_on "Preview" # the above generates two separate lists (not embedded) in CommonMark diff --git a/spec/support/shared_examples/graphql/boards_shared_examples.rb b/spec/support/shared_examples/graphql/boards_shared_examples.rb new file mode 100644 index 00000000000..e8a4c17fb92 --- /dev/null +++ b/spec/support/shared_examples/graphql/boards_shared_examples.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'querying a GraphQL type recent boards' do + describe 'Get list of recently visited boards' do + let(:boards_data) { graphql_data[board_type]['recentIssueBoards']['nodes'] } + + context 'when the request is correct' do + before do + visit_board + parent.add_reporter(user) + post_graphql(query, current_user: user) + end + + it_behaves_like 'a working graphql query' + + it 'returns recent boards for user successfully' do + expect(response).to have_gitlab_http_status(:ok) + expect(graphql_errors).to be_nil + expect(boards_data.size).to eq(1) + expect(boards_data[0]['name']).to eq(board.name) + end + end + + context 'when requests has errors' do + context 'when there are no recently visited boards' do + it 'returns empty result' do + post_graphql(query, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_errors).to be_nil + expect(boards_data).to be_empty + end + end + end + end + + def query(query_params: {}, full_path: parent.full_path) + board_nodes = <<~NODE + nodes { + name + } + NODE + + graphql_query_for( + board_type.to_sym, + { full_path: full_path }, + query_graphql_field(:recent_issue_boards, query_params, board_nodes) + ) + end + + def visit_board + if board_type == 'group' + create(:board_group_recent_visit, group: parent, board: board, user: user) + else + create(:board_project_recent_visit, project: parent, board: board, user: user) + end + end +end diff --git a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb index 011a2157f24..b17e59f0797 100644 --- a/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb +++ b/spec/support/shared_examples/graphql/mutations/can_mutate_spammable_examples.rb @@ -16,17 +16,4 @@ RSpec.shared_examples 'a mutation which can mutate a spammable' do subject end end - - describe "#spam_action_response_fields" do - it 'resolves with spam action fields' do - subject - - # NOTE: We do not need to assert on the specific values of spam action fields here, we only need - # to verify that #spam_action_response_fields was invoked and that the fields are present in the - # response. The specific behavior of #spam_action_response_fields is covered in the - # HasSpamActionResponseFields unit tests. - expect(mutation_response.keys) - .to include('spam', 'spamLogId', 'needsCaptchaResponse', 'captchaSiteKey') - end - end end diff --git a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb index 2bb3d807aa7..14b2663a72c 100644 --- a/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb +++ b/spec/support/shared_examples/graphql/mutations/security/ci_configuration_shared_examples.rb @@ -18,7 +18,7 @@ RSpec.shared_examples_for 'graphql mutations security ci configuration' do ServiceResponse.success(payload: { branch: branch, success_path: success_path }) end - let(:error) { "An error occured!" } + let(:error) { "An error occurred!" } let(:service_error_response) do ServiceResponse.error(message: error) diff --git a/spec/support/shared_examples/integrations/integration_settings_form.rb b/spec/support/shared_examples/integrations/integration_settings_form.rb new file mode 100644 index 00000000000..d0bb40e43ee --- /dev/null +++ b/spec/support/shared_examples/integrations/integration_settings_form.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'integration settings form' do + include IntegrationsHelper + # Note: these specs don't validate channel fields + # which are present on a few integrations + it 'displays all the integrations' do + aggregate_failures do + integrations.each do |integration| + navigate_to_integration(integration) + + page.within('form.integration-settings-form') do + expect(page).to have_field('Active', type: 'checkbox', wait: 0), + "#{integration.title} active field not present" + + fields = parse_json(fields_for_integration(integration)) + fields.each do |field| + field_name = field[:name] + expect(page).to have_field(field[:title], wait: 0), + "#{integration.title} field #{field_name} not present" + end + + events = parse_json(trigger_events_for_integration(integration)) + events.each do |trigger| + # normalizing the title because capybara location is case sensitive + title = normalize_title trigger[:title], integration + + expect(page).to have_field(title, type: 'checkbox', wait: 0), + "#{integration.title} field #{title} checkbox not present" + end + end + end + end + end + + private + + def normalize_title(title, integration) + return 'Merge request' if integration.is_a?(Integrations::Jira) && title == 'merge_request' + + title.titlecase + end + + def parse_json(json) + Gitlab::Json.parse(json, symbolize_names: true) + end +end diff --git a/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb index 5baa6478225..fdca326dbea 100644 --- a/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/experimentation_shared_examples.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true RSpec.shared_examples 'tracks assignment and records the subject' do |experiment, subject_type| + before do + stub_experiments(experiment => true) + end + it 'tracks the assignment', :experiment do expect(experiment(experiment)) .to track(:assignment) @@ -11,9 +15,7 @@ RSpec.shared_examples 'tracks assignment and records the subject' do |experiment end it 'records the subject' do - stub_experiments(experiment => :candidate) - - expect(Experiment).to receive(:add_subject).with(experiment.to_s, variant: :experimental, subject: subject) + expect(Experiment).to receive(:add_subject).with(experiment.to_s, variant: anything, subject: subject) action end diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb new file mode 100644 index 00000000000..6221366ab51 --- /dev/null +++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/code_review_extension_request_examples.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.shared_examples 'a request from an extension' do |event| + before do + stub_application_setting(usage_ping_enabled: true) + end + + def count_unique(date_from:, date_to:) + Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to) + end + + def track_action(params) + described_class.track_api_request_when_trackable(**params) + end + + it 'tracks when the user agent is matching' do + aggregate_failures do + expect(track_action(user: user1, **user_agent)).to be_truthy + expect(track_action(user: user1, **user_agent)).to be_truthy + expect(track_action(user: user2, **user_agent)).to be_truthy + + expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(2) + end + end + + it 'does not track when the user agent is not matching' do + aggregate_failures do + user_agent = { user_agent: 'normal_user_agent' } + + expect(track_action(user: user1, **user_agent)).to be_falsey + expect(track_action(user: user1, **user_agent)).to be_falsey + expect(track_action(user: user2, **user_agent)).to be_falsey + + expect(count_unique(date_from: time - 1.week, date_to: time + 1.week)).to eq(0) + end + end + + it 'does not track if user agent is not present' do + expect(track_action(user: nil, user_agent: nil)).to be_nil + end + + it 'does not track if user is not present' do + expect(track_action(user: nil, **user_agent)).to be_nil + end +end diff --git a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb index d3fd28727b5..b4c438771ce 100644 --- a/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb +++ b/spec/support/shared_examples/lib/sidebars/projects/menus/zentao_menu_shared_examples.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.shared_examples 'ZenTao menu with CE version' do let(:project) { create(:project, has_external_issue_tracker: true) } - let(:user) { project.owner } + let(:user) { project.first_owner } let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) } let(:zentao_integration) { create(:zentao_integration, project: project) } diff --git a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb index 42eec74e64f..5f59d43ad19 100644 --- a/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb +++ b/spec/support/shared_examples/loose_foreign_keys/have_loose_foreign_key.rb @@ -7,6 +7,12 @@ RSpec.shared_examples 'it has loose foreign keys' do let(:fully_qualified_table_name) { "#{connection.current_schema}.#{table_name}" } let(:deleted_records) { LooseForeignKeys::DeletedRecord.where(fully_qualified_table_name: fully_qualified_table_name) } + around do |example| + LooseForeignKeys::DeletedRecord.using_connection(connection) do + example.run + end + end + it 'has at least one loose foreign key definition' do definitions = Gitlab::Database::LooseForeignKeys.definitions_by_table[table_name] expect(definitions.size).to be > 0 @@ -69,7 +75,9 @@ RSpec.shared_examples 'cleanup by a loose foreign key' do expect(find_model).to be_present - LooseForeignKeys::ProcessDeletedRecordsService.new(connection: model.connection).execute + LooseForeignKeys::DeletedRecord.using_connection(parent.connection) do + LooseForeignKeys::ProcessDeletedRecordsService.new(connection: parent.connection).execute + end if foreign_key_definition.on_delete.eql?(:async_delete) expect(find_model).not_to be_present diff --git a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb index d823e7ac221..8ff30021d6e 100644 --- a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb +++ b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb @@ -178,4 +178,22 @@ RSpec.shared_examples 'StageEventModel' do end end end + + describe '#total_time' do + it 'calcualtes total time from the start_event_timestamp and end_event_timestamp columns' do + model = described_class.new(start_event_timestamp: Time.new(2022, 1, 1, 12, 5, 0), end_event_timestamp: Time.new(2022, 1, 1, 12, 6, 30)) + + expect(model.total_time).to eq(90) + end + + context 'when total time is calculated in SQL as an extra column' do + it 'returns the SQL calculated time' do + create(stage_event_factory) # rubocop:disable Rails/SaveBang + + model = described_class.select('*, 5 AS total_time').first + + expect(model.total_time).to eq(5) + end + end + end end diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb index 5b4b8c8fcc1..f7e09cfca62 100644 --- a/spec/support/shared_examples/models/member_shared_examples.rb +++ b/spec/support/shared_examples/models/member_shared_examples.rb @@ -301,8 +301,9 @@ RSpec.shared_examples_for "member creation" do end context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do + let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source } + it 'creates a member_task with the correct attributes', :aggregate_failures do - task_project = source.is_a?(Group) ? create(:project, group: source) : source described_class.new(source, user, :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id).execute member = source.members.last @@ -310,6 +311,43 @@ RSpec.shared_examples_for "member creation" do expect(member.tasks_to_be_done).to match_array([:ci, :code]) expect(member.member_task.project).to eq(task_project) end + + context 'with an already existing member' do + before do + source.add_user(user, :developer) + end + + it 'does not update tasks to be done if tasks already exist', :aggregate_failures do + member = source.members.find_by(user_id: user.id) + create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci)) + + expect do + described_class.new(source, + user, + :developer, + tasks_to_be_done: %w(issues), + tasks_project_id: task_project.id).execute + end.not_to change(MemberTask, :count) + + member.reset + expect(member.tasks_to_be_done).to match_array([:code, :ci]) + expect(member.member_task.project).to eq(task_project) + end + + it 'adds tasks to be done if they do not exist', :aggregate_failures do + expect do + described_class.new(source, + user, + :developer, + tasks_to_be_done: %w(issues), + tasks_project_id: task_project.id).execute + end.to change(MemberTask, :count).by(1) + + member = source.members.find_by(user_id: user.id) + expect(member.tasks_to_be_done).to match_array([:issues]) + expect(member.member_task.project).to eq(task_project) + end + end end end end @@ -393,14 +431,52 @@ RSpec.shared_examples_for "bulk member creation" do end context 'when `tasks_to_be_done` and `tasks_project_id` are passed' do + let(:task_project) { source.is_a?(Group) ? create(:project, group: source) : source } + it 'creates a member_task with the correct attributes', :aggregate_failures do - task_project = source.is_a?(Group) ? create(:project, group: source) : source members = described_class.add_users(source, [user1], :developer, tasks_to_be_done: %w(ci code), tasks_project_id: task_project.id) member = members.last expect(member.tasks_to_be_done).to match_array([:ci, :code]) expect(member.member_task.project).to eq(task_project) end + + context 'with an already existing member' do + before do + source.add_user(user1, :developer) + end + + it 'does not update tasks to be done if tasks already exist', :aggregate_failures do + member = source.members.find_by(user_id: user1.id) + create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci)) + + expect do + described_class.add_users(source, + [user1.id], + :developer, + tasks_to_be_done: %w(issues), + tasks_project_id: task_project.id) + end.not_to change(MemberTask, :count) + + member.reset + expect(member.tasks_to_be_done).to match_array([:code, :ci]) + expect(member.member_task.project).to eq(task_project) + end + + it 'adds tasks to be done if they do not exist', :aggregate_failures do + expect do + described_class.add_users(source, + [user1.id], + :developer, + tasks_to_be_done: %w(issues), + tasks_project_id: task_project.id) + end.to change(MemberTask, :count).by(1) + + member = source.members.find_by(user_id: user1.id) + expect(member.tasks_to_be_done).to match_array([:issues]) + expect(member.member_task.project).to eq(task_project) + end + end end end end diff --git a/spec/support/shared_examples/models/note_access_check_shared_examples.rb b/spec/support/shared_examples/models/note_access_check_shared_examples.rb index 44edafe9091..0c9992b832f 100644 --- a/spec/support/shared_examples/models/note_access_check_shared_examples.rb +++ b/spec/support/shared_examples/models/note_access_check_shared_examples.rb @@ -3,7 +3,7 @@ RSpec.shared_examples 'users with note access' do it 'returns true' do users.each do |user| - expect(note.system_note_with_references_visible_for?(user)).to be_truthy + expect(note.system_note_visible_for?(user)).to be_truthy expect(note.readable_by?(user)).to be_truthy end end @@ -12,7 +12,7 @@ end RSpec.shared_examples 'users without note access' do it 'returns false' do users.each do |user| - expect(note.system_note_with_references_visible_for?(user)).to be_falsy + expect(note.system_note_visible_for?(user)).to be_falsy expect(note.readable_by?(user)).to be_falsy end end diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb index 3f8c3b8960b..6b0ae589efb 100644 --- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb +++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb @@ -235,18 +235,6 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze| it 'does not return them' do expect(subject.to_a).not_to include(package_file_pending_destruction) end - - context 'with packages_installable_package_files disabled' do - before do - stub_feature_flags(packages_installable_package_files: false) - end - - it 'returns them' do - subject - - expect(subject.to_a).to include(package_file_pending_destruction) - end - end end end end diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb index 06326ffac97..ad0bbc0aeff 100644 --- a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb +++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb @@ -115,14 +115,14 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute| expect(ProjectStatistics) .not_to receive(:increment_statistic) - expect(Projects::DestroyService.new(project, project.owner).execute).to eq(true) + expect(Projects::DestroyService.new(project, project.first_owner).execute).to eq(true) end it 'does not schedule a namespace statistics worker' do expect(Namespaces::ScheduleAggregationWorker) .not_to receive(:perform_async) - expect(Projects::DestroyService.new(project, project.owner).execute).to eq(true) + expect(Projects::DestroyService.new(project, project.first_owner).execute).to eq(true) end end end diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb index b43b7946e69..bcb5464ed5b 100644 --- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb +++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb @@ -299,4 +299,51 @@ RSpec.shared_examples 'namespace traversal scopes' do include_examples '.self_and_descendant_ids' end end + + shared_examples '.self_and_hierarchy' do + let(:base_scope) { Group.where(id: base_groups) } + + subject { base_scope.self_and_hierarchy } + + context 'with ancestors only' do + let(:base_groups) { [group_1, group_2] } + + it { is_expected.to match_array(groups) } + end + + context 'with descendants only' do + let(:base_groups) { [deep_nested_group_1, deep_nested_group_2] } + + it { is_expected.to match_array(groups) } + end + + context 'nodes with both ancestors and descendants' do + let(:base_groups) { [nested_group_1, nested_group_2] } + + it { is_expected.to match_array(groups) } + end + + context 'with duplicate base groups' do + let(:base_groups) { [nested_group_1, nested_group_1] } + + it { is_expected.to contain_exactly(group_1, nested_group_1, deep_nested_group_1) } + end + end + + describe '.self_and_hierarchy' do + it_behaves_like '.self_and_hierarchy' + + context "use_traversal_ids_for_self_and_hierarchy_scopes feature flag is false" do + before do + stub_feature_flags(use_traversal_ids_for_self_and_hierarchy_scopes: false) + end + + it_behaves_like '.self_and_hierarchy' + + it 'make recursive queries' do + base_groups = Group.where(id: nested_group_1) + expect { base_groups.self_and_hierarchy.load }.to make_queries_matching(/WITH RECURSIVE/) + end + end + end end diff --git a/spec/support/shared_examples/path_extraction_shared_examples.rb b/spec/support/shared_examples/path_extraction_shared_examples.rb index 39c7c1f2a94..d76348aa26a 100644 --- a/spec/support/shared_examples/path_extraction_shared_examples.rb +++ b/spec/support/shared_examples/path_extraction_shared_examples.rb @@ -40,12 +40,13 @@ RSpec.shared_examples 'assigns ref vars' do end context 'path contains space' do - let(:params) { { path: 'with space', ref: '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' } } + let(:ref) { '38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e' } + let(:path) { 'with space' } it 'is not converted to %20 in @path' do assign_ref_vars - expect(@path).to eq(params[:path]) + expect(@path).to eq(path) end end diff --git a/spec/support/shared_examples/policies/clusterable_shared_examples.rb b/spec/support/shared_examples/policies/clusterable_shared_examples.rb index b96aa71acbe..faf283f9059 100644 --- a/spec/support/shared_examples/policies/clusterable_shared_examples.rb +++ b/spec/support/shared_examples/policies/clusterable_shared_examples.rb @@ -6,12 +6,24 @@ RSpec.shared_examples 'clusterable policies' do subject { described_class.new(current_user, clusterable) } + context 'with a reporter' do + before do + clusterable.add_reporter(current_user) + end + + it { expect_disallowed(:read_cluster) } + it { expect_disallowed(:add_cluster) } + it { expect_disallowed(:create_cluster) } + it { expect_disallowed(:update_cluster) } + it { expect_disallowed(:admin_cluster) } + end + context 'with a developer' do before do clusterable.add_developer(current_user) end - it { expect_disallowed(:read_cluster) } + it { expect_allowed(:read_cluster) } it { expect_disallowed(:add_cluster) } it { expect_disallowed(:create_cluster) } it { expect_disallowed(:update_cluster) } diff --git a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb index 052fd0622d0..f414500f202 100644 --- a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb @@ -66,7 +66,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type| it 'shows the help state when icon is clicked' do page.within '.time-tracking-component-wrap' do - find('.help-button').click + find('[data-testid="helpButton"]').click expect(page).to have_content 'Track time with quick actions' expect(page).to have_content 'Learn more' end @@ -92,8 +92,8 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type| it 'hides the help state when close icon is clicked' do page.within '.time-tracking-component-wrap' do - find('.help-button').click - find('.close-help-button').click + find('[data-testid="helpButton"]').click + find('[data-testid="closeHelpButton"]').click expect(page).not_to have_content 'Track time with quick actions' expect(page).not_to have_content 'Learn more' @@ -102,7 +102,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type| it 'displays the correct help url' do page.within '.time-tracking-component-wrap' do - find('.help-button').click + find('[data-testid="helpButton"]').click expect(find_link('Learn more')[:href]).to have_content('/help/user/project/time_tracking.md') end diff --git a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb index 28decb4011d..2258bdd2c79 100644 --- a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb +++ b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb @@ -73,6 +73,16 @@ RSpec.shared_examples 'rebase quick action' do expect(page).to have_content 'This merge request cannot be rebased while there are conflicts.' end end + + context 'when the merge request branch is protected from force push' do + let!(:protected_branch) { create(:protected_branch, project: project, name: merge_request.source_branch, allow_force_push: false) } + + it 'does not rebase the MR' do + add_note("/rebase") + + expect(page).to have_content 'This merge request branch is protected from force push.' + end + end end context 'when the current user cannot rebase the MR' do diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb index 8bffd1f71e9..a42a1fda62e 100644 --- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb @@ -10,6 +10,8 @@ RSpec.shared_examples 'when the snippet is not found' do end RSpec.shared_examples 'snippet edit usage data counters' do + include SessionHelpers + context 'when user is sessionless' do it 'does not track usage data actions' do expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action) @@ -20,14 +22,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do context 'when user is not sessionless', :clean_gitlab_redis_sessions do before do - session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') - session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] } - - Gitlab::Redis::Sessions.with do |redis| - redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash)) - end - - cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id + stub_session('warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]]) end it 'tracks usage data actions', :clean_gitlab_redis_sessions do diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb index 882c79cb03f..127b1a6d4c4 100644 --- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb @@ -3,11 +3,11 @@ RSpec.shared_examples 'group and project packages query' do include GraphqlHelpers - let_it_be(:versionaless_package) { create(:maven_package, project: project1, version: nil) } - let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'tab', version: '4.0.0', created_at: 5.days.ago) } - let_it_be(:package) { create(:npm_package, project: project1, name: 'uab', version: '5.0.0', created_at: 4.days.ago) } - let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'vab', version: '6.0.0', created_at: 3.days.ago) } - let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'zab', version: '7.0.0', created_at: 2.days.ago) } + let_it_be(:versionless_package) { create(:maven_package, project: project1, version: nil) } + let_it_be(:maven_package) { create(:maven_package, project: project1, name: 'bab', version: '6.0.0', created_at: 1.day.ago) } + let_it_be(:npm_package) { create(:npm_package, project: project1, name: 'cab', version: '7.0.0', created_at: 4.days.ago) } + let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'dab', version: '4.0.0', created_at: 3.days.ago) } + let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'aab', version: '5.0.0', created_at: 2.days.ago) } let_it_be(:composer_metadatum) do create(:composer_metadatum, package: composer_package, target_sha: 'afdeh', @@ -21,11 +21,11 @@ RSpec.shared_examples 'group and project packages query' do let(:fields) do <<~QUERY - count - nodes { - #{all_graphql_fields_for('packages'.classify, excluded: ['project'])} - metadata { #{query_graphql_fragment('ComposerMetadata')} } - } + count + nodes { + #{all_graphql_fields_for('packages'.classify, excluded: ['project'])} + metadata { #{query_graphql_fragment('ComposerMetadata')} } + } QUERY end @@ -47,7 +47,7 @@ RSpec.shared_examples 'group and project packages query' do it 'returns packages successfully' do expect(package_names).to contain_exactly( - package.name, + npm_package.name, maven_package.name, debian_package.name, composer_package.name @@ -88,7 +88,23 @@ RSpec.shared_examples 'group and project packages query' do end describe 'sorting and pagination' do - let_it_be(:ascending_packages) { [maven_package, package, composer_package, debian_package].map { |package| global_id_of(package)} } + let_it_be(:packages_order_map) do + { + TYPE_ASC: [maven_package, npm_package, composer_package, debian_package], + TYPE_DESC: [debian_package, composer_package, npm_package, maven_package], + + NAME_ASC: [debian_package, maven_package, npm_package, composer_package], + NAME_DESC: [composer_package, npm_package, maven_package, debian_package], + + VERSION_ASC: [composer_package, debian_package, maven_package, npm_package], + VERSION_DESC: [npm_package, maven_package, debian_package, composer_package], + + CREATED_ASC: [npm_package, composer_package, debian_package, maven_package], + CREATED_DESC: [maven_package, debian_package, composer_package, npm_package] + } + end + + let(:expected_packages) { sorted_packages.map { |package| global_id_of(package) } } let(:data_path) { [resource_type, :packages] } @@ -96,22 +112,14 @@ RSpec.shared_examples 'group and project packages query' do resource.add_reporter(current_user) end - [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC].each do |order| + [:CREATED_ASC, :NAME_ASC, :VERSION_ASC, :TYPE_ASC, :CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order| context "#{order}" do - it_behaves_like 'sorted paginated query' do - let(:sort_param) { order } - let(:first_param) { 4 } - let(:all_records) { ascending_packages } - end - end - end + let(:sorted_packages) { packages_order_map.fetch(order) } - [:CREATED_DESC, :NAME_DESC, :VERSION_DESC, :TYPE_DESC].each do |order| - context "#{order}" do it_behaves_like 'sorted paginated query' do let(:sort_param) { order } let(:first_param) { 4 } - let(:all_records) { ascending_packages.reverse } + let(:all_records) { expected_packages } end end end @@ -180,7 +188,7 @@ RSpec.shared_examples 'group and project packages query' do context 'include_versionless' do let(:params) { { include_versionless: true } } - it { is_expected.to include({ "name" => versionaless_package.name }) } + it { is_expected.to include({ "name" => versionless_package.name }) } end end end diff --git a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb index 9385706d991..ab93f54111b 100644 --- a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb @@ -49,17 +49,5 @@ RSpec.shared_examples 'a package with files' do expect(response_package_file_ids).not_to include(package_file_pending_destruction.to_global_id.to_s) end - - context 'with packages_installable_package_files disabled' do - before(:context) do - stub_feature_flags(packages_installable_package_files: false) - end - - it 'returns them' do - expect(package.reload.package_files).to include(package_file_pending_destruction) - - expect(response_package_file_ids).to include(package_file_pending_destruction.to_global_id.to_s) - end - end end end diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb index 0434d0beb7e..2a157f6e855 100644 --- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb @@ -190,7 +190,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name| if parent_type == 'projects' context 'by a project owner' do - let(:user) { project.owner } + let(:user) { project.first_owner } it 'sets the creation time on the new note' do post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb index b294467d482..c6c6c44dce8 100644 --- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb +++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb @@ -580,3 +580,88 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do end end end + +# Requires let variables: +# * throttle_setting_prefix: "throttle_authenticated", "throttle_unauthenticated" +RSpec.shared_examples 'rate-limited frontend API requests' do + let(:requests_per_period) { 1 } + let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) } + let(:csrf_session) { { _csrf_token: csrf_token } } + let(:personal_access_token) { nil } + + let(:api_path) { '/projects' } + + # These don't actually exist, so a 404 is the expected response. + let(:files_api_path) { '/projects/1/repository/files/ref/path' } + let(:packages_api_path) { '/projects/1/packages/foo' } + let(:deprecated_api_path) { '/groups/1?with_projects=true' } + + def get_api(path: api_path, csrf: false) + headers = csrf ? { 'X-CSRF-Token' => csrf_token } : nil + get api(path, personal_access_token: personal_access_token), headers: headers + end + + def expect_not_found(&block) + yield + + expect(response).to have_gitlab_http_status(:not_found) + end + + before do + stub_application_setting( + "#{throttle_setting_prefix}_enabled" => true, + "#{throttle_setting_prefix}_requests_per_period" => requests_per_period, + "#{throttle_setting_prefix}_api_enabled" => true, + "#{throttle_setting_prefix}_api_requests_per_period" => requests_per_period, + "#{throttle_setting_prefix}_web_enabled" => true, + "#{throttle_setting_prefix}_web_requests_per_period" => requests_per_period, + "#{throttle_setting_prefix}_files_api_enabled" => true, + "#{throttle_setting_prefix}_packages_api_enabled" => true, + "#{throttle_setting_prefix}_deprecated_api_enabled" => true + ) + + stub_session(csrf_session) + end + + context 'with a CSRF token' do + it 'uses the rate limit for web requests' do + requests_per_period.times { get_api csrf: true } + + expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true } + expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: files_api_path } + expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: packages_api_path } + expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: deprecated_api_path } + + # API rate limit is not triggered yet + expect_ok { get_api } + expect_not_found { get_api path: files_api_path } + expect_not_found { get_api path: packages_api_path } + expect_not_found { get_api path: deprecated_api_path } + end + + context 'without a CSRF session' do + let(:csrf_session) { nil } + + it 'always uses the rate limit for API requests' do + requests_per_period.times { get_api csrf: true } + + expect_rejection("#{throttle_setting_prefix}_api") { get_api csrf: true } + expect_rejection("#{throttle_setting_prefix}_api") { get_api } + end + end + end + + context 'without a CSRF token' do + it 'uses the rate limit for API requests' do + requests_per_period.times { get_api } + + expect_rejection("#{throttle_setting_prefix}_api") { get_api } + + # Web and custom API rate limits are not triggered yet + expect_ok { get_api csrf: true } + expect_not_found { get_api path: files_api_path } + expect_not_found { get_api path: packages_api_path } + expect_not_found { get_api path: deprecated_api_path } + end + end +end diff --git a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb index d9b837258ce..a46c2f0ac5c 100644 --- a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb +++ b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb @@ -140,19 +140,6 @@ RSpec.shared_examples 'issues move service' do |group| expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0) end - if group - context 'when on a group board' do - it 'sends the board_group_id parameter' do - params.merge!(move_after_id: issue1.id, move_before_id: issue2.id) - - match_params = { move_between_ids: [issue1.id, issue2.id], board_group_id: parent.id } - expect(Issues::UpdateService).to receive(:new).with(project: issue.project, current_user: user, params: match_params).and_return(double(execute: build(:issue))) - - described_class.new(parent, user, params).execute(issue) - end - end - end - def reorder_issues(params, issues: []) issues.each do |issue| issue.move_to_end && issue.save! diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb index 87bf134eeb8..c808b9a5318 100644 --- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb +++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb @@ -71,7 +71,6 @@ end RSpec.shared_examples 'an accessible' do before do stub_feature_flags(container_registry_migration_phase1: false) - stub_feature_flags(container_registry_cdn_redirect: false) end let(:access) do @@ -164,10 +163,9 @@ RSpec.shared_examples 'a container registry auth service' do before do stub_feature_flags(container_registry_migration_phase1: false) - stub_feature_flags(container_registry_cdn_redirect: false) end - describe '#full_access_token' do + describe '.full_access_token' do let_it_be(:project) { create(:project) } let(:token) { described_class.full_access_token(project.full_path) } @@ -181,7 +179,26 @@ RSpec.shared_examples 'a container registry auth service' do it_behaves_like 'not a container repository factory' end - describe '#pull_access_token' do + describe '.import_access_token' do + let(:access) do + [{ 'type' => 'registry', + 'name' => 'import', + 'actions' => ['*'] }] + end + + let(:token) { described_class.import_access_token } + + subject { { token: token } } + + it_behaves_like 'a valid token' + it_behaves_like 'not a container repository factory' + + it 'has the correct scope' do + expect(payload).to include('access' => access) + end + end + + describe '.pull_access_token' do let_it_be(:project) { create(:project) } let(:token) { described_class.pull_access_token(project.full_path) } @@ -1126,4 +1143,72 @@ RSpec.shared_examples 'a container registry auth service' do end end end + + context 'when importing' do + let_it_be(:container_repository) { create(:container_repository, :root, :importing) } + let_it_be(:current_project) { container_repository.project } + let_it_be(:current_user) { create(:user) } + + before do + current_project.add_developer(current_user) + end + + shared_examples 'containing the import error' do + it 'includes a helpful error message' do + expect(subject[:errors].first).to include(message: /Your repository is currently being migrated/) + end + end + + context 'push request' do + let(:current_params) do + { scopes: ["repository:#{container_repository.path}:push"] } + end + + it_behaves_like 'a forbidden' do + it_behaves_like 'containing the import error' + end + end + + context 'delete request' do + let(:current_params) do + { scopes: ["repository:#{container_repository.path}:delete"] } + end + + it_behaves_like 'a forbidden' do + it_behaves_like 'containing the import error' + end + end + + context '* request' do + let(:current_params) do + { scopes: ["repository:#{container_repository.path}:*"] } + end + + it_behaves_like 'a forbidden' do + it_behaves_like 'containing the import error' + end + end + + context 'pull request' do + let(:current_params) do + { scopes: ["repository:#{container_repository.path}:pull"] } + end + + let(:project) { current_project } + + it_behaves_like 'a pullable' + end + + context 'mixed request' do + let(:current_params) do + { scopes: ["repository:#{container_repository.path}:pull,push"] } + end + + let(:project) { current_project } + + it_behaves_like 'a forbidden' do + it_behaves_like 'containing the import error' + end + end + end end diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb index 36b0acf5a51..cc26cf87322 100644 --- a/spec/support/shared_examples/services/incident_shared_examples.rb +++ b/spec/support/shared_examples/services/incident_shared_examples.rb @@ -28,28 +28,15 @@ end # # include_examples 'not an incident issue' RSpec.shared_examples 'not an incident issue' do - let(:label_properties) { attributes_for(:label, :incident) } - it 'has not incident as issue type' do expect(issue.issue_type).not_to eq('incident') expect(issue.work_item_type.base_type).not_to eq('incident') end - - it_behaves_like 'does not have incident label' -end - -RSpec.shared_examples 'does not have incident label' do - let(:label_properties) { attributes_for(:label, :incident) } - - it 'has not an incident label' do - expect(issue.labels).not_to include(have_attributes(label_properties)) - end end # This shared example is to test the execution of incident management label services # For example: # - IncidentManagement::CreateIncidentSlaExceededLabelService -# - IncidentManagement::CreateIncidentLabelService # It doesn't require any defined variables diff --git a/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb b/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb new file mode 100644 index 00000000000..661a96266f1 --- /dev/null +++ b/spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'renders registration features prompt' do |disabled_field| + it 'renders a placeholder input with registration features message', :aggregate_failures do + render + + if disabled_field + expect(rendered).to have_field(disabled_field, disabled: true) + end + + expect(rendered).to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: s_('RegistrationFeatures|use this feature') }) + expect(rendered).to have_link(s_('RegistrationFeatures|Registration Features Program')) + end +end + +RSpec.shared_examples 'does not render registration features prompt' do |disabled_field| + it 'does not render a placeholder input with registration features message', :aggregate_failures do + render + + if disabled_field + expect(rendered).not_to have_field(disabled_field, disabled: true) + end + + expect(rendered).not_to have_content(s_("RegistrationFeatures|Want to %{feature_title} for free?") % { feature_title: s_('RegistrationFeatures|use this feature') }) + expect(rendered).not_to have_link(s_('RegistrationFeatures|Registration Features Program')) + end +end diff --git a/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb index 0d3e158d358..7fdf049a823 100644 --- a/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb +++ b/spec/support/shared_examples/workers/background_migration_worker_shared_examples.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.shared_examples 'it runs background migration jobs' do |tracking_database, metric_name| +RSpec.shared_examples 'it runs background migration jobs' do |tracking_database| describe 'defining the job attributes' do it 'defines the data_consistency as always' do expect(described_class.get_data_consistency).to eq(:always) @@ -33,16 +33,6 @@ RSpec.shared_examples 'it runs background migration jobs' do |tracking_database, end end - describe '.unhealthy_metric_name' do - it 'does not raise an error' do - expect { described_class.unhealthy_metric_name }.not_to raise_error - end - - it 'overrides the method to return the unhealthy metric name' do - expect(described_class.unhealthy_metric_name).to eq(metric_name) - end - end - describe '.minimum_interval' do it 'returns 2 minutes' do expect(described_class.minimum_interval).to eq(2.minutes.to_i) @@ -189,11 +179,11 @@ RSpec.shared_examples 'it runs background migration jobs' do |tracking_database, end it 'increments the unhealthy counter' do - counter = Gitlab::Metrics.counter(metric_name, 'msg') + counter = Gitlab::Metrics.counter(:background_migration_database_health_reschedules, 'msg') expect(described_class).to receive(:perform_in) - expect { worker.perform('Foo', [10, 20]) }.to change { counter.get }.by(1) + expect { worker.perform('Foo', [10, 20]) }.to change { counter.get(db_config_name: tracking_database) }.by(1) end context 'when lease_attempts is 0' do diff --git a/spec/support/shared_examples/workers/project_export_shared_examples.rb b/spec/support/shared_examples/workers/project_export_shared_examples.rb index a9bcc3f4f7c..175ef9bd012 100644 --- a/spec/support/shared_examples/workers/project_export_shared_examples.rb +++ b/spec/support/shared_examples/workers/project_export_shared_examples.rb @@ -53,6 +53,10 @@ RSpec.shared_examples 'export worker' do it 'does not raise an exception when strategy is invalid' do expect(::Projects::ImportExport::ExportService).not_to receive(:new) + expect_next_instance_of(ProjectExportJob) do |job| + expect(job).to receive(:finish) + end + expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error end @@ -63,6 +67,18 @@ RSpec.shared_examples 'export worker' do it 'does not raise error when user cannot be found' do expect { subject.perform(non_existing_record_id, project.id, {}) }.not_to raise_error end + + it 'fails the export job status' do + expect_next_instance_of(::Projects::ImportExport::ExportService) do |service| + expect(service).to receive(:execute).and_raise(Gitlab::ImportExport::Error) + end + + expect_next_instance_of(ProjectExportJob) do |job| + expect(job).to receive(:fail_op) + end + + expect { subject.perform(user.id, project.id, {}) }.to raise_error(Gitlab::ImportExport::Error) + end end end |