summaryrefslogtreecommitdiff
path: root/spec/requests/api
diff options
context:
space:
mode:
Diffstat (limited to 'spec/requests/api')
-rw-r--r--spec/requests/api/appearance_spec.rb10
-rw-r--r--spec/requests/api/boards_spec.rb2
-rw-r--r--spec/requests/api/bulk_imports_spec.rb200
-rw-r--r--spec/requests/api/ci/jobs_spec.rb70
-rw-r--r--spec/requests/api/ci/runner/jobs_artifacts_spec.rb39
-rw-r--r--spec/requests/api/commits_spec.rb12
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb29
-rw-r--r--spec/requests/api/environments_spec.rb74
-rw-r--r--spec/requests/api/files_spec.rb120
-rw-r--r--spec/requests/api/graphql/ci/config_spec.rb62
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb8
-rw-r--r--spec/requests/api/graphql/group/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/graphql/group_query_spec.rb2
-rw-r--r--spec/requests/api/graphql/issues_spec.rb65
-rw-r--r--spec/requests/api/graphql/merge_request/merge_request_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/achievements/create_spec.rb78
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_play_spec.rb39
-rw-r--r--spec/requests/api/graphql/mutations/groups/update_spec.rb19
-rw-r--r--spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb130
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/create_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/work_items/update_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/branch_rules_spec.rb45
-rw-r--r--spec/requests/api/graphql/project/issues_spec.rb4
-rw-r--r--spec/requests/api/graphql/project/jobs_spec.rb12
-rw-r--r--spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb17
-rw-r--r--spec/requests/api/graphql/project/runners_spec.rb12
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb2
-rw-r--r--spec/requests/api/graphql/user_spec.rb41
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb18
-rw-r--r--spec/requests/api/group_boards_spec.rb2
-rw-r--r--spec/requests/api/group_export_spec.rb11
-rw-r--r--spec/requests/api/import_github_spec.rb139
-rw-r--r--spec/requests/api/internal/base_spec.rb24
-rw-r--r--spec/requests/api/issues/get_project_issues_spec.rb61
-rw-r--r--spec/requests/api/issues/issues_spec.rb5
-rw-r--r--spec/requests/api/markdown_golden_master_spec.rb9
-rw-r--r--spec/requests/api/merge_requests_spec.rb10
-rw-r--r--spec/requests/api/ml/mlflow_spec.rb10
-rw-r--r--spec/requests/api/nuget_group_packages_spec.rb40
-rw-r--r--spec/requests/api/pages_domains_spec.rb3
-rw-r--r--spec/requests/api/project_debian_distributions_spec.rb25
-rw-r--r--spec/requests/api/project_export_spec.rb14
-rw-r--r--spec/requests/api/projects_spec.rb14
-rw-r--r--spec/requests/api/release/links_spec.rb27
-rw-r--r--spec/requests/api/releases_spec.rb28
-rw-r--r--spec/requests/api/repositories_spec.rb12
-rw-r--r--spec/requests/api/rubygem_packages_spec.rb26
-rw-r--r--spec/requests/api/search_spec.rb20
-rw-r--r--spec/requests/api/settings_spec.rb64
-rw-r--r--spec/requests/api/snippet_repository_storage_moves_spec.rb2
-rw-r--r--spec/requests/api/suggestions_spec.rb2
-rw-r--r--spec/requests/api/todos_spec.rb32
-rw-r--r--spec/requests/api/users_spec.rb277
63 files changed, 1636 insertions, 361 deletions
diff --git a/spec/requests/api/appearance_spec.rb b/spec/requests/api/appearance_spec.rb
index 84d5b091b8d..5aba7e096a7 100644
--- a/spec/requests/api/appearance_spec.rb
+++ b/spec/requests/api/appearance_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
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['pwa_icon']).to be_nil
expect(json_response['favicon']).to be_nil
expect(json_response['footer_message']).to eq('')
expect(json_response['header_logo']).to be_nil
@@ -33,7 +34,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
expect(json_response['new_project_guidelines']).to eq('')
expect(json_response['profile_image_guidelines']).to eq('')
expect(json_response['title']).to eq('')
- expect(json_response['short_title']).to eq('')
+ expect(json_response['pwa_short_name']).to eq('')
end
end
end
@@ -52,7 +53,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
it "allows updating the settings" do
put api("/application/appearance", admin), params: {
title: "GitLab Test Instance",
- short_title: "GitLab",
+ pwa_short_name: "GitLab PWA",
description: "gitlab-test.example.com",
new_project_guidelines: "Please read the FAQs for help.",
profile_image_guidelines: "Custom profile image guidelines"
@@ -62,6 +63,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
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['pwa_icon']).to be_nil
expect(json_response['favicon']).to be_nil
expect(json_response['footer_message']).to eq('')
expect(json_response['header_logo']).to be_nil
@@ -72,7 +74,7 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation do
expect(json_response['new_project_guidelines']).to eq('Please read the FAQs for help.')
expect(json_response['profile_image_guidelines']).to eq('Custom profile image guidelines')
expect(json_response['title']).to eq('GitLab Test Instance')
- expect(json_response['short_title']).to eq('GitLab')
+ expect(json_response['pwa_short_name']).to eq('GitLab PWA')
end
end
@@ -118,12 +120,14 @@ RSpec.describe API::Appearance, 'Appearance', feature_category: :navigation 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"),
+ pwa_icon: 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(:ok)
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['pwa_icon']).to eq("/uploads/-/system/appearance/pwa_icon/#{appearance.id}/dk.png")
expect(json_response['favicon']).to eq("/uploads/-/system/appearance/favicon/#{appearance.id}/dk.png")
end
diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb
index 69804c2c4a4..5f2ff22d0db 100644
--- a/spec/requests/api/boards_spec.rb
+++ b/spec/requests/api/boards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Boards, feature_category: :team_planning do
+RSpec.describe API::Boards, :with_license, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb
index 13f079c69e7..4fb4fbe6d5c 100644
--- a/spec/requests/api/bulk_imports_spec.rb
+++ b/spec/requests/api/bulk_imports_spec.rb
@@ -11,9 +11,26 @@ RSpec.describe API::BulkImports, feature_category: :importers do
let_it_be(:entity_3) { create(:bulk_import_entity, bulk_import: import_2) }
let_it_be(:failure_3) { create(:bulk_import_failure, entity: entity_3) }
+ before do
+ stub_application_setting(bulk_import_enabled: true)
+ end
+
+ shared_examples 'disabled feature' do
+ it 'returns 404' do
+ stub_application_setting(bulk_import_enabled: false)
+
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
describe 'GET /bulk_imports' do
+ let(:request) { get api('/bulk_imports', user), params: params }
+ let(:params) { {} }
+
it 'returns a list of bulk imports authored by the user' do
- get api('/bulk_imports', user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.pluck('id')).to contain_exactly(import_1.id, import_2.id)
@@ -21,26 +38,38 @@ RSpec.describe API::BulkImports, feature_category: :importers do
context 'sort parameter' do
it 'sorts by created_at descending by default' do
- get api('/bulk_imports', user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.pluck('id')).to eq([import_2.id, import_1.id])
end
- it 'sorts by created_at descending when explicitly specified' do
- get api('/bulk_imports', user), params: { sort: 'desc' }
+ context 'when explicitly specified' do
+ context 'when descending' do
+ let(:params) { { sort: 'desc' } }
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.pluck('id')).to eq([import_2.id, import_1.id])
- end
+ it 'sorts by created_at descending' do
+ request
- it 'sorts by created_at ascending when explicitly specified' do
- get api('/bulk_imports', user), params: { sort: 'asc' }
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.pluck('id')).to match_array([import_2.id, import_1.id])
+ end
+ end
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.pluck('id')).to eq([import_1.id, import_2.id])
+ context 'when ascending' do
+ let(:params) { { sort: 'asc' } }
+
+ it 'sorts by created_at ascending when explicitly specified' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.pluck('id')).to match_array([import_1.id, import_2.id])
+ end
+ end
end
end
+
+ include_examples 'disabled feature'
end
describe 'POST /bulk_imports' do
@@ -56,21 +85,10 @@ RSpec.describe API::BulkImports, feature_category: :importers do
end
end
- context 'when bulk_import feature flag is disabled' do
- before do
- stub_feature_flags(bulk_import: false)
- end
-
- it 'returns 404' do
- post api('/bulk_imports', user), params: {}
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
shared_examples 'starting a new migration' do
- it 'starts a new migration' do
- post api('/bulk_imports', user), params: {
+ let(:request) { post api('/bulk_imports', user), params: params }
+ let(:params) do
+ {
configuration: {
url: 'http://gitlab.example',
access_token: 'access_token'
@@ -83,11 +101,45 @@ RSpec.describe API::BulkImports, feature_category: :importers do
}.merge(destination_param)
]
}
+ end
+
+ it 'starts a new migration' do
+ request
expect(response).to have_gitlab_http_status(:created)
expect(json_response['status']).to eq('created')
end
+
+ describe 'migrate projects flag' do
+ context 'when true' do
+ it 'sets true' do
+ params[:entities][0][:migrate_projects] = true
+
+ request
+
+ expect(user.bulk_imports.last.entities.pluck(:migrate_projects)).to contain_exactly(true)
+ end
+ end
+
+ context 'when false' do
+ it 'sets false' do
+ params[:entities][0][:migrate_projects] = false
+
+ request
+
+ expect(user.bulk_imports.last.entities.pluck(:migrate_projects)).to contain_exactly(false)
+ end
+ end
+
+ context 'when unspecified' do
+ it 'sets true' do
+ request
+
+ expect(user.bulk_imports.last.entities.pluck(:migrate_projects)).to contain_exactly(true)
+ end
+ end
+ end
end
include_examples 'starting a new migration' do
@@ -99,8 +151,8 @@ RSpec.describe API::BulkImports, feature_category: :importers do
end
context 'when both destination_name & destination_slug are provided' do
- it 'returns a mutually exclusive error' do
- post api('/bulk_imports', user), params: {
+ let(:params) do
+ {
configuration: {
url: 'http://gitlab.example',
access_token: 'access_token'
@@ -115,6 +167,10 @@ RSpec.describe API::BulkImports, feature_category: :importers do
}
]
}
+ end
+
+ it 'returns a mutually exclusive error' do
+ request
expect(response).to have_gitlab_http_status(:bad_request)
@@ -123,8 +179,8 @@ RSpec.describe API::BulkImports, feature_category: :importers do
end
context 'when neither destination_name nor destination_slug is provided' do
- it 'returns at_least_one_of error' do
- post api('/bulk_imports', user), params: {
+ let(:params) do
+ {
configuration: {
url: 'http://gitlab.example',
access_token: 'access_token'
@@ -137,6 +193,10 @@ RSpec.describe API::BulkImports, feature_category: :importers do
}
]
}
+ end
+
+ it 'returns at_least_one_of error' do
+ request
expect(response).to have_gitlab_http_status(:bad_request)
@@ -144,9 +204,57 @@ RSpec.describe API::BulkImports, feature_category: :importers do
end
end
+ context 'when the source_full_path is invalid' do
+ it 'returns invalid error' do
+ params[:entities][0][:source_full_path] = 'http://example.com/full_path'
+
+ request
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq("entities[0][source_full_path] must be a relative path and not include protocol, sub-domain, " \
+ "or domain information. E.g. 'source/full/path' not 'https://example.com/source/full/path'")
+ end
+ end
+
+ context 'when the destination_namespace is invalid' do
+ it 'returns invalid error' do
+ params[:entities][0][:destination_namespace] = "?not a destination-namespace"
+
+ request
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq("entities[0][destination_namespace] cannot start with a dash or forward slash, " \
+ "or end with a period or forward slash. It can only contain alphanumeric " \
+ "characters, periods, underscores, forward slashes and dashes. " \
+ "E.g. 'destination_namespace' or 'destination/namespace'")
+ end
+ end
+
+ context 'when the destination_namespace is an empty string' do
+ it 'accepts the param and starts a new migration' do
+ params[:entities][0][:destination_namespace] = ''
+
+ request
+ expect(response).to have_gitlab_http_status(:created)
+
+ expect(json_response['status']).to eq('created')
+ end
+ end
+
+ context 'when the destination_slug is invalid' do
+ it 'returns invalid error' do
+ params[:entities][0][:destination_slug] = 'des?tin?atoi-slugg'
+
+ request
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to include("entities[0][destination_slug] cannot start with a dash " \
+ "or forward slash, or end with a period or forward slash. " \
+ "It can only contain alphanumeric characters, periods, underscores, and dashes. " \
+ "E.g. 'destination_namespace' not 'destination/namespace'")
+ end
+ end
+
context 'when provided url is blocked' do
- it 'returns blocked url error' do
- post api('/bulk_imports', user), params: {
+ let(:params) do
+ {
configuration: {
url: 'url',
access_token: 'access_token'
@@ -158,49 +266,71 @@ RSpec.describe API::BulkImports, feature_category: :importers do
destination_namespace: 'destination_namespace'
]
}
+ end
+
+ it 'returns blocked url error' do
+ request
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response['message']).to eq('Validation failed: Url is blocked: Only allowed schemes are http, https')
end
end
+
+ include_examples 'disabled feature'
end
describe 'GET /bulk_imports/entities' do
+ let(:request) { get api('/bulk_imports/entities', user) }
+
it 'returns a list of all import entities authored by the user' do
- get api('/bulk_imports/entities', user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.pluck('id')).to contain_exactly(entity_1.id, entity_2.id, entity_3.id)
end
+
+ include_examples 'disabled feature'
end
describe 'GET /bulk_imports/:id' do
+ let(:request) { get api("/bulk_imports/#{import_1.id}", user) }
+
it 'returns specified bulk import' do
- get api("/bulk_imports/#{import_1.id}", user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(import_1.id)
end
+
+ include_examples 'disabled feature'
end
describe 'GET /bulk_imports/:id/entities' do
+ let(:request) { get api("/bulk_imports/#{import_2.id}/entities", user) }
+
it 'returns specified bulk import entities with failures' do
- get api("/bulk_imports/#{import_2.id}/entities", user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.pluck('id')).to contain_exactly(entity_3.id)
expect(json_response.first['failures'].first['exception_class']).to eq(failure_3.exception_class)
end
+
+ include_examples 'disabled feature'
end
describe 'GET /bulk_imports/:id/entities/:entity_id' do
+ let(:request) { get api("/bulk_imports/#{import_1.id}/entities/#{entity_2.id}", user) }
+
it 'returns specified bulk import entity' do
- get api("/bulk_imports/#{import_1.id}/entities/#{entity_2.id}", user)
+ request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['id']).to eq(entity_2.id)
end
+
+ include_examples 'disabled feature'
end
context 'when user is unauthenticated' do
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 4e348ae64b6..875bfc5b94f 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -487,6 +487,76 @@ RSpec.describe API::Ci::Jobs, feature_category: :continuous_integration do
end
end
+ describe 'GET /projects/:id/jobs offset pagination' do
+ before do
+ running_job
+ end
+
+ it 'returns one record for the first page' do
+ get api("/projects/#{project.id}/jobs", api_user), params: { per_page: 1 }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(running_job.id)
+ end
+
+ it 'returns second record when passed in offset and per_page params' do
+ get api("/projects/#{project.id}/jobs", api_user), params: { page: 2, per_page: 1 }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(job.id)
+ end
+ end
+
+ describe 'GET /projects/:id/jobs keyset pagination' do
+ before do
+ running_job
+ end
+
+ it 'returns first page with cursor to next page' do
+ get api("/projects/#{project.id}/jobs", api_user), params: { pagination: 'keyset', per_page: 1 }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(running_job.id)
+ expect(response.headers["Link"]).to include("cursor")
+ next_cursor = response.headers["Link"].match("(?<cursor_data>cursor=.*?)&")["cursor_data"]
+
+ get api("/projects/#{project.id}/jobs", api_user), params: { pagination: 'keyset', per_page: 1 }.merge(Rack::Utils.parse_query(next_cursor))
+
+ expect(response).to have_gitlab_http_status(:ok)
+ json_response = Gitlab::Json.parse(response.body)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(job.id)
+ expect(response.headers).not_to include("Link")
+ end
+
+ it 'respects scope filters' do
+ get api("/projects/#{project.id}/jobs", api_user), params: { pagination: 'keyset', scope: ['success'] }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(job.id)
+ expect(response.headers).not_to include("Link")
+ end
+
+ context 'with :jobs_api_keyset_pagination disabled' do
+ before do
+ stub_feature_flags(jobs_api_keyset_pagination: false)
+ end
+
+ it 'defaults to offset pagination' do
+ get api("/projects/#{project.id}/jobs", api_user), params: { pagination: 'keyset', per_page: 1 }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(running_job.id)
+ expect(response.headers["Link"]).not_to include("cursor")
+ end
+ end
+ end
+
describe 'GET /projects/:id/jobs rate limited' do
let(:query) { {} }
diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
index 1c119079c50..3d3d699542b 100644
--- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
@@ -575,6 +575,45 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
end
end
+ context 'when access level is private' do
+ subject(:request) { upload_artifacts(file_upload, headers_with_token, params) }
+
+ let(:params) { { artifact_type: :archive, artifact_format: :zip, accessibility: 'private' } }
+
+ it 'sets job artifact access level to private' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(job.reload.job_artifacts_archive).to be_private_accessibility
+ end
+ end
+
+ context 'when access level is public' do
+ subject(:request) { upload_artifacts(file_upload, headers_with_token, params) }
+
+ let(:params) { { artifact_type: :archive, artifact_format: :zip, accessibility: 'public' } }
+
+ it 'sets job artifact access level to public' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(job.reload.job_artifacts_archive).to be_public_accessibility
+ end
+ end
+
+ context 'when access level is unknown' do
+ subject(:request) { upload_artifacts(file_upload, headers_with_token, params) }
+
+ let(:params) { { artifact_type: :archive, artifact_format: :zip } }
+
+ it 'sets job artifact access level to public' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(job.reload.job_artifacts_archive).to be_public_accessibility
+ end
+ end
+
context 'when artifact_type is archive' do
context 'when artifact_format is zip' do
subject(:request) { upload_artifacts(file_upload, headers_with_token, params) }
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 5874d764b00..3932abd20cc 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -2337,18 +2337,6 @@ RSpec.describe API::Commits, feature_category: :source_code_management do
expect(json_response['commit_source']).to eq('gitaly')
end
end
-
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(ssh_commit_signatures: false)
- end
-
- it 'returns 404' do
- get api(route, current_user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
end
end
end
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index c27e165b39b..5258d26be17 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -5,7 +5,17 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
include HttpBasicAuthHelpers
include WorkhorseHelpers
- include_context 'Debian repository shared context', :project, true do
+ include_context 'Debian repository shared context', :project, false do
+ shared_examples 'accept GET request on private project with access to package registry for everyone' do
+ include_context 'Debian repository access', :private, :anonymous, :basic do
+ before do
+ container.project_feature.reload.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'Debian packages GET request', :success
+ end
+ end
+
context 'with invalid parameter' do
let(:url) { "/projects/1/packages/debian/dists/with+space/InRelease" }
@@ -16,54 +26,63 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release.gpg" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNATURE-----/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/Release' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/Release" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Codename: fixture-distribution\n$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/InRelease' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/InRelease" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^-----BEGIN PGP SIGNED MESSAGE-----/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
- describe 'GET projects/:id/packages/debian/dists/*distribution/source/Sources' do
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/source/Sources' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
- describe 'GET projects/:id/packages/debian/dists/*distribution/source/by-hash/SHA256/:file_sha256' do
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/packages/debian/pool/:codename/:letter/:package_name/:package_version/:file_name' do
@@ -90,6 +109,10 @@ RSpec.describe API::DebianProjectPackages, feature_category: :package_registry d
end
end
end
+
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone' do
+ let(:file_name) { 'sample_1.2.3~alpha2.dsc' }
+ end
end
describe 'PUT projects/:id/packages/debian/:file_name' do
diff --git a/spec/requests/api/environments_spec.rb b/spec/requests/api/environments_spec.rb
index d06e70a1a02..6164555ad19 100644
--- a/spec/requests/api/environments_spec.rb
+++ b/spec/requests/api/environments_spec.rb
@@ -4,12 +4,14 @@ require 'spec_helper'
RSpec.describe API::Environments, feature_category: :continuous_delivery do
let_it_be(:user) { create(:user) }
+ let_it_be(:developer) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:project) { create(:project, :private, :repository, namespace: user.namespace) }
let_it_be_with_reload(:environment) { create(:environment, project: project) }
before do
project.add_maintainer(user)
+ project.add_developer(developer)
end
describe 'GET /projects/:id/environments', :aggregate_failures do
@@ -69,6 +71,34 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
expect(json_response.size).to eq(0)
end
+ context "when params[:search] is less than #{described_class::MIN_SEARCH_LENGTH} characters" do
+ before do
+ stub_feature_flags(environment_search_api_min_chars: false)
+ end
+
+ it 'returns a normal response' do
+ get api("/projects/#{project.id}/environments?search=ab", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.size).to eq(0)
+ end
+
+ context 'and environment_search_api_min_chars flag is enabled for the project' do
+ before do
+ stub_feature_flags(environment_search_api_min_chars: project)
+ end
+
+ it 'returns with status 400' do
+ get api("/projects/#{project.id}/environments?search=ab", user)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to include("Search query is less than #{described_class::MIN_SEARCH_LENGTH} characters")
+ end
+ end
+ end
+
it 'returns environment by valid state' do
get api("/projects/#{project.id}/environments?states=available", user)
@@ -154,6 +184,50 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
end
end
+ describe 'POST /projects/:id/environments/stop_stale' do
+ context 'as a maintainer' do
+ it 'returns a 200' do
+ post api("/projects/#{project.id}/environments/stop_stale", user), params: { before: 1.week.ago.to_date.to_s }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'returns a 400 for bad input date' do
+ post api("/projects/#{project.id}/environments/stop_stale", user), params: { before: 1.day.ago.to_date.to_s }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('400 Bad request - Invalid Date')
+ end
+
+ it 'returns a 400 for service error' do
+ expect_next_instance_of(::Environments::StopStaleService) do |service|
+ expect(service).to receive(:execute).and_return(ServiceResponse.error(message: 'Test Error'))
+ end
+
+ post api("/projects/#{project.id}/environments/stop_stale", user), params: { before: 1.week.ago.to_date.to_s }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq('Test Error')
+ end
+ end
+
+ context 'a non member' do
+ it 'rejects the request' do
+ post api("/projects/#{project.id}/environments/stop_stale", non_member), params: { before: 1.week.ago.to_date.to_s }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'a developer' do
+ it 'rejects the request' do
+ post api("/projects/#{project.id}/environments/stop_stale", developer), params: { before: 1.week.ago.to_date.to_s }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
describe 'PUT /projects/:id/environments/:environment_id' do
it 'returns a 200 if name and external_url are changed' do
url = 'https://mepmep.whatever.ninja'
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 9cee3c06bb1..f4066c54c47 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -6,6 +6,24 @@ RSpec.describe API::Files, feature_category: :source_code_management do
include RepoHelpers
let_it_be(:group) { create(:group, :public) }
+ let(:helper) do
+ fake_class = Class.new do
+ include ::API::Helpers::HeadersHelpers
+
+ attr_reader :headers
+
+ def initialize
+ @headers = {}
+ end
+
+ def header(key, value)
+ @headers[key] = value
+ end
+ end
+
+ fake_class.new
+ end
+
let_it_be_with_refind(:user) { create(:user) }
let_it_be(:inherited_guest) { create(:user) }
let_it_be(:inherited_reporter) { create(:user) }
@@ -37,25 +55,9 @@ RSpec.describe API::Files, feature_category: :source_code_management do
}
end
- let(:author_email) { 'user@example.org' }
- let(:author_name) { 'John Doe' }
-
- let(:helper) do
- fake_class = Class.new do
- include ::API::Helpers::HeadersHelpers
-
- attr_reader :headers
-
- def initialize
- @headers = {}
- end
-
- def header(key, value)
- @headers[key] = value
- end
- end
-
- fake_class.new
+ shared_context 'with author parameters' do
+ let(:author_email) { 'user@example.org' }
+ let(:author_name) { 'John Doe' }
end
before_all do
@@ -702,6 +704,80 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
end
+ describe 'HEAD /projects/:id/repository/files/:file_path/raw' do
+ let(:request) { head api(route(file_path) + '/raw', current_user), params: params }
+
+ describe 'response headers' do
+ subject { response.headers }
+
+ context 'and user is a developer' do
+ let(:current_user) { user }
+
+ it 'responds with blob data' do
+ request
+ headers = response.headers
+ expect(headers['X-Gitlab-File-Name']).to eq(file_name)
+ expect(headers['X-Gitlab-File-Path']).to eq('files/ruby/popen.rb')
+ expect(headers['X-Gitlab-Content-Sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
+ expect(headers['X-Gitlab-Ref']).to eq('master')
+ expect(headers['X-Gitlab-Blob-Id']).to eq('7e3e39ebb9b2bf433b4ad17313770fbe4051649c')
+ expect(headers['X-Gitlab-Commit-Id']).to eq(project.repository.commit.id)
+ expect(headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
+ end
+
+ context 'when lfs parameter is true and the project has lfs enabled' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ project.update_attribute(:lfs_enabled, true)
+ end
+
+ let(:request) { head api(route('files%2Flfs%2Flfs_object.iso') + '/raw', current_user), params: params.merge(lfs: true) }
+
+ context 'and the file has an lfs object' do
+ let_it_be(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897') }
+
+ it 'responds with 404' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ context 'and the project has access to the lfs object' do
+ before do
+ project.lfs_objects << lfs_object
+ end
+
+ context 'and lfs uses AWS' do
+ before do
+ stub_lfs_object_storage(config: Gitlab.config.lfs.object_store.merge(connection: {
+ provider: 'AWS',
+ aws_access_key_id: '',
+ aws_secret_access_key: ''
+ }))
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+ end
+
+ it 'redirects to the lfs object file with a signed url' do
+ request
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response.location).to include(lfs_object.reload.file.path)
+ expect(response.location).to include('X-Amz-SignedHeaders')
+ end
+ end
+ end
+ end
+ end
+ end
+
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { head api(route(file_path), guest), params: params }
+ end
+ end
+ end
+ end
+
describe 'GET /projects/:id/repository/files/:file_path/raw' do
shared_examples_for 'repository raw files' do
it 'returns 400 when file path is invalid' do
@@ -1006,6 +1082,8 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
context 'when specifying an author' do
+ include_context 'with author parameters'
+
it 'creates a new file with the specified author' do
params.merge!(author_email: author_email, author_name: author_name)
post api(route('new_file_with_author%2Etxt'), user), params: params
@@ -1163,6 +1241,8 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
context 'when specifying an author' do
+ include_context 'with author parameters'
+
it 'updates a file with the specified author' do
params.merge!(author_email: author_email, author_name: author_name, content: 'New content')
@@ -1236,6 +1316,8 @@ RSpec.describe API::Files, feature_category: :source_code_management do
end
context 'when specifying an author' do
+ include_context 'with author parameters'
+
before do
params.merge!(author_email: author_email, author_name: author_name)
end
diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb
index 8154f132430..5f43a0806f3 100644
--- a/spec/requests/api/graphql/ci/config_spec.rb
+++ b/spec/requests/api/graphql/ci/config_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do
include GraphqlHelpers
include StubRequests
+ include RepoHelpers
subject(:post_graphql_query) { post_graphql(query, current_user: user) }
@@ -245,17 +246,22 @@ RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do
)
end
- before do
- allow_next_instance_of(Repository) do |repository|
- allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_file.yml') do
- YAML.dump(
- build: {
- script: 'build'
- }
- )
- end
+ let(:project_files) do
+ {
+ 'other_file.yml' => <<~YAML
+ build:
+ script: build
+ YAML
+ }
+ end
+
+ around do |example|
+ create_and_delete_files(project, project_files) do
+ example.run
end
+ end
+ before do
post_graphql_query
end
@@ -370,25 +376,33 @@ RSpec.describe 'Query.ciConfig', feature_category: :continuous_integration do
)
end
- before do
- allow_next_instance_of(Repository) do |repository|
- allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_file.yml') do
- YAML.dump(
- build: {
- script: 'build'
- }
- )
- end
+ let(:project_files) do
+ {
+ 'other_file.yml' => <<~YAML
+ build:
+ script: build
+ YAML
+ }
+ end
- allow(repository).to receive(:blob_data_at).with(an_instance_of(String), 'other_project_file.yml') do
- YAML.dump(
- other_project_test: {
- script: 'other_project_test'
- }
- )
+ let(:other_project_files) do
+ {
+ 'other_project_file.yml' => <<~YAML
+ other_project_test:
+ script: other_project_test
+ YAML
+ }
+ end
+
+ around do |example|
+ create_and_delete_files(project, project_files) do
+ create_and_delete_files(other_project, other_project_files) do
+ example.run
end
end
+ end
+ before do
stub_full_request('https://gitlab.com/gitlab-org/gitlab/raw/1234/.hello.yml').to_return(body: remote_file_content)
post_graphql_query
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index 7a1dc614dcf..131cdb77107 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -88,10 +88,10 @@ RSpec.describe 'Query.project.pipeline', feature_category: :continuous_integrati
build_stage = create(:ci_stage, position: 2, name: 'build', project: project, pipeline: pipeline)
test_stage = create(:ci_stage, position: 3, name: 'test', project: project, pipeline: pipeline)
- create(:ci_build, pipeline: pipeline, name: 'docker 1 2', scheduling_type: :stage, stage: build_stage, stage_idx: build_stage.position)
- create(:ci_build, pipeline: pipeline, name: 'docker 2 2', stage: build_stage, stage_idx: build_stage.position, scheduling_type: :dag)
- create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', scheduling_type: :stage, stage: test_stage, stage_idx: test_stage.position)
- test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', scheduling_type: :dag, stage: test_stage, stage_idx: test_stage.position)
+ create(:ci_build, pipeline: pipeline, name: 'docker 1 2', scheduling_type: :stage, ci_stage: build_stage, stage_idx: build_stage.position)
+ create(:ci_build, pipeline: pipeline, name: 'docker 2 2', ci_stage: build_stage, stage_idx: build_stage.position, scheduling_type: :dag)
+ create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', scheduling_type: :stage, ci_stage: test_stage, stage_idx: test_stage.position)
+ test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', scheduling_type: :dag, ci_stage: test_stage, stage_idx: test_stage.position)
create(:ci_build_need, build: test_job, name: 'my test job')
end
diff --git a/spec/requests/api/graphql/group/merge_requests_spec.rb b/spec/requests/api/graphql/group/merge_requests_spec.rb
index 6976685ecc0..adaee3031a9 100644
--- a/spec/requests/api/graphql/group/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/group/merge_requests_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
# Based on ee/spec/requests/api/epics_spec.rb
# Should follow closely in order to ensure all situations are covered
-RSpec.describe 'Query.group.mergeRequests', feature_category: :code_review do
+RSpec.describe 'Query.group.mergeRequests', feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/group_query_spec.rb b/spec/requests/api/graphql/group_query_spec.rb
index bc288c0a98b..ce5816999a6 100644
--- a/spec/requests/api/graphql/group_query_spec.rb
+++ b/spec/requests/api/graphql/group_query_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
# Based on spec/requests/api/groups_spec.rb
# Should follow closely in order to ensure all situations are covered
-RSpec.describe 'getting group information', feature_category: :subgroups do
+RSpec.describe 'getting group information', :with_license, feature_category: :subgroups do
include GraphqlHelpers
include UploadHelpers
diff --git a/spec/requests/api/graphql/issues_spec.rb b/spec/requests/api/graphql/issues_spec.rb
index ba6f8ec2cab..e67c92d6c33 100644
--- a/spec/requests/api/graphql/issues_spec.rb
+++ b/spec/requests/api/graphql/issues_spec.rb
@@ -2,11 +2,13 @@
require 'spec_helper'
+# rubocop:disable RSpec/MultipleMemoizedHelpers
RSpec.describe 'getting an issue list at root level', feature_category: :team_planning do
include GraphqlHelpers
let_it_be(:developer) { create(:user) }
let_it_be(:reporter) { create(:user) }
+ let_it_be(:current_user) { developer }
let_it_be(:group1) { create(:group).tap { |group| group.add_developer(developer) } }
let_it_be(:group2) { create(:group).tap { |group| group.add_developer(developer) } }
let_it_be(:project_a) { create(:project, :repository, :public, group: group1) }
@@ -82,9 +84,11 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
end
let_it_be(:issues, reload: true) { [issue_a, issue_b, issue_c, issue_d, issue_e] }
+ # we need to always provide at least one filter to the query so it doesn't fail
+ let_it_be(:base_params) { { iids: issues.map { |issue| issue.iid.to_s } } }
let(:issue_filter_params) { {} }
- let(:current_user) { developer }
+ let(:all_query_params) { base_params.merge(**issue_filter_params) }
let(:fields) do
<<~QUERY
nodes { id }
@@ -95,6 +99,16 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
group2.add_reporter(reporter)
end
+ shared_examples 'query that requires at least one filter' do
+ it 'requires at least one filter to be provided to the query' do
+ post_graphql(query, current_user: developer)
+
+ expect(graphql_errors).to contain_exactly(
+ hash_including('message' => _('You must provide at least one filter argument for this query'))
+ )
+ end
+ end
+
context 'when the root_level_issues_query feature flag is disabled' do
before do
stub_feature_flags(root_level_issues_query: false)
@@ -107,20 +121,31 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
end
end
+ context 'when no filters are provided' do
+ let(:all_query_params) { {} }
+
+ it_behaves_like 'query that requires at least one filter'
+ end
+
+ context 'when only non filter arguments are provided' do
+ let(:all_query_params) { { sort: :SEVERITY_ASC } }
+
+ it_behaves_like 'query that requires at least one filter'
+ end
+
# All new specs should be added to the shared example if the change also
# affects the `issues` query at the root level of the API.
# Shared example also used in spec/requests/api/graphql/project/issues_spec.rb
it_behaves_like 'graphql issue list request spec' do
let_it_be(:external_user) { create(:user) }
+ let_it_be(:another_user) { reporter }
let(:public_projects) { [project_a, project_c] }
- let(:another_user) { reporter }
let(:issue_nodes_path) { %w[issues nodes] }
# filters
let(:expected_negated_assignee_issues) { [issue_b, issue_c, issue_d, issue_e] }
- let(:expected_unioned_assignee_issues) { [issue_a, issue_c] }
let(:voted_issues) { [issue_a, issue_c] }
let(:no_award_issues) { [issue_b, issue_d, issue_e] }
let(:locked_discussion_issues) { [issue_b, issue_d] }
@@ -148,9 +173,6 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
let(:same_project_issue2) { issue_e }
before_all do
- issue_a.assignee_ids = developer.id
- issue_c.assignee_ids = reporter.id
-
create(:award_emoji, :upvote, user: developer, awardable: issue_a)
create(:award_emoji, :upvote, user: developer, awardable: issue_c)
end
@@ -158,7 +180,7 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
def pagination_query(params)
graphql_query_for(
:issues,
- params,
+ base_params.merge(**params.to_h),
"#{page_info} nodes { id }"
)
end
@@ -177,6 +199,32 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
end
end
+ context 'with rate limiting' do
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit, graphql: true do
+ let_it_be(:current_user) { developer }
+
+ let(:error_message) do
+ 'This endpoint has been requested with the search argument too many times. Try again later.'
+ end
+
+ def request
+ post_graphql(query({ search: 'test' }), current_user: developer)
+ end
+ end
+
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit_unauthenticated, graphql: true do
+ let_it_be(:current_user) { nil }
+
+ let(:error_message) do
+ 'This endpoint has been requested with the search argument too many times. Try again later.'
+ end
+
+ def request
+ post_graphql(query({ search: 'test' }))
+ end
+ end
+ end
+
def execute_query
post_query
end
@@ -185,7 +233,7 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
post_graphql(query, current_user: request_user)
end
- def query(params = issue_filter_params)
+ def query(params = all_query_params)
graphql_query_for(
:issues,
params,
@@ -193,3 +241,4 @@ RSpec.describe 'getting an issue list at root level', feature_category: :team_pl
)
end
end
+# rubocop:enable RSpec/MultipleMemoizedHelpers
diff --git a/spec/requests/api/graphql/merge_request/merge_request_spec.rb b/spec/requests/api/graphql/merge_request/merge_request_spec.rb
index 213697bacc1..02ea7bac920 100644
--- a/spec/requests/api/graphql/merge_request/merge_request_spec.rb
+++ b/spec/requests/api/graphql/merge_request/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Query.merge_request(id)', feature_category: :code_review do
+RSpec.describe 'Query.merge_request(id)', feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:project) { create(:project, :empty_repo) }
diff --git a/spec/requests/api/graphql/mutations/achievements/create_spec.rb b/spec/requests/api/graphql/mutations/achievements/create_spec.rb
new file mode 100644
index 00000000000..1713f050540
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/achievements/create_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Achievements::Create, feature_category: :users do
+ include GraphqlHelpers
+ include WorkhorseHelpers
+
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+
+ let(:mutation) { graphql_mutation(:achievements_create, params) }
+ let(:name) { 'Name' }
+ let(:description) { 'Description' }
+ let(:revokeable) { false }
+ let(:avatar) { fixture_file_upload("spec/fixtures/dk.png") }
+ let(:params) do
+ {
+ namespace_id: group.to_global_id,
+ name: name,
+ avatar: avatar,
+ description: description,
+ revokeable: revokeable
+ }
+ end
+
+ subject { post_graphql_mutation_with_uploads(mutation, current_user: current_user) }
+
+ def mutation_response
+ graphql_mutation_response(:achievements_create)
+ end
+
+ before_all do
+ group.add_developer(developer)
+ group.add_maintainer(maintainer)
+ end
+
+ context 'when the user does not have permission' do
+ let(:current_user) { developer }
+ let(:avatar) {}
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+
+ it 'does not create an achievement' do
+ expect { subject }.not_to change { Achievements::Achievement.count }
+ end
+ end
+
+ context 'when the user has permission' do
+ let(:current_user) { maintainer }
+
+ context 'when the params are invalid' do
+ let(:name) {}
+
+ it 'returns the validation error' do
+ subject
+
+ expect(graphql_errors.to_s).to include('provided invalid value for name (Expected value to not be null)')
+ end
+ end
+
+ it 'creates an achievement' do
+ expect { subject }.to change { Achievements::Achievement.count }.by(1)
+ end
+
+ it 'returns the new achievement' do
+ subject
+
+ expect(graphql_data_at(:achievements_create, :achievement)).to match a_hash_including(
+ 'name' => name,
+ 'namespace' => a_hash_including('id' => group.to_global_id.to_s),
+ 'description' => description,
+ 'revokeable' => revokeable
+ )
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb b/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
index 014a5e0f1c7..9ba80e51dee 100644
--- a/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/job_play_spec.rb
@@ -8,17 +8,25 @@ RSpec.describe 'JobPlay', feature_category: :continuous_integration do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:pipeline) { create(:ci_pipeline, project: project, user: user) }
- let_it_be(:job) { create(:ci_build, pipeline: pipeline, name: 'build') }
+ let_it_be(:job) { create(:ci_build, :playable, pipeline: pipeline, name: 'build') }
- let(:mutation) do
- variables = {
+ let(:variables) do
+ {
id: job.to_global_id.to_s
}
+ end
+
+ let(:mutation) do
graphql_mutation(:job_play, variables,
<<-QL
errors
job {
id
+ manualVariables {
+ nodes {
+ key
+ }
+ }
}
QL
)
@@ -43,4 +51,29 @@ RSpec.describe 'JobPlay', feature_category: :continuous_integration do
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['job']['id']).to eq(job_id)
end
+
+ context 'when given variables' do
+ let(:variables) do
+ {
+ id: job.to_global_id.to_s,
+ variables: [
+ { key: 'MANUAL_VAR_1', value: 'test var' },
+ { key: 'MANUAL_VAR_2', value: 'test var 2' }
+ ]
+ }
+ end
+
+ it 'provides those variables to the job', :aggregated_errors do
+ expect_next_instance_of(Ci::PlayBuildService) do |instance|
+ expect(instance).to receive(:execute).with(an_instance_of(Ci::Build), variables[:variables]).and_call_original
+ end
+
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['job']['manualVariables']['nodes'].pluck('key')).to contain_exactly(
+ 'MANUAL_VAR_1', 'MANUAL_VAR_2'
+ )
+ end
+ end
end
diff --git a/spec/requests/api/graphql/mutations/groups/update_spec.rb b/spec/requests/api/graphql/mutations/groups/update_spec.rb
index ea3d42a4463..a9acc593229 100644
--- a/spec/requests/api/graphql/mutations/groups/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/groups/update_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe 'GroupUpdate', feature_category: :subgroups do
let(:variables) do
{
full_path: group.full_path,
- shared_runners_setting: 'DISABLED_WITH_OVERRIDE'
+ shared_runners_setting: 'DISABLED_AND_OVERRIDABLE'
}
end
@@ -52,6 +52,23 @@ RSpec.describe 'GroupUpdate', feature_category: :subgroups do
expect(group.reload.shared_runners_setting).to eq(variables[:shared_runners_setting].downcase)
end
+ context 'when using DISABLED_WITH_OVERRIDE (deprecated)' do
+ let(:variables) do
+ {
+ full_path: group.full_path,
+ shared_runners_setting: 'DISABLED_WITH_OVERRIDE'
+ }
+ end
+
+ it 'updates shared runners settings with disabled_and_overridable' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_errors).to be_nil
+ expect(group.reload.shared_runners_setting).to eq('disabled_and_overridable')
+ end
+ end
+
context 'when bad arguments are provided' do
let(:variables) { { full_path: '', shared_runners_setting: 'INVALID' } }
diff --git a/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb b/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb
new file mode 100644
index 00000000000..ad70129a7bc
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/members/groups/bulk_update_spec.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'GroupMemberBulkUpdate', feature_category: :subgroups do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_member1) { create(:group_member, group: group, user: user1) }
+ let_it_be(:group_member2) { create(:group_member, group: group, user: user2) }
+ let_it_be(:mutation_name) { :group_member_bulk_update }
+
+ let(:input) do
+ {
+ 'group_id' => group.to_global_id.to_s,
+ 'user_ids' => [user1.to_global_id.to_s, user2.to_global_id.to_s],
+ 'access_level' => 'GUEST'
+ }
+ end
+
+ let(:extra_params) { { expires_at: 10.days.from_now } }
+ let(:input_params) { input.merge(extra_params) }
+ let(:mutation) { graphql_mutation(mutation_name, input_params) }
+ let(:mutation_response) { graphql_mutation_response(mutation_name) }
+
+ context 'when user is not logged-in' do
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user is not an owner' do
+ before do
+ group.add_maintainer(current_user)
+ end
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user is an owner' do
+ before do
+ group.add_owner(current_user)
+ end
+
+ shared_examples 'updates the user access role' do
+ specify do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ new_access_levels = mutation_response['groupMembers'].map { |member| member['accessLevel']['integerValue'] }
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['errors']).to be_empty
+ expect(new_access_levels).to all(be Gitlab::Access::GUEST)
+ end
+ end
+
+ it_behaves_like 'updates the user access role'
+
+ context 'when inherited members are passed' do
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:subgroup_member) { create(:group_member, group: subgroup) }
+
+ let(:input) do
+ {
+ 'group_id' => group.to_global_id.to_s,
+ 'user_ids' => [user1.to_global_id.to_s, user2.to_global_id.to_s, subgroup_member.user.to_global_id.to_s],
+ 'access_level' => 'GUEST'
+ }
+ end
+
+ it 'does not update the members' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ error = Mutations::Members::Groups::BulkUpdate::INVALID_MEMBERS_ERROR
+ expect(json_response['errors'].first['message']).to include(error)
+ end
+ end
+
+ context 'when members count is more than the allowed limit' do
+ let(:max_members_update_limit) { 1 }
+
+ before do
+ stub_const('Mutations::Members::Groups::BulkUpdate::MAX_MEMBERS_UPDATE_LIMIT', max_members_update_limit)
+ end
+
+ it 'does not update the members' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ error = Mutations::Members::Groups::BulkUpdate::MAX_MEMBERS_UPDATE_ERROR
+ expect(json_response['errors'].first['message']).to include(error)
+ end
+ end
+
+ context 'when the update service raises access denied error' do
+ before do
+ allow_next_instance_of(Members::UpdateService) do |instance|
+ allow(instance).to receive(:execute).and_raise(Gitlab::Access::AccessDeniedError)
+ end
+ end
+
+ it 'does not update the members' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['groupMembers']).to be_nil
+ expect(mutation_response['errors'])
+ .to contain_exactly("Unable to update members, please check user permissions.")
+ end
+ end
+
+ context 'when the update service returns an error message' do
+ before do
+ allow_next_instance_of(Members::UpdateService) do |instance|
+ error_result = {
+ message: 'Expires at cannot be a date in the past',
+ status: :error,
+ members: [group_member1]
+ }
+ allow(instance).to receive(:execute).and_return(error_result)
+ end
+ end
+
+ it 'will pass through the error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['groupMembers'].first['id']).to eq(group_member1.to_global_id.to_s)
+ expect(mutation_response['errors']).to contain_exactly('Expires at cannot be a date in the past')
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb
index c954fd50cc4..59f41c5e878 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Creation of a new merge request', feature_category: :code_review do
+RSpec.describe 'Creation of a new merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb
index c41161eff2b..7a1b3982111 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting assignees of a merge request', feature_category: :code_review do
+RSpec.describe 'Setting assignees of a merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
index 364d13291db..b5f2042c42a 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting assignees of a merge request', :assume_throttled, feature_category: :code_review do
+RSpec.describe 'Setting assignees of a merge request', :assume_throttled, feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb
index b48a94fbeb9..0c2e2975350 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting Draft status of a merge request', feature_category: :code_review do
+RSpec.describe 'Setting Draft status of a merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb
index d88982c508c..73a38adf723 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting locked status of a merge request', feature_category: :code_review do
+RSpec.describe 'Setting locked status of a merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb
index a0f0e45d1fc..3907ebad9ce 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting milestone of a merge request', feature_category: :code_review do
+RSpec.describe 'Setting milestone of a merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
let(:current_user) { create(:user) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb
index a5be2a95c8b..fd87112be33 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting reviewers of a merge request', :assume_throttled, feature_category: :code_review do
+RSpec.describe 'Setting reviewers of a merge request', :assume_throttled, feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb
index daf1f529847..0e77b048646 100644
--- a/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb
+++ b/spec/requests/api/graphql/mutations/merge_requests/set_subscription_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Setting subscribed status of a merge request', feature_category: :code_review do
+RSpec.describe 'Setting subscribed status of a merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
it_behaves_like 'a subscribable resource api' do
diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
index 14cb18d04b8..b33a394d023 100644
--- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb
@@ -489,10 +489,10 @@ RSpec.describe 'Update a work item', feature_category: :team_planning do
expect(response).to have_gitlab_http_status(:success)
expect(widgets_response).to include(
{
- 'children' => { 'edges' => [
+ 'children' => { 'edges' => match_array([
{ 'node' => { 'id' => valid_child2.to_global_id.to_s } },
{ 'node' => { 'id' => valid_child1.to_global_id.to_s } }
- ] },
+ ]) },
'parent' => nil,
'type' => 'HIERARCHY'
}
diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb
index 7f6a66e2377..2ca37a49149 100644
--- a/spec/requests/api/graphql/project/branch_rules_spec.rb
+++ b/spec/requests/api/graphql/project/branch_rules_spec.rb
@@ -69,12 +69,6 @@ RSpec.describe 'getting list of branch rules for a project', feature_category: :
before do
create(:protected_branch, project: project)
- allow_next_instance_of(Resolvers::ProjectResolver) do |resolver|
- allow(resolver).to receive(:resolve)
- .with(full_path: project.full_path)
- .and_return(project)
- end
- allow(project.repository).to receive(:branch_names).and_call_original
end
it 'avoids N+1 queries', :use_sql_query_cache, :aggregate_failures do
@@ -93,7 +87,6 @@ RSpec.describe 'getting list of branch rules for a project', feature_category: :
end.not_to exceed_all_query_limit(control)
expect_n_matching_branches_count_fields(3)
- expect(project.repository).to have_received(:branch_names).at_least(2).times
end
def expect_n_matching_branches_count_fields(count)
@@ -110,16 +103,16 @@ RSpec.describe 'getting list of branch rules for a project', feature_category: :
let_it_be(:branch_name_b) { 'diff-*' }
let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] }
let_it_be(:branch_rule_a) do
- create(:protected_branch, project: project, name: branch_name_a, id: 9999)
+ create(:protected_branch, project: project, name: branch_name_a)
end
let_it_be(:branch_rule_b) do
- create(:protected_branch, project: project, name: branch_name_b, id: 10000)
+ create(:protected_branch, project: project, name: branch_name_b)
end
- # branchRules are returned in reverse order, newest first, sorted by primary_key.
- let(:branch_rule_b_data) { branch_rules_data.dig(0, 'node') }
+ # branchRules are returned in alphabetical order
let(:branch_rule_a_data) { branch_rules_data.dig(1, 'node') }
+ let(:branch_rule_b_data) { branch_rules_data.dig(0, 'node') }
before do
post_graphql(query, current_user: current_user, variables: variables)
@@ -128,22 +121,28 @@ RSpec.describe 'getting list of branch rules for a project', feature_category: :
it_behaves_like 'a working graphql query'
it 'includes all fields', :use_sql_query_cache, :aggregate_failures do
- expect(branch_rule_a_data['name']).to eq(branch_name_a)
- expect(branch_rule_a_data['isDefault']).to be(true).or be(false)
- expect(branch_rule_a_data['branchProtection']).to be_present
- expect(branch_rule_a_data['matchingBranchesCount']).to eq(1)
- expect(branch_rule_a_data['createdAt']).to be_present
- expect(branch_rule_a_data['updatedAt']).to be_present
+ expect(branch_rule_a_data).to include(
+ 'name' => branch_name_a,
+ 'isDefault' => be_boolean,
+ 'isProtected' => true,
+ 'matchingBranchesCount' => 1,
+ 'branchProtection' => be_kind_of(Hash),
+ 'createdAt' => be_kind_of(String),
+ 'updatedAt' => be_kind_of(String)
+ )
wildcard_count = TestEnv::BRANCH_SHA.keys.count do |branch_name|
branch_name.starts_with?('diff-')
end
- expect(branch_rule_b_data['name']).to eq(branch_name_b)
- expect(branch_rule_b_data['isDefault']).to be(true).or be(false)
- expect(branch_rule_b_data['branchProtection']).to be_present
- expect(branch_rule_b_data['matchingBranchesCount']).to eq(wildcard_count)
- expect(branch_rule_b_data['createdAt']).to be_present
- expect(branch_rule_b_data['updatedAt']).to be_present
+ expect(branch_rule_b_data).to include(
+ 'name' => branch_name_b,
+ 'isDefault' => be_boolean,
+ 'isProtected' => true,
+ 'matchingBranchesCount' => wildcard_count,
+ 'branchProtection' => be_kind_of(Hash),
+ 'createdAt' => be_kind_of(String),
+ 'updatedAt' => be_kind_of(String)
+ )
end
context 'when limiting the number of results' do
diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb
index ec5e3c6f0de..cc41795f770 100644
--- a/spec/requests/api/graphql/project/issues_spec.rb
+++ b/spec/requests/api/graphql/project/issues_spec.rb
@@ -91,7 +91,6 @@ RSpec.describe 'getting an issue list for a project', feature_category: :team_pl
# filters
let(:expected_negated_assignee_issues) { [issue_b, issue_c, issue_d, issue_e] }
- let(:expected_unioned_assignee_issues) { [issue_a, issue_b] }
let(:voted_issues) { [issue_a] }
let(:no_award_issues) { [issue_b, issue_c, issue_d, issue_e] }
let(:locked_discussion_issues) { [issue_a] }
@@ -119,9 +118,6 @@ RSpec.describe 'getting an issue list for a project', feature_category: :team_pl
let(:same_project_issue2) { issue_b }
before_all do
- issue_a.assignee_ids = current_user.id
- issue_b.assignee_ids = another_user.id
-
create(:award_emoji, :upvote, user: current_user, awardable: issue_a)
end
diff --git a/spec/requests/api/graphql/project/jobs_spec.rb b/spec/requests/api/graphql/project/jobs_spec.rb
index d05d4a2f4b6..aea6cad9e62 100644
--- a/spec/requests/api/graphql/project/jobs_spec.rb
+++ b/spec/requests/api/graphql/project/jobs_spec.rb
@@ -33,10 +33,10 @@ RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration d
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
build_stage = create(:ci_stage, position: 1, name: 'build', project: project, pipeline: pipeline)
test_stage = create(:ci_stage, position: 2, name: 'test', project: project, pipeline: pipeline)
- create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 1 2', stage: build_stage)
- create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 2 2', stage: build_stage)
- create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 1 2', stage: test_stage)
- test_job = create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 2 2', stage: test_stage)
+ create(:ci_build, pipeline: pipeline, name: 'docker 1 2', ci_stage: build_stage)
+ create(:ci_build, pipeline: pipeline, name: 'docker 2 2', ci_stage: build_stage)
+ create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', ci_stage: test_stage)
+ test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', ci_stage: test_stage)
create(:ci_build_need, build: test_job, name: 'docker 1 2')
post_graphql(query, current_user: user)
@@ -45,8 +45,8 @@ RSpec.describe 'Query.project.jobs', feature_category: :continuous_integration d
post_graphql(query, current_user: user)
end
- create(:ci_build, name: 'test-a', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
- test_b_job = create(:ci_build, name: 'test-b', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
+ create(:ci_build, name: 'test-a', ci_stage: test_stage, pipeline: pipeline)
+ test_b_job = create(:ci_build, name: 'test-b', ci_stage: test_stage, pipeline: pipeline)
create(:ci_build_need, build: test_b_job, name: 'docker 2 2')
expect do
diff --git a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb
index 36e148468bc..4884e04ab23 100644
--- a/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting notes for a merge request', feature_category: :code_review do
+RSpec.describe 'getting notes for a merge request', feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:noteable) { create(:merge_request) }
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index b7aafdf305a..6aa96cfc070 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting merge request information nested in a project', feature_category: :code_review do
+RSpec.describe 'getting merge request information nested in a project', feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository, :public) }
diff --git a/spec/requests/api/graphql/project/merge_requests_spec.rb b/spec/requests/api/graphql/project/merge_requests_spec.rb
index b3b4c8fe0d5..8407faa967e 100644
--- a/spec/requests/api/graphql/project/merge_requests_spec.rb
+++ b/spec/requests/api/graphql/project/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'getting merge request listings nested in a project', feature_category: :code_review do
+RSpec.describe 'getting merge request listings nested in a project', feature_category: :code_review_workflow do
include GraphqlHelpers
let_it_be(:group) { create(:group) }
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index 0eeb382510e..abfdf07c288 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -348,10 +348,10 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
build_stage = create(:ci_stage, position: 1, name: 'build', project: project, pipeline: pipeline)
test_stage = create(:ci_stage, position: 2, name: 'test', project: project, pipeline: pipeline)
- create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 1 2', stage: build_stage)
- create(:ci_build, pipeline: pipeline, stage_idx: build_stage.position, name: 'docker 2 2', stage: build_stage)
- create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 1 2', stage: test_stage)
- test_job = create(:ci_build, pipeline: pipeline, stage_idx: test_stage.position, name: 'rspec 2 2', stage: test_stage)
+ create(:ci_build, pipeline: pipeline, name: 'docker 1 2', ci_stage: build_stage)
+ create(:ci_build, pipeline: pipeline, name: 'docker 2 2', ci_stage: build_stage)
+ create(:ci_build, pipeline: pipeline, name: 'rspec 1 2', ci_stage: test_stage)
+ test_job = create(:ci_build, pipeline: pipeline, name: 'rspec 2 2', ci_stage: test_stage)
create(:ci_build_need, build: test_job, name: 'docker 1 2')
post_graphql(query, current_user: current_user)
@@ -360,8 +360,8 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
post_graphql(query, current_user: current_user)
end
- create(:ci_build, name: 'test-a', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
- test_b_job = create(:ci_build, name: 'test-b', stage: test_stage, stage_idx: test_stage.position, pipeline: pipeline)
+ create(:ci_build, name: 'test-a', ci_stage: test_stage, pipeline: pipeline)
+ test_b_job = create(:ci_build, name: 'test-b', ci_stage: test_stage, pipeline: pipeline)
create(:ci_build_need, build: test_b_job, name: 'docker 2 2')
expect do
@@ -409,7 +409,8 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
it 'does not generate N+1 queries', :request_store, :use_sql_query_cache do
# create extra statuses
- create(:generic_commit_status, :pending, name: 'generic-build-a', pipeline: pipeline, stage_idx: 0, stage: 'build')
+ external_stage = create(:ci_stage, position: 10, name: 'external', project: project, pipeline: pipeline)
+ create(:generic_commit_status, :pending, name: 'generic-build-a', pipeline: pipeline, ci_stage: external_stage)
create(:ci_bridge, :failed, name: 'deploy-a', pipeline: pipeline, stage_idx: 2, stage: 'deploy')
# warm up
@@ -419,7 +420,7 @@ RSpec.describe 'getting pipeline information nested in a project', feature_categ
post_graphql(query, current_user: current_user)
end
- create(:generic_commit_status, :pending, name: 'generic-build-b', pipeline: pipeline, stage_idx: 0, stage: 'build')
+ create(:generic_commit_status, :pending, name: 'generic-build-b', pipeline: pipeline, ci_stage: external_stage)
create(:ci_build, :failed, name: 'test-a', pipeline: pipeline, stage_idx: 1, stage: 'test')
create(:ci_build, :running, name: 'test-b', pipeline: pipeline, stage_idx: 1, stage: 'test')
create(:ci_build, :pending, name: 'deploy-b', pipeline: pipeline, stage_idx: 2, stage: 'deploy')
diff --git a/spec/requests/api/graphql/project/runners_spec.rb b/spec/requests/api/graphql/project/runners_spec.rb
index 7304de7bec6..bee7ce2e372 100644
--- a/spec/requests/api/graphql/project/runners_spec.rb
+++ b/spec/requests/api/graphql/project/runners_spec.rb
@@ -53,16 +53,4 @@ RSpec.describe 'Project.runners', feature_category: :runner do
expect(graphql_data_at(:project, :runners, :nodes)).to be_empty
end
end
-
- context 'when on_demand_scans_runner_tags feature flag is disabled' do
- before do
- stub_feature_flags(on_demand_scans_runner_tags: false)
- end
-
- it 'returns no runners' do
- post_graphql(query, current_user: user)
-
- expect(graphql_data_at(:project, :runners, :nodes)).to be_empty
- end
- end
end
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index a59da706a8a..de35c943749 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -263,7 +263,7 @@ RSpec.describe 'getting a work item list for a project', feature_category: :team
GRAPHQL
end
- before do
+ before_all do
create_notes(item1, "some note1")
create_notes(item2, "some note2")
end
diff --git a/spec/requests/api/graphql/user_spec.rb b/spec/requests/api/graphql/user_spec.rb
index 2e1e4971767..3e82d783a18 100644
--- a/spec/requests/api/graphql/user_spec.rb
+++ b/spec/requests/api/graphql/user_spec.rb
@@ -58,4 +58,45 @@ RSpec.describe 'User', feature_category: :users do
)
end
end
+
+ describe 'email fields' do
+ before_all do
+ current_user.commit_email = current_user.emails.first.email
+ current_user.save!
+ end
+
+ let_it_be(:query) do
+ graphql_query_for(
+ :user,
+ { username: current_user.username },
+ 'emails { nodes { email } } commitEmail namespaceCommitEmails { nodes { id } }'
+ )
+ end
+
+ let_it_be(:email_1) { create(:email, user: current_user) }
+ let_it_be(:email_2) { create(:email, user: current_user) }
+ let_it_be(:namespace_commit_email_1) { create(:namespace_commit_email, email: email_1) }
+ let_it_be(:namespace_commit_email_2) { create(:namespace_commit_email, email: email_2) }
+
+ context 'with permission' do
+ it 'returns the relevant email details' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_data['user']['emails']['nodes'].pluck('email')).to match_array(
+ current_user.emails.map(&:email))
+ expect(graphql_data['user']['namespaceCommitEmails']['nodes']).not_to be_empty
+ expect(graphql_data['user']['commitEmail']).to eq(current_user.commit_email)
+ end
+ end
+
+ context 'without permission' do
+ it 'does not return email details' do
+ post_graphql(query, current_user: create(:user))
+
+ expect(graphql_data['user']['emails']['nodes']).to be_empty
+ expect(graphql_data['user']['namespaceCommitEmails']['nodes']).to be_empty
+ expect(graphql_data['user']['commitEmail']).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index df7dbaea420..6b5d437df83 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -193,6 +193,24 @@ RSpec.describe 'Query.work_item(id)', feature_category: :team_planning do
)
end
end
+
+ context 'when ordered by default by created_at' do
+ let_it_be(:newest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.from_now) }
+ let_it_be(:oldest_child) { create(:work_item, :task, project: project, created_at: 5.minutes.ago) }
+ let_it_be(:newest_link) { create(:parent_link, work_item_parent: work_item, work_item: newest_child) }
+ let_it_be(:oldest_link) { create(:parent_link, work_item_parent: work_item, work_item: oldest_child) }
+
+ let(:hierarchy_widget) { work_item_data['widgets'].find { |widget| widget['type'] == 'HIERARCHY' } }
+ let(:hierarchy_children) { hierarchy_widget['children']['nodes'] }
+
+ it 'places the oldest child item to the beginning of the children list' do
+ expect(hierarchy_children.first['id']).to eq(oldest_child.to_gid.to_s)
+ end
+
+ it 'places the newest child item to the end of the children list' do
+ expect(hierarchy_children.last['id']).to eq(newest_child.to_gid.to_s)
+ end
+ end
end
describe 'assignees widget' do
diff --git a/spec/requests/api/group_boards_spec.rb b/spec/requests/api/group_boards_spec.rb
index 01f0e6e2061..acc30b2c137 100644
--- a/spec/requests/api/group_boards_spec.rb
+++ b/spec/requests/api/group_boards_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::GroupBoards, feature_category: :team_planning do
+RSpec.describe API::GroupBoards, :with_license, feature_category: :team_planning do
let_it_be(:user) { create(:user) }
let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb
index 565365506a7..9dd5fe6f7c4 100644
--- a/spec/requests/api/group_export_spec.rb
+++ b/spec/requests/api/group_export_spec.rb
@@ -173,6 +173,8 @@ RSpec.describe API::GroupExport, feature_category: :importers do
let(:status_path) { "/groups/#{group.id}/export_relations/status" }
before do
+ stub_application_setting(bulk_import_enabled: true)
+
group.add_owner(user)
end
@@ -212,11 +214,12 @@ RSpec.describe API::GroupExport, feature_category: :importers do
context 'when export_file.file does not exist' do
it 'returns 404' do
- allow(upload).to receive(:export_file).and_return(nil)
+ allow(export).to receive(:upload).and_return(nil)
get api(download_path, user)
expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Not found')
end
end
end
@@ -234,5 +237,11 @@ RSpec.describe API::GroupExport, feature_category: :importers do
expect(json_response.pluck('status')).to contain_exactly(-1, 0, 1)
end
end
+
+ context 'when bulk import is disabled' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(path, user) }
+ end
+ end
end
end
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index dce82f1cf37..0d75bb94144 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -6,33 +6,35 @@ RSpec.describe API::ImportGithub, feature_category: :importers do
let(:token) { "asdasd12345" }
let(:provider) { :github }
let(:access_params) { { github_access_token: token } }
+ let(:provider_username) { user.username }
+ let(:provider_user) { double('provider', login: provider_username).as_null_object }
+ let(:provider_repo) do
+ {
+ name: 'vim',
+ full_name: "#{provider_username}/vim",
+ owner: double('provider', login: provider_username),
+ description: 'provider',
+ private: false,
+ clone_url: 'https://fake.url/vim.git',
+ has_wiki: true
+ }
+ end
- describe "POST /import/github" do
- let(:user) { create(:user) }
- let(:project) { create(:project) }
- let(:provider_username) { user.username }
- let(:provider_user) { double('provider', login: provider_username) }
- let(:provider_repo) do
- {
- name: 'vim',
- full_name: "#{provider_username}/vim",
- owner: double('provider', login: provider_username),
- description: 'provider',
- private: false,
- clone_url: 'https://fake.url/vim.git',
- has_wiki: true
- }
- end
+ let(:client) { double('client', user: provider_user, repository: provider_repo) }
- before do
- Grape::Endpoint.before_each do |endpoint|
- allow(endpoint).to receive(:client).and_return(double('client', user: provider_user, repository: provider_repo).as_null_object)
- end
+ before do
+ Grape::Endpoint.before_each do |endpoint|
+ allow(endpoint).to receive(:client).and_return(client)
end
+ end
- after do
- Grape::Endpoint.before_each nil
- end
+ after do
+ Grape::Endpoint.before_each nil
+ end
+
+ describe "POST /import/github" do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
it 'rejects requests when Github Importer is disabled' do
stub_application_setting(import_sources: nil)
@@ -90,6 +92,23 @@ RSpec.describe API::ImportGithub, feature_category: :importers do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
+ context 'when target_namespace is blank' do
+ it 'returns 400 response' do
+ allow(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
+ .and_return(double(execute: project))
+
+ post api("/import/github", user), params: {
+ target_namespace: '',
+ personal_access_token: token,
+ repo_id: non_existing_record_id
+ }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq 'target_namespace is empty'
+ end
+ end
+
context 'when unauthenticated user' do
it 'returns 403 response' do
post api("/import/github"), params: {
@@ -150,4 +169,78 @@ RSpec.describe API::ImportGithub, feature_category: :importers do
end
end
end
+
+ describe 'POST /import/github/gists' do
+ let_it_be(:user) { create(:user) }
+ let(:params) { { personal_access_token: token } }
+
+ context 'when feature github_import_gists is enabled' do
+ before do
+ stub_feature_flags(github_import_gists: true)
+ end
+
+ context 'when gists import was started' do
+ before do
+ allow(Import::Github::GistsImportService)
+ .to receive(:new).with(user, client, access_params)
+ .and_return(double(execute: { status: :success }))
+ end
+
+ it 'returns 202' do
+ post api('/import/github/gists', user), params: params
+
+ expect(response).to have_gitlab_http_status(:accepted)
+ end
+ end
+
+ context 'when gists import is in progress' do
+ before do
+ allow(Import::Github::GistsImportService)
+ .to receive(:new).with(user, client, access_params)
+ .and_return(double(execute: { status: :error, message: 'Import already in progress', http_status: :unprocessable_entity }))
+ end
+
+ it 'returns 422 error' do
+ post api('/import/github/gists', user), params: params
+
+ expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(json_response['errors']).to eq('Import already in progress')
+ end
+ end
+
+ context 'when unauthenticated user' do
+ it 'returns 403 error' do
+ post api('/import/github/gists'), params: params
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'when rate limit reached' do
+ before do
+ allow(Import::Github::GistsImportService)
+ .to receive(:new).with(user, client, access_params)
+ .and_raise(Gitlab::GithubImport::RateLimitError)
+ end
+
+ it 'returns 429 error' do
+ post api('/import/github/gists', user), params: params
+
+ expect(response).to have_gitlab_http_status(:too_many_requests)
+ end
+ end
+ end
+
+ context 'when feature github_import_gists is disabled' do
+ before do
+ stub_feature_flags(github_import_gists: false)
+ end
+
+ it 'returns 404 error' do
+ post api('/import/github/gists', user), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index f9284f21aaa..767f3e8b5b5 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -453,28 +453,10 @@ RSpec.describe API::Internal::Base, feature_category: :authentication_and_author
expect(json_response['message']['error']).to eq('This endpoint has been requested too many times. Try again later.')
end
- context 'when rate_limit_gitlab_shell feature flag is disabled' do
- before do
- stub_feature_flags(rate_limit_gitlab_shell: false)
- end
-
- it 'is not throttled by rate limiter' do
- expect(::Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
-
- subject
- end
- end
+ it 'is not throttled by rate limiter' do
+ expect(::Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
- context 'when rate_limit_gitlab_shell_by_ip feature flag is disabled' do
- before do
- stub_feature_flags(rate_limit_gitlab_shell_by_ip: false)
- end
-
- it 'is not throttled by rate limiter' do
- expect(::Gitlab::ApplicationRateLimiter).not_to receive(:throttled?)
-
- subject
- end
+ subject
end
context 'when the IP is in a trusted range' do
diff --git a/spec/requests/api/issues/get_project_issues_spec.rb b/spec/requests/api/issues/get_project_issues_spec.rb
index 70966d23576..6fc3903103b 100644
--- a/spec/requests/api/issues/get_project_issues_spec.rb
+++ b/spec/requests/api/issues/get_project_issues_spec.rb
@@ -11,15 +11,24 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let_it_be(:group) { create(:group, :public) }
- let(:user2) { create(:user) }
- let(:non_member) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:non_member) { create(:user) }
let_it_be(:guest) { create(:user) }
let_it_be(:author) { create(:author) }
let_it_be(:assignee) { create(:assignee) }
- let(:admin) { create(:user, :admin) }
- let(:issue_title) { 'foo' }
- let(:issue_description) { 'closed' }
- let!(:closed_issue) do
+ let_it_be(:admin) { create(:user, :admin) }
+
+ let_it_be(:milestone) { create(:milestone, title: '1.0.0', project: project) }
+ let_it_be(:empty_milestone) do
+ create(:milestone, title: '2.0.0', project: project)
+ end
+
+ let(:no_milestone_title) { 'None' }
+ let(:any_milestone_title) { 'Any' }
+
+ let_it_be(:issue_title) { 'foo' }
+ let_it_be(:issue_description) { 'closed' }
+ let_it_be(:closed_issue) do
create :closed_issue,
author: user,
assignees: [user],
@@ -31,7 +40,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
closed_at: 1.hour.ago
end
- let!(:confidential_issue) do
+ let_it_be(:confidential_issue) do
create :issue,
:confidential,
project: project,
@@ -41,7 +50,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
updated_at: 2.hours.ago
end
- let!(:issue) do
+ let_it_be(:issue) do
create :issue,
author: user,
assignees: [user],
@@ -53,22 +62,12 @@ RSpec.describe API::Issues, feature_category: :team_planning do
description: issue_description
end
- let_it_be(:label) do
- create(:label, title: 'label', color: '#FFAABB', project: project)
- end
+ let_it_be(:label) { create(:label, title: 'label', color: '#FFAABB', project: project) }
+ let_it_be(:label_link) { create(:label_link, label: label, target: issue) }
- let!(:label_link) { create(:label_link, label: label, target: issue) }
- let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
- let_it_be(:empty_milestone) do
- create(:milestone, title: '2.0.0', project: project)
- end
+ let_it_be(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
- let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) }
-
- let(:no_milestone_title) { 'None' }
- let(:any_milestone_title) { 'Any' }
-
- let!(:merge_request1) do
+ let_it_be(:merge_request1) do
create(:merge_request,
:simple,
author: user,
@@ -77,7 +76,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
description: "closes #{issue.to_reference}")
end
- let!(:merge_request2) do
+ let_it_be(:merge_request2) do
create(:merge_request,
:simple,
author: user,
@@ -101,7 +100,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
shared_examples 'project issues statistics' do
it 'returns project issues statistics' do
- get api("/issues_statistics", user), params: params
+ get api("/projects/#{project.id}/issues_statistics", current_user), params: params
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['statistics']).not_to be_nil
@@ -138,6 +137,8 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'issues_statistics' do
+ let(:current_user) { nil }
+
context 'no state is treated as all state' do
let(:params) { {} }
let(:counts) { { all: 2, closed: 1, opened: 1 } }
@@ -534,30 +535,32 @@ RSpec.describe API::Issues, feature_category: :team_planning do
end
context 'issues_statistics' do
+ let(:current_user) { user }
+
context 'no state is treated as all state' do
let(:params) { {} }
- let(:counts) { { all: 2, closed: 1, opened: 1 } }
+ let(:counts) { { all: 3, closed: 1, opened: 2 } }
it_behaves_like 'project issues statistics'
end
context 'statistics when all state is passed' do
let(:params) { { state: :all } }
- let(:counts) { { all: 2, closed: 1, opened: 1 } }
+ let(:counts) { { all: 3, closed: 1, opened: 2 } }
it_behaves_like 'project issues statistics'
end
context 'closed state is treated as all state' do
let(:params) { { state: :closed } }
- let(:counts) { { all: 2, closed: 1, opened: 1 } }
+ let(:counts) { { all: 3, closed: 1, opened: 2 } }
it_behaves_like 'project issues statistics'
end
context 'opened state is treated as all state' do
let(:params) { { state: :opened } }
- let(:counts) { { all: 2, closed: 1, opened: 1 } }
+ let(:counts) { { all: 3, closed: 1, opened: 2 } }
it_behaves_like 'project issues statistics'
end
@@ -592,7 +595,7 @@ RSpec.describe API::Issues, feature_category: :team_planning do
context 'sort does not affect statistics ' do
let(:params) { { state: :opened, order_by: 'updated_at' } }
- let(:counts) { { all: 2, closed: 1, opened: 1 } }
+ let(:counts) { { all: 3, closed: 1, opened: 2 } }
it_behaves_like 'project issues statistics'
end
diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb
index 94f0443e14a..b89db82b150 100644
--- a/spec/requests/api/issues/issues_spec.rb
+++ b/spec/requests/api/issues/issues_spec.rb
@@ -145,6 +145,11 @@ RSpec.describe API::Issues, feature_category: :team_planning do
let(:result) { issuable.id }
end
+ it_behaves_like 'issuable API rate-limited search' do
+ let(:url) { '/issues' }
+ let(:issuable) { issue }
+ end
+
it 'returns authentication error without any scope' do
get api('/issues')
diff --git a/spec/requests/api/markdown_golden_master_spec.rb b/spec/requests/api/markdown_golden_master_spec.rb
deleted file mode 100644
index 1bb5a1d67ae..00000000000
--- a/spec/requests/api/markdown_golden_master_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-# See spec/fixtures/markdown/markdown_golden_master_examples.yml for documentation on how this spec works.
-RSpec.describe API::Markdown, 'Golden Master', feature_category: :team_planning do
- markdown_yml_file_path = File.expand_path('../../fixtures/markdown/markdown_golden_master_examples.yml', __dir__)
- include_context 'API::Markdown Golden Master shared context', markdown_yml_file_path
-end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 0b69000ae7e..4cd93603c31 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -55,6 +55,11 @@ RSpec.describe API::MergeRequests, feature_category: :source_code_management do
let(:issuable) { merge_request }
let(:result) { [merge_request_merged.id, merge_request_locked.id, merge_request_closed.id, merge_request.id] }
end
+
+ it_behaves_like 'issuable API rate-limited search' do
+ let(:url) { endpoint_path }
+ let(:issuable) { merge_request }
+ end
end
context 'when authenticated' do
@@ -663,6 +668,11 @@ RSpec.describe API::MergeRequests, feature_category: :source_code_management do
let(:result) { [merge_request_merged.id, merge_request_locked.id, merge_request_closed.id, merge_request.id] }
end
+ it_behaves_like 'issuable API rate-limited search' do
+ let(:url) { '/merge_requests' }
+ let(:issuable) { merge_request }
+ end
+
it "returns authentication error without any scope" do
get api("/merge_requests")
diff --git a/spec/requests/api/ml/mlflow_spec.rb b/spec/requests/api/ml/mlflow_spec.rb
index c1ed7d56ba4..fdf115f7e92 100644
--- a/spec/requests/api/ml/mlflow_spec.rb
+++ b/spec/requests/api/ml/mlflow_spec.rb
@@ -347,6 +347,7 @@ RSpec.describe API::Ml::Mlflow, feature_category: :mlops do
{
experiment_id: experiment.iid.to_s,
start_time: Time.now.to_i,
+ run_name: "A new Run",
tags: [
{ key: 'hello', value: 'world' }
]
@@ -359,6 +360,7 @@ RSpec.describe API::Ml::Mlflow, feature_category: :mlops do
expected_properties = {
'experiment_id' => params[:experiment_id],
'user_id' => current_user.id.to_s,
+ 'run_name' => "A new Run",
'start_time' => params[:start_time],
'status' => 'RUNNING',
'lifecycle_stage' => 'active'
@@ -407,7 +409,7 @@ RSpec.describe API::Ml::Mlflow, feature_category: :mlops do
'experiment_id' => candidate.experiment.iid.to_s,
'user_id' => candidate.user.id.to_s,
'start_time' => candidate.start_time,
- 'artifact_uri' => "http://www.example.com/api/v4/projects/#{project_id}/packages/generic/ml_candidate_#{candidate.iid}/-/",
+ 'artifact_uri' => "http://www.example.com/api/v4/projects/#{project_id}/packages/generic/ml_candidate_#{candidate.id}/-/",
'status' => "RUNNING",
'lifecycle_stage' => "active"
}
@@ -426,8 +428,8 @@ RSpec.describe API::Ml::Mlflow, feature_category: :mlops do
{ 'key' => candidate.params[1].name, 'value' => candidate.params[1].value }
],
'tags' => [
- { 'key' => 'metadata_1', 'value' => 'value1' },
- { 'key' => 'metadata_2', 'value' => 'value2' }
+ { 'key' => candidate.metadata[0].name, 'value' => candidate.metadata[0].value },
+ { 'key' => candidate.metadata[1].name, 'value' => candidate.metadata[1].value }
]
})
end
@@ -450,7 +452,7 @@ RSpec.describe API::Ml::Mlflow, feature_category: :mlops do
'user_id' => candidate.user.id.to_s,
'start_time' => candidate.start_time,
'end_time' => params[:end_time],
- 'artifact_uri' => "http://www.example.com/api/v4/projects/#{project_id}/packages/generic/ml_candidate_#{candidate.iid}/-/",
+ 'artifact_uri' => "http://www.example.com/api/v4/projects/#{project_id}/packages/generic/ml_candidate_#{candidate.id}/-/",
'status' => 'FAILED',
'lifecycle_stage' => 'active'
}
diff --git a/spec/requests/api/nuget_group_packages_spec.rb b/spec/requests/api/nuget_group_packages_spec.rb
index 9de612f7bc7..4335ad75ab6 100644
--- a/spec/requests/api/nuget_group_packages_spec.rb
+++ b/spec/requests/api/nuget_group_packages_spec.rb
@@ -17,25 +17,51 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
shared_examples 'handling all endpoints' do
describe 'GET /api/v4/groups/:id/-/packages/nuget' do
- it_behaves_like 'handling nuget service requests', anonymous_requests_example_name: 'rejects nuget packages access', anonymous_requests_status: :unauthorized do
+ it_behaves_like 'handling nuget service requests',
+ example_names_with_status: {
+ anonymous_requests_example_name: 'rejects nuget packages access',
+ anonymous_requests_status: :unauthorized,
+ guest_requests_example_name: 'process nuget service index request',
+ guest_requests_status: :success
+ } do
let(:url) { "/groups/#{target.id}/-/packages/nuget/index.json" }
end
end
describe 'GET /api/v4/groups/:id/-/packages/nuget/metadata/*package_name/index' do
- it_behaves_like 'handling nuget metadata requests with package name', anonymous_requests_example_name: 'rejects nuget packages access', anonymous_requests_status: :unauthorized do
+ it_behaves_like 'handling nuget metadata requests with package name',
+ example_names_with_status:
+ {
+ anonymous_requests_example_name: 'rejects nuget packages access',
+ anonymous_requests_status: :unauthorized,
+ guest_requests_example_name: 'rejects nuget packages access',
+ guest_requests_status: :not_found
+ } do
let(:url) { "/groups/#{target.id}/-/packages/nuget/metadata/#{package_name}/index.json" }
end
end
describe 'GET /api/v4/groups/:id/-/packages/nuget/metadata/*package_name/*package_version' do
- it_behaves_like 'handling nuget metadata requests with package name and package version', anonymous_requests_example_name: 'rejects nuget packages access', anonymous_requests_status: :unauthorized do
+ it_behaves_like 'handling nuget metadata requests with package name and package version',
+ example_names_with_status:
+ {
+ anonymous_requests_example_name: 'rejects nuget packages access',
+ anonymous_requests_status: :unauthorized,
+ guest_requests_example_name: 'rejects nuget packages access',
+ guest_requests_status: :not_found
+ } do
let(:url) { "/groups/#{target.id}/-/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
end
end
describe 'GET /api/v4/groups/:id/-/packages/nuget/query' do
- it_behaves_like 'handling nuget search requests', anonymous_requests_example_name: 'rejects nuget packages access', anonymous_requests_status: :unauthorized do
+ it_behaves_like 'handling nuget search requests',
+ example_names_with_status: {
+ anonymous_requests_example_name: 'rejects nuget packages access',
+ anonymous_requests_status: :unauthorized,
+ guest_requests_example_name: 'process empty nuget search request',
+ guest_requests_status: :success
+ } do
let(:url) { "/groups/#{target.id}/-/packages/nuget/query?#{query_parameters.to_query}" }
end
end
@@ -133,13 +159,13 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
describe 'GET /api/v4/groups/:id/-/packages/nuget/metadata/*package_name/index' do
let(:url) { "/groups/#{group.id}/-/packages/nuget/metadata/#{package_name}/index.json" }
- it_behaves_like 'returning response status', :forbidden
+ it_behaves_like 'returning response status', :success
end
describe 'GET /api/v4/groups/:id/-/packages/nuget/metadata/*package_name/*package_version' do
let(:url) { "/groups/#{group.id}/-/packages/nuget/metadata/#{package_name}/#{package.version}.json" }
- it_behaves_like 'returning response status', :forbidden
+ it_behaves_like 'returning response status', :success
end
describe 'GET /api/v4/groups/:id/-/packages/nuget/query' do
@@ -150,7 +176,7 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
let(:query_parameters) { { q: search_term, take: take, skip: skip, prerelease: include_prereleases }.compact }
let(:url) { "/groups/#{group.id}/-/packages/nuget/query?#{query_parameters.to_query}" }
- it_behaves_like 'returning response status', :forbidden
+ it_behaves_like 'returning response status', :success
end
end
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index 65fcf9e006a..ba1fb5105b8 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -265,6 +265,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
project_id: project.id,
namespace_id: project.namespace.id,
root_namespace_id: project.root_namespace.id,
+ domain_id: kind_of(Numeric),
domain: params[:domain]
)
@@ -393,6 +394,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
project_id: project.id,
namespace_id: project.namespace.id,
root_namespace_id: project.root_namespace.id,
+ domain_id: pages_domain_secure.id,
domain: pages_domain_secure.domain
)
end
@@ -556,6 +558,7 @@ RSpec.describe API::PagesDomains, feature_category: :pages do
project_id: project.id,
namespace_id: project.namespace.id,
root_namespace_id: project.root_namespace.id,
+ domain_id: pages_domain.id,
domain: pages_domain.domain
)
diff --git a/spec/requests/api/project_debian_distributions_spec.rb b/spec/requests/api/project_debian_distributions_spec.rb
index 9807f177c5d..dfe93e9fbad 100644
--- a/spec/requests/api/project_debian_distributions_spec.rb
+++ b/spec/requests/api/project_debian_distributions_spec.rb
@@ -5,7 +5,17 @@ RSpec.describe API::ProjectDebianDistributions, feature_category: :package_regis
include HttpBasicAuthHelpers
include WorkhorseHelpers
- include_context 'Debian repository shared context', :project, true do
+ include_context 'Debian repository shared context', :project, false do
+ shared_examples 'accept GET request on private project with access to package registry for everyone' do
+ include_context 'Debian repository access', :private, :anonymous, :basic do
+ before do
+ container.project_feature.reload.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'Debian distributions GET request', :success
+ end
+ end
+
describe 'POST projects/:id/debian_distributions' do
let(:method) { :post }
let(:url) { "/projects/#{container.id}/debian_distributions" }
@@ -18,24 +28,37 @@ RSpec.describe API::ProjectDebianDistributions, feature_category: :package_regis
it_behaves_like 'Debian distributions write endpoint', 'GET', :bad_request, /^{"message":{"codename":\["has already been taken"\]}}$/
end
+
+ context 'with access to package registry for everyone' do
+ include_context 'Debian repository access', :private, :anonymous, :basic do
+ before do
+ container.project_feature.reload.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'Debian distributions POST request', :not_found
+ end
+ end
end
describe 'GET projects/:id/debian_distributions' do
let(:url) { "/projects/#{container.id}/debian_distributions" }
it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^\[{.*"codename":"existing-codename",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/debian_distributions/:codename' do
let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}" }
it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^{.*"codename":"existing-codename",.*"components":\["existing-component"\],.*"architectures":\["all","existing-arch"\]/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'GET projects/:id/debian_distributions/:codename/key.asc' do
let(:url) { "/projects/#{container.id}/debian_distributions/#{distribution.codename}/key.asc" }
it_behaves_like 'Debian distributions read endpoint', 'GET', :success, /^-----BEGIN PGP PUBLIC KEY BLOCK-----/
+ it_behaves_like 'accept GET request on private project with access to package registry for everyone'
end
describe 'PUT projects/:id/debian_distributions/:codename' do
diff --git a/spec/requests/api/project_export_spec.rb b/spec/requests/api/project_export_spec.rb
index fdd76c63069..096f0b73b4c 100644
--- a/spec/requests/api/project_export_spec.rb
+++ b/spec/requests/api/project_export_spec.rb
@@ -511,6 +511,10 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
let_it_be(:status_path) { "/projects/#{project.id}/export_relations/status" }
+ before do
+ stub_application_setting(bulk_import_enabled: true)
+ end
+
context 'when user is a maintainer' do
before do
project.add_maintainer(user)
@@ -584,9 +588,9 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
end
- context 'with bulk_import FF disabled' do
+ context 'with bulk_import is disabled' do
before do
- stub_feature_flags(bulk_import: false)
+ stub_application_setting(bulk_import_enabled: false)
end
describe 'POST /projects/:id/export_relations' do
@@ -641,5 +645,11 @@ RSpec.describe API::ProjectExport, :clean_gitlab_redis_cache, feature_category:
end
end
end
+
+ context 'when bulk import is disabled' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(path, user) }
+ end
+ end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 6e8168c0ee1..d62f8a32453 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1169,7 +1169,7 @@ RSpec.describe API::Projects do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it "assigns attributes to project", :aggregate_failures do
+ it 'assigns attributes to project', :aggregate_failures do
project = attributes_for(:project, {
path: 'camelCasePath',
issues_enabled: false,
@@ -1198,6 +1198,11 @@ RSpec.describe API::Projects do
attrs[:feature_flags_access_level] = 'disabled'
attrs[:infrastructure_access_level] = 'disabled'
attrs[:monitor_access_level] = 'disabled'
+ attrs[:snippets_access_level] = 'disabled'
+ attrs[:wiki_access_level] = 'disabled'
+ attrs[:builds_access_level] = 'disabled'
+ attrs[:merge_requests_access_level] = 'disabled'
+ attrs[:issues_access_level] = 'disabled'
end
post api('/projects', user), params: project
@@ -1228,6 +1233,11 @@ RSpec.describe API::Projects do
expect(project.project_feature.feature_flags_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.infrastructure_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.monitor_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.builds_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED)
+ expect(project.project_feature.snippets_access_level).to eq(ProjectFeature::DISABLED)
end
it 'assigns container_registry_enabled to project', :aggregate_failures do
@@ -2278,7 +2288,7 @@ RSpec.describe API::Projects do
end
end
- context 'when authenticated as an admin' do
+ context 'when authenticated as an admin', :with_license do
before do
stub_container_registry_config(enabled: true, host_port: 'registry.example.org:5000')
end
diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb
index 6036960c43c..4a7821fcb0a 100644
--- a/spec/requests/api/release/links_spec.rb
+++ b/spec/requests/api/release/links_spec.rb
@@ -222,6 +222,24 @@ RSpec.describe API::Release::Links, feature_category: :release_orchestration do
expect(response).to match_response_schema('release/link')
end
+ context 'when using `direct_asset_path`' do
+ before do
+ params[:direct_asset_path] = params.delete(:filepath)
+ end
+
+ it 'creates a new release link successfully' do
+ expect do
+ post api("/projects/#{project.id}/releases/v0.1/assets/links", maintainer), params: params
+ end.to change { Releases::Link.count }.by(1)
+
+ release.reload
+
+ expect(last_release_link.name).to eq('awesome-app.dmg')
+ expect(last_release_link.filepath).to eq('/binaries/awesome-app.dmg')
+ expect(last_release_link.url).to eq('https://example.com/download/awesome-app.dmg')
+ end
+ end
+
context 'when using JOB-TOKEN auth' do
let(:job) { create(:ci_build, :running, user: maintainer) }
@@ -357,6 +375,15 @@ RSpec.describe API::Release::Links, feature_category: :release_orchestration do
expect(response).to match_response_schema('release/link')
end
+ context 'when using `direct_asset_path`' do
+ it 'updates the release link' do
+ put api("/projects/#{project.id}/releases/v0.1/assets/links/#{release_link.id}", maintainer),
+ params: params.merge(direct_asset_path: '/binaries/awesome-app.msi')
+
+ expect(json_response['direct_asset_url']).to eq("http://localhost/#{project.namespace.path}/#{project.name}/-/releases/#{release.tag}/downloads/binaries/awesome-app.msi")
+ end
+ end
+
context 'when using JOB-TOKEN auth' do
let(:job) { create(:ci_build, :running, user: maintainer) }
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index a1aff9a6b1c..e209ad2b2d5 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -573,7 +573,7 @@ RSpec.describe API::Releases, feature_category: :release_orchestration do
end
end
- describe 'GET /projects/:id/releases/:tag_name/downloads/*file_path' do
+ describe 'GET /projects/:id/releases/:tag_name/downloads/*direct_asset_path' do
let!(:release) { create(:release, project: project, tag: 'v0.1', author: maintainer) }
let!(:link) { create(:release_link, release: release, url: "#{url}#{filepath}", filepath: filepath) }
let(:filepath) { '/bin/bigfile.exe' }
@@ -637,6 +637,16 @@ RSpec.describe API::Releases, feature_category: :release_orchestration do
end
end
+ context 'when direct_asset_path is used' do
+ let(:direct_asset_path) { filepath }
+
+ it 'redirects to the file download URL successfully' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads#{direct_asset_path}", maintainer)
+
+ expect(response).to redirect_to("#{url}#{direct_asset_path}")
+ end
+ end
+
context 'when filepath does not exists' do
it 'returns 404 for maintater' do
get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", maintainer)
@@ -911,6 +921,22 @@ RSpec.describe API::Releases, feature_category: :release_orchestration do
end.not_to change { Project.find_by_id(project.id).repository.tag_count }
end
+ context 'when using `direct_asset_path` for the asset link' do
+ before do
+ params[:direct_asset_path] = params.delete(:filepath)
+ end
+
+ it 'creates a new release successfully' do
+ expect do
+ post api("/projects/#{project.id}/releases", maintainer), params: params
+ end.to change { Release.count }.by(1)
+
+ release = project.releases.last
+
+ expect(release.links.last.filepath).to eq('/permanent/path/to/runbook')
+ end
+ end
+
context 'with protected tag' do
context 'when user has access to the protected tag' do
let!(:protected_tag) { create(:protected_tag, :developers_can_create, name: '*', project: project) }
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 393ada1da4f..555ba2bc978 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -353,15 +353,9 @@ RSpec.describe API::Repositories, feature_category: :source_code_management do
expect(response).to have_gitlab_http_status(:too_many_requests)
end
- context "when hotlinking detection is enabled" do
- before do
- stub_feature_flags(repository_archive_hotlinking_interception: true)
- end
-
- it_behaves_like "hotlink interceptor" do
- let(:http_request) do
- get api(route, current_user), headers: headers
- end
+ it_behaves_like "hotlink interceptor" do
+ let(:http_request) do
+ get api(route, current_user), headers: headers
end
end
end
diff --git a/spec/requests/api/rubygem_packages_spec.rb b/spec/requests/api/rubygem_packages_spec.rb
index 6f048fa57a8..34cf6033811 100644
--- a/spec/requests/api/rubygem_packages_spec.rb
+++ b/spec/requests/api/rubygem_packages_spec.rb
@@ -55,11 +55,11 @@ RSpec.describe API::RubygemPackages, feature_category: :package_registry do
end
where(:user_role, :token_type, :valid_token, :status) do
- :guest | :personal_access_token | true | :not_found
+ :guest | :personal_access_token | true | :forbidden
:guest | :personal_access_token | false | :unauthorized
:guest | :deploy_token | true | :not_found
:guest | :deploy_token | false | :unauthorized
- :guest | :job_token | true | :not_found
+ :guest | :job_token | true | :forbidden
:guest | :job_token | false | :unauthorized
:reporter | :personal_access_token | true | :not_found
:reporter | :personal_access_token | false | :unauthorized
@@ -174,6 +174,17 @@ RSpec.describe API::RubygemPackages, feature_category: :package_registry do
end
end
+ context 'with access to package registry for everyone' do
+ let(:snowplow_gitlab_standard_context) { { project: project, namespace: project.namespace, property: 'i_package_rubygems_user' } }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'Rubygems gem download', :anonymous, :success
+ end
+
context 'with package files pending destruction' do
let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, :xml, package: package, file_name: file_name) }
@@ -423,5 +434,16 @@ RSpec.describe API::RubygemPackages, feature_category: :package_registry do
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member]
end
end
+
+ context 'with access to package registry for everyone' do
+ let(:params) { {} }
+
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+ end
+
+ it_behaves_like 'dependency endpoint success', :anonymous, :success
+ end
end
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 430d3b7d187..035f53db12e 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -377,6 +377,26 @@ RSpec.describe API::Search, feature_category: :global_search do
end
end
+ context 'global search is disabled for the given scope' do
+ it 'returns forbidden response' do
+ allow_next_instance_of(SearchService) do |instance|
+ allow(instance).to receive(:global_search_enabled_for_scope?).and_return false
+ end
+ get api(endpoint, user), params: { search: 'awesome', scope: 'issues' }
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'global search is enabled for the given scope' do
+ it 'returns forbidden response' do
+ allow_next_instance_of(SearchService) do |instance|
+ allow(instance).to receive(:global_search_enabled_for_scope?).and_return true
+ end
+ get api(endpoint, user), params: { search: 'awesome', scope: 'issues' }
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
it 'increments the custom search sli error rate with error false if no error occurred' do
expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_error_rate).with(
error: false,
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index e93ef52ef03..4d85849cff3 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -65,6 +65,7 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
expect(json_response['can_create_group']).to eq(true)
expect(json_response['jira_connect_application_key']).to eq(nil)
expect(json_response['jira_connect_proxy_url']).to eq(nil)
+ expect(json_response['user_defaults_to_private_profile']).to eq(false)
end
end
@@ -166,7 +167,9 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
can_create_group: false,
jira_connect_application_key: '123',
jira_connect_proxy_url: 'http://example.com',
- bulk_import_enabled: false
+ bulk_import_enabled: false,
+ allow_runner_registration_token: true,
+ user_defaults_to_private_profile: true
}
expect(response).to have_gitlab_http_status(:ok)
@@ -232,6 +235,8 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
expect(json_response['jira_connect_application_key']).to eq('123')
expect(json_response['jira_connect_proxy_url']).to eq('http://example.com')
expect(json_response['bulk_import_enabled']).to be(false)
+ expect(json_response['allow_runner_registration_token']).to be(true)
+ expect(json_response['user_defaults_to_private_profile']).to be(true)
end
end
@@ -801,5 +806,62 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting, featu
.to include(a_string_matching('is not a number'))
end
end
+
+ context 'with housekeeping enabled' do
+ it 'at least one of housekeeping_incremental_repack_period or housekeeping_optimize_repository_period is required' do
+ put api("/application/settings", admin), params: {
+ housekeeping_enabled: true
+ }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(
+ "housekeeping_incremental_repack_period, housekeeping_optimize_repository_period are missing, exactly one parameter must be provided"
+ )
+ end
+
+ context 'when housekeeping_incremental_repack_period is specified' do
+ it 'requires all three housekeeping settings' do
+ put api("/application/settings", admin), params: {
+ housekeeping_enabled: true,
+ housekeeping_incremental_repack_period: 10
+ }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(
+ "housekeeping_full_repack_period, housekeeping_gc_period, housekeeping_incremental_repack_period provide all or none of parameters"
+ )
+ end
+
+ it 'returns housekeeping_optimize_repository_period value for all housekeeping settings attributes' do
+ put api("/application/settings", admin), params: {
+ housekeeping_enabled: true,
+ housekeeping_gc_period: 150,
+ housekeeping_full_repack_period: 125,
+ housekeeping_incremental_repack_period: 100
+ }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['housekeeping_optimize_repository_period']).to eq(100)
+ expect(json_response['housekeeping_full_repack_period']).to eq(100)
+ expect(json_response['housekeeping_gc_period']).to eq(100)
+ expect(json_response['housekeeping_incremental_repack_period']).to eq(100)
+ end
+ end
+
+ context 'when housekeeping_optimize_repository_period is specified' do
+ it 'returns housekeeping_optimize_repository_period value for all housekeeping settings attributes' do
+ put api("/application/settings", admin), params: {
+ housekeeping_enabled: true,
+ housekeeping_optimize_repository_period: 100
+ }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['housekeeping_optimize_repository_period']).to eq(100)
+ expect(json_response['housekeeping_full_repack_period']).to eq(100)
+ expect(json_response['housekeeping_gc_period']).to eq(100)
+ expect(json_response['housekeeping_incremental_repack_period']).to eq(100)
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/snippet_repository_storage_moves_spec.rb b/spec/requests/api/snippet_repository_storage_moves_spec.rb
index 6081531aee9..9afd8147eb6 100644
--- a/spec/requests/api/snippet_repository_storage_moves_spec.rb
+++ b/spec/requests/api/snippet_repository_storage_moves_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::SnippetRepositoryStorageMoves, feature_category: :gitaly do
+RSpec.describe API::SnippetRepositoryStorageMoves, :with_license, feature_category: :gitaly do
it_behaves_like 'repository_storage_moves API', 'snippets' do
let_it_be(:container) { create(:snippet, :repository).tap { |snippet| snippet.create_repository } }
let_it_be(:storage_move) { create(:snippet_repository_storage_move, :scheduled, container: container) }
diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb
index 93b2435c601..4a4692684e3 100644
--- a/spec/requests/api/suggestions_spec.rb
+++ b/spec/requests/api/suggestions_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe API::Suggestions, feature_category: :code_review do
+RSpec.describe API::Suggestions, feature_category: :code_review_workflow do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index 5a342f79926..8c3bdd5a9f0 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
include DesignManagementTestHelpers
let_it_be(:group) { create(:group) }
+ let_it_be(:group_2) { create(:group) }
let_it_be(:project_1) { create(:project, :repository, group: group) }
let_it_be(:project_2) { create(:project) }
let_it_be(:author_1) { create(:user) }
@@ -15,7 +16,8 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
let_it_be(:work_item) { create(:work_item, :task, project: project_1) }
let_it_be(:merge_request) { create(:merge_request, source_project: project_1) }
let_it_be(:alert) { create(:alert_management_alert, project: project_1) }
- let_it_be(:group_request_todo) { create(:todo, author: author_1, user: john_doe, target: group, action: Todo::MEMBER_ACCESS_REQUESTED) }
+ let_it_be(:project_request_todo) { create(:todo, author: author_1, user: john_doe, target: project_2, action: Todo::MEMBER_ACCESS_REQUESTED) }
+ let_it_be(:group_request_todo) { create(:todo, author: author_1, user: john_doe, target: group_2, action: Todo::MEMBER_ACCESS_REQUESTED) }
let_it_be(:alert_todo) { create(:todo, project: project_1, author: john_doe, user: john_doe, target: alert) }
let_it_be(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
let_it_be(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe, target: issue) }
@@ -72,7 +74,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
- expect(json_response.length).to eq(7)
+ expect(json_response.length).to eq(8)
expect(json_response[0]).to include(
'id' => pending_5.id,
@@ -133,11 +135,23 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
'target_type' => 'Namespace',
'action_name' => 'member_access_requested',
'target' => hash_including(
- 'id' => group.id,
- 'name' => group.name,
- 'full_path' => group.full_path
+ 'id' => group_2.id,
+ 'name' => group_2.name,
+ 'full_path' => group_2.full_path
),
- 'target_url' => Gitlab::Routing.url_helpers.group_group_members_url(group, tab: 'access_requests')
+ 'target_url' => Gitlab::Routing.url_helpers.group_group_members_url(group_2, tab: 'access_requests')
+ )
+
+ expect(json_response[7]).to include(
+ 'target_type' => 'Project',
+ 'action_name' => 'member_access_requested',
+ 'target' => hash_including(
+ 'id' => project_2.id,
+ 'name' => project_2.name,
+ 'path' => project_2.path
+ ),
+ 'target_url' => Gitlab::Routing.url_helpers.project_project_members_url(project_2, tab: 'access_requests'),
+ 'body' => project_2.full_path
)
end
@@ -149,7 +163,7 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
get api('/todos', john_doe)
- expect(json_response.count).to eq(7)
+ expect(json_response.count).to eq(8)
expect(json_response.map { |t| t['id'] }).not_to include(no_access_todo.id, pending_4.id)
end
end
@@ -242,8 +256,10 @@ RSpec.describe API::Todos, feature_category: :source_code_management do
merge_request_3 = create(:merge_request, :jira_branch, source_project: new_todo.project)
create(:on_commit_todo, project: new_todo.project, author: author_1, user: john_doe, target: merge_request_3)
create(:todo, project: new_todo.project, author: author_2, user: john_doe, target: merge_request_3)
+ create(:todo, author: author_2, user: john_doe, target: project_2, action: Todo::MEMBER_ACCESS_REQUESTED)
+ create(:todo, author: author_2, user: john_doe, target: group_2, action: Todo::MEMBER_ACCESS_REQUESTED)
- expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(6)
+ expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control1).with_threshold(5)
control2 = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) }
create_issue_todo_for(john_doe)
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index bfb71d95f5e..c063187fdf4 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1454,6 +1454,46 @@ RSpec.describe API::Users, feature_category: :users do
include_examples 'does not allow the "read_user" scope'
end
+
+ context "`private_profile` attribute" do
+ context "based on the application setting" do
+ before do
+ stub_application_setting(user_defaults_to_private_profile: true)
+ end
+
+ let(:params) { attributes_for(:user) }
+
+ shared_examples_for 'creates the user with the value of `private_profile` based on the application setting' do
+ specify do
+ post api("/users", admin), params: params
+
+ expect(response).to have_gitlab_http_status(:created)
+ user = User.find_by(id: json_response['id'], private_profile: true)
+ expect(user).to be_present
+ end
+ end
+
+ context 'when the attribute is not overridden in params' do
+ it_behaves_like 'creates the user with the value of `private_profile` based on the application setting'
+ end
+
+ context 'when the attribute is overridden in params' do
+ it 'creates the user with the value of `private_profile` same as the value of the overridden param' do
+ post api("/users", admin), params: params.merge(private_profile: false)
+
+ expect(response).to have_gitlab_http_status(:created)
+ user = User.find_by(id: json_response['id'], private_profile: false)
+ expect(user).to be_present
+ end
+
+ context 'overridden as `nil`' do
+ let(:params) { attributes_for(:user, private_profile: nil) }
+
+ it_behaves_like 'creates the user with the value of `private_profile` based on the application setting'
+ end
+ end
+ end
+ end
end
describe "PUT /users/:id" do
@@ -1634,12 +1674,6 @@ RSpec.describe API::Users, feature_category: :users do
expect(user.reload.external?).to be_truthy
end
- it "private profile is false by default" do
- put api("/users/#{user.id}", admin), params: {}
-
- expect(user.reload.private_profile).to eq(false)
- end
-
it "does have default values for theme and color-scheme ID" do
put api("/users/#{user.id}", admin), params: {}
@@ -1647,13 +1681,6 @@ RSpec.describe API::Users, feature_category: :users do
expect(user.reload.color_scheme_id).to eq(Gitlab::ColorSchemes.default.id)
end
- it "updates private profile" do
- put api("/users/#{user.id}", admin), params: { private_profile: true }
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(user.reload.private_profile).to eq(true)
- end
-
it "updates viewing diffs file by file" do
put api("/users/#{user.id}", admin), params: { view_diffs_file_by_file: true }
@@ -1661,22 +1688,40 @@ RSpec.describe API::Users, feature_category: :users do
expect(user.reload.user_preference.view_diffs_file_by_file?).to eq(true)
end
- it "updates private profile to false when nil is given" do
- user.update!(private_profile: true)
+ context 'updating `private_profile`' do
+ it "updates private profile" do
+ current_value = user.private_profile
+ new_value = !current_value
- put api("/users/#{user.id}", admin), params: { private_profile: nil }
+ put api("/users/#{user.id}", admin), params: { private_profile: new_value }
- expect(response).to have_gitlab_http_status(:ok)
- expect(user.reload.private_profile).to eq(false)
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(user.reload.private_profile).to eq(new_value)
+ end
+
+ context 'when `private_profile` is set to `nil`' do
+ before do
+ stub_application_setting(user_defaults_to_private_profile: true)
+ end
- it "does not modify private profile when field is not provided" do
- user.update!(private_profile: true)
+ it "updates private_profile to value of the application setting" do
+ user.update!(private_profile: false)
- put api("/users/#{user.id}", admin), params: {}
+ put api("/users/#{user.id}", admin), params: { private_profile: nil }
- expect(response).to have_gitlab_http_status(:ok)
- expect(user.reload.private_profile).to eq(true)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(user.reload.private_profile).to eq(true)
+ end
+ end
+
+ it "does not modify private profile when field is not provided" do
+ user.update!(private_profile: true)
+
+ put api("/users/#{user.id}", admin), params: {}
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(user.reload.private_profile).to eq(true)
+ end
end
it "does not modify theme or color-scheme ID when field is not provided" do
@@ -3617,6 +3662,15 @@ RSpec.describe API::Users, feature_category: :users do
expect(response.body).to eq('true')
expect(user.reload.state).to eq('blocked')
end
+
+ it 'saves a custom attribute', :freeze_time, feature_category: :insider_threat do
+ block_user
+
+ custom_attribute = user.custom_attributes.last
+
+ expect(custom_attribute.key).to eq(UserCustomAttribute::BLOCKED_BY)
+ expect(custom_attribute.value).to eq("#{admin.username}/#{admin.id}+#{Time.current}")
+ end
end
context 'with an ldap blocked user' do
@@ -3708,6 +3762,15 @@ RSpec.describe API::Users, feature_category: :users do
expect(response).to have_gitlab_http_status(:created)
expect(blocked_user.reload.state).to eq('active')
end
+
+ it 'saves a custom attribute', :freeze_time, feature_category: :insider_threat do
+ unblock_user
+
+ custom_attribute = blocked_user.custom_attributes.last
+
+ expect(custom_attribute.key).to eq(UserCustomAttribute::UNBLOCKED_BY)
+ expect(custom_attribute.value).to eq("#{admin.username}/#{admin.id}+#{Time.current}")
+ end
end
context 'with a ldap blocked user' do
@@ -4045,60 +4108,164 @@ RSpec.describe API::Users, feature_category: :users do
end
end
- describe 'GET /user/status' do
- let(:path) { '/user/status' }
+ describe '/user/status' do
+ let(:user_status) { create(:user_status, clear_status_at: 8.hours.from_now) }
+ let(:user_with_status) { user_status.user }
+ let(:params) { {} }
+ let(:request_user) { user }
- it_behaves_like 'rendering user status'
- end
+ shared_examples '/user/status successful response' do
+ context 'when request is successful' do
+ let(:params) { { emoji: 'smirk', message: 'hello world' } }
- describe 'PUT /user/status' do
- it 'saves the status' do
- put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world' }
+ it 'saves the status' do
+ set_user_status
- expect(response).to have_gitlab_http_status(:success)
- expect(json_response['emoji']).to eq('smirk')
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response['emoji']).to eq('smirk')
+ expect(json_response['message']).to eq('hello world')
+ end
+ end
end
- it 'renders errors when the status was invalid' do
- put api('/user/status', user), params: { emoji: 'does not exist', message: 'hello world' }
+ shared_examples '/user/status unsuccessful response' do
+ context 'when request is unsuccessful' do
+ let(:params) { { emoji: 'does not exist', message: 'hello world' } }
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']['emoji']).to be_present
+ it 'renders errors' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']['emoji']).to be_present
+ end
+ end
end
- it 'deletes the status when passing empty values' do
- put api('/user/status', user)
+ shared_examples '/user/status passing nil for params' do
+ context 'when passing nil for params' do
+ let(:params) { { emoji: nil, message: nil, clear_status_after: nil } }
+ let(:request_user) { user_with_status }
- expect(response).to have_gitlab_http_status(:success)
- expect(user.reload.status).to be_nil
+ it 'deletes the status' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user_with_status.status).to be_nil
+ end
+ end
end
- context 'when clear_status_after is given' do
- it 'sets the clear_status_at column' do
- freeze_time do
+ shared_examples '/user/status clear_status_after field' do
+ context 'when clear_status_after is valid', :freeze_time do
+ let(:params) { { emoji: 'smirk', message: 'hello world', clear_status_after: '3_hours' } }
+
+ it 'sets the clear_status_at column' do
expected_clear_status_at = 3.hours.from_now
- put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: '3_hours' }
+ set_user_status
expect(response).to have_gitlab_http_status(:success)
- expect(user.status.reload.clear_status_at).to be_within(1.minute).of(expected_clear_status_at)
- expect(Time.parse(json_response["clear_status_at"])).to be_within(1.minute).of(expected_clear_status_at)
+ expect(user.status.clear_status_at).to be_like_time(expected_clear_status_at)
+ expect(Time.parse(json_response["clear_status_at"])).to be_like_time(expected_clear_status_at)
end
end
- it 'unsets the clear_status_at column' do
- user.create_status!(clear_status_at: 5.hours.ago)
+ context 'when clear_status_after is nil' do
+ let(:params) { { emoji: 'smirk', message: 'hello world', clear_status_after: nil } }
+ let(:request_user) { user_with_status }
- put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: nil }
+ it 'unsets the clear_status_at column' do
+ set_user_status
- expect(response).to have_gitlab_http_status(:success)
- expect(user.status.reload.clear_status_at).to be_nil
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user_with_status.status.clear_status_at).to be_nil
+ end
end
- it 'raises error when unknown status value is given' do
- put api('/user/status', user), params: { emoji: 'smirk', message: 'hello world', clear_status_after: 'wrong' }
+ context 'when clear_status_after is invalid' do
+ let(:params) { { emoji: 'smirk', message: 'hello world', clear_status_after: 'invalid' } }
- expect(response).to have_gitlab_http_status(:bad_request)
+ it 'raises error when unknown status value is given' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+ end
+
+ describe 'GET' do
+ let(:path) { '/user/status' }
+
+ it_behaves_like 'rendering user status'
+ end
+
+ describe 'PUT' do
+ subject(:set_user_status) { put api('/user/status', request_user), params: params }
+
+ include_examples '/user/status successful response'
+
+ include_examples '/user/status unsuccessful response'
+
+ include_examples '/user/status passing nil for params'
+
+ include_examples '/user/status clear_status_after field'
+
+ context 'when passing empty params' do
+ let(:request_user) { user_with_status }
+
+ it 'deletes the status' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user_with_status.status).to be_nil
+ end
+ end
+
+ context 'when clear_status_after is not given' do
+ let(:params) { { emoji: 'smirk', message: 'hello world' } }
+ let(:request_user) { user_with_status }
+
+ it 'unsets clear_status_at column' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user_with_status.status.clear_status_at).to be_nil
+ end
+ end
+ end
+
+ describe 'PATCH' do
+ subject(:set_user_status) { patch api('/user/status', request_user), params: params }
+
+ include_examples '/user/status successful response'
+
+ include_examples '/user/status unsuccessful response'
+
+ include_examples '/user/status passing nil for params'
+
+ include_examples '/user/status clear_status_after field'
+
+ context 'when passing empty params' do
+ let(:request_user) { user_with_status }
+
+ it 'does not update the status' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user_with_status.status).to eq(user_status)
+ end
+ end
+
+ context 'when clear_status_after is not given' do
+ let(:params) { { emoji: 'smirk', message: 'hello world' } }
+ let(:request_user) { user_with_status }
+
+ it 'does not unset clear_status_at column' do
+ set_user_status
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(user_with_status.status.clear_status_at).not_to be_nil
+ end
end
end
end