diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-19 22:11:55 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-19 22:11:55 +0000 |
commit | 5a8431feceba47fd8e1804d9aa1b1730606b71d5 (patch) | |
tree | e5df8e0ceee60f4af8093f5c4c2f934b8abced05 /spec/requests | |
parent | 4d477238500c347c6553d335d920bedfc5a46869 (diff) | |
download | gitlab-ce-5a8431feceba47fd8e1804d9aa1b1730606b71d5.tar.gz |
Add latest changes from gitlab-org/gitlab@12-5-stable-ee
Diffstat (limited to 'spec/requests')
101 files changed, 1918 insertions, 160 deletions
diff --git a/spec/requests/api/access_requests_spec.rb b/spec/requests/api/access_requests_spec.rb index 100f3d33c7b..3bfca00776f 100644 --- a/spec/requests/api/access_requests_spec.rb +++ b/spec/requests/api/access_requests_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::AccessRequests do diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb index 53fc3096751..438d5dbf018 100644 --- a/spec/requests/api/applications_spec.rb +++ b/spec/requests/api/applications_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Applications, :api do diff --git a/spec/requests/api/avatar_spec.rb b/spec/requests/api/avatar_spec.rb index 9bc49bd5982..c8bc7f8a4a2 100644 --- a/spec/requests/api/avatar_spec.rb +++ b/spec/requests/api/avatar_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Avatar do diff --git a/spec/requests/api/award_emoji_spec.rb b/spec/requests/api/award_emoji_spec.rb index 342fcfa1041..80040cddd4d 100644 --- a/spec/requests/api/award_emoji_spec.rb +++ b/spec/requests/api/award_emoji_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::AwardEmoji do diff --git a/spec/requests/api/badges_spec.rb b/spec/requests/api/badges_spec.rb index 771a78a2d91..ea0a7d4c9b7 100644 --- a/spec/requests/api/badges_spec.rb +++ b/spec/requests/api/badges_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Badges do diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb index 0b9c0c2ebe9..8a67e956165 100644 --- a/spec/requests/api/boards_spec.rb +++ b/spec/requests/api/boards_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Boards do diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index f9c8b42afa8..675b06b057c 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Branches do @@ -117,6 +119,25 @@ describe API::Branches do it_behaves_like 'repository branches' end + + it 'does not submit N+1 DB queries', :request_store do + create(:protected_branch, name: 'master', project: project) + + # Make sure no setup step query is recorded. + get api(route, current_user), params: { per_page: 100 } + + control = ActiveRecord::QueryRecorder.new do + get api(route, current_user), params: { per_page: 100 } + end + + new_branch_name = 'protected-branch' + CreateBranchService.new(project, current_user).execute(new_branch_name, 'master') + create(:protected_branch, name: new_branch_name, project: project) + + expect do + get api(route, current_user), params: { per_page: 100 } + end.not_to exceed_query_limit(control) + end end context 'when authenticated', 'as a guest' do @@ -602,7 +623,7 @@ describe API::Branches do post api(route, user), params: { branch: 'new_design3', ref: 'foo' } expect(response).to have_gitlab_http_status(400) - expect(json_response['message']).to eq('Invalid reference name') + expect(json_response['message']).to eq('Invalid reference name: new_design3') end end diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb index 0b48b79219c..541acb29857 100644 --- a/spec/requests/api/broadcast_messages_spec.rb +++ b/spec/requests/api/broadcast_messages_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::BroadcastMessages do diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 6cb02ba2f6b..639b8e96343 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::CommitStatuses do @@ -278,7 +280,7 @@ describe API::CommitStatuses do } end - it 'update the correct pipeline' do + it 'update the correct pipeline', :sidekiq_might_not_need_inline do subject expect(first_pipeline.reload.status).to eq('created') @@ -302,7 +304,7 @@ describe API::CommitStatuses do expect(json_response['status']).to eq('success') end - it 'retries a commit status' do + it 'retries a commit status', :sidekiq_might_not_need_inline do expect(CommitStatus.count).to eq 2 expect(CommitStatus.first).to be_retried expect(CommitStatus.last.pipeline).to be_success diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 90ff1d12bf1..d8da1c001b0 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'mime/types' @@ -369,7 +371,7 @@ describe API::Commits do valid_c_params[:start_project] = public_project.id end - it 'adds a new commit to forked_project and returns a 201' do + it 'adds a new commit to forked_project and returns a 201', :sidekiq_might_not_need_inline do expect_request_with_status(201) { post api(url, guest), params: valid_c_params } .to change { last_commit_id(forked_project, valid_c_params[:branch]) } .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) } @@ -381,14 +383,14 @@ describe API::Commits do valid_c_params[:start_project] = public_project.full_path end - it 'adds a new commit to forked_project and returns a 201' do + it 'adds a new commit to forked_project and returns a 201', :sidekiq_might_not_need_inline do expect_request_with_status(201) { post api(url, guest), params: valid_c_params } .to change { last_commit_id(forked_project, valid_c_params[:branch]) } .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) } end end - context 'when branch already exists' do + context 'when branch already exists', :sidekiq_might_not_need_inline do before do valid_c_params.delete(:start_branch) valid_c_params[:branch] = 'master' @@ -835,7 +837,7 @@ describe API::Commits do } end - it 'allows pushing to the source branch of the merge request' do + it 'allows pushing to the source branch of the merge request', :sidekiq_might_not_need_inline do post api(url, user), params: push_params('feature') expect(response).to have_gitlab_http_status(:created) @@ -1087,6 +1089,20 @@ describe API::Commits do expect(json_response.first.keys).to include 'diff' end + context 'when hard limits are lower than the number of files' do + before do + allow(Commit).to receive(:max_diff_options).and_return(max_files: 1) + end + + it 'respects the limit' do + get api(route, current_user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response.size).to be <= 1 + end + end + context 'when ref does not exist' do let(:commit_id) { 'unknown' } @@ -1360,6 +1376,12 @@ describe API::Commits do it_behaves_like '400 response' do let(:request) { post api(route, current_user), params: { branch: 'markdown' } } end + + it 'includes an error_code in the response' do + post api(route, current_user), params: { branch: 'markdown' } + + expect(json_response['error_code']).to eq 'empty' + end end context 'when ref contains a dot' do @@ -1417,7 +1439,7 @@ describe API::Commits do let(:project_id) { forked_project.id } - it 'allows access from a maintainer that to the source branch' do + it 'allows access from a maintainer that to the source branch', :sidekiq_might_not_need_inline do post api(route, user), params: { branch: 'feature' } expect(response).to have_gitlab_http_status(:created) @@ -1519,6 +1541,19 @@ describe API::Commits do let(:request) { post api(route, current_user) } end end + + context 'when commit is already reverted in the target branch' do + it 'includes an error_code in the response' do + # First one actually reverts + post api(route, current_user), params: { branch: 'markdown' } + + # Second one is redundant and should be empty + post api(route, current_user), params: { branch: 'markdown' } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error_code']).to eq 'empty' + end + end end context 'when authenticated', 'as a developer' do diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb index e0cc18abcca..4579ccfad80 100644 --- a/spec/requests/api/deploy_keys_spec.rb +++ b/spec/requests/api/deploy_keys_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::DeployKeys do diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb index ad7be531979..26849c0991d 100644 --- a/spec/requests/api/deployments_spec.rb +++ b/spec/requests/api/deployments_spec.rb @@ -12,9 +12,9 @@ describe API::Deployments do describe 'GET /projects/:id/deployments' do let(:project) { create(:project) } - let!(:deployment_1) { create(:deployment, :success, project: project, iid: 11, ref: 'master', created_at: Time.now) } - let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago) } - let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'patch', created_at: 2.days.ago) } + let!(:deployment_1) { create(:deployment, :success, project: project, iid: 11, ref: 'master', created_at: Time.now, updated_at: Time.now) } + let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago) } + let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'patch', created_at: 2.days.ago, updated_at: 1.hour.ago) } context 'as member of the project' do it 'returns projects deployments sorted by id asc' do @@ -57,6 +57,8 @@ describe API::Deployments do 'iid' | 'desc' | [:deployment_2, :deployment_1, :deployment_3] 'ref' | 'asc' | [:deployment_2, :deployment_1, :deployment_3] 'ref' | 'desc' | [:deployment_3, :deployment_1, :deployment_2] + 'updated_at' | 'asc' | [:deployment_2, :deployment_3, :deployment_1] + 'updated_at' | 'desc' | [:deployment_1, :deployment_3, :deployment_2] end with_them do @@ -137,14 +139,42 @@ describe API::Deployments do expect(response).to have_gitlab_http_status(500) end + + it 'links any merged merge requests to the deployment' do + mr = create( + :merge_request, + :merged, + target_project: project, + source_project: project, + target_branch: 'master', + source_branch: 'foo' + ) + + post( + api("/projects/#{project.id}/deployments", user), + params: { + environment: 'production', + sha: sha, + ref: 'master', + tag: false, + status: 'success' + } + ) + + deploy = project.deployments.last + + expect(deploy.merge_requests).to eq([mr]) + end end context 'as a developer' do - it 'creates a new deployment' do - developer = create(:user) + let(:developer) { create(:user) } + before do project.add_developer(developer) + end + it 'creates a new deployment' do post( api("/projects/#{project.id}/deployments", developer), params: { @@ -161,6 +191,32 @@ describe API::Deployments do expect(json_response['sha']).to eq(sha) expect(json_response['ref']).to eq('master') end + + it 'links any merged merge requests to the deployment' do + mr = create( + :merge_request, + :merged, + target_project: project, + source_project: project, + target_branch: 'master', + source_branch: 'foo' + ) + + post( + api("/projects/#{project.id}/deployments", developer), + params: { + environment: 'production', + sha: sha, + ref: 'master', + tag: false, + status: 'success' + } + ) + + deploy = project.deployments.last + + expect(deploy.merge_requests).to eq([mr]) + end end context 'as non member' do @@ -182,7 +238,7 @@ describe API::Deployments do end describe 'PUT /projects/:id/deployments/:deployment_id' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } let(:build) { create(:ci_build, :failed, project: project) } let(:environment) { create(:environment, project: project) } let(:deploy) do @@ -191,7 +247,8 @@ describe API::Deployments do :failed, project: project, environment: environment, - deployable: nil + deployable: nil, + sha: project.commit.sha ) end @@ -216,6 +273,26 @@ describe API::Deployments do expect(response).to have_gitlab_http_status(200) expect(json_response['status']).to eq('success') end + + it 'links merge requests when the deployment status changes to success', :sidekiq_inline do + mr = create( + :merge_request, + :merged, + target_project: project, + source_project: project, + target_branch: 'master', + source_branch: 'foo' + ) + + put( + api("/projects/#{project.id}/deployments/#{deploy.id}", user), + params: { status: 'success' } + ) + + deploy = project.deployments.last + + expect(deploy.merge_requests).to eq([mr]) + end end context 'as a developer' do diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb index 0420201efe3..68f7d407b54 100644 --- a/spec/requests/api/discussions_spec.rb +++ b/spec/requests/api/discussions_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Discussions do diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb index cfee3f6c0f8..2a34e623a7e 100644 --- a/spec/requests/api/doorkeeper_access_spec.rb +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'doorkeeper access' do diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index 745f3c55ac8..aa273e97209 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Environments do diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index 992fd5e9c66..9f8d254a00c 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Events do diff --git a/spec/requests/api/features_spec.rb b/spec/requests/api/features_spec.rb index 57a57e69a00..dfd14f89dbf 100644 --- a/spec/requests/api/features_spec.rb +++ b/spec/requests/api/features_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Features do @@ -118,14 +120,13 @@ describe API::Features do post api("/features/#{feature_name}", admin), params: { value: 'true', user: user.username, feature_group: 'perf_team' } expect(response).to have_gitlab_http_status(201) - expect(json_response).to eq( - 'name' => 'my_feature', - 'state' => 'conditional', - 'gates' => [ - { 'key' => 'boolean', 'value' => false }, - { 'key' => 'groups', 'value' => ['perf_team'] }, - { 'key' => 'actors', 'value' => ["User:#{user.id}"] } - ]) + expect(json_response['name']).to eq('my_feature') + expect(json_response['state']).to eq('conditional') + expect(json_response['gates']).to contain_exactly( + { 'key' => 'boolean', 'value' => false }, + { 'key' => 'groups', 'value' => ['perf_team'] }, + { 'key' => 'actors', 'value' => ["User:#{user.id}"] } + ) end end diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 21b67357543..ec18156f49f 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Files do diff --git a/spec/requests/api/graphql/current_user/todos_query_spec.rb b/spec/requests/api/graphql/current_user/todos_query_spec.rb new file mode 100644 index 00000000000..82deba0d92c --- /dev/null +++ b/spec/requests/api/graphql/current_user/todos_query_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Query current user todos' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:commit_todo) { create(:on_commit_todo, user: current_user, project: create(:project, :repository)) } + let_it_be(:issue_todo) { create(:todo, user: current_user, target: create(:issue)) } + let_it_be(:merge_request_todo) { create(:todo, user: current_user, target: create(:merge_request)) } + + let(:fields) do + <<~QUERY + nodes { + #{all_graphql_fields_for('todos'.classify)} + } + QUERY + end + + let(:query) do + graphql_query_for('currentUser', {}, query_graphql_field('todos', {}, fields)) + end + + subject { graphql_data.dig('currentUser', 'todos', 'nodes') } + + before do + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + it 'contains the expected ids' do + is_expected.to include( + a_hash_including('id' => commit_todo.to_global_id.to_s), + a_hash_including('id' => issue_todo.to_global_id.to_s), + a_hash_including('id' => merge_request_todo.to_global_id.to_s) + ) + end + + it 'returns Todos for all target types' do + is_expected.to include( + a_hash_including('targetType' => 'COMMIT'), + a_hash_including('targetType' => 'ISSUE'), + a_hash_including('targetType' => 'MERGEREQUEST') + ) + end +end diff --git a/spec/requests/api/graphql/current_user_query_spec.rb b/spec/requests/api/graphql/current_user_query_spec.rb new file mode 100644 index 00000000000..9db638ea59e --- /dev/null +++ b/spec/requests/api/graphql/current_user_query_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'getting project information' do + include GraphqlHelpers + + let(:query) do + graphql_query_for('currentUser', {}, 'name') + end + + subject { graphql_data['currentUser'] } + + before do + post_graphql(query, current_user: current_user) + end + + context 'when there is a current_user' do + set(:current_user) { create(:user) } + + it_behaves_like 'a working graphql query' + + it { is_expected.to include('name' => current_user.name) } + end + + context 'when there is no current_user' do + let(:current_user) { nil } + + it_behaves_like 'a working graphql query' + + it { is_expected.to be_nil } + end +end diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb index 1e799a0a42a..2aeb75a10b4 100644 --- a/spec/requests/api/graphql/gitlab_schema_spec.rb +++ b/spec/requests/api/graphql/gitlab_schema_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'GitlabSchema configurations' do diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb new file mode 100644 index 00000000000..8f908b7bf88 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Setting assignees of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:assignee) { create(:user) } + let(:assignee2) { create(:user) } + let(:input) { { assignee_usernames: [assignee.username] } } + let(:expected_result) do + [{ 'username' => assignee.username }] + end + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_set_assignees, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + mergeRequest { + id + assignees { + nodes { + username + } + } + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_set_assignees) + end + + def mutation_assignee_nodes + mutation_response['mergeRequest']['assignees']['nodes'] + end + + before do + project.add_developer(current_user) + project.add_developer(assignee) + project.add_developer(assignee2) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + it 'does not allow members without the right permission to add assignees' do + user = create(:user) + project.add_guest(user) + + post_graphql_mutation(mutation, current_user: user) + + expect(graphql_errors).not_to be_empty + end + + context 'with assignees already assigned' do + before do + merge_request.assignees = [assignee2] + merge_request.save! + end + + it 'replaces the assignee' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_assignee_nodes).to match_array(expected_result) + end + end + + context 'when passing an empty list of assignees' do + let(:input) { { assignee_usernames: [] } } + + before do + merge_request.assignees = [assignee2] + merge_request.save! + end + + it 'removes assignee' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_assignee_nodes).to eq([]) + end + end + + context 'when passing append as true' do + let(:input) { { assignee_usernames: [assignee2.username], operation_mode: Types::MutationOperationModeEnum.enum[:append] } } + + before do + # In CE, APPEND is a NOOP as you can't have multiple assignees + # We test multiple assignment in EE specs + stub_licensed_features(multiple_merge_request_assignees: false) + + merge_request.assignees = [assignee] + merge_request.save! + end + + it 'does not replace the assignee in CE' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_assignee_nodes).to match_array(expected_result) + end + end + + context 'when passing remove as true' do + let(:input) { { assignee_usernames: [assignee.username], operation_mode: Types::MutationOperationModeEnum.enum[:remove] } } + let(:expected_result) { [] } + + before do + merge_request.assignees = [assignee] + merge_request.save! + end + + it 'removes the users in the list, while adding none' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_assignee_nodes).to match_array(expected_result) + end + end +end diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb new file mode 100644 index 00000000000..2112ff0dc74 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Setting labels of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:label) { create(:label, project: project) } + let(:label2) { create(:label, project: project) } + let(:input) { { label_ids: [GitlabSchema.id_from_object(label).to_s] } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_set_labels, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + mergeRequest { + id + labels { + nodes { + id + } + } + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_set_labels) + end + + def mutation_label_nodes + mutation_response['mergeRequest']['labels']['nodes'] + end + + before do + project.add_developer(current_user) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + it 'sets the merge request labels, removing existing ones' do + merge_request.update(labels: [label2]) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_label_nodes.count).to eq(1) + expect(mutation_label_nodes[0]['id']).to eq(label.to_global_id.to_s) + end + + context 'when passing label_ids empty array as input' do + let(:input) { { label_ids: [] } } + + it 'removes the merge request labels' do + merge_request.update!(labels: [label]) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_label_nodes.count).to eq(0) + end + end + + context 'when passing operation_mode as APPEND' do + let(:input) { { operation_mode: Types::MutationOperationModeEnum.enum[:append], label_ids: [GitlabSchema.id_from_object(label).to_s] } } + + before do + merge_request.update!(labels: [label2]) + end + + it 'sets the labels, without removing others' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_label_nodes.count).to eq(2) + expect(mutation_label_nodes).to contain_exactly({ 'id' => label.to_global_id.to_s }, { 'id' => label2.to_global_id.to_s }) + end + end + + context 'when passing operation_mode as REMOVE' do + let(:input) { { operation_mode: Types::MutationOperationModeEnum.enum[:remove], label_ids: [GitlabSchema.id_from_object(label).to_s] } } + + before do + merge_request.update!(labels: [label, label2]) + end + + it 'removes the labels, without removing others' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_label_nodes.count).to eq(1) + expect(mutation_label_nodes[0]['id']).to eq(label2.to_global_id.to_s) + end + end +end diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb new file mode 100644 index 00000000000..c45da613591 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Setting locked status of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:input) { { locked: true } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_set_locked, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + mergeRequest { + id + discussionLocked + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_set_locked)['mergeRequest']['discussionLocked'] + end + + before do + project.add_developer(current_user) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + it 'marks the merge request as WIP' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to eq(true) + end + + it 'does not do anything if the merge request was already locked' do + merge_request.update!(discussion_locked: true) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to eq(true) + end + + context 'when passing locked false as input' do + let(:input) { { locked: false } } + + it 'does not do anything if the merge request was not marked locked' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to eq(false) + end + + it 'unmarks the merge request as locked' do + merge_request.update!(discussion_locked: true) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to eq(false) + end + end +end diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb new file mode 100644 index 00000000000..bd558edf9c5 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Setting milestone of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:milestone) { create(:milestone, project: project) } + let(:input) { { milestone_id: GitlabSchema.id_from_object(milestone).to_s } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_set_milestone, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + mergeRequest { + id + milestone { + id + } + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_set_milestone) + end + + before do + project.add_developer(current_user) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + it 'sets the merge request milestone' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']['milestone']['id']).to eq(milestone.to_global_id.to_s) + end + + context 'when passing milestone_id nil as input' do + let(:input) { { milestone_id: nil } } + + it 'removes the merge request milestone' do + merge_request.update!(milestone: milestone) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']['milestone']).to be_nil + end + end +end diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb new file mode 100644 index 00000000000..975735bf246 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Setting subscribed status of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:input) { { subscribed_state: true } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_set_subscription, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + mergeRequest { + id + subscribed + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_set_subscription)['mergeRequest']['subscribed'] + end + + before do + project.add_developer(current_user) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + it 'marks the merge request as WIP' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to eq(true) + end + + context 'when passing subscribe false as input' do + let(:input) { { subscribed_state: false } } + + it 'unmarks the merge request as subscribed' do + merge_request.subscribe(current_user, project) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to eq(false) + end + end +end diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb index bbc477ba485..4492c51dbd7 100644 --- a/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb +++ b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Setting WIP status of a merge request' do diff --git a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb new file mode 100644 index 00000000000..fabbb3aeb49 --- /dev/null +++ b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Marking todos done' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:author) { create(:user) } + let_it_be(:other_user) { create(:user) } + + let_it_be(:todo1) { create(:todo, user: current_user, author: author, state: :pending) } + let_it_be(:todo2) { create(:todo, user: current_user, author: author, state: :done) } + + let_it_be(:other_user_todo) { create(:todo, user: other_user, author: author, state: :pending) } + + let(:input) { { id: todo1.to_global_id.to_s } } + + let(:mutation) do + graphql_mutation(:todo_mark_done, input, + <<-QL.strip_heredoc + clientMutationId + errors + todo { + id + state + } + QL + ) + end + + def mutation_response + graphql_mutation_response(:todo_mark_done) + end + + it 'marks a single todo as done' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(todo1.reload.state).to eq('done') + expect(todo2.reload.state).to eq('done') + expect(other_user_todo.reload.state).to eq('pending') + + todo = mutation_response['todo'] + expect(todo['id']).to eq(todo1.to_global_id.to_s) + expect(todo['state']).to eq('done') + end + + context 'when todo is already marked done' do + let(:input) { { id: todo2.to_global_id.to_s } } + + it 'has the expected response' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(todo1.reload.state).to eq('pending') + expect(todo2.reload.state).to eq('done') + expect(other_user_todo.reload.state).to eq('pending') + + todo = mutation_response['todo'] + expect(todo['id']).to eq(todo2.to_global_id.to_s) + expect(todo['state']).to eq('done') + end + end + + context 'when todo does not belong to requesting user' do + let(:input) { { id: other_user_todo.to_global_id.to_s } } + let(:access_error) { 'The resource that you are attempting to access does not exist or you don\'t have permission to perform this action' } + + it 'contains the expected error' do + post_graphql_mutation(mutation, current_user: current_user) + + errors = json_response['errors'] + expect(errors).not_to be_blank + expect(errors.first['message']).to eq(access_error) + + expect(todo1.reload.state).to eq('pending') + expect(todo2.reload.state).to eq('done') + expect(other_user_todo.reload.state).to eq('pending') + end + end + + context 'when using an invalid gid' do + let(:input) { { id: 'invalid_gid' } } + let(:invalid_gid_error) { 'invalid_gid is not a valid GitLab id.' } + + it 'contains the expected error' do + post_graphql_mutation(mutation, current_user: current_user) + + errors = json_response['errors'] + expect(errors).not_to be_blank + expect(errors.first['message']).to eq(invalid_gid_error) + + expect(todo1.reload.state).to eq('pending') + expect(todo2.reload.state).to eq('done') + expect(other_user_todo.reload.state).to eq('pending') + end + end +end diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb index 4f9f916f22e..4ce7a3912a3 100644 --- a/spec/requests/api/graphql/project/issues_spec.rb +++ b/spec/requests/api/graphql/project/issues_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'getting an issue list for a project' do @@ -62,7 +64,7 @@ describe 'getting an issue list for a project' do end end - it "is expected to check permissions on the first issue only" do + it 'is expected to check permissions on the first issue only' do allow(Ability).to receive(:allowed?).and_call_original # Newest first, we only want to see the newest checked expect(Ability).not_to receive(:allowed?).with(current_user, :read_issue, issues.first) @@ -114,4 +116,141 @@ describe 'getting an issue list for a project' do end end end + + describe 'sorting and pagination' do + let(:start_cursor) { graphql_data['project']['issues']['pageInfo']['startCursor'] } + let(:end_cursor) { graphql_data['project']['issues']['pageInfo']['endCursor'] } + + context 'when sorting by due date' do + let(:sort_project) { create(:project, :public) } + + let!(:due_issue1) { create(:issue, project: sort_project, due_date: 3.days.from_now) } + let!(:due_issue2) { create(:issue, project: sort_project, due_date: nil) } + let!(:due_issue3) { create(:issue, project: sort_project, due_date: 2.days.ago) } + let!(:due_issue4) { create(:issue, project: sort_project, due_date: nil) } + let!(:due_issue5) { create(:issue, project: sort_project, due_date: 1.day.ago) } + + let(:params) { 'sort: DUE_DATE_ASC' } + + def query(issue_params = params) + graphql_query_for( + 'project', + { 'fullPath' => sort_project.full_path }, + <<~ISSUES + issues(#{issue_params}) { + pageInfo { + endCursor + } + edges { + node { + iid + dueDate + } + } + } + ISSUES + ) + end + + before do + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + context 'when ascending' do + it 'sorts issues' do + expect(grab_iids).to eq [due_issue3.iid, due_issue5.iid, due_issue1.iid, due_issue4.iid, due_issue2.iid] + end + + context 'when paginating' do + let(:params) { 'sort: DUE_DATE_ASC, first: 2' } + + it 'sorts issues' do + expect(grab_iids).to eq [due_issue3.iid, due_issue5.iid] + + cursored_query = query("sort: DUE_DATE_ASC, after: \"#{end_cursor}\"") + post_graphql(cursored_query, current_user: current_user) + response_data = JSON.parse(response.body)['data']['project']['issues']['edges'] + + expect(grab_iids(response_data)).to eq [due_issue1.iid, due_issue4.iid, due_issue2.iid] + end + end + end + + context 'when descending' do + let(:params) { 'sort: DUE_DATE_DESC' } + + it 'sorts issues' do + expect(grab_iids).to eq [due_issue1.iid, due_issue5.iid, due_issue3.iid, due_issue4.iid, due_issue2.iid] + end + + context 'when paginating' do + let(:params) { 'sort: DUE_DATE_DESC, first: 2' } + + it 'sorts issues' do + expect(grab_iids).to eq [due_issue1.iid, due_issue5.iid] + + cursored_query = query("sort: DUE_DATE_DESC, after: \"#{end_cursor}\"") + post_graphql(cursored_query, current_user: current_user) + response_data = JSON.parse(response.body)['data']['project']['issues']['edges'] + + expect(grab_iids(response_data)).to eq [due_issue3.iid, due_issue4.iid, due_issue2.iid] + end + end + end + end + + context 'when sorting by relative position' do + let(:sort_project) { create(:project, :public) } + + let!(:relative_issue1) { create(:issue, project: sort_project, relative_position: 2000) } + let!(:relative_issue2) { create(:issue, project: sort_project, relative_position: nil) } + let!(:relative_issue3) { create(:issue, project: sort_project, relative_position: 1000) } + let!(:relative_issue4) { create(:issue, project: sort_project, relative_position: nil) } + let!(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) } + + let(:params) { 'sort: RELATIVE_POSITION_ASC' } + + def query(issue_params = params) + graphql_query_for( + 'project', + { 'fullPath' => sort_project.full_path }, + "issues(#{issue_params}) { pageInfo { endCursor} edges { node { iid dueDate } } }" + ) + end + + before do + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + context 'when ascending' do + it 'sorts issues' do + expect(grab_iids).to eq [relative_issue5.iid, relative_issue3.iid, relative_issue1.iid, relative_issue4.iid, relative_issue2.iid] + end + + context 'when paginating' do + let(:params) { 'sort: RELATIVE_POSITION_ASC, first: 2' } + + it 'sorts issues' do + expect(grab_iids).to eq [relative_issue5.iid, relative_issue3.iid] + + cursored_query = query("sort: RELATIVE_POSITION_ASC, after: \"#{end_cursor}\"") + post_graphql(cursored_query, current_user: current_user) + response_data = JSON.parse(response.body)['data']['project']['issues']['edges'] + + expect(grab_iids(response_data)).to eq [relative_issue1.iid, relative_issue4.iid, relative_issue2.iid] + end + end + end + end + end + + def grab_iids(data = issues_data) + data.map do |issue| + issue.dig('node', 'iid').to_i + end + end end diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb index 74820d39102..70c21666799 100644 --- a/spec/requests/api/graphql/project/merge_request_spec.rb +++ b/spec/requests/api/graphql/project/merge_request_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'getting merge request information nested in a project' do diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb index 0727ada4691..fbb22958d51 100644 --- a/spec/requests/api/graphql/project_query_spec.rb +++ b/spec/requests/api/graphql/project_query_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'getting project information' do diff --git a/spec/requests/api/group_boards_spec.rb b/spec/requests/api/group_boards_spec.rb index b400a7f55ef..232ec9aca32 100644 --- a/spec/requests/api/group_boards_spec.rb +++ b/spec/requests/api/group_boards_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::GroupBoards do diff --git a/spec/requests/api/group_clusters_spec.rb b/spec/requests/api/group_clusters_spec.rb index 46e3dd650cc..97465647a87 100644 --- a/spec/requests/api/group_clusters_spec.rb +++ b/spec/requests/api/group_clusters_spec.rb @@ -286,12 +286,15 @@ describe API::GroupClusters do let(:update_params) do { domain: domain, - platform_kubernetes_attributes: platform_kubernetes_attributes + platform_kubernetes_attributes: platform_kubernetes_attributes, + management_project_id: management_project_id } end let(:domain) { 'new-domain.com' } let(:platform_kubernetes_attributes) { {} } + let(:management_project) { create(:project, group: group) } + let(:management_project_id) { management_project.id } let(:cluster) do create(:cluster, :group, :provided_by_gcp, @@ -308,6 +311,8 @@ describe API::GroupClusters do context 'authorized user' do before do + management_project.add_maintainer(current_user) + put api("/groups/#{group.id}/clusters/#{cluster.id}", current_user), params: update_params cluster.reload @@ -320,6 +325,7 @@ describe API::GroupClusters do it 'updates cluster attributes' do expect(cluster.domain).to eq('new-domain.com') + expect(cluster.management_project).to eq(management_project) end end @@ -332,6 +338,7 @@ describe API::GroupClusters do it 'does not update cluster attributes' do expect(cluster.domain).to eq('old-domain.com') + expect(cluster.management_project).to be_nil end it 'returns validation errors' do @@ -339,6 +346,18 @@ describe API::GroupClusters do end end + context 'current user does not have access to management_project_id' do + let(:management_project_id) { create(:project).id } + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'returns validation errors' do + expect(json_response['message']['management_project_id'].first).to match('don\'t have permission') + end + end + context 'with a GCP cluster' do context 'when user tries to change GCP specific fields' do let(:platform_kubernetes_attributes) do diff --git a/spec/requests/api/group_container_repositories_spec.rb b/spec/requests/api/group_container_repositories_spec.rb index 0a41e455d01..785006253d8 100644 --- a/spec/requests/api/group_container_repositories_spec.rb +++ b/spec/requests/api/group_container_repositories_spec.rb @@ -3,10 +3,10 @@ require 'spec_helper' describe API::GroupContainerRepositories do - set(:group) { create(:group, :private) } - set(:project) { create(:project, :private, group: group) } - let(:reporter) { create(:user) } - let(:guest) { create(:user) } + let_it_be(:group) { create(:group, :private) } + let_it_be(:project) { create(:project, :private, group: group) } + let_it_be(:reporter) { create(:user) } + let_it_be(:guest) { create(:user) } let(:root_repository) { create(:container_repository, :root, project: project) } let(:test_repository) { create(:container_repository, project: project) } @@ -44,6 +44,8 @@ describe API::GroupContainerRepositories do let(:object) { group } end + it_behaves_like 'a gitlab tracking event', described_class.name, 'list_repositories' + context 'with invalid group id' do let(:url) { '/groups/123412341234/registry/repositories' } diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb new file mode 100644 index 00000000000..ac4853e5388 --- /dev/null +++ b/spec/requests/api/group_export_spec.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::GroupExport do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } + + let(:path) { "/groups/#{group.id}/export" } + let(:download_path) { "/groups/#{group.id}/export/download" } + + let(:export_path) { "#{Dir.tmpdir}/group_export_spec" } + + before do + allow_next_instance_of(Gitlab::ImportExport) do |import_export| + expect(import_export).to receive(:storage_path).and_return(export_path) + end + end + + after do + FileUtils.rm_rf(export_path, secure: true) + end + + describe 'GET /groups/:group_id/export/download' do + let(:upload) { ImportExportUpload.new(group: group) } + + before do + stub_uploads_object_storage(ImportExportUploader) + + group.add_owner(user) + end + + context 'when export file exists' do + before do + upload.export_file = fixture_file_upload('spec/fixtures/group_export.tar.gz', "`/tar.gz") + upload.save! + end + + it 'downloads exported group archive' do + get api(download_path, user) + + expect(response).to have_gitlab_http_status(200) + end + + context 'when export_file.file does not exist' do + before do + expect_next_instance_of(ImportExportUploader) do |uploader| + expect(uploader).to receive(:file).and_return(nil) + end + end + + it 'returns 404' do + get api(download_path, user) + + expect(response).to have_gitlab_http_status(404) + end + end + end + + context 'when export file does not exist' do + it 'returns 404' do + get api(download_path, user) + + expect(response).to have_gitlab_http_status(404) + end + end + end + + describe 'POST /groups/:group_id/export' do + context 'when user is a group owner' do + before do + group.add_owner(user) + end + + it 'accepts download' do + post api(path, user) + + expect(response).to have_gitlab_http_status(202) + end + end + + context 'when user is not a group owner' do + before do + group.add_developer(user) + end + + it 'forbids the request' do + post api(path, user) + + expect(response).to have_gitlab_http_status(403) + end + end + end +end diff --git a/spec/requests/api/group_milestones_spec.rb b/spec/requests/api/group_milestones_spec.rb index 6980eb7f55d..3e9b6246434 100644 --- a/spec/requests/api/group_milestones_spec.rb +++ b/spec/requests/api/group_milestones_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::GroupMilestones do diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb index d50bae3dc47..abdc3a40360 100644 --- a/spec/requests/api/group_variables_spec.rb +++ b/spec/requests/api/group_variables_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::GroupVariables do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 902a5ec2a86..cb97398805a 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Groups do diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index a1a007811fe..bbfe40041a1 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'raven/transports/dummy' require_relative '../../../config/initializers/sentry' diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb index 68df02d4d8d..3ff7102479c 100644 --- a/spec/requests/api/import_github_spec.rb +++ b/spec/requests/api/import_github_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ImportGithub do diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index 01a2e33c0d9..fcff2cde730 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Internal::Base do @@ -316,6 +318,7 @@ describe API::Internal::Base do expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true') expect(user.reload.last_activity_on).to eql(Date.today) end end @@ -335,6 +338,7 @@ describe API::Internal::Base do expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true') expect(user.reload.last_activity_on).to be_nil end end @@ -407,7 +411,6 @@ describe API::Internal::Base do context "custom action" do let(:access_checker) { double(Gitlab::GitAccess) } - let(:message) { 'CustomActionError message' } let(:payload) do { 'action' => 'geo_proxy_to_primary', @@ -418,8 +421,8 @@ describe API::Internal::Base do } } end - - let(:custom_action_result) { Gitlab::GitAccessResult::CustomAction.new(payload, message) } + let(:console_messages) { ['informational message'] } + let(:custom_action_result) { Gitlab::GitAccessResult::CustomAction.new(payload, console_messages) } before do project.add_guest(user) @@ -446,8 +449,8 @@ describe API::Internal::Base do expect(response).to have_gitlab_http_status(300) expect(json_response['status']).to be_truthy - expect(json_response['message']).to eql(message) expect(json_response['payload']).to eql(payload) + expect(json_response['gl_console_messages']).to eql(console_messages) expect(user.reload.last_activity_on).to be_nil end end @@ -577,6 +580,7 @@ describe API::Internal::Base do expect(json_response["gitaly"]["repository"]["relative_path"]).to eq(project.repository.gitaly_repository.relative_path) expect(json_response["gitaly"]["address"]).to eq(Gitlab::GitalyClient.address(project.repository_storage)) expect(json_response["gitaly"]["token"]).to eq(Gitlab::GitalyClient.token(project.repository_storage)) + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-inforef-uploadpack-cache' => 'true') end end diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 89ee6f896f9..020e7659a4c 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Jobs do @@ -595,7 +597,7 @@ describe API::Jobs do context 'find proper job' do shared_examples 'a valid file' do - context 'when artifacts are stored locally' do + context 'when artifacts are stored locally', :sidekiq_might_not_need_inline do let(:download_headers) do { 'Content-Transfer-Encoding' => 'binary', 'Content-Disposition' => @@ -674,7 +676,7 @@ describe API::Jobs do let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC } let(:public_builds) { true } - it 'allows to access artifacts' do + it 'allows to access artifacts', :sidekiq_might_not_need_inline do expect(response).to have_gitlab_http_status(200) expect(response.headers.to_h) .to include('Content-Type' => 'application/json', @@ -711,7 +713,7 @@ describe API::Jobs do let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE } let(:public_builds) { true } - it 'returns a specific artifact file for a valid path' do + it 'returns a specific artifact file for a valid path', :sidekiq_might_not_need_inline do expect(Gitlab::Workhorse) .to receive(:send_artifacts_entry) .and_call_original @@ -732,7 +734,7 @@ describe API::Jobs do sha: project.commit('improve/awesome').sha) end - it 'returns a specific artifact file for a valid path' do + it 'returns a specific artifact file for a valid path', :sidekiq_might_not_need_inline do get_artifact_file(artifact, 'improve/awesome') expect(response).to have_gitlab_http_status(200) diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb index f37d84fddef..6802a0cfdab 100644 --- a/spec/requests/api/keys_spec.rb +++ b/spec/requests/api/keys_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Keys do diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 7089da3d351..d027738c8db 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Labels do diff --git a/spec/requests/api/lint_spec.rb b/spec/requests/api/lint_spec.rb index f52cdf1c459..46d23bd16b9 100644 --- a/spec/requests/api/lint_spec.rb +++ b/spec/requests/api/lint_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Lint do diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb index 0cf5c5677b9..99263f2fc1e 100644 --- a/spec/requests/api/markdown_spec.rb +++ b/spec/requests/api/markdown_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" describe API::Markdown do diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index eb55d747179..f2942020e16 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Members do @@ -24,7 +26,7 @@ describe API::Members do shared_examples 'GET /:source_type/:id/members/(all)' do |source_type, all| let(:members_url) do - "/#{source_type.pluralize}/#{source.id}/members".tap do |url| + (+"/#{source_type.pluralize}/#{source.id}/members").tap do |url| url << "/all" if all end end @@ -149,9 +151,15 @@ describe API::Members do expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.map { |u| u['id'] }).to eq [developer.id, maintainer.id, nested_user.id, project_user.id, linked_group_user.id] - expect(json_response.map { |u| u['access_level'] }).to eq [Gitlab::Access::DEVELOPER, Gitlab::Access::OWNER, Gitlab::Access::DEVELOPER, - Gitlab::Access::DEVELOPER, Gitlab::Access::DEVELOPER] + + expected_users_and_access_levels = [ + [developer.id, Gitlab::Access::DEVELOPER], + [maintainer.id, Gitlab::Access::OWNER], + [nested_user.id, Gitlab::Access::DEVELOPER], + [project_user.id, Gitlab::Access::DEVELOPER], + [linked_group_user.id, Gitlab::Access::DEVELOPER] + ] + expect(json_response.map { |u| [u['id'], u['access_level']] }).to match_array(expected_users_and_access_levels) end it 'finds all group members including inherited members' do diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb index 8a67d98fc4c..9de76c2fe50 100644 --- a/spec/requests/api/merge_request_diffs_spec.rb +++ b/spec/requests/api/merge_request_diffs_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" describe API::MergeRequestDiffs, 'MergeRequestDiffs' do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 05160a33e61..c96c80b6998 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" describe API::MergeRequests do @@ -10,7 +12,7 @@ describe API::MergeRequests do let(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) } let(:milestone) { create(:milestone, title: '1.0.0', project: project) } let(:milestone1) { create(:milestone, title: '0.9', project: project) } - let!(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, title: "Test", created_at: base_time) } + let!(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time) } let!(:merge_request_closed) { create(:merge_request, state: "closed", milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) } let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignees: [user], source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds, merge_commit_sha: '9999999999999999999999999999999999999999') } let!(:merge_request_locked) { create(:merge_request, state: "locked", milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, title: "Locked test", created_at: base_time + 1.second) } @@ -699,16 +701,20 @@ describe API::MergeRequests do expect(json_response.first['id']).to eq merge_request_closed.id end - it 'avoids N+1 queries' do - control = ActiveRecord::QueryRecorder.new do - get api("/projects/#{project.id}/merge_requests", user) - end.count + context 'a project which enforces all discussions to be resolved' do + let!(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) } - create(:merge_request, author: user, assignees: [user], source_project: project, target_project: project, created_at: base_time) + it 'avoids N+1 queries' do + control = ActiveRecord::QueryRecorder.new do + get api("/projects/#{project.id}/merge_requests", user) + end.count - expect do - get api("/projects/#{project.id}/merge_requests", user) - end.not_to exceed_query_limit(control) + create(:merge_request, author: user, assignees: [user], source_project: project, target_project: project, created_at: base_time) + + expect do + get api("/projects/#{project.id}/merge_requests", user) + end.not_to exceed_query_limit(control) + end end end @@ -775,6 +781,8 @@ describe API::MergeRequests do expect(json_response['merge_error']).to eq(merge_request.merge_error) expect(json_response['user']['can_merge']).to be_truthy expect(json_response).not_to include('rebase_in_progress') + expect(json_response['has_conflicts']).to be_falsy + expect(json_response['blocking_discussions_resolved']).to be_truthy end it 'exposes description and title html when render_html is true' do @@ -921,7 +929,7 @@ describe API::MergeRequests do allow_collaboration: true) end - it 'includes the `allow_collaboration` field' do + it 'includes the `allow_collaboration` field', :sidekiq_might_not_need_inline do get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user) expect(json_response['allow_collaboration']).to be_truthy @@ -1035,14 +1043,12 @@ describe API::MergeRequests do describe 'POST /projects/:id/merge_requests/:merge_request_iid/pipelines' do before do - allow_any_instance_of(Ci::Pipeline) - .to receive(:ci_yaml_file) - .and_return(YAML.dump({ - rspec: { - script: 'ls', - only: ['merge_requests'] - } - })) + stub_ci_pipeline_yaml_file(YAML.dump({ + rspec: { + script: 'ls', + only: ['merge_requests'] + } + })) end let(:project) do @@ -1326,7 +1332,7 @@ describe API::MergeRequests do context 'accepts remove_source_branch parameter' do let(:params) do { title: 'Test merge_request', - source_branch: 'markdown', + source_branch: 'feature_conflict', target_branch: 'master', author: user } end @@ -1406,7 +1412,7 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(400) end - it 'allows setting `allow_collaboration`' do + it 'allows setting `allow_collaboration`', :sidekiq_might_not_need_inline do post api("/projects/#{forked_project.id}/merge_requests", user2), params: { title: 'Test merge_request', source_branch: "feature_conflict", target_branch: "master", author: user2, target_project_id: project.id, allow_collaboration: true } expect(response).to have_gitlab_http_status(201) @@ -1438,7 +1444,7 @@ describe API::MergeRequests do end end - it "returns 201 when target_branch is specified and for the same project" do + it "returns 201 when target_branch is specified and for the same project", :sidekiq_might_not_need_inline do post api("/projects/#{forked_project.id}/merge_requests", user2), params: { title: 'Test merge_request', target_branch: 'master', source_branch: 'markdown', author: user2, target_project_id: forked_project.id } expect(response).to have_gitlab_http_status(201) @@ -1486,7 +1492,7 @@ describe API::MergeRequests do end describe "PUT /projects/:id/merge_requests/:merge_request_iid/merge" do - let(:pipeline) { create(:ci_pipeline_without_jobs) } + let(:pipeline) { create(:ci_pipeline) } it "returns merge_request in case of success" do put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) @@ -1633,6 +1639,21 @@ describe API::MergeRequests do expect(source_repository.branch_exists?(source_branch)).to be_falsy end end + + context "performing a ff-merge with squash" do + let(:merge_request) { create(:merge_request, :rebased, source_project: project, squash: true) } + + before do + project.update(merge_requests_ff_only_enabled: true) + end + + it "records the squash commit SHA and returns it in the response" do + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['squash_commit_sha'].length).to eq(40) + end + end end describe "GET /projects/:id/merge_requests/:merge_request_iid/merge_ref", :clean_gitlab_redis_shared_state do @@ -2152,6 +2173,16 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(409) end + + it "returns 409 if rebase can't lock the row" do + allow_any_instance_of(MergeRequest).to receive(:with_lock).and_raise(ActiveRecord::LockWaitTimeout) + expect(RebaseWorker).not_to receive(:perform_async) + + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user) + + expect(response).to have_gitlab_http_status(409) + expect(json_response['message']).to eq(MergeRequest::REBASE_LOCK_MESSAGE) + end end describe 'Time tracking' do diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index 2e376109b42..e0bf1509be3 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Namespaces do diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index 6c1e30791d2..e57d7699892 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Notes do diff --git a/spec/requests/api/notification_settings_spec.rb b/spec/requests/api/notification_settings_spec.rb index 4ed667ad0dc..09fc0197c58 100644 --- a/spec/requests/api/notification_settings_spec.rb +++ b/spec/requests/api/notification_settings_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::NotificationSettings do diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb index 3811ec751de..8d7b3fa3c09 100644 --- a/spec/requests/api/oauth_tokens_spec.rb +++ b/spec/requests/api/oauth_tokens_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'OAuth tokens' do diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb index 28abe1a8456..821a210a414 100644 --- a/spec/requests/api/pages/internal_access_spec.rb +++ b/spec/requests/api/pages/internal_access_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe "Internal Project Pages Access" do diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb index 6af441caf74..ec84762b05a 100644 --- a/spec/requests/api/pages/private_access_spec.rb +++ b/spec/requests/api/pages/private_access_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe "Private Project Pages Access" do diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb index d99224eca5b..67b8cfb8fbc 100644 --- a/spec/requests/api/pages/public_access_spec.rb +++ b/spec/requests/api/pages/public_access_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe "Public Project Pages Access" do diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb index 326b724666d..6b774e9335e 100644 --- a/spec/requests/api/pages_domains_spec.rb +++ b/spec/requests/api/pages_domains_spec.rb @@ -1,15 +1,22 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::PagesDomains do - set(:project) { create(:project, path: 'my.project', pages_https_only: false) } - set(:user) { create(:user) } - set(:admin) { create(:admin) } + let_it_be(:project) { create(:project, path: 'my.project', pages_https_only: false) } + let_it_be(:user) { create(:user) } + let_it_be(:admin) { create(:admin) } - set(:pages_domain) { create(:pages_domain, :without_key, :without_certificate, domain: 'www.domain.test', project: project) } - set(:pages_domain_secure) { create(:pages_domain, domain: 'ssl.domain.test', project: project) } - set(:pages_domain_expired) { create(:pages_domain, :with_expired_certificate, domain: 'expired.domain.test', project: project) } + let_it_be(:pages_domain) { create(:pages_domain, :without_key, :without_certificate, domain: 'www.domain.test', project: project) } + let_it_be(:pages_domain_secure) { create(:pages_domain, domain: 'ssl.domain.test', project: project) } + let_it_be(:pages_domain_with_letsencrypt) { create(:pages_domain, :letsencrypt, domain: 'letsencrypt.domain.test', project: project) } + let_it_be(:pages_domain_expired) { create(:pages_domain, :with_expired_certificate, domain: 'expired.domain.test', project: project) } let(:pages_domain_params) { build(:pages_domain, :without_key, :without_certificate, domain: 'www.other-domain.test').slice(:domain) } + let(:pages_domain_with_letsencrypt_params) do + build(:pages_domain, :without_key, :without_certificate, domain: 'www.other-domain.test', auto_ssl_enabled: true) + .slice(:domain, :auto_ssl_enabled) + end let(:pages_domain_secure_params) { build(:pages_domain, domain: 'ssl.other-domain.test', project: project).slice(:domain, :certificate, :key) } let(:pages_domain_secure_key_missmatch_params) {build(:pages_domain, :with_trusted_chain, project: project).slice(:domain, :certificate, :key) } let(:pages_domain_secure_missing_chain_params) {build(:pages_domain, :with_missing_chain, project: project).slice(:certificate) } @@ -20,6 +27,7 @@ describe API::PagesDomains do let(:route_secure_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_secure.domain}" } let(:route_expired_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_expired.domain}" } let(:route_vacant_domain) { "/projects/#{project.id}/pages/domains/www.vacant-domain.test" } + let(:route_letsencrypt_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_with_letsencrypt.domain}" } before do allow(Gitlab.config.pages).to receive(:enabled).and_return(true) @@ -45,9 +53,10 @@ describe API::PagesDomains do expect(response).to match_response_schema('public_api/v4/pages_domain_basics') expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(3) + expect(json_response.size).to eq(4) expect(json_response.last).to have_key('domain') expect(json_response.last).to have_key('project_id') + expect(json_response.last).to have_key('auto_ssl_enabled') expect(json_response.last).to have_key('certificate_expiration') expect(json_response.last['certificate_expiration']['expired']).to be true expect(json_response.first).not_to have_key('certificate_expiration') @@ -71,7 +80,7 @@ describe API::PagesDomains do expect(response).to match_response_schema('public_api/v4/pages_domains') expect(response).to include_pagination_headers expect(json_response).to be_an Array - expect(json_response.size).to eq(3) + expect(json_response.size).to eq(4) expect(json_response.map { |pages_domain| pages_domain['domain'] }).to include(pages_domain.domain) expect(json_response.last).to have_key('domain') end @@ -164,6 +173,7 @@ describe API::PagesDomains do expect(json_response['url']).to eq(pages_domain_secure.url) expect(json_response['certificate']['subject']).to eq(pages_domain_secure.subject) expect(json_response['certificate']['expired']).to be false + expect(json_response['auto_ssl_enabled']).to be false end it 'returns pages domain with an expired certificate' do @@ -173,6 +183,18 @@ describe API::PagesDomains do expect(response).to match_response_schema('public_api/v4/pages_domain/detail') expect(json_response['certificate']['expired']).to be true end + + it 'returns pages domain with letsencrypt' do + get api(route_letsencrypt_domain, user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(json_response['domain']).to eq(pages_domain_with_letsencrypt.domain) + expect(json_response['url']).to eq(pages_domain_with_letsencrypt.url) + expect(json_response['certificate']['subject']).to eq(pages_domain_with_letsencrypt.subject) + expect(json_response['certificate']['expired']).to be false + expect(json_response['auto_ssl_enabled']).to be true + end end context 'when domain is vacant' do @@ -244,6 +266,7 @@ describe API::PagesDomains do expect(pages_domain.domain).to eq(params[:domain]) expect(pages_domain.certificate).to be_nil expect(pages_domain.key).to be_nil + expect(pages_domain.auto_ssl_enabled).to be false end it 'creates a new secure pages domain' do @@ -255,6 +278,29 @@ describe API::PagesDomains do expect(pages_domain.domain).to eq(params_secure[:domain]) expect(pages_domain.certificate).to eq(params_secure[:certificate]) expect(pages_domain.key).to eq(params_secure[:key]) + expect(pages_domain.auto_ssl_enabled).to be false + end + + it 'creates domain with letsencrypt enabled' do + post api(route, user), params: pages_domain_with_letsencrypt_params + pages_domain = PagesDomain.find_by(domain: json_response['domain']) + + expect(response).to have_gitlab_http_status(201) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(pages_domain.domain).to eq(pages_domain_with_letsencrypt_params[:domain]) + expect(pages_domain.auto_ssl_enabled).to be true + end + + it 'creates domain with letsencrypt enabled and provided certificate' do + post api(route, user), params: params_secure.merge(auto_ssl_enabled: true) + pages_domain = PagesDomain.find_by(domain: json_response['domain']) + + expect(response).to have_gitlab_http_status(201) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(pages_domain.domain).to eq(params_secure[:domain]) + expect(pages_domain.certificate).to eq(params_secure[:certificate]) + expect(pages_domain.key).to eq(params_secure[:key]) + expect(pages_domain.auto_ssl_enabled).to be true end it 'fails to create pages domain without key' do @@ -321,13 +367,14 @@ describe API::PagesDomains do shared_examples_for 'put pages domain' do it 'updates pages domain removing certificate' do - put api(route_secure_domain, user) + put api(route_secure_domain, user), params: { certificate: nil, key: nil } pages_domain_secure.reload expect(response).to have_gitlab_http_status(200) expect(response).to match_response_schema('public_api/v4/pages_domain/detail') expect(pages_domain_secure.certificate).to be_nil expect(pages_domain_secure.key).to be_nil + expect(pages_domain_secure.auto_ssl_enabled).to be false end it 'updates pages domain adding certificate' do @@ -340,6 +387,37 @@ describe API::PagesDomains do expect(pages_domain.key).to eq(params_secure[:key]) end + it 'updates pages domain adding certificate with letsencrypt' do + put api(route_domain, user), params: params_secure.merge(auto_ssl_enabled: true) + pages_domain.reload + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(pages_domain.certificate).to eq(params_secure[:certificate]) + expect(pages_domain.key).to eq(params_secure[:key]) + expect(pages_domain.auto_ssl_enabled).to be true + end + + it 'updates pages domain enabling letsencrypt' do + put api(route_domain, user), params: { auto_ssl_enabled: true } + pages_domain.reload + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(pages_domain.auto_ssl_enabled).to be true + end + + it 'updates pages domain disabling letsencrypt while preserving the certificate' do + put api(route_letsencrypt_domain, user), params: { auto_ssl_enabled: false } + pages_domain_with_letsencrypt.reload + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('public_api/v4/pages_domain/detail') + expect(pages_domain_with_letsencrypt.auto_ssl_enabled).to be false + expect(pages_domain_with_letsencrypt.key).to be + expect(pages_domain_with_letsencrypt.certificate).to be + end + it 'updates pages domain with expired certificate' do put api(route_expired_domain, user), params: params_secure pages_domain_expired.reload diff --git a/spec/requests/api/pipeline_schedules_spec.rb b/spec/requests/api/pipeline_schedules_spec.rb index 072bd02f2ac..5c8ccce2e37 100644 --- a/spec/requests/api/pipeline_schedules_spec.rb +++ b/spec/requests/api/pipeline_schedules_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::PipelineSchedules do diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index 3ac63dc381b..cce52cfc1ca 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -673,7 +673,7 @@ describe API::Pipelines do let!(:build) { create(:ci_build, :running, pipeline: pipeline) } context 'authorized user' do - it 'retries failed builds' do + it 'retries failed builds', :sidekiq_might_not_need_inline do post api("/projects/#{project.id}/pipelines/#{pipeline.id}/cancel", user) expect(response).to have_gitlab_http_status(200) diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb index a7b919de2ef..04e59238877 100644 --- a/spec/requests/api/project_clusters_spec.rb +++ b/spec/requests/api/project_clusters_spec.rb @@ -281,11 +281,14 @@ describe API::ProjectClusters do let(:api_url) { 'https://kubernetes.example.com' } let(:namespace) { 'new-namespace' } let(:platform_kubernetes_attributes) { { namespace: namespace } } + let(:management_project) { create(:project, namespace: project.namespace) } + let(:management_project_id) { management_project.id } let(:update_params) do { domain: 'new-domain.com', - platform_kubernetes_attributes: platform_kubernetes_attributes + platform_kubernetes_attributes: platform_kubernetes_attributes, + management_project_id: management_project_id } end @@ -310,6 +313,8 @@ describe API::ProjectClusters do context 'authorized user' do before do + management_project.add_maintainer(current_user) + put api("/projects/#{project.id}/clusters/#{cluster.id}", current_user), params: update_params cluster.reload @@ -323,6 +328,7 @@ describe API::ProjectClusters do it 'updates cluster attributes' do expect(cluster.domain).to eq('new-domain.com') expect(cluster.platform_kubernetes.namespace).to eq('new-namespace') + expect(cluster.management_project).to eq(management_project) end end @@ -336,6 +342,7 @@ describe API::ProjectClusters do it 'does not update cluster attributes' do expect(cluster.domain).not_to eq('new_domain.com') expect(cluster.platform_kubernetes.namespace).not_to eq('invalid_namespace') + expect(cluster.management_project).not_to eq(management_project) end it 'returns validation errors' do @@ -343,6 +350,18 @@ describe API::ProjectClusters do end end + context 'current user does not have access to management_project_id' do + let(:management_project_id) { create(:project).id } + + it 'responds with 400' do + expect(response).to have_gitlab_http_status(400) + end + + it 'returns validation errors' do + expect(json_response['message']['management_project_id'].first).to match('don\'t have permission') + end + end + context 'with a GCP cluster' do context 'when user tries to change GCP specific fields' do let(:platform_kubernetes_attributes) do diff --git a/spec/requests/api/project_container_repositories_spec.rb b/spec/requests/api/project_container_repositories_spec.rb index 3ac7ff7656b..d04db134db0 100644 --- a/spec/requests/api/project_container_repositories_spec.rb +++ b/spec/requests/api/project_container_repositories_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectContainerRepositories do @@ -44,6 +46,7 @@ describe API::ProjectContainerRepositories do it_behaves_like 'rejected container repository access', :guest, :forbidden it_behaves_like 'rejected container repository access', :anonymous, :not_found + it_behaves_like 'a gitlab tracking event', described_class.name, 'list_repositories' it_behaves_like 'returns repositories for allowed users', :reporter, 'project' do let(:object) { project } @@ -55,6 +58,7 @@ describe API::ProjectContainerRepositories do it_behaves_like 'rejected container repository access', :developer, :forbidden it_behaves_like 'rejected container repository access', :anonymous, :not_found + it_behaves_like 'a gitlab tracking event', described_class.name, 'delete_repository' context 'for maintainer' do let(:api_user) { maintainer } @@ -83,6 +87,8 @@ describe API::ProjectContainerRepositories do stub_container_registry_tags(repository: root_repository.path, tags: %w(rootA latest)) end + it_behaves_like 'a gitlab tracking event', described_class.name, 'list_tags' + it 'returns a list of tags' do subject @@ -109,6 +115,7 @@ describe API::ProjectContainerRepositories do it_behaves_like 'rejected container repository access', :developer, :forbidden it_behaves_like 'rejected container repository access', :anonymous, :not_found + it_behaves_like 'a gitlab tracking event', described_class.name, 'delete_tag_bulk' end context 'for maintainer' do @@ -220,6 +227,7 @@ describe API::ProjectContainerRepositories do it 'properly removes tag' do expect(service).to receive(:execute).with(root_repository) { { status: :success } } expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(root_repository.project, api_user, tags: %w[rootA]) { service } + expect(Gitlab::Tracking).to receive(:event).with(described_class.name, 'delete_tag', {}) subject @@ -235,6 +243,7 @@ describe API::ProjectContainerRepositories do it 'properly removes tag' do expect(service).to receive(:execute).with(root_repository) { { status: :success } } expect(Projects::ContainerRepository::DeleteTagsService).to receive(:new).with(root_repository.project, api_user, tags: %w[rootA]) { service } + expect(Gitlab::Tracking).to receive(:event).with(described_class.name, 'delete_tag', {}) subject diff --git a/spec/requests/api/project_events_spec.rb b/spec/requests/api/project_events_spec.rb index 8c2db6e4c62..d466dca9884 100644 --- a/spec/requests/api/project_events_spec.rb +++ b/spec/requests/api/project_events_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectEvents do diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb index 7de8935097a..605ff888234 100644 --- a/spec/requests/api/project_export_spec.rb +++ b/spec/requests/api/project_export_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectExport do @@ -370,7 +372,7 @@ describe API::ProjectExport do end context 'when overriding description' do - it 'starts' do + it 'starts', :sidekiq_might_not_need_inline do params = { description: "Foo" } expect_any_instance_of(Projects::ImportExport::ExportService).to receive(:execute) diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index b88a8b95201..06c09b100ac 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectHooks, 'ProjectHooks' do diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index d2b1fb063b8..866adbd424e 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectImport do @@ -153,7 +155,7 @@ describe API::ProjectImport do expect(import_project.import_data.data['override_params']).to be_empty end - it 'correctly overrides params during the import' do + it 'correctly overrides params during the import', :sidekiq_might_not_need_inline do override_params = { 'description' => 'Hello world' } perform_enqueued_jobs do diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb index 895f05a98e8..df6d83c1e65 100644 --- a/spec/requests/api/project_milestones_spec.rb +++ b/spec/requests/api/project_milestones_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectMilestones do diff --git a/spec/requests/api/project_snapshots_spec.rb b/spec/requests/api/project_snapshots_spec.rb index 2857715cdbe..cdd44f71649 100644 --- a/spec/requests/api/project_snapshots_spec.rb +++ b/spec/requests/api/project_snapshots_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectSnapshots do diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index ef0cabad4b0..cac3f07d0d0 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectSnippets do diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb index 80e5033dab4..2bf864afe87 100644 --- a/spec/requests/api/project_templates_spec.rb +++ b/spec/requests/api/project_templates_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProjectTemplates do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 99d2a68ef53..f1447536e0f 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples 'languages and percentages JSON response' do @@ -15,7 +17,7 @@ shared_examples 'languages and percentages JSON response' do end context "when the languages haven't been detected yet" do - it 'returns expected language values' do + it 'returns expected language values', :sidekiq_might_not_need_inline do get api("/projects/#{project.id}/languages", user) expect(response).to have_gitlab_http_status(:ok) @@ -360,6 +362,30 @@ describe API::Projects do end end + context 'and using id_after' do + it_behaves_like 'projects response' do + let(:filter) { { id_after: project2.id } } + let(:current_user) { user } + let(:projects) { [public_project, project, project2, project3].select { |p| p.id > project2.id } } + end + end + + context 'and using id_before' do + it_behaves_like 'projects response' do + let(:filter) { { id_before: project2.id } } + let(:current_user) { user } + let(:projects) { [public_project, project, project2, project3].select { |p| p.id < project2.id } } + end + end + + context 'and using both id_after and id_before' do + it_behaves_like 'projects response' do + let(:filter) { { id_before: project2.id, id_after: public_project.id } } + let(:current_user) { user } + let(:projects) { [public_project, project, project2, project3].select { |p| p.id < project2.id && p.id > public_project.id } } + end + end + context 'and membership=true' do it_behaves_like 'projects response' do let(:filter) { { membership: true } } @@ -606,6 +632,7 @@ describe API::Projects do merge_requests_enabled: false, wiki_enabled: false, resolve_outdated_diff_discussions: false, + remove_source_branch_after_merge: true, only_allow_merge_if_pipeline_succeeds: false, request_access_enabled: true, only_allow_merge_if_all_discussions_are_resolved: false, @@ -722,6 +749,22 @@ describe API::Projects do expect(json_response['resolve_outdated_diff_discussions']).to be_truthy end + it 'sets a project as not removing source branches' do + project = attributes_for(:project, remove_source_branch_after_merge: false) + + post api('/projects', user), params: project + + expect(json_response['remove_source_branch_after_merge']).to be_falsey + end + + it 'sets a project as removing source branches' do + project = attributes_for(:project, remove_source_branch_after_merge: true) + + post api('/projects', user), params: project + + expect(json_response['remove_source_branch_after_merge']).to be_truthy + end + it 'sets a project as allowing merge even if build fails' do project = attributes_for(:project, only_allow_merge_if_pipeline_succeeds: false) @@ -829,6 +872,63 @@ describe API::Projects do expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id) end + context 'and using id_after' do + let!(:another_public_project) { create(:project, :public, name: 'another_public_project', creator_id: user4.id, namespace: user4.namespace) } + + it 'only returns projects with id_after filter given' do + get api("/users/#{user4.id}/projects?id_after=#{public_project.id}", user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(another_public_project.id) + end + + it 'returns both projects without a id_after filter' do + get api("/users/#{user4.id}/projects", user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id, another_public_project.id) + end + end + + context 'and using id_before' do + let!(:another_public_project) { create(:project, :public, name: 'another_public_project', creator_id: user4.id, namespace: user4.namespace) } + + it 'only returns projects with id_before filter given' do + get api("/users/#{user4.id}/projects?id_before=#{another_public_project.id}", user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id) + end + + it 'returns both projects without a id_before filter' do + get api("/users/#{user4.id}/projects", user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id, another_public_project.id) + end + end + + context 'and using both id_before and id_after' do + let!(:more_projects) { create_list(:project, 5, :public, creator_id: user4.id, namespace: user4.namespace) } + + it 'only returns projects with id matching the range' do + get api("/users/#{user4.id}/projects?id_after=#{more_projects.first.id}&id_before=#{more_projects.last.id}", user) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(*more_projects[1..-2].map(&:id)) + end + end + it 'returns projects filtered by username' do get api("/users/#{user4.username}/projects/", user) @@ -980,6 +1080,22 @@ describe API::Projects do expect(json_response['resolve_outdated_diff_discussions']).to be_truthy end + it 'sets a project as not removing source branches' do + project = attributes_for(:project, remove_source_branch_after_merge: false) + + post api("/projects/user/#{user.id}", admin), params: project + + expect(json_response['remove_source_branch_after_merge']).to be_falsey + end + + it 'sets a project as removing source branches' do + project = attributes_for(:project, remove_source_branch_after_merge: true) + + post api("/projects/user/#{user.id}", admin), params: project + + expect(json_response['remove_source_branch_after_merge']).to be_truthy + end + it 'sets a project as allowing merge even if build fails' do project = attributes_for(:project, only_allow_merge_if_pipeline_succeeds: false) post api("/projects/user/#{user.id}", admin), params: project @@ -1157,6 +1273,7 @@ describe API::Projects do expect(json_response['wiki_access_level']).to be_present expect(json_response['builds_access_level']).to be_present expect(json_response['resolve_outdated_diff_discussions']).to eq(project.resolve_outdated_diff_discussions) + expect(json_response['remove_source_branch_after_merge']).to be_truthy expect(json_response['container_registry_enabled']).to be_present expect(json_response['created_at']).to be_present expect(json_response['last_activity_at']).to be_present diff --git a/spec/requests/api/protected_branches_spec.rb b/spec/requests/api/protected_branches_spec.rb index f90558d77a9..67ce704b3f3 100644 --- a/spec/requests/api/protected_branches_spec.rb +++ b/spec/requests/api/protected_branches_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProtectedBranches do diff --git a/spec/requests/api/protected_tags_spec.rb b/spec/requests/api/protected_tags_spec.rb index 41363dcc1c3..5a962cd5667 100644 --- a/spec/requests/api/protected_tags_spec.rb +++ b/spec/requests/api/protected_tags_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::ProtectedTags do diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb index 99d0ceee76b..bf05587fe03 100644 --- a/spec/requests/api/releases_spec.rb +++ b/spec/requests/api/releases_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Releases do diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 6f4bb525c89..ba301147d43 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' require 'mime/types' diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 70a95663aea..6138036b0af 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Runner, :clean_gitlab_redis_shared_state do @@ -312,7 +314,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do let(:root_namespace) { create(:namespace) } let(:namespace) { create(:namespace, parent: root_namespace) } let(:project) { create(:project, namespace: namespace, shared_runners_enabled: false) } - let(:pipeline) { create(:ci_pipeline_without_jobs, project: project, ref: 'master') } + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') } let(:runner) { create(:ci_runner, :project, projects: [project]) } let(:job) do create(:ci_build, :artifacts, :extended_options, @@ -610,7 +612,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end context 'when job is made for merge request' do - let(:pipeline) { create(:ci_pipeline_without_jobs, source: :merge_request_event, project: project, ref: 'feature', merge_request: merge_request) } + let(:pipeline) { create(:ci_pipeline, source: :merge_request_event, project: project, ref: 'feature', merge_request: merge_request) } let!(:job) { create(:ci_build, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) } let(:merge_request) { create(:merge_request) } diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index d26fbee6957..8daba204d50 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Runners do diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb index 8abdcaa2e0e..24d7f1e313c 100644 --- a/spec/requests/api/search_spec.rb +++ b/spec/requests/api/search_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Search do @@ -436,6 +438,7 @@ describe API::Search do expect(response).to have_gitlab_http_status(200) expect(json_response.size).to eq(2) + expect(json_response.first['path']).to eq('PROCESS.md') expect(json_response.first['filename']).to eq('PROCESS.md') end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index 7153fcc99d7..a080b59173f 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" describe API::Services do @@ -100,7 +102,7 @@ describe API::Services do expect(json_response['properties'].keys).to match_array(service_instance.api_field_names) end - it "returns empty hash if properties and data fields are empty" do + it "returns empty hash or nil values if properties and data fields are empty" do # deprecated services are not valid for update initialized_service.update_attribute(:properties, {}) @@ -112,7 +114,7 @@ describe API::Services do get api("/projects/#{project.id}/services/#{dashed_service}", user) expect(response).to have_gitlab_http_status(200) - expect(json_response['properties'].keys).to be_empty + expect(json_response['properties'].values.compact).to be_empty end it "returns error when authenticated but not a project owner" do diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb index f3bfb258029..b7586307929 100644 --- a/spec/requests/api/settings_spec.rb +++ b/spec/requests/api/settings_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Settings, 'Settings' do @@ -16,6 +18,10 @@ describe API::Settings, 'Settings' do expect(json_response['password_authentication_enabled']).to be_truthy expect(json_response['plantuml_enabled']).to be_falsey expect(json_response['plantuml_url']).to be_nil + expect(json_response['default_ci_config_path']).to be_nil + expect(json_response['sourcegraph_enabled']).to be_falsey + expect(json_response['sourcegraph_url']).to be_nil + expect(json_response['sourcegraph_public_only']).to be_truthy expect(json_response['default_project_visibility']).to be_a String expect(json_response['default_snippet_visibility']).to be_a String expect(json_response['default_group_visibility']).to be_a String @@ -42,17 +48,22 @@ describe API::Settings, 'Settings' do storages = Gitlab.config.repositories.storages .merge({ 'custom' => 'tmp/tests/custom_repositories' }) allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) + Feature.get(:sourcegraph).enable end it "updates application settings" do put api("/application/settings", admin), params: { + default_ci_config_path: 'debian/salsa-ci.yml', default_projects_limit: 3, default_project_creation: 2, password_authentication_enabled_for_web: false, repository_storages: ['custom'], plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com', + sourcegraph_enabled: true, + sourcegraph_url: 'https://sourcegraph.com', + sourcegraph_public_only: false, default_snippet_visibility: 'internal', restricted_visibility_levels: ['public'], default_artifacts_expire_in: '2 days', @@ -78,12 +89,16 @@ describe API::Settings, 'Settings' do } expect(response).to have_gitlab_http_status(200) + expect(json_response['default_ci_config_path']).to eq('debian/salsa-ci.yml') expect(json_response['default_projects_limit']).to eq(3) expect(json_response['default_project_creation']).to eq(::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS) expect(json_response['password_authentication_enabled_for_web']).to be_falsey expect(json_response['repository_storages']).to eq(['custom']) expect(json_response['plantuml_enabled']).to be_truthy expect(json_response['plantuml_url']).to eq('http://plantuml.example.com') + expect(json_response['sourcegraph_enabled']).to be_truthy + expect(json_response['sourcegraph_url']).to eq('https://sourcegraph.com') + expect(json_response['sourcegraph_public_only']).to eq(false) expect(json_response['default_snippet_visibility']).to eq('internal') expect(json_response['restricted_visibility_levels']).to eq(['public']) expect(json_response['default_artifacts_expire_in']).to eq('2 days') @@ -176,7 +191,8 @@ describe API::Settings, 'Settings' do snowplow_collector_hostname: "snowplow.example.com", snowplow_cookie_domain: ".example.com", snowplow_enabled: true, - snowplow_site_id: "site_id" + snowplow_app_id: "app_id", + snowplow_iglu_registry_url: 'https://example.com' } end @@ -220,6 +236,61 @@ describe API::Settings, 'Settings' do end end + context 'EKS integration settings' do + let(:attribute_names) { settings.keys.map(&:to_s) } + let(:sensitive_attributes) { %w(eks_secret_access_key) } + let(:exposed_attributes) { attribute_names - sensitive_attributes } + + let(:settings) do + { + eks_integration_enabled: true, + eks_account_id: '123456789012', + eks_access_key_id: 'access-key-id-12', + eks_secret_access_key: 'secret-access-key' + } + end + + it 'includes attributes in the API' do + get api("/application/settings", admin) + + expect(response).to have_gitlab_http_status(200) + exposed_attributes.each do |attribute| + expect(json_response.keys).to include(attribute) + end + end + + it 'does not include sensitive attributes in the API' do + get api("/application/settings", admin) + + expect(response).to have_gitlab_http_status(200) + sensitive_attributes.each do |attribute| + expect(json_response.keys).not_to include(attribute) + end + end + + it 'allows updating the settings' do + put api("/application/settings", admin), params: settings + + expect(response).to have_gitlab_http_status(200) + settings.each do |attribute, value| + expect(ApplicationSetting.current.public_send(attribute)).to eq(value) + end + end + + context 'EKS integration is enabled but params are blank' do + let(:settings) { Hash[eks_integration_enabled: true] } + + it 'does not update the settings' do + put api("/application/settings", admin), params: settings + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to include('eks_account_id is missing') + expect(json_response['error']).to include('eks_access_key_id is missing') + expect(json_response['error']).to include('eks_secret_access_key is missing') + end + end + end + context "missing plantuml_url value when plantuml_enabled is true" do it "returns a blank parameter error message" do put api("/application/settings", admin), params: { plantuml_enabled: true } @@ -294,5 +365,14 @@ describe API::Settings, 'Settings' do expect(json_response['domain_blacklist']).to eq(['domain3.com', '*.domain4.com']) end end + + context "missing sourcegraph_url value when sourcegraph_enabled is true" do + it "returns a blank parameter error message" do + put api("/application/settings", admin), params: { sourcegraph_enabled: true } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to eq('sourcegraph_url is missing') + end + end end end diff --git a/spec/requests/api/sidekiq_metrics_spec.rb b/spec/requests/api/sidekiq_metrics_spec.rb index fff9adb7f57..438b1475c54 100644 --- a/spec/requests/api/sidekiq_metrics_spec.rb +++ b/spec/requests/api/sidekiq_metrics_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::SidekiqMetrics do @@ -23,6 +25,10 @@ describe API::SidekiqMetrics do expect(response).to have_gitlab_http_status(200) expect(json_response).to be_a Hash + expect(json_response['jobs']).to be_a Hash + expect(json_response['jobs'].keys) + .to contain_exactly(*%w[processed failed enqueued dead]) + expect(json_response['jobs'].values).to all(be_an(Integer)) end it 'defines the `compound_metrics` endpoint' do diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb index e7eaaea2418..36d2a0d7ea7 100644 --- a/spec/requests/api/snippets_spec.rb +++ b/spec/requests/api/snippets_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Snippets do diff --git a/spec/requests/api/system_hooks_spec.rb b/spec/requests/api/system_hooks_spec.rb index 0e2f3face71..79790b1e999 100644 --- a/spec/requests/api/system_hooks_spec.rb +++ b/spec/requests/api/system_hooks_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::SystemHooks do diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index c4f4a2cb889..3c6ec631664 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Tags do diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index d1e16ab9ca9..b6ba417d892 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Templates do diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb index 9f0d5ad5d12..4121a0f3f3a 100644 --- a/spec/requests/api/todos_spec.rb +++ b/spec/requests/api/todos_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Todos do diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 8ea3d16a41f..fd1104fa978 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Triggers do diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index ee4e783e9ac..1a1e80f1ce3 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Users do @@ -633,32 +635,6 @@ describe API::Users do end end - describe "GET /users/sign_up" do - context 'when experimental signup_flow is active' do - before do - stub_experiment(signup_flow: true) - end - - it "shows sign up page" do - get "/users/sign_up" - expect(response).to have_gitlab_http_status(200) - expect(response).to render_template(:new) - end - end - - context 'when experimental signup_flow is not active' do - before do - stub_experiment(signup_flow: false) - end - - it "redirects to sign in page" do - get "/users/sign_up" - expect(response).to have_gitlab_http_status(302) - expect(response).to redirect_to(new_user_session_path(anchor: 'register-pane')) - end - end - end - describe "PUT /users/:id" do let!(:admin_user) { create(:admin) } @@ -1277,7 +1253,7 @@ describe API::Users do admin end - it "deletes user" do + it "deletes user", :sidekiq_might_not_need_inline do perform_enqueued_jobs { delete api("/users/#{user.id}", admin) } expect(response).to have_gitlab_http_status(204) @@ -1312,7 +1288,7 @@ describe API::Users do end context "hard delete disabled" do - it "moves contributions to the ghost user" do + it "moves contributions to the ghost user", :sidekiq_might_not_need_inline do perform_enqueued_jobs { delete api("/users/#{user.id}", admin) } expect(response).to have_gitlab_http_status(204) @@ -1322,7 +1298,7 @@ describe API::Users do end context "hard delete enabled" do - it "removes contributions" do + it "removes contributions", :sidekiq_might_not_need_inline do perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) } expect(response).to have_gitlab_http_status(204) diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index 69f105b71a8..dfecd43cbfa 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Variables do diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb index e06f8bbc095..e2117ca45ee 100644 --- a/spec/requests/api/version_spec.rb +++ b/spec/requests/api/version_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe API::Version do diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb index 97de26650db..310caa92eb9 100644 --- a/spec/requests/api/wikis_spec.rb +++ b/spec/requests/api/wikis_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' # For every API endpoint we test 3 states of wikis: diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index e58f1b7d9dc..1b17d492b0c 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Git HTTP requests' do @@ -87,7 +89,7 @@ describe 'Git HTTP requests' do end shared_examples_for 'pulls are allowed' do - it do + it 'allows pulls' do download(path, env) do |response| expect(response).to have_gitlab_http_status(:ok) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) @@ -96,7 +98,7 @@ describe 'Git HTTP requests' do end shared_examples_for 'pushes are allowed' do - it do + it 'allows pushes', :sidekiq_might_not_need_inline do upload(path, env) do |response| expect(response).to have_gitlab_http_status(:ok) expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE) @@ -450,16 +452,22 @@ describe 'Git HTTP requests' do context "when authentication fails" do context "when the user is IP banned" do before do - Gitlab.config.rack_attack.git_basic_auth['enabled'] = true + stub_rack_attack_setting(enabled: true, ip_whitelist: []) end - it "responds with status 401" do + it "responds with status 403" do expect(Rack::Attack::Allow2Ban).to receive(:filter).and_return(true) - allow_any_instance_of(ActionDispatch::Request).to receive(:ip).and_return('1.2.3.4') + expect(Gitlab::AuthLogger).to receive(:error).with({ + message: 'Rack_Attack', + env: :blocklist, + remote_ip: '127.0.0.1', + request_method: 'GET', + path: "/#{path}/info/refs?service=git-upload-pack" + }) clone_get(path, env) - expect(response).to have_gitlab_http_status(:unauthorized) + expect(response).to have_gitlab_http_status(:forbidden) end end end @@ -493,7 +501,7 @@ describe 'Git HTTP requests' do context "when the user isn't blocked" do before do - Gitlab.config.rack_attack.git_basic_auth['enabled'] = true + stub_rack_attack_setting(enabled: true, bantime: 1.minute, findtime: 5.minutes, maxretry: 2, ip_whitelist: []) end it "resets the IP in Rack Attack on download" do @@ -652,9 +660,11 @@ describe 'Git HTTP requests' do response.status end + include_context 'rack attack cache store' + it "repeated attempts followed by successful attempt" do options = Gitlab.config.rack_attack.git_basic_auth - maxretry = options[:maxretry] - 1 + maxretry = options[:maxretry] ip = '1.2.3.4' allow_any_instance_of(ActionDispatch::Request).to receive(:ip).and_return(ip) @@ -666,12 +676,6 @@ describe 'Git HTTP requests' do expect(attempt_login(true)).to eq(200) expect(Rack::Attack::Allow2Ban.banned?(ip)).to be_falsey - - maxretry.times.each do - expect(attempt_login(false)).to eq(401) - end - - Rack::Attack::Allow2Ban.reset(ip, options) end end @@ -843,8 +847,8 @@ describe 'Git HTTP requests' do get "/#{project.full_path}/blob/master/info/refs" end - it "returns not found" do - expect(response).to have_gitlab_http_status(:not_found) + it "redirects" do + expect(response).to have_gitlab_http_status(302) end end end diff --git a/spec/requests/groups/milestones_controller_spec.rb b/spec/requests/groups/milestones_controller_spec.rb index af19d931284..977cccad29f 100644 --- a/spec/requests/groups/milestones_controller_spec.rb +++ b/spec/requests/groups/milestones_controller_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe Groups::MilestonesController do diff --git a/spec/requests/groups/registry/repositories_controller_spec.rb b/spec/requests/groups/registry/repositories_controller_spec.rb new file mode 100644 index 00000000000..35fdeaab604 --- /dev/null +++ b/spec/requests/groups/registry/repositories_controller_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Groups::Registry::RepositoriesController do + let_it_be(:group, reload: true) { create(:group) } + let_it_be(:user) { create(:user) } + + before do + stub_container_registry_config(enabled: true) + + group.add_reporter(user) + login_as(user) + end + + describe 'GET groups/:group_id/-/container_registries.json' do + it 'avoids N+1 queries' do + project = create(:project, group: group) + create(:container_repository, project: project) + endpoint = group_container_registries_path(group, format: :json) + + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { get(endpoint) }.count + + create_list(:project, 2, group: group).each do |project| + create_list(:container_repository, 2, project: project) + end + + expect { get(endpoint) }.not_to exceed_all_query_limit(control_count) + + # sanity check that response is 200 + expect(response).to have_http_status(200) + repositories = json_response + expect(repositories.count).to eq(5) + end + end +end diff --git a/spec/requests/health_controller_spec.rb b/spec/requests/health_controller_spec.rb new file mode 100644 index 00000000000..61412815039 --- /dev/null +++ b/spec/requests/health_controller_spec.rb @@ -0,0 +1,227 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe HealthController do + include StubENV + + let(:token) { Gitlab::CurrentSettings.health_check_access_token } + let(:whitelisted_ip) { '1.1.1.1' } + let(:not_whitelisted_ip) { '2.2.2.2' } + let(:params) { {} } + let(:headers) { {} } + + before do + allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip]) + stub_storage_settings({}) # Hide the broken storage + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + end + + shared_context 'endpoint querying database' do + it 'does query database' do + control_count = ActiveRecord::QueryRecorder.new { subject }.count + + expect(control_count).not_to be_zero + end + end + + shared_context 'endpoint not querying database' do + it 'does not query database' do + control_count = ActiveRecord::QueryRecorder.new { subject }.count + + expect(control_count).to be_zero + end + end + + shared_context 'endpoint not found' do + it 'responds with resource not found' do + subject + + expect(response.status).to eq(404) + end + end + + describe 'GET /-/health' do + subject { get '/-/health', params: params, headers: headers } + + shared_context 'endpoint responding with health data' do + it 'responds with health checks data' do + subject + + expect(response.status).to eq(200) + expect(response.body).to eq('GitLab OK') + end + end + + context 'accessed from whitelisted ip' do + before do + stub_remote_addr(whitelisted_ip) + end + + it_behaves_like 'endpoint responding with health data' + it_behaves_like 'endpoint not querying database' + end + + context 'accessed from not whitelisted ip' do + before do + stub_remote_addr(not_whitelisted_ip) + end + + it_behaves_like 'endpoint not querying database' + it_behaves_like 'endpoint not found' + end + end + + describe 'GET /-/readiness' do + subject { get '/-/readiness', params: params, headers: headers } + + shared_context 'endpoint responding with readiness data' do + context 'when requesting instance-checks' do + it 'responds with readiness checks data' do + expect(Gitlab::HealthChecks::MasterCheck).to receive(:check) { true } + + subject + + expect(json_response).to include({ 'status' => 'ok' }) + expect(json_response['master_check']).to contain_exactly({ 'status' => 'ok' }) + end + + it 'responds with readiness checks data when a failure happens' do + expect(Gitlab::HealthChecks::MasterCheck).to receive(:check) { false } + + subject + + expect(json_response).to include({ 'status' => 'failed' }) + expect(json_response['master_check']).to contain_exactly( + { 'status' => 'failed', 'message' => 'unexpected Master check result: false' }) + + expect(response.status).to eq(503) + expect(response.headers['X-GitLab-Custom-Error']).to eq(1) + end + end + + context 'when requesting all checks' do + before do + params.merge!(all: true) + end + + it 'responds with readiness checks data' do + subject + + expect(json_response['db_check']).to contain_exactly({ 'status' => 'ok' }) + expect(json_response['cache_check']).to contain_exactly({ 'status' => 'ok' }) + expect(json_response['queues_check']).to contain_exactly({ 'status' => 'ok' }) + expect(json_response['shared_state_check']).to contain_exactly({ 'status' => 'ok' }) + expect(json_response['gitaly_check']).to contain_exactly( + { 'status' => 'ok', 'labels' => { 'shard' => 'default' } }) + end + + it 'responds with readiness checks data when a failure happens' do + allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return( + Gitlab::HealthChecks::Result.new('redis_check', false, "check error")) + + subject + + expect(json_response['cache_check']).to contain_exactly({ 'status' => 'ok' }) + expect(json_response['redis_check']).to contain_exactly( + { 'status' => 'failed', 'message' => 'check error' }) + + expect(response.status).to eq(503) + expect(response.headers['X-GitLab-Custom-Error']).to eq(1) + end + end + end + + context 'accessed from whitelisted ip' do + before do + stub_remote_addr(whitelisted_ip) + end + + it_behaves_like 'endpoint not querying database' + it_behaves_like 'endpoint responding with readiness data' + + context 'when requesting all checks' do + before do + params.merge!(all: true) + end + + it_behaves_like 'endpoint querying database' + end + end + + context 'accessed from not whitelisted ip' do + before do + stub_remote_addr(not_whitelisted_ip) + end + + it_behaves_like 'endpoint not querying database' + it_behaves_like 'endpoint not found' + end + + context 'accessed with valid token' do + context 'token passed in request header' do + let(:headers) { { TOKEN: token } } + + it_behaves_like 'endpoint responding with readiness data' + it_behaves_like 'endpoint querying database' + end + + context 'token passed as URL param' do + let(:params) { { token: token } } + + it_behaves_like 'endpoint responding with readiness data' + it_behaves_like 'endpoint querying database' + end + end + end + + describe 'GET /-/liveness' do + subject { get '/-/liveness', params: params, headers: headers } + + shared_context 'endpoint responding with liveness data' do + it 'responds with liveness checks data' do + subject + + expect(json_response).to eq('status' => 'ok') + end + end + + context 'accessed from whitelisted ip' do + before do + stub_remote_addr(whitelisted_ip) + end + + it_behaves_like 'endpoint not querying database' + it_behaves_like 'endpoint responding with liveness data' + end + + context 'accessed from not whitelisted ip' do + before do + stub_remote_addr(not_whitelisted_ip) + end + + it_behaves_like 'endpoint not querying database' + it_behaves_like 'endpoint not found' + + context 'accessed with valid token' do + context 'token passed in request header' do + let(:headers) { { TOKEN: token } } + + it_behaves_like 'endpoint responding with liveness data' + it_behaves_like 'endpoint querying database' + end + + context 'token passed as URL param' do + let(:params) { { token: token } } + + it_behaves_like 'endpoint responding with liveness data' + it_behaves_like 'endpoint querying database' + end + end + end + end + + def stub_remote_addr(ip) + headers.merge!(REMOTE_ADDR: ip) + end +end diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb index 8b2c698fee1..c1f99115612 100644 --- a/spec/requests/jwt_controller_spec.rb +++ b/spec/requests/jwt_controller_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe JwtController do diff --git a/spec/requests/lfs_locks_api_spec.rb b/spec/requests/lfs_locks_api_spec.rb index 11436e5cd0c..41f54162266 100644 --- a/spec/requests/lfs_locks_api_spec.rb +++ b/spec/requests/lfs_locks_api_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Git LFS File Locking API' do diff --git a/spec/requests/oauth_tokens_spec.rb b/spec/requests/oauth_tokens_spec.rb index 3873e754060..bb1c25d686e 100644 --- a/spec/requests/oauth_tokens_spec.rb +++ b/spec/requests/oauth_tokens_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'OAuth Tokens requests' do diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index dfa17c5ff27..bac1a4e18c8 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'OpenID Connect requests' do diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index 25390f8a23e..93a1aafde23 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'cycle analytics events' do @@ -48,7 +50,7 @@ describe 'cycle analytics events' do expect(json_response['events'].first['iid']).to eq(first_mr_iid) end - it 'lists the test events' do + it 'lists the test events', :sidekiq_might_not_need_inline do get project_cycle_analytics_test_path(project, format: :json) expect(json_response['events']).not_to be_empty @@ -64,14 +66,14 @@ describe 'cycle analytics events' do expect(json_response['events'].first['iid']).to eq(first_mr_iid) end - it 'lists the staging events' do + it 'lists the staging events', :sidekiq_might_not_need_inline do get project_cycle_analytics_staging_path(project, format: :json) expect(json_response['events']).not_to be_empty expect(json_response['events'].first['date']).not_to be_empty end - it 'lists the production events' do + it 'lists the production events', :sidekiq_might_not_need_inline do get project_cycle_analytics_production_path(project, format: :json) first_issue_iid = project.issues.sort_by_attribute(:created_desc).pluck(:iid).first.to_s @@ -81,7 +83,7 @@ describe 'cycle analytics events' do end context 'specific branch' do - it 'lists the test events' do + it 'lists the test events', :sidekiq_might_not_need_inline do branch = project.merge_requests.first.source_branch get project_cycle_analytics_test_path(project, format: :json, branch: branch) diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index ca8720cd414..4d5055a7e27 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Rack Attack global throttles' do @@ -20,6 +22,7 @@ describe 'Rack Attack global throttles' do } end + let(:request_method) { 'GET' } let(:requests_per_period) { 1 } let(:period_in_seconds) { 10000 } let(:period) { period_in_seconds.seconds } @@ -81,7 +84,7 @@ describe 'Rack Attack global throttles' do expect(response).to have_http_status 200 end - expect_any_instance_of(Rack::Attack::Request).to receive(:ip).and_return('1.2.3.4') + expect_any_instance_of(Rack::Attack::Request).to receive(:ip).at_least(:once).and_return('1.2.3.4') # would be over limit for the same IP get url_that_does_not_require_authentication @@ -141,15 +144,15 @@ describe 'Rack Attack global throttles' do let(:api_partial_url) { '/todos' } context 'with the token in the query string' do - let(:get_args) { [api(api_partial_url, personal_access_token: token)] } - let(:other_user_get_args) { [api(api_partial_url, personal_access_token: other_user_token)] } + let(:request_args) { [api(api_partial_url, personal_access_token: token)] } + let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token)] } it_behaves_like 'rate-limited token-authenticated requests' end context 'with the token in the headers' do - let(:get_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) } - let(:other_user_get_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) } + let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) } + let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) } it_behaves_like 'rate-limited token-authenticated requests' end @@ -168,15 +171,15 @@ describe 'Rack Attack global throttles' do let(:api_partial_url) { '/todos' } context 'with the token in the query string' do - let(:get_args) { [api(api_partial_url, oauth_access_token: token)] } - let(:other_user_get_args) { [api(api_partial_url, oauth_access_token: other_user_token)] } + let(:request_args) { [api(api_partial_url, oauth_access_token: token)] } + let(:other_user_request_args) { [api(api_partial_url, oauth_access_token: other_user_token)] } it_behaves_like 'rate-limited token-authenticated requests' end context 'with the token in the headers' do - let(:get_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) } - let(:other_user_get_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) } + let(:request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(token)) } + let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, oauth_token_headers(other_user_token)) } it_behaves_like 'rate-limited token-authenticated requests' end @@ -188,8 +191,8 @@ describe 'Rack Attack global throttles' do let(:throttle_setting_prefix) { 'throttle_authenticated_web' } context 'with the token in the query string' do - let(:get_args) { [rss_url(user), params: nil] } - let(:other_user_get_args) { [rss_url(other_user), params: nil] } + let(:request_args) { [rss_url(user), params: nil] } + let(:other_user_request_args) { [rss_url(other_user), params: nil] } it_behaves_like 'rate-limited token-authenticated requests' end @@ -204,10 +207,13 @@ describe 'Rack Attack global throttles' do end describe 'protected paths' do + let(:request_method) { 'POST' } + context 'unauthenticated requests' do let(:protected_path_that_does_not_require_authentication) do - '/users/confirmation' + '/users/sign_in' end + let(:post_params) { { user: { login: 'username', password: 'password' } } } before do settings_to_set[:throttle_protected_paths_requests_per_period] = requests_per_period # 1 @@ -222,7 +228,7 @@ describe 'Rack Attack global throttles' do it 'allows requests over the rate limit' do (1 + requests_per_period).times do - get protected_path_that_does_not_require_authentication + post protected_path_that_does_not_require_authentication, params: post_params expect(response).to have_http_status 200 end end @@ -236,11 +242,11 @@ describe 'Rack Attack global throttles' do it 'rejects requests over the rate limit' do requests_per_period.times do - get protected_path_that_does_not_require_authentication + post protected_path_that_does_not_require_authentication, params: post_params expect(response).to have_http_status 200 end - expect_rejection { get protected_path_that_does_not_require_authentication } + expect_rejection { post protected_path_that_does_not_require_authentication, params: post_params } end context 'when Omnibus throttle is present' do @@ -251,7 +257,7 @@ describe 'Rack Attack global throttles' do it 'allows requests over the rate limit' do (1 + requests_per_period).times do - get protected_path_that_does_not_require_authentication + post protected_path_that_does_not_require_authentication, params: post_params expect(response).to have_http_status 200 end end @@ -265,11 +271,11 @@ describe 'Rack Attack global throttles' do let(:other_user) { create(:user) } let(:other_user_token) { create(:personal_access_token, user: other_user) } let(:throttle_setting_prefix) { 'throttle_protected_paths' } - let(:api_partial_url) { '/users' } + let(:api_partial_url) { '/user/emails' } let(:protected_paths) do [ - '/api/v4/users' + '/api/v4/user/emails' ] end @@ -279,22 +285,22 @@ describe 'Rack Attack global throttles' do end context 'with the token in the query string' do - let(:get_args) { [api(api_partial_url, personal_access_token: token)] } - let(:other_user_get_args) { [api(api_partial_url, personal_access_token: other_user_token)] } + let(:request_args) { [api(api_partial_url, personal_access_token: token)] } + let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token)] } it_behaves_like 'rate-limited token-authenticated requests' end context 'with the token in the headers' do - let(:get_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) } - let(:other_user_get_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) } + let(:request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(token)) } + let(:other_user_request_args) { api_get_args_with_token_headers(api_partial_url, personal_access_token_headers(other_user_token)) } it_behaves_like 'rate-limited token-authenticated requests' end context 'when Omnibus throttle is present' do - let(:get_args) { [api(api_partial_url, personal_access_token: token)] } - let(:other_user_get_args) { [api(api_partial_url, personal_access_token: other_user_token)] } + let(:request_args) { [api(api_partial_url, personal_access_token: token)] } + let(:other_user_request_args) { [api(api_partial_url, personal_access_token: other_user_token)] } before do settings_to_set[:"#{throttle_setting_prefix}_requests_per_period"] = requests_per_period @@ -308,8 +314,8 @@ describe 'Rack Attack global throttles' do it 'allows requests over the rate limit' do (1 + requests_per_period).times do - get(*get_args) - expect(response).to have_http_status 200 + post(*request_args) + expect(response).not_to have_http_status 429 end end end @@ -318,7 +324,7 @@ describe 'Rack Attack global throttles' do describe 'web requests authenticated with regular login' do let(:throttle_setting_prefix) { 'throttle_protected_paths' } let(:user) { create(:user) } - let(:url_that_requires_authentication) { '/dashboard/snippets' } + let(:url_that_requires_authentication) { '/users/confirmation' } let(:protected_paths) do [ @@ -348,8 +354,8 @@ describe 'Rack Attack global throttles' do it 'allows requests over the rate limit' do (1 + requests_per_period).times do - get url_that_requires_authentication - expect(response).to have_http_status 200 + post url_that_requires_authentication + expect(response).not_to have_http_status 429 end end end diff --git a/spec/requests/request_profiler_spec.rb b/spec/requests/request_profiler_spec.rb index 851affbcf88..36ccfc6b400 100644 --- a/spec/requests/request_profiler_spec.rb +++ b/spec/requests/request_profiler_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe 'Request Profiler' do |