summaryrefslogtreecommitdiff
path: root/spec/requests
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests')
-rw-r--r--spec/requests/api/appearance_spec.rb142
-rw-r--r--spec/requests/api/deployments_spec.rb86
-rw-r--r--spec/requests/api/discussions_spec.rb12
-rw-r--r--spec/requests/api/environments_spec.rb2
-rw-r--r--spec/requests/api/error_tracking_spec.rb79
-rw-r--r--spec/requests/api/events_spec.rb15
-rw-r--r--spec/requests/api/graphql/mutations/snippets/mark_as_spam_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb1
-rw-r--r--spec/requests/api/graphql/project/grafana_integration_spec.rb64
-rw-r--r--spec/requests/api/groups_spec.rb2
-rw-r--r--spec/requests/api/internal/base_spec.rb34
-rw-r--r--spec/requests/api/issues/get_group_issues_spec.rb27
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb20
-rw-r--r--spec/requests/api/issues/issues_spec.rb11
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb10
-rw-r--r--spec/requests/api/jobs_spec.rb2
-rw-r--r--spec/requests/api/keys_spec.rb30
-rw-r--r--spec/requests/api/merge_requests_spec.rb103
-rw-r--r--spec/requests/api/notes_spec.rb69
-rw-r--r--spec/requests/api/pipelines_spec.rb4
-rw-r--r--spec/requests/api/projects_spec.rb151
-rw-r--r--spec/requests/api/remote_mirrors_spec.rb43
-rw-r--r--spec/requests/api/runner_spec.rb88
-rw-r--r--spec/requests/api/services_spec.rb33
-rw-r--r--spec/requests/api/triggers_spec.rb16
-rw-r--r--spec/requests/api/wikis_spec.rb2
-rw-r--r--spec/requests/self_monitoring_project_spec.rb224
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