diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-20 12:40:42 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-20 12:40:42 +0300 |
commit | ee664acb356f8123f4f6b00b73c1e1cf0866c7fb (patch) | |
tree | f8479f94a28f66654c6a4f6fb99bad6b4e86a40e /spec/controllers | |
parent | 62f7d5c5b69180e82ae8196b7b429eeffc8e7b4f (diff) |
Add latest changes from gitlab-org/gitlab@15-5-stable-eev15.5.0-rc42
Diffstat (limited to 'spec/controllers')
42 files changed, 826 insertions, 1356 deletions
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index ab0cad989cb..0ad0a111156 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -211,6 +211,13 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set expect(ApplicationSetting.current.valid_runner_registrars).to eq(['project']) end + it 'updates can_create_group setting' do + put :update, params: { application_setting: { can_create_group: false } } + + expect(response).to redirect_to(general_admin_application_settings_path) + expect(ApplicationSetting.current.can_create_group).to eq(false) + end + context "personal access token prefix settings" do let(:application_settings) { ApplicationSetting.current } diff --git a/spec/controllers/admin/cohorts_controller_spec.rb b/spec/controllers/admin/cohorts_controller_spec.rb index 766073977c6..50626a5da91 100644 --- a/spec/controllers/admin/cohorts_controller_spec.rb +++ b/spec/controllers/admin/cohorts_controller_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Admin::CohortsController do let(:target_id) { 'i_analytics_cohorts' } end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject { get :index } let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } diff --git a/spec/controllers/admin/dev_ops_report_controller_spec.rb b/spec/controllers/admin/dev_ops_report_controller_spec.rb index 5d7a7e089aa..52a46b5e99a 100644 --- a/spec/controllers/admin/dev_ops_report_controller_spec.rb +++ b/spec/controllers/admin/dev_ops_report_controller_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Admin::DevOpsReportController do let(:request_params) { { tab: 'devops-score' } } end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject { get :show, format: :html } let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } diff --git a/spec/controllers/admin/groups_controller_spec.rb b/spec/controllers/admin/groups_controller_spec.rb index fb843ac6a7a..37cb0a1f289 100644 --- a/spec/controllers/admin/groups_controller_spec.rb +++ b/spec/controllers/admin/groups_controller_spec.rb @@ -44,64 +44,4 @@ RSpec.describe Admin::GroupsController do end.to change { Namespace::AdminNote.count }.by(1) end end - - describe 'PUT #members_update' do - let_it_be(:group_user) { create(:user) } - - it 'adds user to members', :aggregate_failures, :snowplow do - put :members_update, params: { - id: group, - user_id: group_user.id, - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(admin_group_path(group)) - expect(group.users).to include group_user - expect_snowplow_event( - category: 'Members::CreateService', - action: 'create_member', - label: 'admin-group-page', - property: 'existing_user', - user: admin - ) - end - - it 'can add unlimited members', :aggregate_failures do - put :members_update, params: { - id: group, - user_id: 1.upto(1000).to_a.join(','), - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(admin_group_path(group)) - end - - it 'adds no user to members', :aggregate_failures do - put :members_update, params: { - id: group, - user_id: '', - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'No users specified.' - expect(response).to redirect_to(admin_group_path(group)) - expect(group.users).not_to include group_user - end - - it 'updates the project_creation_level successfully' do - expect do - post :update, params: { id: group.to_param, group: { project_creation_level: ::Gitlab::Access::NO_ONE_PROJECT_ACCESS } } - end.to change { group.reload.project_creation_level }.to(::Gitlab::Access::NO_ONE_PROJECT_ACCESS) - end - - it 'updates the subgroup_creation_level successfully' do - expect do - post :update, - params: { id: group.to_param, - group: { subgroup_creation_level: ::Gitlab::Access::OWNER_SUBGROUP_ACCESS } } - end.to change { group.reload.subgroup_creation_level }.to(::Gitlab::Access::OWNER_SUBGROUP_ACCESS) - end - end end diff --git a/spec/controllers/admin/usage_trends_controller_spec.rb b/spec/controllers/admin/usage_trends_controller_spec.rb index 356f603bf57..87cf8988b4e 100644 --- a/spec/controllers/admin/usage_trends_controller_spec.rb +++ b/spec/controllers/admin/usage_trends_controller_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Admin::UsageTrendsController do let(:target_id) { 'i_analytics_instance_statistics' } end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject { get :index } let(:feature_flag_name) { :route_hll_to_snowplow_phase2 } diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 70e58124d21..e9b39d44e46 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -96,7 +96,7 @@ RSpec.describe AutocompleteController do end context 'user order' do - it 'shows exact matches first' do + it 'shows exact matches first', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/375028' do reported_user = create(:user, username: 'reported_user', name: 'Doug') user = create(:user, username: 'user', name: 'User') user1 = create(:user, username: 'user1', name: 'Ian') diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb deleted file mode 100644 index 3e1cdfccc61..00000000000 --- a/spec/controllers/boards/issues_controller_spec.rb +++ /dev/null @@ -1,596 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Boards::IssuesController do - include ExternalAuthorizationServiceHelpers - - let(:project) { create(:project, :private) } - let(:board) { create(:board, project: project) } - let(:user) { create(:user) } - let(:guest) { create(:user) } - - let(:planning) { create(:label, project: project, name: 'Planning') } - let(:development) { create(:label, project: project, name: 'Development') } - - let!(:list1) { create(:list, board: board, label: planning, position: 0) } - let!(:list2) { create(:list, board: board, label: development, position: 1) } - - before do - project.add_maintainer(user) - project.add_guest(guest) - end - - describe 'GET index', :request_store do - let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join('spec/fixtures/dk.png'))) } - - context 'with invalid board id' do - it 'returns a not found 404 response' do - list_issues user: user, board: non_existing_record_id, list: list2 - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'when list id is present' do - context 'with valid list id' do - let(:group) { create(:group, :private, projects: [project]) } - let(:group_board) { create(:board, group: group) } - let!(:list3) { create(:list, board: group_board, label: development, position: 2) } - let(:sub_group_1) { create(:group, :private, parent: group) } - - before do - group.add_maintainer(user) - end - - it 'returns issues that have the list label applied' do - issue = create(:labeled_issue, project: project, labels: [planning]) - create(:labeled_issue, project: project, labels: [planning]) - create(:labeled_issue, project: project, labels: [development], due_date: Date.tomorrow) - create(:labeled_issue, project: project, labels: [development], assignees: [johndoe]) - issue.subscribe(johndoe, project) - expect(Issue).to receive(:move_nulls_to_end) - - list_issues user: user, board: board, list: list2 - - expect(response).to match_response_schema('entities/issue_boards') - expect(json_response['issues'].length).to eq 2 - expect(development.issues.map(&:relative_position)).not_to include(nil) - end - - it 'returns issues by closed_at in descending order in closed list' do - create(:closed_issue, project: project, title: 'New Issue 1', closed_at: 1.day.ago) - create(:closed_issue, project: project, title: 'New Issue 2', closed_at: 1.week.ago) - - list_issues user: user, board: board, list: board.lists.last.id - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['issues'].length).to eq(2) - expect(json_response['issues'][0]['title']).to eq('New Issue 1') - expect(json_response['issues'][1]['title']).to eq('New Issue 2') - end - - it 'avoids N+1 database queries' do - create(:labeled_issue, project: project, labels: [development]) - control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list2) }.count - - # 25 issues is bigger than the page size - # the relative position will ignore the `#make_sure_position_set` queries - create_list(:labeled_issue, 25, project: project, labels: [development], assignees: [johndoe], relative_position: 1) - - expect { list_issues(user: user, board: board, list: list2) }.not_to exceed_query_limit(control_count) - end - - it 'avoids N+1 database queries when adding a project', :request_store do - create(:labeled_issue, project: project, labels: [development]) - control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count - - 2.times do - p = create(:project, group: group) - create(:labeled_issue, project: p, labels: [development]) - end - - project_2 = create(:project, group: group) - create(:labeled_issue, project: project_2, labels: [development], assignees: [johndoe]) - - # because each issue without relative_position must be updated with - # a different value, we have 8 extra queries per issue - expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1)) - end - - it 'avoids N+1 database queries when adding a subgroup, project, and issue' do - create(:project, group: sub_group_1) - create(:labeled_issue, project: project, labels: [development]) - control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count - project_2 = create(:project, group: group) - - 2.times do - p = create(:project, group: sub_group_1) - create(:labeled_issue, project: p, labels: [development]) - end - - create(:labeled_issue, project: project_2, labels: [development], assignees: [johndoe]) - - expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1)) - end - - it 'does not query issues table more than once' do - recorder = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: board, list: list1) } - query_count = recorder.occurrences.select { |query,| query.match?(/FROM "?issues"?/) }.each_value.first - - expect(query_count).to eq(1) - end - - context 'when block_issue_repositioning feature flag is enabled' do - before do - stub_feature_flags(block_issue_repositioning: true) - end - - it 'does not reposition issues with null position' do - expect(Issue).not_to receive(:move_nulls_to_end) - - list_issues(user: user, board: group_board, list: list3) - end - end - end - - context 'with invalid list id' do - it 'returns a not found 404 response' do - list_issues user: user, board: board, list: non_existing_record_id - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - context 'when list id is missing' do - it 'returns opened issues without board labels applied' do - bug = create(:label, project: project, name: 'Bug') - create(:issue, project: project) - create(:labeled_issue, project: project, labels: [planning]) - create(:labeled_issue, project: project, labels: [development]) - create(:labeled_issue, project: project, labels: [bug]) - - list_issues user: user, board: board - - expect(response).to match_response_schema('entities/issue_boards') - expect(json_response['issues'].length).to eq 2 - end - end - - context 'with unauthorized user' do - let(:unauth_user) { create(:user) } - - it 'returns a forbidden 403 response' do - list_issues user: unauth_user, board: board, list: list2 - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - context 'with external authorization' do - before do - sign_in(user) - enable_external_authorization_service_check - end - - it 'returns a 403 for group boards' do - group = create(:group) - group_board = create(:board, group: group) - - list_issues(user: user, board: group_board) - - expect(response).to have_gitlab_http_status(:forbidden) - end - - it 'is successful for project boards' do - project_board = create(:board, project: project) - - list_issues(user: user, board: project_board) - - expect(response).to have_gitlab_http_status(:ok) - end - end - - describe 'PUT bulk_move' do - let(:todo) { create(:group_label, group: group, name: 'Todo') } - let(:development) { create(:group_label, group: group, name: 'Development') } - let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user } - let(:guest) { create(:group_member, :guest, user: create(:user), group: group ).user } - let(:project) { create(:project, group: group) } - let(:group) { create(:group) } - let(:board) { create(:board, project: project) } - let(:list1) { create(:list, board: board, label: todo, position: 0) } - let(:list2) { create(:list, board: board, label: development, position: 1) } - let(:issue1) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 10) } - let(:issue2) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 20) } - let(:issue3) { create(:labeled_issue, project: project, labels: [todo], author: user, relative_position: 30) } - let(:issue4) { create(:labeled_issue, project: project, labels: [development], author: user, relative_position: 100) } - - let(:move_params) do - { - board_id: board.id, - ids: [issue1.id, issue2.id, issue3.id], - from_list_id: list1.id, - to_list_id: list2.id, - move_before_id: issue4.id, - move_after_id: nil - } - end - - before do - project.add_maintainer(user) - project.add_guest(guest) - end - - shared_examples 'move issues endpoint provider' do - before do - sign_in(signed_in_user) - end - - it 'responds as expected' do - put :bulk_move, params: move_issues_params - expect(response).to have_gitlab_http_status(expected_status) - - if expected_status == 200 - expect(json_response).to include( - 'count' => move_issues_params[:ids].size, - 'success' => true - ) - - expect(json_response['issues'].pluck('id')).to match_array(move_issues_params[:ids]) - end - end - - it 'moves issues as expected' do - put :bulk_move, params: move_issues_params - expect(response).to have_gitlab_http_status(expected_status) - - list_issues user: requesting_user, board: board, list: list2 - expect(response).to have_gitlab_http_status(:ok) - - expect(response).to match_response_schema('entities/issue_boards') - - responded_issues = json_response['issues'] - expect(responded_issues.length).to eq expected_issue_count - - ids_in_order = responded_issues.pluck('id') - expect(ids_in_order).to eq(expected_issue_ids_in_order) - end - end - - context 'when items are moved to another list' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) { move_params } - let(:requesting_user) { user } - let(:expected_status) { 200 } - let(:expected_issue_count) { 4 } - let(:expected_issue_ids_in_order) { [issue4.id, issue1.id, issue2.id, issue3.id] } - end - end - - context 'when moving just one issue' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:ids] = [issue2.id] - end - end - - let(:requesting_user) { user } - let(:expected_status) { 200 } - let(:expected_issue_count) { 2 } - let(:expected_issue_ids_in_order) { [issue4.id, issue2.id] } - end - end - - context 'when user is not allowed to move issue' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { guest } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:ids] = [issue2.id] - end - end - - let(:requesting_user) { user } - let(:expected_status) { 403 } - let(:expected_issue_count) { 1 } - let(:expected_issue_ids_in_order) { [issue4.id] } - end - end - - context 'when issues should be moved visually above existing issue in list' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:move_after_id] = issue4.id - hash[:move_before_id] = nil - end - end - - let(:requesting_user) { user } - let(:expected_status) { 200 } - let(:expected_issue_count) { 4 } - let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id, issue4.id] } - end - end - - context 'when destination list is empty' do - before do - # Remove issue from list - issue4.labels -= [development] - issue4.save! - end - - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:move_before_id] = nil - end - end - - let(:requesting_user) { user } - let(:expected_status) { 200 } - let(:expected_issue_count) { 3 } - let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id] } - end - end - - context 'when no position arguments are given' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:move_before_id] = nil - end - end - - let(:requesting_user) { user } - let(:expected_status) { 200 } - let(:expected_issue_count) { 4 } - let(:expected_issue_ids_in_order) { [issue1.id, issue2.id, issue3.id, issue4.id] } - end - end - - context 'when move_before_id and move_after_id are given' do - let(:issue5) { create(:labeled_issue, project: project, labels: [development], author: user, relative_position: 90) } - - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:move_before_id] = issue5.id - hash[:move_after_id] = issue4.id - end - end - - let(:requesting_user) { user } - let(:expected_status) { 200 } - let(:expected_issue_count) { 5 } - let(:expected_issue_ids_in_order) { [issue5.id, issue1.id, issue2.id, issue3.id, issue4.id] } - end - end - - context 'when request contains too many issues' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:ids] = (0..51).to_a - end - end - - let(:requesting_user) { user } - let(:expected_status) { 422 } - let(:expected_issue_count) { 1 } - let(:expected_issue_ids_in_order) { [issue4.id] } - end - end - - context 'when request is malformed' do - it_behaves_like 'move issues endpoint provider' do - let(:signed_in_user) { user } - let(:move_issues_params) do - move_params.dup.tap do |hash| - hash[:ids] = 'foobar' - end - end - - let(:requesting_user) { user } - let(:expected_status) { 400 } - let(:expected_issue_count) { 1 } - let(:expected_issue_ids_in_order) { [issue4.id] } - end - end - end - - def list_issues(user:, board:, list: nil) - sign_in(user) - - params = { - board_id: board.to_param, - list_id: list.try(:to_param) - } - - unless board.try(:parent).is_a?(Group) - params[:namespace_id] = project.namespace.to_param - params[:project_id] = project - end - - get :index, params: params.compact - end - end - - describe 'POST create' do - context 'when trying to create issue on an unauthorized project' do - let(:unauthorized_project) { create(:project, :private) } - let(:issue_params) { { project_id: unauthorized_project.id } } - - it 'creates the issue on the board\'s project' do - expect do - create_issue user: user, board: board, list: list1, title: 'New issue', additional_issue_params: issue_params - end.to change(Issue, :count).by(1) - - created_issue = Issue.last - - expect(created_issue.project).to eq(project) - expect(unauthorized_project.reload.issues.count).to eq(0) - end - end - - context 'with valid params' do - before do - create_issue user: user, board: board, list: list1, title: 'New issue' - end - - it 'returns a successful 200 response' do - expect(response).to have_gitlab_http_status(:ok) - end - - it 'returns the created issue' do - expect(response).to match_response_schema('entities/issue_board') - end - - it 'sets the default work_item_type' do - expect(Issue.last.work_item_type.base_type).to eq('issue') - end - end - - context 'with invalid params' do - context 'when title is nil' do - it 'returns an unprocessable entity 422 response' do - create_issue user: user, board: board, list: list1, title: nil - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - context 'when list does not belongs to project board' do - it 'returns a not found 404 response' do - list = create(:list) - - create_issue user: user, board: board, list: list, title: 'New issue' - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'with invalid board id' do - it 'returns a not found 404 response' do - create_issue user: user, board: non_existing_record_id, list: list1, title: 'New issue' - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'with invalid list id' do - it 'returns a not found 404 response' do - create_issue user: user, board: board, list: non_existing_record_id, title: 'New issue' - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - context 'with guest user' do - context 'in open list' do - it 'returns a successful 200 response' do - open_list = board.lists.create!(list_type: :backlog) - create_issue user: guest, board: board, list: open_list, title: 'New issue' - - expect(response).to have_gitlab_http_status(:ok) - end - end - - context 'in label list' do - it 'returns a forbidden 403 response' do - create_issue user: guest, board: board, list: list1, title: 'New issue' - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - end - - def create_issue(user:, board:, list:, title:, additional_issue_params: {}) - sign_in(user) - - post :create, params: { - board_id: board.to_param, - list_id: list.to_param, - issue: { title: title, project_id: project.id }.merge(additional_issue_params) - }, - format: :json - end - end - - describe 'PATCH update' do - let!(:issue) { create(:labeled_issue, project: project, labels: [planning]) } - - context 'with valid params' do - it 'returns a successful 200 response' do - move user: user, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id - - expect(response).to have_gitlab_http_status(:ok) - end - - it 'moves issue to the desired list' do - move user: user, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id - - expect(issue.reload.labels).to contain_exactly(development) - end - end - - context 'with invalid params' do - it 'returns a unprocessable entity 422 response for invalid lists' do - move user: user, board: board, issue: issue, from_list_id: nil, to_list_id: nil - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - - it 'returns a not found 404 response for invalid board id' do - move user: user, board: non_existing_record_id, issue: issue, from_list_id: list1.id, to_list_id: list2.id - - expect(response).to have_gitlab_http_status(:not_found) - end - - it 'returns a not found 404 response for invalid issue id' do - move user: user, board: board, issue: double(id: non_existing_record_id), from_list_id: list1.id, to_list_id: list2.id - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'with unauthorized user' do - let(:guest) { create(:user) } - - before do - project.add_guest(guest) - end - - it 'returns a forbidden 403 response' do - move user: guest, board: board, issue: issue, from_list_id: list1.id, to_list_id: list2.id - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - def move(user:, board:, issue:, from_list_id:, to_list_id:) - sign_in(user) - - patch :update, params: { - namespace_id: project.namespace.to_param, - project_id: project.id, - board_id: board.to_param, - id: issue.id, - from_list_id: from_list_id, - to_list_id: to_list_id - }, - format: :json - end - end -end diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb deleted file mode 100644 index 95334974e66..00000000000 --- a/spec/controllers/boards/lists_controller_spec.rb +++ /dev/null @@ -1,333 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Boards::ListsController do - let(:project) { create(:project) } - let(:board) { create(:board, project: project) } - let(:user) { create(:user) } - let(:guest) { create(:user) } - - before do - project.add_maintainer(user) - project.add_guest(guest) - end - - describe 'GET index' do - before do - create(:list, board: board) - end - - it 'returns a successful 200 response' do - read_board_list user: user, board: board - - expect(response).to have_gitlab_http_status(:ok) - expect(response.media_type).to eq 'application/json' - end - - it 'returns a list of board lists' do - read_board_list user: user, board: board - - expect(response).to match_response_schema('lists') - expect(json_response.length).to eq 3 - end - - context 'when another user has list preferences' do - before do - board.lists.first.update_preferences_for(guest, collapsed: true) - end - - it 'returns the complete list of board lists' do - read_board_list user: user, board: board - - expect(json_response.length).to eq 3 - end - end - - context 'with unauthorized user' do - let(:unauth_user) { create(:user) } - - it 'returns a forbidden 403 response' do - read_board_list user: unauth_user, board: board - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - def read_board_list(user:, board:) - sign_in(user) - - get :index, params: { - namespace_id: project.namespace.to_param, - project_id: project, - board_id: board.to_param - }, - format: :json - end - end - - describe 'POST create' do - context 'with valid params' do - let(:label) { create(:label, project: project, name: 'Development') } - - it 'returns a successful 200 response' do - create_board_list user: user, board: board, label_id: label.id - - expect(response).to have_gitlab_http_status(:ok) - end - - it 'returns the created list' do - create_board_list user: user, board: board, label_id: label.id - - expect(response).to match_response_schema('list') - end - end - - context 'with invalid params' do - context 'when label is nil' do - it 'returns an unprocessable entity 422 response' do - create_board_list user: user, board: board, label_id: nil - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response['errors']).to eq(['Label not found']) - end - end - - context 'when label that does not belongs to project' do - it 'returns an unprocessable entity 422 response' do - label = create(:label, name: 'Development') - - create_board_list user: user, board: board, label_id: label.id - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response['errors']).to eq(['Label not found']) - end - end - end - - context 'with unauthorized user' do - it 'returns a forbidden 403 response' do - label = create(:label, project: project, name: 'Development') - - create_board_list user: guest, board: board, label_id: label.id - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - def create_board_list(user:, board:, label_id:) - sign_in(user) - - post :create, params: { - namespace_id: project.namespace.to_param, - project_id: project, - board_id: board.to_param, - list: { label_id: label_id } - }, - format: :json - end - end - - describe 'PATCH update' do - let!(:planning) { create(:list, board: board, position: 0) } - let!(:development) { create(:list, board: board, position: 1) } - - context 'with valid position' do - it 'returns a successful 200 response' do - move user: user, board: board, list: planning, position: 1 - - expect(response).to have_gitlab_http_status(:ok) - end - - it 'moves the list to the desired position' do - move user: user, board: board, list: planning, position: 1 - - expect(planning.reload.position).to eq 1 - end - end - - context 'with invalid position' do - it 'returns an unprocessable entity 422 response' do - move user: user, board: board, list: planning, position: 6 - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - context 'with invalid list id' do - it 'returns a not found 404 response' do - move user: user, board: board, list: non_existing_record_id, position: 1 - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'with unauthorized user' do - it 'returns a 422 unprocessable entity response' do - move user: guest, board: board, list: planning, position: 6 - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - context 'with collapsed preference' do - it 'saves collapsed preference for user' do - save_setting user: user, board: board, list: planning, setting: { collapsed: true } - - expect(planning.preferences_for(user).collapsed).to eq(true) - expect(response).to have_gitlab_http_status(:ok) - end - - it 'saves not collapsed preference for user' do - save_setting user: user, board: board, list: planning, setting: { collapsed: false } - - expect(planning.preferences_for(user).collapsed).to eq(false) - expect(response).to have_gitlab_http_status(:ok) - end - end - - context 'with a list_type other than :label' do - let!(:closed) { create(:closed_list, board: board, position: 2) } - - it 'saves collapsed preference for user' do - save_setting user: user, board: board, list: closed, setting: { collapsed: true } - - expect(closed.preferences_for(user).collapsed).to eq(true) - expect(response).to have_gitlab_http_status(:ok) - end - - it 'saves not collapsed preference for user' do - save_setting user: user, board: board, list: closed, setting: { collapsed: false } - - expect(closed.preferences_for(user).collapsed).to eq(false) - expect(response).to have_gitlab_http_status(:ok) - end - end - - def move(user:, board:, list:, position:) - sign_in(user) - - params = { namespace_id: project.namespace.to_param, - project_id: project.id, - board_id: board.to_param, - id: list.to_param, - list: { position: position }, - format: :json } - - patch :update, params: params, as: :json - end - - def save_setting(user:, board:, list:, setting: {}) - sign_in(user) - - params = { namespace_id: project.namespace.to_param, - project_id: project.id, - board_id: board.to_param, - id: list.to_param, - list: setting, - format: :json } - - patch :update, params: params, as: :json - end - end - - describe 'DELETE destroy' do - let!(:planning) { create(:list, board: board, position: 0) } - - context 'with valid list id' do - it 'returns a successful 200 response' do - remove_board_list user: user, board: board, list: planning - - expect(response).to have_gitlab_http_status(:ok) - end - - it 'removes list from board' do - expect { remove_board_list user: user, board: board, list: planning }.to change(board.lists, :size).by(-1) - end - end - - context 'with invalid list id' do - it 'returns a not found 404 response' do - remove_board_list user: user, board: board, list: non_existing_record_id - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'with unauthorized user' do - it 'returns a forbidden 403 response' do - remove_board_list user: guest, board: board, list: planning - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - context 'with an error service response' do - it 'returns an unprocessable entity response' do - allow(Boards::Lists::DestroyService).to receive(:new) - .and_return(double(execute: ServiceResponse.error(message: 'error'))) - - remove_board_list user: user, board: board, list: planning - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - def remove_board_list(user:, board:, list:) - sign_in(user) - - delete :destroy, params: { - namespace_id: project.namespace.to_param, - project_id: project, - board_id: board.to_param, - id: list.to_param - }, - format: :json - end - end - - describe 'POST generate' do - context 'when board lists is empty' do - it 'returns a successful 200 response' do - generate_default_lists user: user, board: board - - expect(response).to have_gitlab_http_status(:ok) - end - - it 'returns the defaults lists' do - generate_default_lists user: user, board: board - - expect(response).to match_response_schema('lists') - end - end - - context 'when board lists is not empty' do - it 'returns an unprocessable entity 422 response' do - create(:list, board: board) - - generate_default_lists user: user, board: board - - expect(response).to have_gitlab_http_status(:unprocessable_entity) - end - end - - context 'with unauthorized user' do - it 'returns a forbidden 403 response' do - generate_default_lists user: guest, board: board - - expect(response).to have_gitlab_http_status(:forbidden) - end - end - - def generate_default_lists(user:, board:) - sign_in(user) - - post :generate, params: { - namespace_id: project.namespace.to_param, - project_id: project, - board_id: board.to_param - }, - format: :json - end - end -end diff --git a/spec/controllers/concerns/boards_responses_spec.rb b/spec/controllers/concerns/boards_responses_spec.rb deleted file mode 100644 index 553a547d42c..00000000000 --- a/spec/controllers/concerns/boards_responses_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe BoardsResponses do - let(:controller_class) do - Class.new do - include BoardsResponses - end - end - - subject(:controller) { controller_class.new } - - describe '#serialize_as_json' do - let!(:board) { create(:board) } - - it 'serializes properly' do - expected = { "id" => board.id } - - expect(subject.serialize_as_json(board)).to include(expected) - end - end -end diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb index 2e734d81ea0..28b79a10624 100644 --- a/spec/controllers/concerns/product_analytics_tracking_spec.rb +++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb @@ -51,15 +51,21 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do end end - def expect_tracking(user: self.user) + def expect_redis_hll_tracking expect(Gitlab::UsageDataCounters::HLLRedisCounter).to have_received(:track_event) .with('g_analytics_valuestream', values: instance_of(String)) + end + + def expect_snowplow_tracking(user) + context = Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: 'g_analytics_valuestream') + .to_context.to_json expect_snowplow_event( category: anything, action: 'g_analytics_valuestream', namespace: group, - user: user + user: user, + context: [context] ) end @@ -77,7 +83,8 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do it 'tracks the event' do get :index - expect_tracking + expect_redis_hll_tracking + expect_snowplow_tracking(user) end context 'when FF is disabled' do @@ -97,7 +104,8 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do get :index - expect_tracking + expect_redis_hll_tracking + expect_snowplow_tracking(user) end it 'does not track the event if DNT is enabled' do @@ -137,7 +145,8 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do get :show, params: { id: 1 } - expect_tracking(user: nil) + expect_redis_hll_tracking + expect_snowplow_tracking(nil) end end @@ -151,21 +160,24 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do it 'tracks the event when there is custom id' do get :show, params: { id: 1 } - expect_tracking(user: nil) + expect_redis_hll_tracking + expect_snowplow_tracking(nil) end - it 'does not track the HLL event when there is no custom id' do - allow(controller).to receive(:get_custom_id).and_return(nil) + context 'when there is no custom_id set' do + before do + allow(controller).to receive(:get_custom_id).and_return(nil) - get :show, params: { id: 2 } + get :show, params: { id: 2 } + end - expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) - expect_snowplow_event( - category: anything, - action: 'g_analytics_valuestream', - namespace: group, - user: nil - ) + it 'does not track the HLL event' do + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + end + + it 'tracks Snowplow event' do + expect_snowplow_tracking(nil) + end end end end diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb index f9a6afb95ea..32304815bbb 100644 --- a/spec/controllers/concerns/send_file_upload_spec.rb +++ b/spec/controllers/concerns/send_file_upload_spec.rb @@ -96,9 +96,10 @@ RSpec.describe SendFileUpload do expect(controller).to receive(:params).at_least(:once).and_return(width: '64') expect(controller).to receive(:head).with(:ok) - expect(Gitlab::Workhorse).to receive(:send_scaled_image).with(a_string_matching('^(/.+|https://.+)'), 64, 'image/png').and_return([ - Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux" - ]) + expect(Gitlab::Workhorse).to receive(:send_scaled_image) + .with(a_string_matching('^(/.+|https://.+)'), 64, 'image/png') + .and_return([Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux"]) + expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, "send-scaled-img:faux") subject diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index aed310531e6..21810f64cb4 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -4,11 +4,14 @@ require 'spec_helper' RSpec.describe DashboardController do context 'signed in' do - let(:user) { create(:user) } - let(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } - before do + before_all do project.add_maintainer(user) + end + + before do sign_in(user) end @@ -30,6 +33,28 @@ RSpec.describe DashboardController do end it_behaves_like 'issuables requiring filter', :issues + + it 'includes tasks in issue list' do + task = create(:work_item, :task, project: project, author: user) + + get :issues, params: { author_id: user.id } + + expect(assigns[:issues].map(&:id)).to include(task.id) + end + + context 'when work_items is disabled' do + before do + stub_feature_flags(work_items: false) + end + + it 'does not include tasks in issue list' do + task = create(:work_item, :task, project: project, author: user) + + get :issues, params: { author_id: user.id } + + expect(assigns[:issues].map(&:id)).not_to include(task.id) + end + end end describe 'GET merge requests' do diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb index 6201cddecb0..4e441f86765 100644 --- a/spec/controllers/groups/boards_controller_spec.rb +++ b/spec/controllers/groups/boards_controller_spec.rb @@ -3,11 +3,14 @@ require 'spec_helper' RSpec.describe Groups::BoardsController do - let(:group) { create(:group) } - let(:user) { create(:user) } + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } - before do + before_all do group.add_maintainer(user) + end + + before do sign_in(user) end @@ -57,46 +60,17 @@ RSpec.describe Groups::BoardsController do end end - context 'when format is JSON' do - it 'return an array with one group board' do - create(:board, group: group) - - expect(Boards::VisitsFinder).not_to receive(:new) - - list_boards format: :json - - expect(response).to match_response_schema('boards') - expect(json_response.length).to eq 1 - end - - context 'with unauthorized user' do - before do - expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original - allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_issue_board, group).and_return(false) - end - - it 'returns a not found 404 response' do - list_boards format: :json - - expect(response).to have_gitlab_http_status(:not_found) - expect(response.media_type).to eq 'application/json' - end - end - end - it_behaves_like 'disabled when using an external authorization service' do subject { list_boards } end - def list_boards(format: :html) - get :index, params: { group_id: group }, format: format + def list_boards + get :index, params: { group_id: group } end end describe 'GET show' do - let!(:board) { create(:board, group: group) } + let_it_be(:board) { create(:board, group: group) } context 'when format is HTML' do it 'renders template' do @@ -123,12 +97,12 @@ RSpec.describe Groups::BoardsController do end context 'when user is signed out' do - let(:group) { create(:group, :public) } + let(:public_board) { create(:board, group: create(:group, :public)) } it 'does not save visit' do sign_out(user) - expect { read_board board: board }.to change(BoardGroupRecentVisit, :count).by(0) + expect { read_board board: public_board }.to change(BoardGroupRecentVisit, :count).by(0) expect(response).to render_template :show expect(response.media_type).to eq 'text/html' @@ -136,37 +110,11 @@ RSpec.describe Groups::BoardsController do end end - context 'when format is JSON' do - it 'returns project board' do - expect(Boards::Visits::CreateService).not_to receive(:new) - - read_board board: board, format: :json - - expect(response).to match_response_schema('board') - end - - context 'with unauthorized user' do - before do - expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original - allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_group, group).and_return(false) - end - - it 'returns a not found 404 response' do - read_board board: board, format: :json - - expect(response).to have_gitlab_http_status(:not_found) - expect(response.media_type).to eq 'application/json' - end - end - end - context 'when board does not belong to group' do it 'returns a not found 404 response' do another_board = create(:board) - read_board board: another_board + get :show, params: { group_id: group, id: another_board.to_param } expect(response).to have_gitlab_http_status(:not_found) end @@ -176,12 +124,8 @@ RSpec.describe Groups::BoardsController do subject { read_board board: board } end - def read_board(board:, format: :html) - get :show, params: { - group_id: group, - id: board.to_param - }, - format: format + def read_board(board:) + get :show, params: { group_id: board.group, id: board.to_param } end end end diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb index 77c62c0d930..6dbf0803892 100644 --- a/spec/controllers/groups/runners_controller_spec.rb +++ b/spec/controllers/groups/runners_controller_spec.rb @@ -8,9 +8,11 @@ RSpec.describe Groups::RunnersController do let_it_be(:project) { create(:project, group: group) } let!(:runner) { create(:ci_runner, :group, groups: [group]) } - let!(:runner_project) { create(:ci_runner, :project, projects: [project]) } + let!(:project_runner) { create(:ci_runner, :project, projects: [project]) } + let!(:instance_runner) { create(:ci_runner, :instance) } - let(:params_runner_project) { { group_id: group, id: runner_project } } + let(:params_runner_project) { { group_id: group, id: project_runner } } + let(:params_runner_instance) { { group_id: group, id: instance_runner } } let(:params) { { group_id: group, id: runner } } before do @@ -70,8 +72,15 @@ RSpec.describe Groups::RunnersController do expect(response).to render_template(:show) end + it 'renders show with 200 status code instance runner' do + get :show, params: { group_id: group, id: instance_runner } + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template(:show) + end + it 'renders show with 200 status code project runner' do - get :show, params: { group_id: group, id: runner_project } + get :show, params: { group_id: group, id: project_runner } expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:show) @@ -89,8 +98,14 @@ RSpec.describe Groups::RunnersController do expect(response).to have_gitlab_http_status(:not_found) end + it 'renders a 404 instance runner' do + get :show, params: { group_id: group, id: instance_runner } + + expect(response).to have_gitlab_http_status(:not_found) + end + it 'renders a 404 project runner' do - get :show, params: { group_id: group, id: runner_project } + get :show, params: { group_id: group, id: project_runner } expect(response).to have_gitlab_http_status(:not_found) end @@ -103,15 +118,21 @@ RSpec.describe Groups::RunnersController do group.add_owner(user) end - it 'renders show with 200 status code' do + it 'renders edit with 200 status code' do get :edit, params: { group_id: group, id: runner } expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:edit) end - it 'renders show with 200 status code project runner' do - get :edit, params: { group_id: group, id: runner_project } + it 'renders a 404 instance runner' do + get :edit, params: { group_id: group, id: instance_runner } + + expect(response).to have_gitlab_http_status(:not_found) + end + + it 'renders edit with 200 status code project runner' do + get :edit, params: { group_id: group, id: project_runner } expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:edit) @@ -130,7 +151,7 @@ RSpec.describe Groups::RunnersController do end it 'renders a 404 project runner' do - get :edit, params: { group_id: group, id: runner_project } + get :edit, params: { group_id: group, id: project_runner } expect(response).to have_gitlab_http_status(:not_found) end @@ -154,15 +175,26 @@ RSpec.describe Groups::RunnersController do expect(runner.reload.description).to eq(new_desc) end + it 'does not update the instance runner' do + new_desc = instance_runner.description.swapcase + + expect do + post :update, params: params_runner_instance.merge(runner: { description: new_desc } ) + end.to not_change { instance_runner.ensure_runner_queue_value } + .and not_change { instance_runner.description } + + expect(response).to have_gitlab_http_status(:not_found) + end + it 'updates the project runner, ticks the queue, and redirects project runner' do - new_desc = runner_project.description.swapcase + new_desc = project_runner.description.swapcase expect do post :update, params: params_runner_project.merge(runner: { description: new_desc } ) - end.to change { runner_project.ensure_runner_queue_value } + end.to change { project_runner.ensure_runner_queue_value } expect(response).to have_gitlab_http_status(:found) - expect(runner_project.reload.description).to eq(new_desc) + expect(project_runner.reload.description).to eq(new_desc) end end @@ -182,15 +214,26 @@ RSpec.describe Groups::RunnersController do expect(runner.reload.description).to eq(old_desc) end + it 'rejects the update and responds 404 instance runner' do + old_desc = instance_runner.description + + expect do + post :update, params: params_runner_instance.merge(runner: { description: old_desc.swapcase } ) + end.not_to change { instance_runner.ensure_runner_queue_value } + + expect(response).to have_gitlab_http_status(:not_found) + expect(instance_runner.reload.description).to eq(old_desc) + end + it 'rejects the update and responds 404 project runner' do - old_desc = runner_project.description + old_desc = project_runner.description expect do post :update, params: params_runner_project.merge(runner: { description: old_desc.swapcase } ) - end.not_to change { runner_project.ensure_runner_queue_value } + end.not_to change { project_runner.ensure_runner_queue_value } expect(response).to have_gitlab_http_status(:not_found) - expect(runner_project.reload.description).to eq(old_desc) + expect(project_runner.reload.description).to eq(old_desc) end end end diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index c4e4eeec953..5bbe236077c 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -229,7 +229,7 @@ RSpec.describe GroupsController, factory_default: :keep do sign_in(user) expect do - post :create, params: { group: { name: 'new_group', path: "new_group" } } + post :create, params: { group: { name: 'new_group', path: 'new_group' } } end.to change { Group.count }.by(1) expect(response).to have_gitlab_http_status(:found) @@ -240,13 +240,31 @@ RSpec.describe GroupsController, factory_default: :keep do sign_in(create(:admin)) expect do - post :create, params: { group: { name: 'new_group', path: "new_group" } } + post :create, params: { group: { name: 'new_group', path: 'new_group' } } end.to change { Group.count }.by(1) expect(response).to have_gitlab_http_status(:found) end end + context 'when creating chat team' do + before do + stub_mattermost_setting(enabled: true) + end + + it 'triggers Mattermost::CreateTeamService' do + sign_in(user) + + expect_next_instance_of(::Mattermost::CreateTeamService) do |service| + expect(service).to receive(:execute).and_return({ name: 'test-chat-team', id: 1 }) + end + + post :create, params: { group: { name: 'new_group', path: 'new_group', create_chat_team: 1 } } + + expect(response).to have_gitlab_http_status(:found) + end + end + context 'when creating subgroups' do [true, false].each do |can_create_group_status| context "and can_create_group is #{can_create_group_status}" do diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb index 7f55c4407dd..47290b1d0d6 100644 --- a/spec/controllers/health_check_controller_spec.rb +++ b/spec/controllers/health_check_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe HealthCheckController, :request_store do +RSpec.describe HealthCheckController, :request_store, :use_clean_rails_memory_store_caching do include StubENV let(:xml_response) { Hash.from_xml(response.body)['hash'] } @@ -93,12 +93,13 @@ RSpec.describe HealthCheckController, :request_store do context 'when a service is down and an endpoint is accessed from whitelisted ip' do before do - allow(HealthCheck::Utils).to receive(:process_checks).with(['standard']).and_return('The server is on fire') - allow(HealthCheck::Utils).to receive(:process_checks).with(['email']).and_return('Email is on fire') + allow(::HealthCheck).to receive(:include_error_in_response_body).and_return(true) allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(whitelisted_ip) end it 'supports failure plaintext response' do + expect(HealthCheck::Utils).to receive(:process_checks).with(['standard']).and_return('The server is on fire') + get :index expect(response).to have_gitlab_http_status(:internal_server_error) @@ -107,6 +108,8 @@ RSpec.describe HealthCheckController, :request_store do end it 'supports failure json response' do + expect(HealthCheck::Utils).to receive(:process_checks).with(['standard']).and_return('The server is on fire') + get :index, format: :json expect(response).to have_gitlab_http_status(:internal_server_error) @@ -116,6 +119,8 @@ RSpec.describe HealthCheckController, :request_store do end it 'supports failure xml response' do + expect(HealthCheck::Utils).to receive(:process_checks).with(['standard']).and_return('The server is on fire') + get :index, format: :xml expect(response).to have_gitlab_http_status(:internal_server_error) @@ -125,6 +130,8 @@ RSpec.describe HealthCheckController, :request_store do end it 'supports failure responses for specific checks' do + expect(HealthCheck::Utils).to receive(:process_checks).with(['email']).and_return('Email is on fire') + get :index, params: { checks: 'email' }, format: :json expect(response).to have_gitlab_http_status(:internal_server_error) diff --git a/spec/controllers/import/bulk_imports_controller_spec.rb b/spec/controllers/import/bulk_imports_controller_spec.rb index 3be12717664..a0bb39f3e98 100644 --- a/spec/controllers/import/bulk_imports_controller_spec.rb +++ b/spec/controllers/import/bulk_imports_controller_spec.rb @@ -247,10 +247,10 @@ RSpec.describe Import::BulkImportsController do "source_full_path" => "full_path", "destination_slug" => "destination_name", "destination_namespace" => "root" }, - { "source_type" => "group_entity2", - "source_full_path" => "full_path2", - "destination_slug" => "destination_name2", - "destination_namespace" => "root" }] + { "source_type" => "group_entity", + "source_full_path" => "full_path", + "destination_slug" => "destination_name", + "destination_namespace" => "invalid-namespace" }] end before do @@ -308,6 +308,21 @@ RSpec.describe Import::BulkImportsController do expect(json_response).to match_array([{ "success" => true, "id" => bulk_import.id, "message" => nil }]) end end + + context 'when source type is project' do + let(:bulk_import_params) do + [{ "source_type" => "project_entity", + "source_full_path" => "full_path", + "destination_slug" => "destination_name", + "destination_namespace" => "root" }] + end + + it 'returns 422' do + post :create, params: { bulk_import: bulk_import_params } + + expect(response).to have_gitlab_http_status(:unprocessable_entity) + end + end end end diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 269eb62cae6..f3632e7370c 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -44,13 +44,6 @@ RSpec.describe Import::GithubController do end describe "GET callback" do - before do - allow(controller).to receive(:get_token).and_return(token) - allow(controller).to receive(:oauth_options).and_return({}) - - stub_omniauth_provider('github') - end - context "when auth state param is missing from session" do it "reports an error" do get :callback @@ -63,17 +56,31 @@ RSpec.describe Import::GithubController do context "when auth state param is present in session" do let(:valid_auth_state) { "secret-state" } - before do - session[:github_auth_state_key] = valid_auth_state - end + context 'when remove_legacy_github_client feature is disabled' do + before do + stub_feature_flags(remove_legacy_github_client: false) + allow_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client| + allow(client).to receive(:get_token).and_return(token) + end + session[:github_auth_state_key] = valid_auth_state + end + + it "updates access token if state param is valid" do + token = "asdasd12345" - it "updates access token if state param is valid" do - token = "asdasd12345" + get :callback, params: { state: valid_auth_state } - get :callback, params: { state: valid_auth_state } + expect(session[:github_access_token]).to eq(token) + expect(controller).to redirect_to(status_import_github_url) + end + + it "includes namespace_id from query params if it is present" do + namespace_id = 1 + + get :callback, params: { state: valid_auth_state, namespace_id: namespace_id } - expect(session[:github_access_token]).to eq(token) - expect(controller).to redirect_to(status_import_github_url) + expect(controller).to redirect_to(status_import_github_url(namespace_id: namespace_id)) + end end it "reports an error if state param is invalid" do @@ -83,12 +90,31 @@ RSpec.describe Import::GithubController do expect(flash[:alert]).to eq('Access denied to your GitHub account.') end - it "includes namespace_id from query params if it is present" do - namespace_id = 1 + context 'when remove_legacy_github_client feature is enabled' do + before do + stub_feature_flags(remove_legacy_github_client: true) + allow_next_instance_of(OAuth2::Client) do |client| + allow(client).to receive_message_chain(:auth_code, :get_token, :token).and_return(token) + end + session[:github_auth_state_key] = valid_auth_state + end + + it "updates access token if state param is valid" do + token = "asdasd12345" - get :callback, params: { state: valid_auth_state, namespace_id: namespace_id } + get :callback, params: { state: valid_auth_state } + + expect(session[:github_access_token]).to eq(token) + expect(controller).to redirect_to(status_import_github_url) + end - expect(controller).to redirect_to(status_import_github_url(namespace_id: namespace_id)) + it "includes namespace_id from query params if it is present" do + namespace_id = 1 + + get :callback, params: { state: valid_auth_state, namespace_id: namespace_id } + + expect(controller).to redirect_to(status_import_github_url(namespace_id: namespace_id)) + end end end end @@ -218,7 +244,7 @@ RSpec.describe Import::GithubController do it 'makes request to github search api' do expect_next_instance_of(Octokit::Client) do |client| - expect(client).to receive(:user).and_return(double(login: user_login)) + expect(client).to receive(:user).and_return({ login: user_login }) expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum }) end @@ -234,7 +260,7 @@ RSpec.describe Import::GithubController do context 'when no page is specified' do it 'requests first page' do expect_next_instance_of(Octokit::Client) do |client| - expect(client).to receive(:user).and_return(double(login: user_login)) + expect(client).to receive(:user).and_return({ login: user_login }) expect(client).to receive(:search_repositories).with(search_query, { page: 1, per_page: 25 }).and_return({ items: [].to_enum }) end @@ -250,7 +276,7 @@ RSpec.describe Import::GithubController do context 'when page is specified' do it 'requests repos with specified page' do expect_next_instance_of(Octokit::Client) do |client| - expect(client).to receive(:user).and_return(double(login: user_login)) + expect(client).to receive(:user).and_return({ login: user_login }) expect(client).to receive(:search_repositories).with(search_query, { page: 2, per_page: 25 }).and_return({ items: [].to_enum }) end @@ -321,4 +347,37 @@ RSpec.describe Import::GithubController do expect(json_response[0]['stats']).to include('imported') end end + + describe "POST cancel" do + let_it_be(:project) { create(:project, :import_started, import_type: 'github', import_url: 'https://fake.url') } + + context 'when project import was canceled' do + before do + allow(Import::Github::CancelProjectImportService) + .to receive(:new).with(project, user) + .and_return(double(execute: { status: :success, project: project })) + end + + it 'returns success' do + post :cancel, params: { project_id: project.id } + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'when project import was not canceled' do + before do + allow(Import::Github::CancelProjectImportService) + .to receive(:new).with(project, user) + .and_return(double(execute: { status: :error, message: 'The import cannot be canceled because it is finished', http_status: :bad_request })) + end + + it 'returns error' do + post :cancel, params: { project_id: project.id } + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['errors']).to eq('The import cannot be canceled because it is finished') + end + end + end end diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb index 99e9644da66..8dee0490fd6 100644 --- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb +++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb @@ -35,6 +35,18 @@ RSpec.describe Profiles::PersonalAccessTokensController do expect(created_token).not_to be_nil expect(created_token.expires_at).to eq(expires_at) end + + it 'does not allow creation when personal access tokens are disabled' do + allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: true) + + post :create, params: { personal_access_token: token_attributes } + + expect(response).to have_gitlab_http_status(:not_found) + end + + it_behaves_like "#create access token" do + let(:url) { :create } + end end describe '#index' do @@ -66,6 +78,14 @@ RSpec.describe Profiles::PersonalAccessTokensController do ) end + it 'returns 404 when personal access tokens are disabled' do + allow(::Gitlab::CurrentSettings).to receive_messages(personal_access_tokens_disabled?: true) + + get :index + + expect(response).to have_gitlab_http_status(:not_found) + end + context "access_token_pagination feature flag is enabled" do before do stub_feature_flags(access_token_pagination: true) diff --git a/spec/controllers/profiles/preferences_controller_spec.rb b/spec/controllers/profiles/preferences_controller_spec.rb index 7add3a72337..e2a216bb462 100644 --- a/spec/controllers/profiles/preferences_controller_spec.rb +++ b/spec/controllers/profiles/preferences_controller_spec.rb @@ -53,7 +53,8 @@ RSpec.describe Profiles::PreferencesController do first_day_of_week: '1', preferred_language: 'jp', tab_width: '5', - render_whitespace_in_code: 'true' + render_whitespace_in_code: 'true', + use_legacy_web_ide: 'true' }.with_indifferent_access expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(prefs).permit!) diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb index 33cba675777..1dd564427d3 100644 --- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb +++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb @@ -31,13 +31,26 @@ RSpec.describe Profiles::TwoFactorAuthsController do shared_examples 'user must enter a valid current password' do let(:current_password) { '123' } - let(:redirect_path) { profile_two_factor_auth_path } + let(:error_message) { { message: _('You must provide a valid current password') } } it 'requires the current password', :aggregate_failures do go - expect(response).to redirect_to(redirect_path) - expect(flash[:alert]).to eq(_('You must provide a valid current password')) + expect(assigns[:error]).to eq(error_message) + expect(response).to render_template(:show) + end + + it 'assigns qr_code' do + code = double('qr code') + expect(subject).to receive(:build_qr_code).and_return(code) + + go + expect(assigns[:qr_code]).to eq(code) + end + + it 'assigns account_string' do + go + expect(assigns[:account_string]).to eq("#{Gitlab.config.gitlab.host}:#{user.email}") end context 'when the user is on the last sign in attempt' do @@ -58,8 +71,7 @@ RSpec.describe Profiles::TwoFactorAuthsController do it 'does not require the current password', :aggregate_failures do go - expect(response).not_to redirect_to(redirect_path) - expect(flash[:alert]).to be_nil + expect(assigns[:error]).not_to eq(error_message) end end @@ -71,8 +83,7 @@ RSpec.describe Profiles::TwoFactorAuthsController do it 'does not require the current password', :aggregate_failures do go - expect(response).not_to redirect_to(redirect_path) - expect(flash[:alert]).to be_nil + expect(assigns[:error]).not_to eq(error_message) end end @@ -84,8 +95,7 @@ RSpec.describe Profiles::TwoFactorAuthsController do it 'does not require the current password', :aggregate_failures do go - expect(response).not_to redirect_to(redirect_path) - expect(flash[:alert]).to be_nil + expect(assigns[:error]).not_to eq(error_message) end end end diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb index 263f488ddbf..808e67eff3d 100644 --- a/spec/controllers/projects/artifacts_controller_spec.rb +++ b/spec/controllers/projects/artifacts_controller_spec.rb @@ -229,7 +229,7 @@ RSpec.describe Projects::ArtifactsController do expect(response.body).to include( 'You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. ' \ 'To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline configuration or CI/CD settings. ' \ - 'If you need to view this job log, a project maintainer must add you to the project with developer permissions or higher.' + 'If you need to view this job log, a project maintainer or owner must add you to the project with developer permissions or higher.' ) end end diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb index a5274b6543e..7077aae6b45 100644 --- a/spec/controllers/projects/autocomplete_sources_controller_spec.rb +++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb @@ -5,37 +5,133 @@ require 'spec_helper' RSpec.describe Projects::AutocompleteSourcesController do let_it_be(:group, reload: true) { create(:group) } let_it_be(:project) { create(:project, namespace: group) } - let_it_be(:issue) { create(:issue, project: project) } + let_it_be(:public_project) { create(:project, :public, group: group) } + let_it_be(:development) { create(:label, project: project, name: 'Development') } + let_it_be(:issue) { create(:labeled_issue, project: project, labels: [development]) } let_it_be(:user) { create(:user) } def members_by_username(username) json_response.find { |member| member['username'] == username } end - describe 'GET members' do + describe 'GET commands' do + before do + group.add_owner(user) + end + + context 'with a public project' do + shared_examples 'issuable commands' do + it 'returns empty array when no user logged in' do + get :commands, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issuable_type, type_id: issuable_iid } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to eq([]) + end + + it 'raises an error when no target type specified' do + sign_in(user) + + expect { get :commands, format: :json, params: { namespace_id: group.path, project_id: project.path } } + .to raise_error(ActionController::ParameterMissing) + end + + it 'returns an array of commands' do + sign_in(user) + + get :commands, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issuable_type, type_id: issuable_iid } + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to be_present + end + end + + context 'with an issue' do + let(:issuable_type) { issue.class.name } + let(:issuable_iid) { issue.iid } + + it_behaves_like 'issuable commands' + end + + context 'with merge request' do + let(:merge_request) { create(:merge_request, target_project: public_project, source_project: public_project) } + let(:issuable_type) { merge_request.class.name } + let(:issuable_iid) { merge_request.iid } + + it_behaves_like 'issuable commands' + end + end + end + + describe 'GET labels' do before do group.add_owner(user) sign_in(user) end - it 'returns an array of member object' do - get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + it 'raises an error when no target type specified' do + expect { get :labels, format: :json, params: { namespace_id: group.path, project_id: project.path } } + .to raise_error(ActionController::ParameterMissing) + end + + it 'returns an array of labels' do + get :labels, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + + expect(json_response).to be_a(Array) + expect(json_response.count).to eq(1) + expect(json_response[0]['title']).to eq('Development') + end + end + + describe 'GET members' do + context 'when logged in' do + before do + group.add_owner(user) + sign_in(user) + end + + it 'returns 400 when no target type specified' do + expect { get :members, format: :json, params: { namespace_id: group.path, project_id: project.path } } + .to raise_error(ActionController::ParameterMissing) + end + + it 'returns an array of member object' do + get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } + + expect(members_by_username('all').symbolize_keys).to include( + username: 'all', + name: 'All Project and Group Members', + count: 1) + + expect(members_by_username(group.full_path).symbolize_keys).to include( + type: group.class.name, + name: group.full_name, + avatar_url: group.avatar_url, + count: 1) + + expect(members_by_username(user.username).symbolize_keys).to include( + type: user.class.name, + name: user.name, + avatar_url: user.avatar_url) + end + end + + context 'when anonymous' do + it 'redirects to login page' do + get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } - expect(members_by_username('all').symbolize_keys).to include( - username: 'all', - name: 'All Project and Group Members', - count: 1) + expect(response).to redirect_to new_user_session_path + end - expect(members_by_username(group.full_path).symbolize_keys).to include( - type: group.class.name, - name: group.full_name, - avatar_url: group.avatar_url, - count: 1) + context 'with public project' do + it 'returns no members' do + get :members, format: :json, params: { namespace_id: group.path, project_id: public_project.path, type: issue.class.name, type_id: issue.id } - expect(members_by_username(user.username).symbolize_keys).to include( - type: user.class.name, - name: user.name, - avatar_url: user.avatar_url) + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to be_a(Array) + expect(json_response.count).to eq(1) + expect(json_response.first['count']).to eq(0) + end + end end end @@ -88,7 +184,7 @@ RSpec.describe Projects::AutocompleteSourcesController do it 'lists contacts' do group.add_developer(user) - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path } + get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } emails = json_response.map { |contact_data| contact_data["email"] } expect(emails).to match_array([contact_1.email, contact_2.email]) @@ -97,7 +193,7 @@ RSpec.describe Projects::AutocompleteSourcesController do context 'when a user can not read contacts' do it 'renders 404' do - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path } + get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } expect(response).to have_gitlab_http_status(:not_found) end @@ -108,7 +204,7 @@ RSpec.describe Projects::AutocompleteSourcesController do it 'renders 404' do group.add_developer(user) - get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path } + get :contacts, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id } expect(response).to have_gitlab_http_status(:not_found) end diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb index cde3a8d4761..89d0669f47b 100644 --- a/spec/controllers/projects/boards_controller_spec.rb +++ b/spec/controllers/projects/boards_controller_spec.rb @@ -3,11 +3,14 @@ require 'spec_helper' RSpec.describe Projects::BoardsController do - let(:project) { create(:project) } - let(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } - before do + before_all do project.add_maintainer(user) + end + + before do sign_in(user) end @@ -16,77 +19,63 @@ RSpec.describe Projects::BoardsController do expect { list_boards }.to change(project.boards, :count).by(1) end - it 'sets boards_endpoint instance variable to a boards path' do + it 'renders template' do list_boards - expect(assigns(:boards_endpoint)).to eq project_boards_path(project) + expect(response).to render_template :index + expect(response.media_type).to eq 'text/html' end - context 'when format is HTML' do - it 'renders template' do - list_boards + context 'when there are recently visited boards' do + let_it_be(:boards) { create_list(:board, 3, resource_parent: project) } - expect(response).to render_template :index - expect(response.media_type).to eq 'text/html' + before_all do + visit_board(boards[2], Time.current + 1.minute) + visit_board(boards[0], Time.current + 2.minutes) + visit_board(boards[1], Time.current + 5.minutes) end - context 'with unauthorized user' do - before do - expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original - allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) - end - - it 'returns a not found 404 response' do - list_boards + it 'redirects to latest visited board' do + list_boards - expect(response).to have_gitlab_http_status(:not_found) - expect(response.media_type).to eq 'text/html' - end + expect(response).to redirect_to( + namespace_project_board_path(namespace_id: project.namespace, project_id: project, id: boards[1].id) + ) end - context 'when user is signed out' do - let(:project) { create(:project, :public) } - - it 'renders template' do - sign_out(user) + def visit_board(board, time) + create(:board_project_recent_visit, project: project, board: board, user: user, updated_at: time) + end + end - board = create(:board, project: project) - create(:board_project_recent_visit, project: board.project, board: board, user: user) + context 'with unauthorized user' do + before do + expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original + allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) + allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) + end - list_boards + it 'returns a not found 404 response' do + list_boards - expect(response).to render_template :index - expect(response.media_type).to eq 'text/html' - end + expect(response).to have_gitlab_http_status(:not_found) + expect(response.media_type).to eq 'text/html' end end - context 'when format is JSON' do - it 'returns a list of project boards' do - create_list(:board, 2, project: project) - - expect(Boards::VisitsFinder).not_to receive(:new) - - list_boards format: :json + context 'when user is signed out' do + let(:project) { create(:project, :public) } - expect(response).to match_response_schema('boards') - expect(json_response.length).to eq 2 - end + it 'renders template' do + sign_out(user) - context 'with unauthorized user' do - before do - expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original - allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) - end + board = create(:board, project: project) + create(:board_project_recent_visit, project: board.project, board: board, user: user) - it 'returns a not found 404 response' do - list_boards format: :json + list_boards - expect(response).to have_gitlab_http_status(:not_found) - expect(response.media_type).to eq 'application/json' - end + expect(response).to render_template :index + expect(response.media_type).to eq 'text/html' end end @@ -104,23 +93,16 @@ RSpec.describe Projects::BoardsController do subject { list_boards } end - def list_boards(format: :html) + def list_boards get :index, params: { namespace_id: project.namespace, project_id: project - }, - format: format + } end end describe 'GET show' do - let!(:board) { create(:board, project: project) } - - it 'sets boards_endpoint instance variable to a boards path' do - read_board board: board - - expect(assigns(:boards_endpoint)).to eq project_boards_path(project) - end + let_it_be(:board) { create(:board, project: project) } context 'when format is HTML' do it 'renders template' do @@ -146,12 +128,12 @@ RSpec.describe Projects::BoardsController do end context 'when user is signed out' do - let(:project) { create(:project, :public) } + let(:public_board) { create(:board, project: create(:project, :public)) } it 'does not save visit' do sign_out(user) - expect { read_board board: board }.to change(BoardProjectRecentVisit, :count).by(0) + expect { read_board board: public_board }.to change(BoardProjectRecentVisit, :count).by(0) expect(response).to render_template :show expect(response.media_type).to eq 'text/html' @@ -159,48 +141,18 @@ RSpec.describe Projects::BoardsController do end end - context 'when format is JSON' do - it 'returns project board' do - expect(Boards::Visits::CreateService).not_to receive(:new) - - read_board board: board, format: :json - - expect(response).to match_response_schema('board') - end - - context 'with unauthorized user' do - before do - expect(Ability).to receive(:allowed?).with(user, :log_in, :global).and_call_original - allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true) - allow(Ability).to receive(:allowed?).with(user, :read_issue_board, project).and_return(false) - end - - it 'returns a not found 404 response' do - read_board board: board, format: :json - - expect(response).to have_gitlab_http_status(:not_found) - expect(response.media_type).to eq 'application/json' - end - end - end - context 'when board does not belong to project' do it 'returns a not found 404 response' do another_board = create(:board) - read_board board: another_board + get :show, params: { namespace_id: project.namespace, project_id: project, id: another_board.to_param } expect(response).to have_gitlab_http_status(:not_found) end end - def read_board(board:, format: :html) - get :show, params: { - namespace_id: project.namespace, - project_id: project, - id: board.to_param - }, - format: format + def read_board(board:) + get :show, params: { namespace_id: board.project.namespace, project_id: board.project, id: board.to_param } end end end diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 6ed6f7017e3..3751b89951c 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -67,11 +67,13 @@ RSpec.describe Projects::CompareController do from: from_ref, to: to_ref, w: whitespace, - page: page + page: page, + straight: straight } end let(:whitespace) { nil } + let(:straight) { nil } let(:page) { nil } context 'when the refs exist in the same project' do @@ -142,6 +144,58 @@ RSpec.describe Projects::CompareController do end end + context 'when comparing missing commits between source and target' do + let(:from_project_id) { nil } + let(:from_ref) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } + let(:to_ref) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' } + let(:page) { 1 } + + context 'when comparing them in the other direction' do + let(:straight) { "false" } + let(:from_ref) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' } + let(:to_ref) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } + + it 'the commits are there' do + show_request + + expect(response).to be_successful + expect(assigns(:commits).length).to be >= 2 + expect(assigns(:diffs).raw_diff_files.size).to be >= 2 + expect(assigns(:diffs).diff_files.first).to be_present + end + end + + context 'with straight mode true' do + let(:from_ref) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } + let(:to_ref) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' } + + let(:straight) { "true" } + + it 'the commits are empty, but the removed lines are visible as diffs' do + show_request + + expect(response).to be_successful + expect(assigns(:commits).length).to be == 0 + expect(assigns(:diffs).diff_files.size).to be >= 4 + end + end + + context 'with straight mode false' do + let(:from_ref) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' } + let(:to_ref) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' } + + let(:straight) { "false" } + + it 'the additional commits are not visible in diffs and commits' do + show_request + + expect(response).to be_successful + expect(assigns(:commits).length).to be == 0 + expect(assigns(:diffs).diff_files.size).to be == 0 + end + end + end + context 'when the refs exist in different projects but the user cannot see' do let(:from_project_id) { private_fork.id } let(:from_ref) { 'improve%2Fmore-awesome' } @@ -450,10 +504,13 @@ RSpec.describe Projects::CompareController do project_id: project, from: from_ref, to: to_ref, + straight: straight, format: :json } end + let(:straight) { nil } + context 'when the source and target refs exist' do let(:from_ref) { 'improve%2Fawesome' } let(:to_ref) { 'feature' } @@ -469,10 +526,43 @@ RSpec.describe Projects::CompareController do escaped_to_ref = Addressable::URI.unescape(to_ref) compare_service = CompareService.new(project, escaped_to_ref) + compare = compare_service.execute(project, escaped_from_ref, straight: false) + + expect(CompareService).to receive(:new).with(project, escaped_to_ref).and_return(compare_service) + expect(compare_service).to receive(:execute).with(project, escaped_from_ref, straight: false).and_return(compare) + + expect(compare).to receive(:commits).and_return(CommitCollection.new(project, [signature_commit, non_signature_commit])) + expect(non_signature_commit).to receive(:has_signature?).and_return(false) + end + + it 'returns only the commit with a signature' do + signatures_request + + expect(response).to have_gitlab_http_status(:ok) + signatures = json_response['signatures'] + + expect(signatures.size).to eq(1) + expect(signatures.first['commit_sha']).to eq(signature_commit.sha) + expect(signatures.first['html']).to be_present + end + end + + context 'when the user has access to the project with straight compare' do + render_views + + let(:signature_commit) { project.commit_by(oid: '0b4bc9a49b562e85de7cc9e834518ea6828729b9') } + let(:non_signature_commit) { build(:commit, project: project, safe_message: "message", sha: 'non_signature_commit') } + let(:straight) { "true" } + + before do + escaped_from_ref = Addressable::URI.unescape(from_ref) + escaped_to_ref = Addressable::URI.unescape(to_ref) + + compare_service = CompareService.new(project, escaped_to_ref) compare = compare_service.execute(project, escaped_from_ref) expect(CompareService).to receive(:new).with(project, escaped_to_ref).and_return(compare_service) - expect(compare_service).to receive(:execute).with(project, escaped_from_ref).and_return(compare) + expect(compare_service).to receive(:execute).with(project, escaped_from_ref, straight: true).and_return(compare) expect(compare).to receive(:commits).and_return(CommitCollection.new(project, [signature_commit, non_signature_commit])) expect(non_signature_commit).to receive(:has_signature?).and_return(false) diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb index f5dd8abd67b..034e6104f99 100644 --- a/spec/controllers/projects/cycle_analytics_controller_spec.rb +++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Projects::CycleAnalyticsController do let(:target_id) { 'p_analytics_valuestream' } end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject { get :show, params: request_params, format: :html } let(:request_params) { { namespace_id: project.namespace, project_id: project } } diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb index 308146ce792..fd844808d81 100644 --- a/spec/controllers/projects/deploy_keys_controller_spec.rb +++ b/spec/controllers/projects/deploy_keys_controller_spec.rb @@ -72,13 +72,15 @@ RSpec.describe Projects::DeployKeysController do end describe 'POST create' do + let(:deploy_key_content) { attributes_for(:deploy_key)[:key] } + def create_params(title = 'my-key') { namespace_id: project.namespace.path, project_id: project.path, deploy_key: { title: title, - key: attributes_for(:deploy_key)[:key], + key: deploy_key_content, deploy_keys_projects_attributes: { '0' => { can_push: '1' } } } } @@ -96,13 +98,38 @@ RSpec.describe Projects::DeployKeysController do expect(response).to redirect_to(project_settings_repository_path(project, anchor: 'js-deploy-keys-settings')) end - context 'when the deploy key is invalid' do + context 'when the deploy key has an invalid title' do it 'shows an alert with the validations errors' do post :create, params: create_params(nil) expect(flash[:alert]).to eq("Title can't be blank, Deploy keys projects deploy key title can't be blank") end end + + context 'when the deploy key is not supported SSH public key' do + let(:deploy_key_content) { 'bogus ssh public key' } + + it 'shows an alert with a help link' do + post :create, params: create_params + + expect(assigns(:key).errors.count).to be > 1 + expect(flash[:alert]).to eq('Deploy Key must be a <a target="_blank" rel="noopener noreferrer" ' \ + 'href="/help/user/ssh#supported-ssh-key-types">supported SSH public key.</a>') + end + end + + context 'when the deploy key already exists' do + before do + create(:deploy_key, title: 'my-key', key: deploy_key_content, projects: [project]) + end + + it 'shows an alert with the validations errors' do + post :create, params: create_params + + expect(flash[:alert]).to eq("Fingerprint sha256 has already been taken, " \ + "Deploy keys projects deploy key fingerprint sha256 has already been taken") + end + end end describe '/enable/:id' do diff --git a/spec/controllers/projects/design_management/designs/raw_images_controller_spec.rb b/spec/controllers/projects/design_management/designs/raw_images_controller_spec.rb index 55ab0f0eefa..2d39e0e5317 100644 --- a/spec/controllers/projects/design_management/designs/raw_images_controller_spec.rb +++ b/spec/controllers/projects/design_management/designs/raw_images_controller_spec.rb @@ -132,7 +132,7 @@ RSpec.describe Projects::DesignManagement::Designs::RawImagesController do subject expect(response.header['ETag']).to be_present - expect(response.header['Cache-Control']).to eq("max-age=60, private") + expect(response.header['Cache-Control']).to eq("max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60") end end diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb index 9227c7dd70a..3dfc22927cf 100644 --- a/spec/controllers/projects/graphs_controller_spec.rb +++ b/spec/controllers/projects/graphs_controller_spec.rb @@ -90,7 +90,7 @@ RSpec.describe Projects::GraphsController do let(:target_id) { 'p_analytics_repo' } end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject do sign_in(user) get :charts, params: request_params, format: :html diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index c48be8efb1b..0c3795540e0 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -1107,6 +1107,46 @@ RSpec.describe Projects::IssuesController do end end + context 'when create service return an unrecoverable error with http_status' do + let(:http_status) { 403 } + + before do + allow_next_instance_of(::Issues::CreateService) do |create_service| + allow(create_service).to receive(:execute).and_return( + ServiceResponse.error(message: 'unrecoverable error', http_status: http_status) + ) + end + end + + it 'renders 403 and logs the error' do + expect(Gitlab::AppLogger).to receive(:warn).with( + message: 'Cannot create issue', + errors: ['unrecoverable error'], + http_status: http_status + ) + + post_new_issue + + expect(response).to have_gitlab_http_status :forbidden + end + + context 'when no render method is found for the returned http_status' do + let(:http_status) { nil } + + it 'renders 404 and logs the error' do + expect(Gitlab::AppLogger).to receive(:warn).with( + message: 'Cannot create issue', + errors: ['unrecoverable error'], + http_status: http_status + ) + + post_new_issue + + expect(response).to have_gitlab_http_status :not_found + end + end + end + it 'creates the issue successfully', :aggregate_failures do issue = post_new_issue @@ -1661,13 +1701,27 @@ RSpec.describe Projects::IssuesController do end it 'allows CSV export' do - expect(IssuableExportCsvWorker).to receive(:perform_async).with(:issue, viewer.id, project.id, anything) + expect(IssuableExportCsvWorker).to receive(:perform_async) + .with(:issue, viewer.id, project.id, hash_including('issue_types' => Issue::TYPES_FOR_LIST)) request_csv expect(response).to redirect_to(project_issues_path(project)) expect(controller).to set_flash[:notice].to match(/\AYour CSV export has started/i) end + + context 'when work_items is disabled' do + before do + stub_feature_flags(work_items: false) + end + + it 'does not include tasks in CSV export' do + expect(IssuableExportCsvWorker).to receive(:perform_async) + .with(:issue, viewer.id, project.id, hash_including('issue_types' => Issue::TYPES_FOR_LIST.excluding('task'))) + + request_csv + end + end end context 'when not logged in' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 9c4baeae836..a41abd8c16d 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -77,7 +77,8 @@ RSpec.describe Projects::MergeRequestsController do merge_request, 'json', diff_head: true, - view: 'inline')) + view: 'inline', + w: '0')) end context 'when diff files were cleaned' do @@ -498,7 +499,7 @@ RSpec.describe Projects::MergeRequestsController do context 'when a squash commit message is passed' do let(:message) { 'My custom squash commit message' } - it 'passes the same message to SquashService', :sidekiq_might_not_need_inline do + it 'passes the same message to SquashService', :sidekiq_inline do params = { squash: '1', squash_commit_message: message, sha: merge_request.diff_head_sha } @@ -790,7 +791,7 @@ RSpec.describe Projects::MergeRequestsController do context 'with private builds' do context 'for the target project member' do - it 'does not respond with serialized pipelines', :sidekiq_might_not_need_inline do + it 'does not respond with serialized pipelines' do expect(json_response['pipelines']).to be_empty expect(json_response['count']['all']).to eq(0) expect(response).to include_pagination_headers @@ -800,7 +801,7 @@ RSpec.describe Projects::MergeRequestsController do context 'for the source project member' do let(:user) { fork_user } - it 'responds with serialized pipelines', :sidekiq_might_not_need_inline do + it 'responds with serialized pipelines' do expect(json_response['pipelines']).to be_present expect(json_response['count']['all']).to eq(1) expect(response).to include_pagination_headers @@ -816,7 +817,7 @@ RSpec.describe Projects::MergeRequestsController do end context 'for the target project member' do - it 'does not respond with serialized pipelines', :sidekiq_might_not_need_inline do + it 'does not respond with serialized pipelines' do expect(json_response['pipelines']).to be_present expect(json_response['count']['all']).to eq(1) expect(response).to include_pagination_headers @@ -826,7 +827,7 @@ RSpec.describe Projects::MergeRequestsController do context 'for the source project member' do let(:user) { fork_user } - it 'responds with serialized pipelines', :sidekiq_might_not_need_inline do + it 'responds with serialized pipelines' do expect(json_response['pipelines']).to be_present expect(json_response['count']['all']).to eq(1) expect(response).to include_pagination_headers @@ -1855,13 +1856,13 @@ RSpec.describe Projects::MergeRequestsController do create(:merge_request, source_project: forked, target_project: project, target_branch: 'master', head_pipeline: pipeline) end - it 'links to the environment on that project', :sidekiq_might_not_need_inline do + it 'links to the environment on that project' do get_ci_environments_status expect(json_response.first['url']).to match(/#{forked.full_path}/) end - context "when environment_target is 'merge_commit'", :sidekiq_might_not_need_inline do + context "when environment_target is 'merge_commit'" do it 'returns nothing' do get_ci_environments_status(environment_target: 'merge_commit') @@ -1891,13 +1892,13 @@ RSpec.describe Projects::MergeRequestsController do # we're trying to reduce the overall number of queries for this method. # set a hard limit for now. https://gitlab.com/gitlab-org/gitlab-foss/issues/52287 - it 'keeps queries in check', :sidekiq_might_not_need_inline do + it 'keeps queries in check' do control_count = ActiveRecord::QueryRecorder.new { get_ci_environments_status }.count expect(control_count).to be <= 137 end - it 'has no N+1 SQL issues for environments', :request_store, :sidekiq_might_not_need_inline, retry: 0 do + it 'has no N+1 SQL issues for environments', :request_store, retry: 0 do # First run to insert test data from lets, which does take up some 30 queries get_ci_environments_status @@ -2144,7 +2145,7 @@ RSpec.describe Projects::MergeRequestsController do sign_in(fork_owner) end - it 'returns 200', :sidekiq_might_not_need_inline do + it 'returns 200' do expect_rebase_worker_for(fork_owner) post_rebase diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index b62353784b3..28da7eff8fc 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -44,6 +44,26 @@ RSpec.describe Projects::MilestonesController do end end + describe "#create" do + it 'does not redirect without redirect_path' do + post :create, params: { namespace_id: project.namespace.id, project_id: project.id, milestone: { title: 'test' } } + + expect(response).to redirect_to(project_milestone_path(project, project.milestones.last)) + end + + it 'redirects when given a redirect_path' do + post :create, params: { namespace_id: project.namespace.id, project_id: project.id, redirect_path: 'new_release', milestone: { title: 'test' } } + + expect(response).to redirect_to(new_project_release_path(project)) + end + + it 'will not redirect when given a redirect_path with an error' do + post :create, params: { namespace_id: project.namespace.id, project_id: project.id, redirect_path: 'new_release', milestone: { title: nil } } + + expect(response).to have_gitlab_http_status(:ok) + end + end + describe "#index" do context "as html" do def render_index(project:, page:, search_title: '') diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb index 691508d1e14..b29bbef0c40 100644 --- a/spec/controllers/projects/pages_domains_controller_spec.rb +++ b/spec/controllers/projects/pages_domains_controller_spec.rb @@ -63,9 +63,15 @@ RSpec.describe Projects::PagesDomainsController do describe 'POST create' do it "creates a new pages domain" do - expect do - post(:create, params: request_params.merge(pages_domain: pages_domain_params)) - end.to change { PagesDomain.count }.by(1) + expect { post(:create, params: request_params.merge(pages_domain: pages_domain_params)) } + .to change { PagesDomain.count }.by(1) + .and publish_event(PagesDomains::PagesDomainCreatedEvent) + .with( + project_id: project.id, + namespace_id: project.namespace.id, + root_namespace_id: project.root_namespace.id, + domain: pages_domain_params[:domain] + ) created_domain = PagesDomain.reorder(:id).last @@ -106,6 +112,17 @@ RSpec.describe Projects::PagesDomainsController do end.to change { pages_domain.reload.certificate }.to(pages_domain_params[:user_provided_certificate]) end + it 'publishes PagesDomainUpdatedEvent event' do + expect { patch(:update, params: params) } + .to publish_event(PagesDomains::PagesDomainUpdatedEvent) + .with( + project_id: project.id, + namespace_id: project.namespace.id, + root_namespace_id: project.root_namespace.id, + domain: pages_domain.domain + ) + end + it 'redirects to the project page' do patch(:update, params: params) @@ -134,6 +151,11 @@ RSpec.describe Projects::PagesDomainsController do expect(response).to render_template('show') end + + it 'does not publish PagesDomainUpdatedEvent event' do + expect { patch(:update, params: params) } + .to not_publish_event(PagesDomains::PagesDomainUpdatedEvent) + end end context 'when parameters include the domain' do @@ -197,9 +219,15 @@ RSpec.describe Projects::PagesDomainsController do describe 'DELETE destroy' do it "deletes the pages domain" do - expect do - delete(:destroy, params: request_params.merge(id: pages_domain.domain)) - end.to change { PagesDomain.count }.by(-1) + expect { delete(:destroy, params: request_params.merge(id: pages_domain.domain)) } + .to change(PagesDomain, :count).by(-1) + .and publish_event(PagesDomains::PagesDomainDeletedEvent) + .with( + project_id: project.id, + namespace_id: project.namespace.id, + root_namespace_id: project.root_namespace.id, + domain: pages_domain.domain + ) expect(response).to redirect_to(project_pages_path(project)) end @@ -216,6 +244,17 @@ RSpec.describe Projects::PagesDomainsController do expect(response).to redirect_to(project_pages_domain_path(project, pages_domain)) end + it 'publishes PagesDomainUpdatedEvent event' do + expect { subject } + .to publish_event(PagesDomains::PagesDomainUpdatedEvent) + .with( + project_id: project.id, + namespace_id: project.namespace.id, + root_namespace_id: project.root_namespace.id, + domain: pages_domain.domain + ) + end + it 'removes certificate' do expect do subject @@ -245,6 +284,11 @@ RSpec.describe Projects::PagesDomainsController do expect(pages_domain.key).to be_present end + it 'does not publish PagesDomainUpdatedEvent event' do + expect { subject } + .to not_publish_event(PagesDomains::PagesDomainUpdatedEvent) + end + it 'redirects to show page with a flash message' do subject diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index b9acaf65892..6e2de0c4d57 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -859,7 +859,7 @@ RSpec.describe Projects::PipelinesController do let(:target_id) { ['p_analytics_pipelines', tab[:event]] } end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject { get :charts, params: request_params, format: :html } let(:request_params) { { namespace_id: project.namespace, project_id: project, id: pipeline.id, chart: tab[:chart_param] } } diff --git a/spec/controllers/projects/prometheus/metrics_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb index cd195b95100..327651b2058 100644 --- a/spec/controllers/projects/prometheus/metrics_controller_spec.rb +++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Projects::Prometheus::MetricsController do let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:prometheus_project) } + let_it_be(:project) { create(:project, :with_prometheus_integration) } let(:prometheus_adapter) { double('prometheus_adapter', can_query?: true) } diff --git a/spec/controllers/projects/protected_branches_controller_spec.rb b/spec/controllers/projects/protected_branches_controller_spec.rb index 4996bd90005..14728618633 100644 --- a/spec/controllers/projects/protected_branches_controller_spec.rb +++ b/spec/controllers/projects/protected_branches_controller_spec.rb @@ -4,26 +4,23 @@ require('spec_helper') RSpec.describe Projects::ProtectedBranchesController do let_it_be_with_reload(:project) { create(:project, :repository) } - let_it_be(:maintainer) { create(:user) } + let_it_be_with_reload(:empty_project) { create(:project, :empty_repo) } + let_it_be(:maintainer) { create(:user, maintainer_projects: [project, empty_project]) } let(:protected_branch) { create(:protected_branch, project: project) } let(:project_params) { { namespace_id: project.namespace.to_param, project_id: project } } let(:base_params) { project_params.merge(id: protected_branch.id) } let(:user) { maintainer } - before_all do - project.add_maintainer(maintainer) - end - before do sign_in(user) end describe "GET #index" do - let(:project) { create(:project_empty_repo, :public) } + it 'redirects to repository settings' do + get(:index, params: { namespace_id: empty_project.namespace.to_param, project_id: empty_project }) - it "redirects empty repo to projects page" do - get(:index, params: { namespace_id: project.namespace.to_param, project_id: project }) + expect(response).to redirect_to(project_settings_repository_path(empty_project)) end end @@ -42,6 +39,18 @@ RSpec.describe Projects::ProtectedBranchesController do end.to change(ProtectedBranch, :count).by(1) end + context 'when repository is empty' do + let(:project) { empty_project } + + it 'creates the protected branch rule' do + expect do + post(:create, params: project_params.merge(protected_branch: create_params)) + end.to change(ProtectedBranch, :count).by(1) + + expect(response).to have_gitlab_http_status(:found) + end + end + context 'when a policy restricts rule creation' do it "prevents creation of the protected branch rule" do disallow(:create_protected_branch, an_instance_of(ProtectedBranch)) @@ -63,6 +72,17 @@ RSpec.describe Projects::ProtectedBranchesController do expect(json_response["name"]).to eq('new_name') end + context 'when repository is empty' do + let(:project) { empty_project } + + it 'updates the protected branch rule' do + put(:update, params: base_params.merge(protected_branch: update_params)) + + expect(protected_branch.reload.name).to eq('new_name') + expect(json_response["name"]).to eq('new_name') + end + end + context 'when a policy restricts rule update' do it "prevents update of the protected branch rule" do disallow(:update_protected_branch, protected_branch) @@ -83,6 +103,16 @@ RSpec.describe Projects::ProtectedBranchesController do expect { ProtectedBranch.find(protected_branch.id) }.to raise_error(ActiveRecord::RecordNotFound) end + context 'when repository is empty' do + let(:project) { empty_project } + + it 'deletes the protected branch rule' do + delete(:destroy, params: base_params) + + expect { ProtectedBranch.find(protected_branch.id) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + context 'when a policy restricts rule deletion' do it "prevents deletion of the protected branch rule" do disallow(:destroy_protected_branch, protected_branch) diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index e0d88fa799f..1c9aafacbd9 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -247,9 +247,11 @@ RSpec.describe Projects::RawController do sign_in create(:user) request_file - expect(response.cache_control[:public]).to eq(true) - expect(response.cache_control[:max_age]).to eq(60) + expect(response.headers['ETag']).to eq("\"bdd5aa537c1e1f6d1b66de4bac8a6132\"") expect(response.cache_control[:no_store]).to be_nil + expect(response.header['Cache-Control']).to eq( + 'max-age=60, public, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end context 'when a public project has private repo' do @@ -260,7 +262,9 @@ RSpec.describe Projects::RawController do sign_in user request_file - expect(response.header['Cache-Control']).to include('max-age=60, private') + expect(response.header['Cache-Control']).to eq( + 'max-age=60, private, must-revalidate, stale-while-revalidate=60, stale-if-error=300, s-maxage=60' + ) end end @@ -274,6 +278,21 @@ RSpec.describe Projects::RawController do expect(response).to have_gitlab_http_status(:not_modified) end end + + context 'when improve_blobs_cache_headers disabled' do + before do + stub_feature_flags(improve_blobs_cache_headers: false) + end + + it 'uses weak etags with a restricted set of headers' do + sign_in create(:user) + request_file + + expect(response.headers['ETag']).to eq("W/\"bdd5aa537c1e1f6d1b66de4bac8a6132\"") + expect(response.cache_control[:no_store]).to be_nil + expect(response.header['Cache-Control']).to eq('max-age=60, public') + end + end end end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index b30610d98d7..b5797e374f3 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -231,7 +231,7 @@ RSpec.describe ProjectsController do end context "project with broken repo" do - let_it_be(:empty_project) { create(:project_broken_repo, :public) } + let_it_be(:empty_project) { create(:project, :public) } before do sign_in(user) @@ -246,8 +246,6 @@ RSpec.describe ProjectsController do end it "renders the empty project view" do - allow(Project).to receive(:repo).and_raise(Gitlab::Git::Repository::NoRepository) - expect(response).to render_template('projects/no_repo') end end @@ -299,14 +297,16 @@ RSpec.describe ProjectsController do end it "renders files even with invalid license" do + invalid_license = ::Gitlab::Git::DeclaredLicense.new(key: 'woozle', name: 'woozle wuzzle') + controller.instance_variable_set(:@project, public_project) - expect(public_project.repository).to receive(:license_key).and_return('woozle wuzzle').at_least(:once) + expect(public_project.repository).to receive(:license).and_return(invalid_license).at_least(:once) get_show expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template('_files') - expect(response.body).to have_content('LICENSE') # would be 'MIT license' if stub not works + expect(response.body).to have_content('woozle wuzzle') end describe 'tracking events', :snowplow do @@ -1233,26 +1233,6 @@ RSpec.describe ProjectsController do get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" } end - context 'when use_gitaly_pagination_for_refs is disabled' do - before do - stub_feature_flags(use_gitaly_pagination_for_refs: false) - end - - it 'does not use gitaly pagination' do - expected_params = ActionController::Parameters.new(ref: '123456', per_page: 100).permit! - - expect_next_instance_of(BranchesFinder, project.repository, expected_params) do |finder| - expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original - end - - expect_next_instance_of(TagsFinder, project.repository, expected_params) do |finder| - expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original - end - - get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" } - end - end - context 'when gitaly is unavailable' do before do expect_next_instance_of(TagsFinder) do |finder| diff --git a/spec/controllers/registrations/welcome_controller_spec.rb b/spec/controllers/registrations/welcome_controller_spec.rb index 14e88d469ba..a3b246fbedd 100644 --- a/spec/controllers/registrations/welcome_controller_spec.rb +++ b/spec/controllers/registrations/welcome_controller_spec.rb @@ -31,7 +31,6 @@ RSpec.describe Registrations::WelcomeController do context 'when role and setup_for_company is set' do before do - stub_feature_flags(about_your_company_registration_flow: false) user.update!(setup_for_company: false) sign_in(user) end @@ -61,10 +60,6 @@ RSpec.describe Registrations::WelcomeController do end describe '#update' do - before do - stub_feature_flags(about_your_company_registration_flow: false) - end - subject(:update) do patch :update, params: { user: { role: 'software_developer', setup_for_company: 'false' } } end diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 4131bd148da..7ab66b04a6e 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -218,7 +218,7 @@ RSpec.describe SearchController do end end - it_behaves_like 'Snowplow event tracking' do + it_behaves_like 'Snowplow event tracking with RedisHLL context' do subject { get :show, params: { group_id: namespace.id, scope: 'blobs', search: 'term' } } let(:project) { nil } diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 0e0770fb94c..80cf060bc45 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -399,18 +399,30 @@ RSpec.describe SessionsController do end context 'when OTP is invalid' do - before do - authenticate_2fa(otp_attempt: 'invalid') - end + let(:code) { 'invalid' } it 'does not authenticate' do + authenticate_2fa(otp_attempt: code) + expect(subject.current_user).not_to eq user end it 'warns about invalid OTP code' do + authenticate_2fa(otp_attempt: code) + expect(controller).to set_flash.now[:alert] .to(/Invalid two-factor code/) end + + it 'sends an email to the user informing about the attempt to sign in with a wrong OTP code' do + controller.request.remote_addr = '1.2.3.4' + + expect_next_instance_of(NotificationService) do |instance| + expect(instance).to receive(:two_factor_otp_attempt_failed).with(user, '1.2.3.4') + end + + authenticate_2fa(otp_attempt: code) + end end end diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index c27e58634f6..e128db8d1c1 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -216,7 +216,7 @@ RSpec.describe UploadsController do end context 'for PDF files' do - let(:filename) { 'git-cheat-sheet.pdf' } + let(:filename) { 'sample.pdf' } let(:expected_disposition) { 'inline;' } let(:note) { create(:note, :with_pdf_attachment, project: project) } |