diff options
Diffstat (limited to 'spec/requests')
27 files changed, 1214 insertions, 60 deletions
diff --git a/spec/requests/api/appearance_spec.rb b/spec/requests/api/appearance_spec.rb new file mode 100644 index 00000000000..40fd216f32d --- /dev/null +++ b/spec/requests/api/appearance_spec.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::Appearance, 'Appearance' do + let_it_be(:user) { create(:user) } + let_it_be(:admin) { create(:admin) } + + describe "GET /application/appearance" do + context 'as a non-admin user' do + it "returns 403" do + get api("/application/appearance", user) + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'as an admin user' do + it "returns appearance" do + get api("/application/appearance", admin) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_an Hash + expect(json_response['description']).to eq('') + expect(json_response['email_header_and_footer_enabled']).to be(false) + expect(json_response['favicon']).to be_nil + expect(json_response['footer_message']).to eq('') + expect(json_response['header_logo']).to be_nil + expect(json_response['header_message']).to eq('') + expect(json_response['logo']).to be_nil + expect(json_response['message_background_color']).to eq('#E75E40') + expect(json_response['message_font_color']).to eq('#FFFFFF') + expect(json_response['new_project_guidelines']).to eq('') + expect(json_response['title']).to eq('') + end + end + end + + describe "PUT /application/appearance" do + context 'as a non-admin user' do + it "returns 403" do + put api("/application/appearance", user), params: { title: "Test" } + + expect(response).to have_gitlab_http_status(403) + end + end + + context 'as an admin user' do + context "instance basics" do + it "allows updating the settings" do + put api("/application/appearance", admin), params: { + title: "GitLab Test Instance", + description: "gitlab-test.example.com", + new_project_guidelines: "Please read the FAQs for help." + } + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_an Hash + expect(json_response['description']).to eq('gitlab-test.example.com') + expect(json_response['email_header_and_footer_enabled']).to be(false) + expect(json_response['favicon']).to be_nil + expect(json_response['footer_message']).to eq('') + expect(json_response['header_logo']).to be_nil + expect(json_response['header_message']).to eq('') + expect(json_response['logo']).to be_nil + expect(json_response['message_background_color']).to eq('#E75E40') + expect(json_response['message_font_color']).to eq('#FFFFFF') + expect(json_response['new_project_guidelines']).to eq('Please read the FAQs for help.') + expect(json_response['title']).to eq('GitLab Test Instance') + end + end + + context "system header and footer" do + it "allows updating the settings" do + settings = { + footer_message: "This is a Header", + header_message: "This is a Footer", + message_font_color: "#ffffff", + message_background_color: "#009999", + email_header_and_footer_enabled: true + } + + put api("/application/appearance", admin), params: settings + + expect(response).to have_gitlab_http_status(200) + settings.each do |attribute, value| + expect(Appearance.current.public_send(attribute)).to eq(value) + end + end + + context "fails on invalid color values" do + it "with message_font_color" do + put api("/application/appearance", admin), params: { message_font_color: "No Color" } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']['message_font_color']).to contain_exactly('must be a valid color code') + end + + it "with message_background_color" do + put api("/application/appearance", admin), params: { message_background_color: "#1" } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']['message_background_color']).to contain_exactly('must be a valid color code') + end + end + end + + context "instance logos" do + let_it_be(:appearance) { create(:appearance) } + + it "allows updating the image files" do + put api("/application/appearance", admin), params: { + logo: fixture_file_upload("spec/fixtures/dk.png", "image/png"), + header_logo: fixture_file_upload("spec/fixtures/dk.png", "image/png"), + favicon: fixture_file_upload("spec/fixtures/dk.png", "image/png") + } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['logo']).to eq("/uploads/-/system/appearance/logo/#{appearance.id}/dk.png") + expect(json_response['header_logo']).to eq("/uploads/-/system/appearance/header_logo/#{appearance.id}/dk.png") + expect(json_response['favicon']).to eq("/uploads/-/system/appearance/favicon/#{appearance.id}/dk.png") + end + + context "fails on invalid color images" do + it "with string instead of file" do + put api("/application/appearance", admin), params: { logo: 'not-a-file.png' } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['error']).to eq("logo is invalid") + end + + it "with .svg file instead of .png" do + put api("/application/appearance", admin), params: { favicon: fixture_file_upload("spec/fixtures/logo_sample.svg", "image/svg") } + + expect(response).to have_gitlab_http_status(400) + expect(json_response['message']['favicon']).to contain_exactly("You are not allowed to upload \"svg\" files, allowed types: png, ico") + end + end + end + end + end +end diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb index 3dc8e5749d4..d8fc234cbae 100644 --- a/spec/requests/api/deployments_spec.rb +++ b/spec/requests/api/deployments_spec.rb @@ -11,10 +11,10 @@ describe API::Deployments do end describe 'GET /projects/:id/deployments' do - let(:project) { create(:project) } + let(:project) { create(:project, :repository) } 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) } + let!(:deployment_2) { create(:deployment, :success, project: project, iid: 12, ref: 'master', created_at: 1.day.ago, updated_at: 2.hours.ago) } + let!(:deployment_3) { create(:deployment, :success, project: project, iid: 8, ref: 'master', 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 @@ -40,6 +40,18 @@ describe API::Deployments do end end + context 'with the environment filter specifed' do + it 'returns deployments for the environment' do + get( + api("/projects/#{project.id}/deployments", user), + params: { environment: deployment_1.environment.name } + ) + + expect(json_response.size).to eq(1) + expect(json_response.first['iid']).to eq(deployment_1.iid) + end + end + describe 'ordering' do let(:order_by) { 'iid' } let(:sort) { 'desc' } @@ -343,38 +355,70 @@ describe API::Deployments do end end - context 'prevent N + 1 queries' do - context 'when the endpoint returns multiple records' do - let(:project) { create(:project) } + describe 'GET /projects/:id/deployments/:deployment_id/merge_requests' do + let(:project) { create(:project, :repository) } + let!(:deployment) { create(:deployment, :success, project: project) } - def create_record - create(:deployment, :success, project: project) - end + subject { get api("/projects/#{project.id}/deployments/#{deployment.id}/merge_requests", user) } + + context 'when a user is not a member of the deployment project' do + let(:user) { build(:user) } + + it 'returns a 404 status code' do + subject - def request_with_query_count - ActiveRecord::QueryRecorder.new { trigger_request }.count + expect(response).to have_gitlab_http_status(404) end + end + + context 'when a user member of the deployment project' do + let_it_be(:project2) { create(:project) } + let!(:merge_request1) { create(:merge_request, source_project: project, target_project: project) } + let!(:merge_request2) { create(:merge_request, source_project: project, target_project: project, state: 'closed') } + let!(:merge_request3) { create(:merge_request, source_project: project2, target_project: project2) } + + it 'returns the relevant merge requests linked to a deployment for a project' do + deployment.merge_requests << [merge_request1, merge_request2] - def trigger_request - get api("/projects/#{project.id}/deployments?order_by=updated_at&sort=asc", user) + subject + + expect(response).to have_gitlab_http_status(200) + expect(json_response.map { |d| d['id'] }).to contain_exactly(merge_request1.id, merge_request2.id) end - before do - create_record + context 'when a deployment is not associated to any existing merge requests' do + it 'returns an empty array' do + subject + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to eq([]) + end end + end + end - it 'succeeds' do - trigger_request + context 'prevent N + 1 queries' do + context 'when the endpoint returns multiple records' do + let(:project) { create(:project, :repository) } + let!(:deployment) { create(:deployment, :success, project: project) } - expect(response).to have_gitlab_http_status(200) + subject { get api("/projects/#{project.id}/deployments?order_by=updated_at&sort=asc", user) } + + it 'succeeds', :aggregate_failures do + subject + expect(response).to have_gitlab_http_status(200) expect(json_response.size).to eq(1) end - it 'does not increase the query count' do - expect { create_record }.not_to change { request_with_query_count } + context 'with 10 more records' do + it 'does not increase the query count', :aggregate_failures do + create_list(:deployment, 10, :success, project: project) + + expect { subject }.not_to be_n_plus_1_query - expect(json_response.size).to eq(2) + expect(json_response.size).to eq(11) + end end end end diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb index 68f7d407b54..f37a02e7135 100644 --- a/spec/requests/api/discussions_spec.rb +++ b/spec/requests/api/discussions_spec.rb @@ -49,6 +49,18 @@ describe API::Discussions do it_behaves_like 'discussions API', 'projects', 'merge_requests', 'iid', can_reply_to_individual_notes: true it_behaves_like 'diff discussions API', 'projects', 'merge_requests', 'iid' it_behaves_like 'resolvable discussions API', 'projects', 'merge_requests', 'iid' + + context "when position is for a previous commit on the merge request" do + it "returns a 400 bad request error because the line_code is old" do + # SHA taken from an earlier commit listed in spec/factories/merge_requests.rb + position = diff_note.position.to_h.merge(new_line: 'c1acaa58bbcbc3eafe538cb8274ba387047b69f8') + + post api("/projects/#{project.id}/merge_requests/#{noteable['iid']}/discussions", user), + params: { body: 'hi!', position: position } + + expect(response).to have_gitlab_http_status(400) + end + end end context 'when noteable is a Commit' do diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb index aa273e97209..bdb0ef44038 100644 --- a/spec/requests/api/environments_spec.rb +++ b/spec/requests/api/environments_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' describe API::Environments do let(:user) { create(:user) } let(:non_member) { create(:user) } - let(:project) { create(:project, :private, namespace: user.namespace) } + let(:project) { create(:project, :private, :repository, namespace: user.namespace) } let!(:environment) { create(:environment, project: project) } before do diff --git a/spec/requests/api/error_tracking_spec.rb b/spec/requests/api/error_tracking_spec.rb new file mode 100644 index 00000000000..48ddc7f5a75 --- /dev/null +++ b/spec/requests/api/error_tracking_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe API::ErrorTracking do + describe "GET /projects/:id/error_tracking/settings" do + let(:user) { create(:user) } + let(:setting) { create(:project_error_tracking_setting) } + let(:project) { setting.project } + + def make_request + get api("/projects/#{project.id}/error_tracking/settings", user) + end + + context 'when authenticated as maintainer' do + before do + project.add_maintainer(user) + end + + it 'returns project settings' do + make_request + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to eq( + 'active' => setting.enabled, + 'project_name' => setting.project_name, + 'sentry_external_url' => setting.sentry_external_url, + 'api_url' => setting.api_url + ) + end + end + + context 'without a project setting' do + let(:project) { create(:project) } + + before do + project.add_maintainer(user) + end + + it 'returns 404' do + make_request + + expect(response).to have_gitlab_http_status(:not_found) + expect(json_response['message']) + .to eq('404 Error Tracking Setting Not Found') + end + end + + context 'when authenticated as reporter' do + before do + project.add_reporter(user) + end + + it 'returns 403' do + make_request + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when authenticated as non-member' do + it 'returns 404' do + make_request + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when unauthenticated' do + let(:user) { nil } + + it 'returns 401' do + make_request + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + end +end diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index 9f8d254a00c..240f9a02877 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -8,6 +8,8 @@ describe API::Events do let(:private_project) { create(:project, :private, creator_id: user.id, namespace: user.namespace) } let(:closed_issue) { create(:closed_issue, project: private_project, author: user) } let!(:closed_issue_event) { create(:event, project: private_project, author: user, target: closed_issue, action: Event::CLOSED, created_at: Date.new(2016, 12, 30)) } + let(:closed_issue2) { create(:closed_issue, project: private_project, author: non_member) } + let!(:closed_issue_event2) { create(:event, project: private_project, author: non_member, target: closed_issue2, action: Event::CLOSED, created_at: Date.new(2016, 12, 30)) } describe 'GET /events' do context 'when unauthenticated' do @@ -27,6 +29,19 @@ describe API::Events do expect(json_response).to be_an Array expect(json_response.size).to eq(1) end + + context 'when scope is passed' do + it 'returns all events across projects' do + private_project.add_developer(non_member) + + get api('/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31&scope=all', 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.size).to eq(2) + end + end end context 'when the requesting token has "read_user" scope' do diff --git a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb index 0e8fe4987b9..f80a3401134 100644 --- a/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb @@ -52,8 +52,8 @@ describe 'Mark snippet as spam' do end it 'marks snippet as spam' do - expect_next_instance_of(SpamService) do |instance| - expect(instance).to receive(:mark_as_spam!) + expect_next_instance_of(Spam::MarkAsSpamService) do |instance| + expect(instance).to receive(:execute) end post_graphql_mutation(mutation, current_user: current_user) diff --git a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb index d10380dab3a..664206dec29 100644 --- a/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb +++ b/spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb @@ -56,6 +56,7 @@ describe 'getting a detailed sentry error' do expect(error_data['status']).to eql sentry_detailed_error.status.upcase expect(error_data['firstSeen']).to eql sentry_detailed_error.first_seen expect(error_data['lastSeen']).to eql sentry_detailed_error.last_seen + expect(error_data['gitlabCommit']).to be nil end it 'is expected to return the frequency correctly' do diff --git a/spec/requests/api/graphql/project/grafana_integration_spec.rb b/spec/requests/api/graphql/project/grafana_integration_spec.rb new file mode 100644 index 00000000000..6075efb0cbd --- /dev/null +++ b/spec/requests/api/graphql/project/grafana_integration_spec.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe 'Getting Grafana Integration' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :repository) } + let_it_be(:current_user) { project.owner } + let_it_be(:grafana_integration) { create(:grafana_integration, project: project) } + + let(:fields) do + <<~QUERY + #{all_graphql_fields_for('GrafanaIntegration'.classify)} + QUERY + end + + let(:query) do + graphql_query_for( + 'project', + { 'fullPath' => project.full_path }, + query_graphql_field('grafanaIntegration', {}, fields) + ) + end + + context 'with grafana integration data' do + let(:integration_data) { graphql_data['project']['grafanaIntegration'] } + + context 'without project admin permissions' do + let(:user) { create(:user) } + + before do + project.add_developer(user) + post_graphql(query, current_user: user) + end + + it_behaves_like 'a working graphql query' + + it { expect(integration_data).to be nil } + end + + context 'with project admin permissions' do + before do + post_graphql(query, current_user: current_user) + end + + it_behaves_like 'a working graphql query' + + it { expect(integration_data['token']).to eql grafana_integration.token } + it { expect(integration_data['grafanaUrl']).to eql grafana_integration.grafana_url } + + it do + expect( + integration_data['createdAt'] + ).to eql grafana_integration.created_at.strftime('%Y-%m-%dT%H:%M:%SZ') + end + + it do + expect( + integration_data['updatedAt'] + ).to eql grafana_integration.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ') + end + end + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index a4f68df928f..35b77832c73 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -358,6 +358,7 @@ describe API::Groups do expect(json_response['two_factor_grace_period']).to eq(group1.two_factor_grace_period) expect(json_response['auto_devops_enabled']).to eq(group1.auto_devops_enabled) expect(json_response['emails_disabled']).to eq(group1.emails_disabled) + expect(json_response['mentions_disabled']).to eq(group1.mentions_disabled) expect(json_response['project_creation_level']).to eq('maintainer') expect(json_response['subgroup_creation_level']).to eq('maintainer') expect(json_response['web_url']).to eq(group1.web_url) @@ -556,6 +557,7 @@ describe API::Groups do expect(json_response['two_factor_grace_period']).to eq(48) expect(json_response['auto_devops_enabled']).to eq(nil) expect(json_response['emails_disabled']).to eq(nil) + expect(json_response['mentions_disabled']).to eq(nil) expect(json_response['project_creation_level']).to eq("noone") expect(json_response['subgroup_creation_level']).to eq("maintainer") expect(json_response['request_access_enabled']).to eq(true) diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index ecbb81294a0..12e6e7c7a09 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -326,7 +326,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-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true') + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true') expect(user.reload.last_activity_on).to eql(Date.today) end end @@ -346,7 +346,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-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true') + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true') expect(user.reload.last_activity_on).to be_nil end end @@ -389,6 +389,12 @@ describe API::Internal::Base do end end end + + it_behaves_like 'storing arguments in the application context' do + let(:expected_params) { { user: key.user.username, project: project.full_path } } + + subject { push(key, project) } + end end context "access denied" do @@ -588,7 +594,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-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true') + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-inforef-uploadpack-cache' => 'true', 'gitaly-feature-get-tag-messages-go' => 'true', 'gitaly-feature-filter-shas-with-signatures-go' => 'true', 'gitaly-feature-cache-invalidator' => 'true') end end @@ -885,6 +891,12 @@ describe API::Internal::Base do post api('/internal/post_receive'), params: valid_params end + it_behaves_like 'storing arguments in the application context' do + let(:expected_params) { { user: user.username, project: project.full_path } } + + subject { post api('/internal/post_receive'), params: valid_params } + end + context 'when there are merge_request push options' do before do valid_params[:push_options] = ['merge_request.create'] @@ -1000,6 +1012,22 @@ describe API::Internal::Base do it 'does not try to notify that project moved' do allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(nil) + expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message) + + post api('/internal/post_receive'), params: valid_params + + expect(response).to have_gitlab_http_status(200) + end + end + + context 'when project is nil' do + let(:gl_repository) { 'project-foo' } + + it 'does not try to notify that project moved' do + allow(Gitlab::GlRepository).to receive(:parse).and_return([nil, Gitlab::GlRepository::PROJECT]) + + expect(Gitlab::Checks::ProjectMoved).not_to receive(:fetch_message) + post api('/internal/post_receive'), params: valid_params expect(response).to have_gitlab_http_status(200) diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb index 3ee08758f99..ef63902ffd7 100644 --- a/spec/requests/api/issues/get_group_issues_spec.rb +++ b/spec/requests/api/issues/get_group_issues_spec.rb @@ -688,5 +688,32 @@ describe API::Issues do end end end + + context "#to_reference" do + it 'exposes reference path in context of group' do + get api(base_url, user) + + expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}") + expect(json_response.first['references']['relative']).to eq("#{group_closed_issue.project.path}##{group_closed_issue.iid}") + expect(json_response.first['references']['full']).to eq("#{group_closed_issue.project.full_path}##{group_closed_issue.iid}") + end + + context 'referencing from parent group' do + let(:parent_group) { create(:group) } + + before do + group.update(parent_id: parent_group.id) + group_closed_issue.reload + end + + it 'exposes reference path in context of parent group' do + get api("/groups/#{parent_group.id}/issues") + + expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}") + expect(json_response.first['references']['relative']).to eq("#{group_closed_issue.project.full_path}##{group_closed_issue.iid}") + expect(json_response.first['references']['full']).to eq("#{group_closed_issue.project.full_path}##{group_closed_issue.iid}") + end + end + end end end diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb index 59aeb91edd2..e031cc9b0c6 100644 --- a/spec/requests/api/issues/get_project_issues_spec.rb +++ b/spec/requests/api/issues/get_project_issues_spec.rb @@ -299,6 +299,26 @@ describe API::Issues do it_behaves_like 'labeled issues with labels and label_name params' end + context 'with_labels_details' do + let(:label_b) { create(:label, title: 'foo', project: project) } + let(:label_c) { create(:label, title: 'bar', project: project) } + + it 'avoids N+1 queries' do + control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) do + get api("/projects/#{project.id}/issues?with_labels_details=true", user) + end.count + + new_issue = create(:issue, project: project) + create(:label_link, label: label, target: new_issue) + create(:label_link, label: label_b, target: new_issue) + create(:label_link, label: label_c, target: new_issue) + + expect do + get api("/projects/#{project.id}/issues?with_labels_details=true", user) + end.not_to exceed_all_query_limit(control_count) + end + end + it 'returns issues matching given search string for title' do get api("#{base_url}/issues?search=#{issue.title}", user) diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb index 50a0a80b542..a3538aa98b1 100644 --- a/spec/requests/api/issues/issues_spec.rb +++ b/spec/requests/api/issues/issues_spec.rb @@ -805,6 +805,17 @@ describe API::Issues do end end + describe 'GET /projects/:id/issues/:issue_iid' do + it 'exposes full reference path' do + get api("/projects/#{project.id}/issues/#{issue.iid}", user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['references']['short']).to eq("##{issue.iid}") + expect(json_response['references']['relative']).to eq("##{issue.iid}") + expect(json_response['references']['full']).to eq("#{project.parent.path}/#{project.path}##{issue.iid}") + end + end + describe 'DELETE /projects/:id/issues/:issue_iid' do it 'rejects a non member from deleting an issue' do delete api("/projects/#{project.id}/issues/#{issue.iid}", non_member) diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb index e9f678d164e..67404cf10df 100644 --- a/spec/requests/api/issues/post_projects_issues_spec.rb +++ b/spec/requests/api/issues/post_projects_issues_spec.rb @@ -160,6 +160,16 @@ describe API::Issues do expect(json_response['iid']).not_to eq 9001 end end + + context 'when an issue with the same IID exists on database' do + it 'returns 409' do + post api("/projects/#{project.id}/issues", admin), + params: { title: 'new issue', iid: issue.iid } + + expect(response).to have_gitlab_http_status(409) + expect(json_response['message']).to eq 'Duplicated issue' + end + end end it 'creates a new project issue' do diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index 82bf607b911..1e1099ebcb6 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -244,7 +244,7 @@ describe API::Jobs do get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), params: query end.count - 3.times { create(:ci_build, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) } + create_list(:ci_build, 3, :trace_artifact, :artifacts, :test_reports, pipeline: pipeline) expect do get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), params: query diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb index f7da1abcfdf..c743cb3f633 100644 --- a/spec/requests/api/keys_spec.rb +++ b/spec/requests/api/keys_spec.rb @@ -106,6 +106,36 @@ describe API::Keys do expect(json_response['user']['is_admin']).to be_nil end + + context 'when searching a DeployKey' do + let(:project) { create(:project, :repository) } + let(:project_push) { create(:project, :repository) } + let(:deploy_key) { create(:deploy_key) } + + let!(:deploy_keys_project) do + create(:deploy_keys_project, project: project, deploy_key: deploy_key) + end + + let!(:deploy_keys_project_push) do + create(:deploy_keys_project, project: project_push, deploy_key: deploy_key, can_push: true) + end + + it 'returns user and projects if SSH sha256 fingerprint for DeployKey found' do + user.keys << deploy_key + + get api("/keys?fingerprint=#{URI.encode_www_form_component("SHA256:" + deploy_key.fingerprint_sha256)}", admin) + + expect(response).to have_gitlab_http_status(200) + expect(json_response['title']).to eq(deploy_key.title) + expect(json_response['user']['id']).to eq(user.id) + + expect(json_response['deploy_keys_projects'].count).to eq(2) + expect(json_response['deploy_keys_projects'][0]['project_id']).to eq(deploy_keys_project.project.id) + expect(json_response['deploy_keys_projects'][0]['can_push']).to eq(deploy_keys_project.can_push) + expect(json_response['deploy_keys_projects'][1]['project_id']).to eq(deploy_keys_project_push.project.id) + expect(json_response['deploy_keys_projects'][1]['can_push']).to eq(deploy_keys_project_push.can_push) + end + end end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index e5ad1a6378e..ae0596bea98 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -88,6 +88,34 @@ describe API::MergeRequests do expect(json_response.first['merge_commit_sha']).not_to be_nil expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha) end + + context 'with labels_details' do + it 'returns labels with details' do + path = endpoint_path + "?with_labels_details=true" + + get api(path, user) + + expect(response).to have_gitlab_http_status(200) + expect(json_response.last['labels'].pluck('name')).to eq([label2.title, label.title]) + expect(json_response.last['labels'].first).to match_schema('/public_api/v4/label_basic') + end + + it 'avoids N+1 queries' do + path = endpoint_path + "?with_labels_details=true" + + control = ActiveRecord::QueryRecorder.new do + get api(path, user) + end.count + + mr = create(:merge_request) + create(:label_link, label: label, target: mr) + create(:label_link, label: label2, target: mr) + + expect do + get api(path, user) + end.not_to exceed_query_limit(control) + end + end end it 'returns an array of all merge_requests using simple mode' do @@ -736,6 +764,33 @@ describe API::MergeRequests do it_behaves_like 'merge requests list' end + + context "#to_reference" do + it 'exposes reference path in context of group' do + get api("/groups/#{group.id}/merge_requests", user) + + expect(json_response.first['references']['short']).to eq("!#{merge_request_merged.iid}") + expect(json_response.first['references']['relative']).to eq("#{merge_request_merged.target_project.path}!#{merge_request_merged.iid}") + expect(json_response.first['references']['full']).to eq("#{merge_request_merged.target_project.full_path}!#{merge_request_merged.iid}") + end + + context 'referencing from parent group' do + let(:parent_group) { create(:group) } + + before do + group.update(parent_id: parent_group.id) + merge_request_merged.reload + end + + it 'exposes reference path in context of parent group' do + get api("/groups/#{parent_group.id}/merge_requests") + + expect(json_response.first['references']['short']).to eq("!#{merge_request_merged.iid}") + expect(json_response.first['references']['relative']).to eq("#{merge_request_merged.target_project.full_path}!#{merge_request_merged.iid}") + expect(json_response.first['references']['full']).to eq("#{merge_request_merged.target_project.full_path}!#{merge_request_merged.iid}") + end + end + end end describe "GET /projects/:id/merge_requests/:merge_request_iid" do @@ -783,6 +838,9 @@ describe API::MergeRequests do 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 + expect(json_response['references']['short']).to eq("!#{merge_request.iid}") + expect(json_response['references']['relative']).to eq("!#{merge_request.iid}") + expect(json_response['references']['full']).to eq("#{merge_request.target_project.full_path}!#{merge_request.iid}") end it 'exposes description and title html when render_html is true' do @@ -1491,7 +1549,7 @@ describe API::MergeRequests do end end - describe "PUT /projects/:id/merge_requests/:merge_request_iid/merge" do + describe "PUT /projects/:id/merge_requests/:merge_request_iid/merge", :clean_gitlab_redis_cache do let(:pipeline) { create(:ci_pipeline) } it "returns merge_request in case of success" do @@ -1579,6 +1637,15 @@ describe API::MergeRequests do expect(merge_request.reload.state).to eq('opened') end + it 'merges if the head pipeline already succeeded and `merge_when_pipeline_succeeds` is passed' do + create(:ci_pipeline, :success, sha: merge_request.diff_head_sha, merge_requests_as_head_pipeline: [merge_request]) + + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), params: { merge_when_pipeline_succeeds: true } + + expect(response).to have_gitlab_http_status(200) + expect(json_response['state']).to eq('merged') + end + it "enables merge when pipeline succeeds if the pipeline is active" do allow_any_instance_of(MergeRequest).to receive_messages(head_pipeline: pipeline, actual_head_pipeline: pipeline) allow(pipeline).to receive(:active?).and_return(true) @@ -2155,16 +2222,34 @@ describe API::MergeRequests do end describe 'PUT :id/merge_requests/:merge_request_iid/rebase' do - it 'enqueues a rebase of the merge request against the target branch' do - Sidekiq::Testing.fake! do - put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user) + context 'when rebase can be performed' do + it 'enqueues a rebase of the merge request against the target branch' do + Sidekiq::Testing.fake! do + expect do + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user) + end.to change { RebaseWorker.jobs.size }.by(1) + end + + expect(response).to have_gitlab_http_status(202) + expect(merge_request.reload).to be_rebase_in_progress + expect(json_response['rebase_in_progress']).to be(true) end - expect(response).to have_gitlab_http_status(202) - expect(RebaseWorker.jobs.size).to eq(1) + context 'when skip_ci parameter is set' do + it 'enqueues a rebase of the merge request with skip_ci flag set' do + expect(RebaseWorker).to receive(:perform_async).with(merge_request.id, user.id, true).and_call_original - expect(merge_request.reload).to be_rebase_in_progress - expect(json_response['rebase_in_progress']).to be(true) + Sidekiq::Testing.fake! do + expect do + put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/rebase", user), params: { skip_ci: true } + end.to change { RebaseWorker.jobs.size }.by(1) + end + + expect(response).to have_gitlab_http_status(202) + expect(merge_request.reload).to be_rebase_in_progress + expect(json_response['rebase_in_progress']).to be(true) + end + end end it 'returns 403 if the user cannot push to the branch' do @@ -2193,7 +2278,7 @@ describe API::MergeRequests do 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) + expect(json_response['message']).to eq('Failed to enqueue the rebase operation, possibly due to a long-lived transaction. Try again later.') end end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index cc2038a7245..b4416344ecf 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -101,6 +101,75 @@ describe API::Notes do expect(json_response.first['body']).to eq(cross_reference_note.note) end end + + context "activity filters" do + let!(:user_reference_note) do + create :note, + noteable: ext_issue, project: ext_proj, + note: "Hello there general!", + system: false + end + + let(:test_url) {"/projects/#{ext_proj.id}/issues/#{ext_issue.iid}/notes"} + + shared_examples 'a notes request' do + it 'is a note array response' do + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + end + end + + context "when not provided" do + let(:count) { 2 } + + before do + get api(test_url, private_user) + end + + it_behaves_like 'a notes request' + + it 'returns all the notes' do + expect(json_response.count).to eq(count) + end + end + + context "when all_notes provided" do + let(:count) { 2 } + + before do + get api(test_url + "?activity_filter=all_notes", private_user) + end + + it_behaves_like 'a notes request' + + it 'returns all the notes' do + expect(json_response.count).to eq(count) + end + end + + context "when provided" do + using RSpec::Parameterized::TableSyntax + + where(:filter, :count, :system_notable) do + "only_comments" | 1 | false + "only_activity" | 1 | true + end + + with_them do + before do + get api(test_url + "?activity_filter=#{filter}", private_user) + end + + it_behaves_like 'a notes request' + + it "properly filters the returned notables" do + expect(json_response.count).to eq(count) + expect(json_response.first["system"]).to be system_notable + end + end + end + end end describe "GET /projects/:id/noteable/:noteable_id/notes/:note_id" do diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index a9d570b5696..75e3013d362 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -254,9 +254,7 @@ describe API::Pipelines do context 'when order_by and sort are specified' do context 'when order_by user_id' do before do - 3.times do - create(:ci_pipeline, project: project, user: create(:user)) - end + create_list(:ci_pipeline, 3, project: project, user: create(:user)) end context 'when sort parameter is valid' do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 9af4f484f99..fce49d0248c 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -570,6 +570,102 @@ describe API::Projects do let(:projects) { Project.all } end end + + context 'with keyset pagination' do + let(:current_user) { user } + let(:projects) { [public_project, project, project2, project3] } + + context 'headers and records' do + let(:params) { { pagination: 'keyset', order_by: :id, sort: :asc, per_page: 1 } } + + it 'includes a pagination header with link to the next page' do + get api('/projects', current_user), params: params + + expect(response.header).to include('Links') + expect(response.header['Links']).to include('pagination=keyset') + expect(response.header['Links']).to include("id_after=#{public_project.id}") + end + + it 'contains only the first project with per_page = 1' do + get api('/projects', current_user), params: params + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_an Array + expect(json_response.map { |p| p['id'] }).to contain_exactly(public_project.id) + end + + it 'still includes a link if the end has reached and there is no more data after this page' do + get api('/projects', current_user), params: params.merge(id_after: project2.id) + + expect(response.header).to include('Links') + expect(response.header['Links']).to include('pagination=keyset') + expect(response.header['Links']).to include("id_after=#{project3.id}") + end + + it 'does not include a next link when the page does not have any records' do + get api('/projects', current_user), params: params.merge(id_after: Project.maximum(:id)) + + expect(response.header).not_to include('Links') + end + + it 'returns an empty array when the page does not have any records' do + get api('/projects', current_user), params: params.merge(id_after: Project.maximum(:id)) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to eq([]) + end + + it 'responds with 501 if order_by is different from id' do + get api('/projects', current_user), params: params.merge(order_by: :created_at) + + expect(response).to have_gitlab_http_status(405) + end + end + + context 'with descending sorting' do + let(:params) { { pagination: 'keyset', order_by: :id, sort: :desc, per_page: 1 } } + + it 'includes a pagination header with link to the next page' do + get api('/projects', current_user), params: params + + expect(response.header).to include('Links') + expect(response.header['Links']).to include('pagination=keyset') + expect(response.header['Links']).to include("id_before=#{project3.id}") + end + + it 'contains only the last project with per_page = 1' do + get api('/projects', current_user), params: params + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_an Array + expect(json_response.map { |p| p['id'] }).to contain_exactly(project3.id) + end + end + + context 'retrieving the full relation' do + let(:params) { { pagination: 'keyset', order_by: :id, sort: :desc, per_page: 2 } } + + it 'returns all projects' do + url = '/projects' + requests = 0 + ids = [] + + while url && requests <= 5 # circuit breaker + requests += 1 + get api(url, current_user), params: params + + links = response.header['Links'] + url = links&.match(/<[^>]+(\/projects\?[^>]+)>; rel="next"/) do |match| + match[1] + end + + ids += JSON.parse(response.body).map { |p| p['id'] } + end + + expect(ids).to contain_exactly(*projects.map(&:id)) + end + end + end end describe 'POST /projects' do @@ -635,6 +731,7 @@ describe API::Projects do wiki_enabled: false, resolve_outdated_diff_discussions: false, remove_source_branch_after_merge: true, + autoclose_referenced_issues: true, only_allow_merge_if_pipeline_succeeds: false, request_access_enabled: true, only_allow_merge_if_all_discussions_are_resolved: false, @@ -807,6 +904,22 @@ describe API::Projects do expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy end + it 'sets a project as enabling auto close referenced issues' do + project = attributes_for(:project, autoclose_referenced_issues: true) + + post api('/projects', user), params: project + + expect(json_response['autoclose_referenced_issues']).to be_truthy + end + + it 'sets a project as disabling auto close referenced issues' do + project = attributes_for(:project, autoclose_referenced_issues: false) + + post api('/projects', user), params: project + + expect(json_response['autoclose_referenced_issues']).to be_falsey + end + it 'sets the merge method of a project to rebase merge' do project = attributes_for(:project, merge_method: 'rebase_merge') @@ -1626,6 +1739,14 @@ describe API::Projects do end end end + + it_behaves_like 'storing arguments in the application context' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, :public) } + let(:expected_params) { { user: user.username, project: project.full_path } } + + subject { get api("/projects/#{project.id}", user) } + end end describe 'GET /projects/:id/users' do @@ -2226,6 +2347,22 @@ describe API::Projects do put api("/projects/#{project3.id}", user4), params: project_param expect(response).to have_gitlab_http_status(403) end + + it 'updates container_expiration_policy' do + project_param = { + container_expiration_policy_attributes: { + cadence: '1month', + keep_n: 1 + } + } + + put api("/projects/#{project3.id}", user4), params: project_param + + expect(response).to have_gitlab_http_status(200) + + expect(json_response['container_expiration_policy']['cadence']).to eq('1month') + expect(json_response['container_expiration_policy']['keep_n']).to eq(1) + end end context 'when authenticated as project developer' do @@ -2721,6 +2858,20 @@ describe API::Projects do expect(json_response['message']).to eq('401 Unauthorized') end end + + context 'forking disabled' do + before do + project.project_feature.update_attribute( + :forking_access_level, ProjectFeature::DISABLED) + end + + it 'denies project to be forked' do + post api("/projects/#{project.id}/fork", admin) + + expect(response).to have_gitlab_http_status(409) + expect(json_response['message']['forked_from_project_id']).to eq(['is forbidden']) + end + end end describe 'POST /projects/:id/housekeeping' do diff --git a/spec/requests/api/remote_mirrors_spec.rb b/spec/requests/api/remote_mirrors_spec.rb index c5ba9bd223e..065d9c7ca5b 100644 --- a/spec/requests/api/remote_mirrors_spec.rb +++ b/spec/requests/api/remote_mirrors_spec.rb @@ -5,14 +5,13 @@ require 'spec_helper' describe API::RemoteMirrors do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project, :repository, :remote_mirror) } + let_it_be(:developer) { create(:user) { |u| project.add_developer(u) } } describe 'GET /projects/:id/remote_mirrors' do let(:route) { "/projects/#{project.id}/remote_mirrors" } it 'requires `admin_remote_mirror` permission' do - project.add_developer(user) - - get api(route, user) + get api(route, developer) expect(response).to have_gitlab_http_status(:unauthorized) end @@ -26,6 +25,7 @@ describe API::RemoteMirrors do expect(response).to match_response_schema('remote_mirrors') end + # TODO: Remove flag: https://gitlab.com/gitlab-org/gitlab/issues/38121 context 'with the `remote_mirrors_api` feature disabled' do before do stub_feature_flags(remote_mirrors_api: false) @@ -38,4 +38,41 @@ describe API::RemoteMirrors do end end end + + describe 'PUT /projects/:id/remote_mirrors/:mirror_id' do + let(:route) { ->(id) { "/projects/#{project.id}/remote_mirrors/#{id}" } } + let(:mirror) { project.remote_mirrors.first } + + it 'requires `admin_remote_mirror` permission' do + put api(route[mirror.id], developer) + + expect(response).to have_gitlab_http_status(:unauthorized) + end + + it 'updates a remote mirror' do + project.add_maintainer(user) + + put api(route[mirror.id], user), params: { + enabled: '0', + only_protected_branches: 'true' + } + + expect(response).to have_gitlab_http_status(:success) + expect(json_response['enabled']).to eq(false) + expect(json_response['only_protected_branches']).to eq(true) + end + + # TODO: Remove flag: https://gitlab.com/gitlab-org/gitlab/issues/38121 + context 'with the `remote_mirrors_api` feature disabled' do + before do + stub_feature_flags(remote_mirrors_api: false) + end + + it 'responds with `not_found`' do + put api(route[mirror.id], user) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end end diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index cc6cadb190a..a313f75e3ec 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -1154,6 +1154,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(job.reload.trace.raw).to eq 'BUILD TRACE appended' expect(response.header).to have_key 'Range' expect(response.header).to have_key 'Job-Status' + expect(response.header).to have_key 'X-GitLab-Trace-Update-Interval' end context 'when job has been updated recently' do @@ -1291,6 +1292,41 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(response.header['Job-Status']).to eq 'canceled' end end + + context 'when build trace is being watched' do + before do + job.trace.being_watched! + end + + it 'returns X-GitLab-Trace-Update-Interval as 3' do + patch_the_trace + + expect(response.status).to eq 202 + expect(response.header['X-GitLab-Trace-Update-Interval']).to eq('3') + end + end + + context 'when build trace is not being watched' do + it 'returns X-GitLab-Trace-Update-Interval as 30' do + patch_the_trace + + expect(response.status).to eq 202 + expect(response.header['X-GitLab-Trace-Update-Interval']).to eq('30') + end + end + + context 'when feature flag runner_job_trace_update_interval_header is disabled' do + before do + stub_feature_flags(runner_job_trace_update_interval_header: { enabled: false }) + end + + it 'does not return X-GitLab-Trace-Update-Interval header' do + patch_the_trace + + expect(response.status).to eq 202 + expect(response.header).not_to have_key 'X-GitLab-Trace-Update-Interval' + end + end end context 'when Runner makes a force-patch' do @@ -1792,6 +1828,58 @@ describe API::Runner, :clean_gitlab_redis_shared_state do end end end + + context 'when artifact_type is metrics_referee' do + context 'when artifact_format is gzip' do + let(:file_upload) { fixture_file_upload('spec/fixtures/referees/metrics_referee.json.gz') } + let(:params) { { artifact_type: :metrics_referee, artifact_format: :gzip } } + + it 'stores metrics_referee data' do + upload_artifacts(file_upload, headers_with_token, params) + + expect(response).to have_gitlab_http_status(201) + expect(job.reload.job_artifacts_metrics_referee).not_to be_nil + end + end + + context 'when artifact_format is raw' do + let(:file_upload) { fixture_file_upload('spec/fixtures/referees/metrics_referee.json.gz') } + let(:params) { { artifact_type: :metrics_referee, artifact_format: :raw } } + + it 'returns an error' do + upload_artifacts(file_upload, headers_with_token, params) + + expect(response).to have_gitlab_http_status(400) + expect(job.reload.job_artifacts_metrics_referee).to be_nil + end + end + end + + context 'when artifact_type is network_referee' do + context 'when artifact_format is gzip' do + let(:file_upload) { fixture_file_upload('spec/fixtures/referees/network_referee.json.gz') } + let(:params) { { artifact_type: :network_referee, artifact_format: :gzip } } + + it 'stores network_referee data' do + upload_artifacts(file_upload, headers_with_token, params) + + expect(response).to have_gitlab_http_status(201) + expect(job.reload.job_artifacts_network_referee).not_to be_nil + end + end + + context 'when artifact_format is raw' do + let(:file_upload) { fixture_file_upload('spec/fixtures/referees/network_referee.json.gz') } + let(:params) { { artifact_type: :network_referee, artifact_format: :raw } } + + it 'returns an error' do + upload_artifacts(file_upload, headers_with_token, params) + + expect(response).to have_gitlab_http_status(400) + expect(job.reload.job_artifacts_network_referee).to be_nil + end + end + end end context 'when artifacts are being stored outside of tmp path' do diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index 7c7620389b4..08f58387bf8 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -10,6 +10,38 @@ describe API::Services do create(:project, creator_id: user.id, namespace: user.namespace) end + describe "GET /projects/:id/services" do + it 'returns authentication error when unauthenticated' do + get api("/projects/#{project.id}/services") + + expect(response).to have_gitlab_http_status(401) + end + + it "returns error when authenticated but user is not a project owner" do + project.add_developer(user2) + get api("/projects/#{project.id}/services", user2) + + expect(response).to have_gitlab_http_status(403) + end + + context 'project with services' do + let!(:active_service) { create(:emails_on_push_service, project: project, active: true) } + let!(:service) { create(:custom_issue_tracker_service, project: project, active: false) } + + it "returns a list of all active services" do + get api("/projects/#{project.id}/services", user) + + aggregate_failures 'expect successful response with all active services' do + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_an Array + expect(json_response.count).to eq(1) + expect(json_response.first['slug']).to eq('emails-on-push') + expect(response).to match_response_schema('public_api/v4/services') + end + end + end + end + Service.available_services_names.each do |service| describe "PUT /projects/:id/services/#{service.dasherize}" do include_context service @@ -30,6 +62,7 @@ describe API::Services do put api("/projects/#{project.id}/services/#{dashed_service}?#{query_strings}", user), params: service_attrs expect(response).to have_gitlab_http_status(200) + expect(json_response['slug']).to eq(dashed_service) events.each do |event| next if event == "foo" diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index fd1104fa978..d54d112cd9f 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -87,22 +87,6 @@ describe API::Triggers do expect(pipeline.variables.map { |v| { v.key => v.value } }.last).to eq(variables) end end - - context 'when legacy trigger' do - before do - trigger.update(owner: nil) - end - - it 'creates pipeline' do - post api("/projects/#{project.id}/trigger/pipeline"), params: options.merge(ref: 'master') - - expect(response).to have_gitlab_http_status(201) - expect(json_response).to include('id' => pipeline.id) - pipeline.builds.reload - expect(pipeline.builds.pending.size).to eq(2) - expect(pipeline.builds.size).to eq(5) - end - end end context 'when triggering a pipeline from a trigger token' do diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb index 310caa92eb9..2e0b7a30480 100644 --- a/spec/requests/api/wikis_spec.rb +++ b/spec/requests/api/wikis_spec.rb @@ -115,7 +115,7 @@ describe API::Wikis do end [:title, :content, :format].each do |part| - it "it updates with wiki with missing #{part}" do + it "updates with wiki with missing #{part}" do payload.delete(part) put(api(url, user), params: payload) diff --git a/spec/requests/self_monitoring_project_spec.rb b/spec/requests/self_monitoring_project_spec.rb new file mode 100644 index 00000000000..d562a34aec4 --- /dev/null +++ b/spec/requests/self_monitoring_project_spec.rb @@ -0,0 +1,224 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Self-Monitoring project requests' do + let(:admin) { create(:admin) } + + describe 'POST #create_self_monitoring_project' do + let(:worker_class) { SelfMonitoringProjectCreateWorker } + + subject { post create_self_monitoring_project_admin_application_settings_path } + + it_behaves_like 'not accessible to non-admin users' + + context 'with admin user' do + before do + login_as(admin) + end + + context 'with feature flag disabled' do + it_behaves_like 'not accessible if feature flag is disabled' + end + + context 'with feature flag enabled' do + let(:status_api) { status_create_self_monitoring_project_admin_application_settings_path } + + it_behaves_like 'triggers async worker, returns sidekiq job_id with response accepted' + end + end + end + + describe 'GET #status_create_self_monitoring_project' do + let(:worker_class) { SelfMonitoringProjectCreateWorker } + let(:job_id) { 'job_id' } + + subject do + get status_create_self_monitoring_project_admin_application_settings_path, + params: { job_id: job_id } + end + + it_behaves_like 'not accessible to non-admin users' + + context 'with admin user' do + before do + login_as(admin) + end + + context 'with feature flag disabled' do + it_behaves_like 'not accessible if feature flag is disabled' + end + + context 'with feature flag enabled' do + it_behaves_like 'handles invalid job_id' + + context 'when job is in progress' do + before do + allow(worker_class).to receive(:in_progress?) + .with(job_id) + .and_return(true) + end + + it_behaves_like 'sets polling header and returns accepted' do + let(:in_progress_message) { 'Job to create self-monitoring project is in progress' } + end + end + + context 'when self-monitoring project and job do not exist' do + let(:job_id) { nil } + + it 'returns bad_request' do + subject + + aggregate_failures do + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to eq( + 'message' => 'Self-monitoring project does not exist. Please check logs ' \ + 'for any error messages' + ) + end + end + end + + context 'when self-monitoring project exists' do + let(:project) { build(:project) } + + before do + stub_application_setting(instance_administration_project_id: 1) + stub_application_setting(instance_administration_project: project) + end + + it 'does not need job_id' do + get status_create_self_monitoring_project_admin_application_settings_path + + aggregate_failures do + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq( + 'project_id' => 1, + 'project_full_path' => project.full_path + ) + end + end + + it 'returns success with job_id' do + subject + + aggregate_failures do + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq( + 'project_id' => 1, + 'project_full_path' => project.full_path + ) + end + end + end + end + end + end + + describe 'DELETE #delete_self_monitoring_project' do + let(:worker_class) { SelfMonitoringProjectDeleteWorker } + + subject { delete delete_self_monitoring_project_admin_application_settings_path } + + it_behaves_like 'not accessible to non-admin users' + + context 'with admin user' do + before do + login_as(admin) + end + + context 'with feature flag disabled' do + it_behaves_like 'not accessible if feature flag is disabled' + end + + context 'with feature flag enabled' do + let(:status_api) { status_delete_self_monitoring_project_admin_application_settings_path } + + it_behaves_like 'triggers async worker, returns sidekiq job_id with response accepted' + end + end + end + + describe 'GET #status_delete_self_monitoring_project' do + let(:worker_class) { SelfMonitoringProjectDeleteWorker } + let(:job_id) { 'job_id' } + + subject do + get status_delete_self_monitoring_project_admin_application_settings_path, + params: { job_id: job_id } + end + + it_behaves_like 'not accessible to non-admin users' + + context 'with admin user' do + before do + login_as(admin) + end + + context 'with feature flag disabled' do + it_behaves_like 'not accessible if feature flag is disabled' + end + + context 'with feature flag enabled' do + it_behaves_like 'handles invalid job_id' + + context 'when job is in progress' do + before do + allow(worker_class).to receive(:in_progress?) + .with(job_id) + .and_return(true) + + stub_application_setting(instance_administration_project_id: 1) + end + + it_behaves_like 'sets polling header and returns accepted' do + let(:in_progress_message) { 'Job to delete self-monitoring project is in progress' } + end + end + + context 'when self-monitoring project exists and job does not exist' do + before do + stub_application_setting(instance_administration_project_id: 1) + end + + it 'returns bad_request' do + subject + + aggregate_failures do + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to eq( + 'message' => 'Self-monitoring project was not deleted. Please check logs ' \ + 'for any error messages' + ) + end + end + end + + context 'when self-monitoring project does not exist' do + it 'does not need job_id' do + get status_delete_self_monitoring_project_admin_application_settings_path + + aggregate_failures do + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq( + 'message' => 'Self-monitoring project has been successfully deleted' + ) + end + end + + it 'returns success with job_id' do + subject + + aggregate_failures do + expect(response).to have_gitlab_http_status(:success) + expect(json_response).to eq( + 'message' => 'Self-monitoring project has been successfully deleted' + ) + end + end + end + end + end + end +end |