summaryrefslogtreecommitdiff
path: root/spec/requests
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-04-21 15:21:10 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-04-21 15:21:10 +0000
commite33f87ac0fabaab468ce4b457996cc0f1b1bb648 (patch)
tree8bf0de72a9acac014cfdaddab7d463b208294af2 /spec/requests
parent5baf990db20a75078684702782c24399ef9eb0fa (diff)
downloadgitlab-ce-e33f87ac0fabaab468ce4b457996cc0f1b1bb648.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/requests')
-rw-r--r--spec/requests/api/deploy_tokens_spec.rb7
-rw-r--r--spec/requests/api/graphql/boards/board_lists_query_spec.rb137
-rw-r--r--spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb109
-rw-r--r--spec/requests/api/graphql/mutations/jira_import/start_spec.rb14
-rw-r--r--spec/requests/api/graphql/project/jira_import_spec.rb2
-rw-r--r--spec/requests/api/internal/base_spec.rb118
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb2
-rw-r--r--spec/requests/api/issues/put_projects_issues_spec.rb9
-rw-r--r--spec/requests/api/markdown_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb16
-rw-r--r--spec/requests/api/project_milestones_spec.rb8
-rw-r--r--spec/requests/api/project_snippets_spec.rb11
-rw-r--r--spec/requests/api/project_statistics_spec.rb8
-rw-r--r--spec/requests/api/projects_spec.rb4
-rw-r--r--spec/requests/api/runners_spec.rb67
-rw-r--r--spec/requests/api/snippets_spec.rb37
-rw-r--r--spec/requests/api/terraform/state_spec.rb238
17 files changed, 650 insertions, 139 deletions
diff --git a/spec/requests/api/deploy_tokens_spec.rb b/spec/requests/api/deploy_tokens_spec.rb
index 5948c3d719f..499c334d491 100644
--- a/spec/requests/api/deploy_tokens_spec.rb
+++ b/spec/requests/api/deploy_tokens_spec.rb
@@ -205,10 +205,11 @@ describe API::DeployTokens do
context 'deploy token creation' do
shared_examples 'creating a deploy token' do |entity, unauthenticated_response|
+ let(:expires_time) { 1.year.from_now }
let(:params) do
{
name: 'Foo',
- expires_at: 1.year.from_now,
+ expires_at: expires_time,
scopes: [
'read_repository'
],
@@ -240,6 +241,10 @@ describe API::DeployTokens do
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/deploy_token')
+ expect(json_response['name']).to eq('Foo')
+ expect(json_response['scopes']).to eq(['read_repository'])
+ expect(json_response['username']).to eq('Bar')
+ expect(json_response['expires_at'].to_time.to_i).to eq(expires_time.to_i)
end
context 'with no optional params given' do
diff --git a/spec/requests/api/graphql/boards/board_lists_query_spec.rb b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
new file mode 100644
index 00000000000..711c20784b0
--- /dev/null
+++ b/spec/requests/api/graphql/boards/board_lists_query_spec.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'get board lists' do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:unauth_user) { create(:user) }
+ let_it_be(:project) { create(:project, creator_id: user.id, namespace: user.namespace ) }
+ let_it_be(:group) { create(:group, :private) }
+ let_it_be(:project_label) { create(:label, project: project, name: 'Development') }
+ let_it_be(:project_label2) { create(:label, project: project, name: 'Testing') }
+ let_it_be(:group_label) { create(:group_label, group: group, name: 'Development') }
+ let_it_be(:group_label2) { create(:group_label, group: group, name: 'Testing') }
+
+ let(:params) { '' }
+ let(:board) { }
+ let(:board_parent_type) { board_parent.class.to_s.downcase }
+ let(:board_data) { graphql_data[board_parent_type]['boards']['edges'].first['node'] }
+ let(:lists_data) { board_data['lists']['edges'] }
+ let(:start_cursor) { board_data['lists']['pageInfo']['startCursor'] }
+ let(:end_cursor) { board_data['lists']['pageInfo']['endCursor'] }
+
+ def query(list_params = params)
+ graphql_query_for(
+ board_parent_type,
+ { 'fullPath' => board_parent.full_path },
+ <<~BOARDS
+ boards(first: 1) {
+ edges {
+ node {
+ #{field_with_params('lists', list_params)} {
+ pageInfo {
+ startCursor
+ endCursor
+ }
+ edges {
+ node {
+ #{all_graphql_fields_for('board_lists'.classify)}
+ }
+ }
+ }
+ }
+ }
+ }
+ BOARDS
+ )
+ end
+
+ shared_examples 'group and project board lists query' do
+ let!(:board) { create(:board, resource_parent: board_parent) }
+
+ context 'when the user does not have access to the board' do
+ it 'returns nil' do
+ post_graphql(query, current_user: unauth_user)
+
+ expect(graphql_data[board_parent_type]).to be_nil
+ end
+ end
+
+ context 'when user can read the board' do
+ before do
+ board_parent.add_reporter(user)
+ end
+
+ describe 'sorting and pagination' do
+ context 'when using default sorting' do
+ let!(:label_list) { create(:list, board: board, label: label, position: 10) }
+ let!(:label_list2) { create(:list, board: board, label: label2, position: 2) }
+ let!(:backlog_list) { create(:backlog_list, board: board) }
+ let(:closed_list) { board.lists.find_by(list_type: :closed) }
+
+ before do
+ post_graphql(query, current_user: user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ context 'when ascending' do
+ let(:lists) { [backlog_list, label_list2, label_list, closed_list] }
+ let(:expected_list_gids) do
+ lists.map { |list| list.to_global_id.to_s }
+ end
+
+ it 'sorts lists' do
+ expect(grab_ids).to eq expected_list_gids
+ end
+
+ context 'when paginating' do
+ let(:params) { 'first: 2' }
+
+ it 'sorts boards' do
+ expect(grab_ids).to eq expected_list_gids.first(2)
+
+ cursored_query = query("after: \"#{end_cursor}\"")
+ post_graphql(cursored_query, current_user: user)
+
+ response_data = grab_list_data(response.body)
+
+ expect(grab_ids(response_data)).to eq expected_list_gids.drop(2).first(2)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe 'for a project' do
+ let(:board_parent) { project }
+ let(:label) { project_label }
+ let(:label2) { project_label2 }
+
+ it_behaves_like 'group and project board lists query'
+ end
+
+ describe 'for a group' do
+ let(:board_parent) { group }
+ let(:label) { group_label }
+ let(:label2) { group_label2 }
+
+ before do
+ allow(board_parent).to receive(:multiple_issue_boards_available?).and_return(false)
+ end
+
+ it_behaves_like 'group and project board lists query'
+ end
+
+ def grab_ids(data = lists_data)
+ data.map { |list| list.dig('node', 'id') }
+ end
+
+ def grab_list_data(response_body)
+ JSON.parse(response_body)['data'][board_parent_type]['boards']['edges'][0]['node']['lists']['edges']
+ end
+end
diff --git a/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
new file mode 100644
index 00000000000..f5a5f0a9ec2
--- /dev/null
+++ b/spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Getting Metrics Dashboard Annotations' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:path) { 'config/prometheus/common_metrics.yml' }
+ let_it_be(:from) { "2020-04-01T03:29:25Z" }
+ let_it_be(:to) { Time.zone.now.advance(minutes: 5) }
+ let_it_be(:annotation) { create(:metrics_dashboard_annotation, environment: environment, dashboard_path: path) }
+ let_it_be(:annotation_for_different_env) { create(:metrics_dashboard_annotation, dashboard_path: path) }
+ let_it_be(:annotation_for_different_dashboard) { create(:metrics_dashboard_annotation, environment: environment, dashboard_path: ".gitlab/dashboards/test.yml") }
+ let_it_be(:to_old_annotation) do
+ create(:metrics_dashboard_annotation, environment: environment, starting_at: Time.parse(from).advance(minutes: -5), dashboard_path: path)
+ end
+ let_it_be(:to_new_annotation) do
+ create(:metrics_dashboard_annotation, environment: environment, starting_at: to.advance(minutes: 5), dashboard_path: path)
+ end
+
+ let(:fields) do
+ <<~QUERY
+ #{all_graphql_fields_for('MetricsDashboardAnnotation'.classify)}
+ QUERY
+ end
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath:"#{project.full_path}") {
+ environments(name: "#{environment.name}") {
+ nodes {
+ metricsDashboard(path: "#{path}"){
+ annotations(#{args}){
+ nodes {
+ #{fields}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ context 'feature flag metrics_dashboard_annotations' do
+ let(:args) { "from: \"#{from}\", to: \"#{to}\"" }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ context 'is off' do
+ before do
+ stub_feature_flags(metrics_dashboard_annotations: false)
+ post_graphql(query, current_user: current_user)
+ end
+
+ it 'returns empty nodes array' do
+ annotations = graphql_data.dig('project', 'environments', 'nodes')[0].dig('metricsDashboard', 'annotations', 'nodes')
+
+ expect(annotations).to be_empty
+ end
+ end
+
+ context 'is on' do
+ before do
+ stub_feature_flags(metrics_dashboard_annotations: true)
+ post_graphql(query, current_user: current_user)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns annotations' do
+ annotations = graphql_data.dig('project', 'environments', 'nodes')[0].dig('metricsDashboard', 'annotations', 'nodes')
+
+ expect(annotations).to match_array [{
+ "description" => annotation.description,
+ "id" => annotation.to_global_id.to_s,
+ "panelId" => annotation.panel_xid,
+ "startingAt" => annotation.starting_at.to_s,
+ "endingAt" => nil
+ }]
+ end
+
+ context 'arguments' do
+ context 'from is missing' do
+ let(:args) { "to: \"#{from}\"" }
+
+ it 'returns error' do
+ post_graphql(query, current_user: current_user)
+
+ expect(graphql_errors[0]).to include("message" => "Field 'annotations' is missing required arguments: from")
+ end
+ end
+
+ context 'to is missing' do
+ let(:args) { "from: \"#{from}\"" }
+
+ it_behaves_like 'a working graphql query'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb
index feca89558e3..c7dcb21ad83 100644
--- a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb
+++ b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb
@@ -99,12 +99,6 @@ describe 'Starting a Jira Import' do
it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira integration not configured.']
end
- context 'when issues feature are disabled' do
- let_it_be(:project, reload: true) { create(:project, :issues_disabled) }
-
- it_behaves_like 'a mutation that returns errors in the response', errors: ['Cannot import because issues are not available in this project.']
- end
-
context 'when when project has Jira service' do
let!(:service) { create(:jira_service, project: project) }
@@ -112,13 +106,19 @@ describe 'Starting a Jira Import' do
project.reload
end
+ context 'when issues feature are disabled' do
+ let_it_be(:project, reload: true) { create(:project, :issues_disabled) }
+
+ it_behaves_like 'a mutation that returns errors in the response', errors: ['Cannot import because issues are not available in this project.']
+ end
+
context 'when jira_project_key not provided' do
let(:jira_project_key) { '' }
it_behaves_like 'a mutation that returns errors in the response', errors: ['Unable to find Jira project to import data from.']
end
- context 'when jira import successfully scheduled' do
+ context 'when Jira import successfully scheduled' do
it 'schedules a Jira import' do
post_graphql_mutation(mutation, current_user: current_user)
diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb
index 43e1bb13342..2e631fb56ba 100644
--- a/spec/requests/api/graphql/project/jira_import_spec.rb
+++ b/spec/requests/api/graphql/project/jira_import_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe 'query jira import data' do
+describe 'query Jira import data' do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index dc75fdab639..93c2233e021 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -766,29 +766,98 @@ describe API::Internal::Base do
end
context 'project does not exist' do
- it 'returns a 200 response with status: false' do
- project.destroy
+ context 'git pull' do
+ it 'returns a 200 response with status: false' do
+ project.destroy
- pull(key, project)
+ pull(key, project)
- expect(response).to have_gitlab_http_status(:not_found)
- expect(json_response["status"]).to be_falsey
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response["status"]).to be_falsey
+ end
+
+ it 'returns a 200 response when using a project path that does not exist' do
+ post(
+ api("/internal/allowed"),
+ params: {
+ key_id: key.id,
+ project: 'project/does-not-exist.git',
+ action: 'git-upload-pack',
+ secret_token: secret_token,
+ protocol: 'ssh'
+ }
+ )
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response["status"]).to be_falsey
+ end
end
- it 'returns a 200 response when using a project path that does not exist' do
- post(
- api("/internal/allowed"),
- params: {
- key_id: key.id,
- project: 'project/does-not-exist.git',
- action: 'git-upload-pack',
- secret_token: secret_token,
- protocol: 'ssh'
- }
- )
+ context 'git push' do
+ before do
+ stub_const('Gitlab::QueryLimiting::Transaction::THRESHOLD', 120)
+ end
- expect(response).to have_gitlab_http_status(:not_found)
- expect(json_response["status"]).to be_falsey
+ subject { push_with_path(key, full_path: path, changes: '_any') }
+
+ context 'from a user/group namespace' do
+ let!(:path) { "#{user.namespace.path}/notexist.git" }
+
+ it 'creates the project' do
+ expect do
+ subject
+ end.to change { Project.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['status']).to be_truthy
+ end
+ end
+
+ context 'from the personal snippet path' do
+ let!(:path) { 'snippets/notexist.git' }
+
+ it 'does not create snippet' do
+ expect do
+ subject
+ end.not_to change { Snippet.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'from a project path' do
+ context 'from an non existent project path' do
+ let!(:path) { "#{user.namespace.path}/notexist/snippets/notexist.git" }
+
+ it 'does not create project' do
+ expect do
+ subject
+ end.not_to change { Project.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'does not create snippet' do
+ expect do
+ subject
+ end.not_to change { Snippet.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'from an existent project path' do
+ let!(:path) { "#{project.full_path}/notexist/snippets/notexist.git" }
+
+ it 'does not create snippet' do
+ expect do
+ subject
+ end.not_to change { Snippet.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
end
end
@@ -1062,18 +1131,27 @@ describe API::Internal::Base do
end
def push(key, container, protocol = 'ssh', env: nil, changes: nil)
+ push_with_path(key,
+ full_path: full_path_for(container),
+ gl_repository: gl_repository_for(container),
+ protocol: protocol,
+ env: env,
+ changes: changes)
+ end
+
+ def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: nil, changes: nil)
changes ||= 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master'
params = {
changes: changes,
key_id: key.id,
- project: full_path_for(container),
- gl_repository: gl_repository_for(container),
+ project: full_path,
action: 'git-receive-pack',
secret_token: secret_token,
protocol: protocol,
env: env
}
+ params[:gl_repository] = gl_repository if gl_repository
post(
api("/internal/allowed"),
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index 1444f43003f..2e1e5d3204e 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -403,7 +403,7 @@ describe API::Issues do
end
before do
- expect_next_instance_of(Spam::SpamCheckService) do |spam_service|
+ expect_next_instance_of(Spam::SpamActionService) do |spam_service|
expect(spam_service).to receive_messages(check_for_spam?: true)
end
expect_next_instance_of(Spam::AkismetService) do |akismet_service|
diff --git a/spec/requests/api/issues/put_projects_issues_spec.rb b/spec/requests/api/issues/put_projects_issues_spec.rb
index ffc5e2b1db8..2ab8b9d7877 100644
--- a/spec/requests/api/issues/put_projects_issues_spec.rb
+++ b/spec/requests/api/issues/put_projects_issues_spec.rb
@@ -182,6 +182,8 @@ describe API::Issues do
end
describe 'PUT /projects/:id/issues/:issue_iid with spam filtering' do
+ include_context 'includes Spam constants'
+
def update_issue
put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: params
end
@@ -195,11 +197,12 @@ describe API::Issues do
end
before do
- expect_next_instance_of(Spam::SpamCheckService) do |spam_service|
+ expect_next_instance_of(Spam::SpamActionService) do |spam_service|
expect(spam_service).to receive_messages(check_for_spam?: true)
end
- expect_next_instance_of(Spam::AkismetService) do |akismet_service|
- expect(akismet_service).to receive_messages(spam?: true)
+
+ expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service|
+ expect(verdict_service).to receive(:execute).and_return(DISALLOW)
end
end
diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb
index 9b787e76740..09342b06744 100644
--- a/spec/requests/api/markdown_spec.rb
+++ b/spec/requests/api/markdown_spec.rb
@@ -3,8 +3,6 @@
require "spec_helper"
describe API::Markdown do
- RSpec::Matchers.define_negated_matcher :exclude, :include
-
describe "POST /markdown" do
let(:user) {} # No-op. It gets overwritten in the contexts below.
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 40d6f171116..af2ce7f7aef 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -892,6 +892,7 @@ describe API::MergeRequests do
expect(json_response['merge_error']).to eq(merge_request.merge_error)
expect(json_response['user']['can_merge']).to be_truthy
expect(json_response).not_to include('rebase_in_progress')
+ expect(json_response['first_contribution']).to be_falsy
expect(json_response['has_conflicts']).to be_falsy
expect(json_response['blocking_discussions_resolved']).to be_truthy
expect(json_response['references']['short']).to eq("!#{merge_request.iid}")
@@ -915,6 +916,21 @@ describe API::MergeRequests do
expect(json_response).to include('rebase_in_progress')
end
+ context 'when author is not a member without any merged merge requests' do
+ let(:non_member) { create(:user) }
+
+ before do
+ merge_request.update(author: non_member)
+ end
+
+ it 'exposes first_contribution as true' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['first_contribution']).to be_truthy
+ end
+ end
+
context 'merge_request_metrics' do
let(:pipeline) { create(:ci_empty_pipeline) }
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
index a40878fc807..c5911d51706 100644
--- a/spec/requests/api/project_milestones_spec.rb
+++ b/spec/requests/api/project_milestones_spec.rb
@@ -24,13 +24,13 @@ describe API::ProjectMilestones do
project.add_reporter(reporter)
end
- it 'returns 404 response when the project does not exists' do
+ it 'returns 404 response when the project does not exist' do
delete api("/projects/0/milestones/#{milestone.id}", user)
expect(response).to have_gitlab_http_status(:not_found)
end
- it 'returns 404 response when the milestone does not exists' do
+ it 'returns 404 response when the milestone does not exist' do
delete api("/projects/#{project.id}/milestones/0", user)
expect(response).to have_gitlab_http_status(:not_found)
@@ -44,7 +44,7 @@ describe API::ProjectMilestones do
end
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
- it 'creates an activity event when an milestone is closed' do
+ it 'creates an activity event when a milestone is closed' do
expect(Event).to receive(:create!)
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
@@ -91,7 +91,7 @@ describe API::ProjectMilestones do
end
end
- context 'when no such resources' do
+ context 'when no such resource' do
before do
group.add_developer(user)
end
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 89ade15c1f6..d439107beb1 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -94,7 +94,7 @@ describe API::ProjectSnippets do
expect(json_response['title']).to eq(snippet.title)
expect(json_response['description']).to eq(snippet.description)
- expect(json_response['file_name']).to eq(snippet.file_name)
+ expect(json_response['file_name']).to eq(snippet.file_name_on_repo)
expect(json_response['ssh_url_to_repo']).to eq(snippet.ssh_url_to_repo)
expect(json_response['http_url_to_repo']).to eq(snippet.http_url_to_repo)
end
@@ -460,14 +460,13 @@ describe API::ProjectSnippets do
end
describe 'GET /projects/:project_id/snippets/:id/raw' do
- let(:snippet) { create(:project_snippet, author: admin, project: project) }
+ let_it_be(:snippet) { create(:project_snippet, :repository, author: admin, project: project) }
it 'returns raw text' do
get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin)
expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type).to eq 'text/plain'
- expect(response.body).to eq(snippet.content)
end
it 'returns 404 for invalid snippet id' do
@@ -482,5 +481,11 @@ describe API::ProjectSnippets do
let(:request) { get api("/projects/#{project_no_snippets.id}/snippets/123/raw", admin) }
end
end
+
+ it_behaves_like 'snippet blob content' do
+ let_it_be(:snippet_with_empty_repo) { create(:project_snippet, :empty_repo, author: admin, project: project) }
+
+ subject { get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", snippet.author) }
+ end
end
end
diff --git a/spec/requests/api/project_statistics_spec.rb b/spec/requests/api/project_statistics_spec.rb
index 5d0b506cc92..1f48c081043 100644
--- a/spec/requests/api/project_statistics_spec.rb
+++ b/spec/requests/api/project_statistics_spec.rb
@@ -50,13 +50,5 @@ describe API::ProjectStatistics do
expect(response).to have_gitlab_http_status(:forbidden)
expect(json_response['message']).to eq('403 Forbidden')
end
-
- it 'responds with 404 when daily_statistics_enabled? is false' do
- stub_feature_flags(project_daily_statistics: { thing: public_project, enabled: false })
-
- get api("/projects/#{public_project.id}/statistics", maintainer)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 190afb9cda5..853155cea7a 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -2414,7 +2414,8 @@ describe API::Projects do
project_param = {
container_expiration_policy_attributes: {
cadence: '1month',
- keep_n: 1
+ keep_n: 1,
+ name_regex_keep: 'foo.*'
}
}
@@ -2424,6 +2425,7 @@ describe API::Projects do
expect(json_response['container_expiration_policy']['cadence']).to eq('1month')
expect(json_response['container_expiration_policy']['keep_n']).to eq(1)
+ expect(json_response['container_expiration_policy']['name_regex_keep']).to eq('foo.*')
end
end
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 164be8f0da6..67c258260bf 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -3,35 +3,34 @@
require 'spec_helper'
describe API::Runners do
- let(:admin) { create(:user, :admin) }
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
- let(:group_guest) { create(:user) }
- let(:group_reporter) { create(:user) }
- let(:group_developer) { create(:user) }
- let(:group_maintainer) { create(:user) }
-
- let(:project) { create(:project, creator_id: user.id) }
- let(:project2) { create(:project, creator_id: user.id) }
-
- let(:group) { create(:group).tap { |group| group.add_owner(user) } }
- let(:subgroup) { create(:group, parent: group) }
-
- let!(:shared_runner) { create(:ci_runner, :instance, description: 'Shared runner') }
- let!(:project_runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
- let!(:two_projects_runner) { create(:ci_runner, :project, description: 'Two projects runner', projects: [project, project2]) }
- let!(:group_runner_a) { create(:ci_runner, :group, description: 'Group runner A', groups: [group]) }
- let!(:group_runner_b) { create(:ci_runner, :group, description: 'Group runner B', groups: [subgroup]) }
-
- before do
- # Set project access for users
- create(:group_member, :guest, user: group_guest, group: group)
- create(:group_member, :reporter, user: group_reporter, group: group)
- create(:group_member, :developer, user: group_developer, group: group)
- create(:group_member, :maintainer, user: group_maintainer, group: group)
- create(:project_member, :maintainer, user: user, project: project)
- create(:project_member, :maintainer, user: user, project: project2)
- create(:project_member, :reporter, user: user2, project: project)
+ let_it_be(:admin) { create(:user, :admin) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:group_guest) { create(:user) }
+ let_it_be(:group_reporter) { create(:user) }
+ let_it_be(:group_developer) { create(:user) }
+ let_it_be(:group_maintainer) { create(:user) }
+
+ let_it_be(:project) { create(:project, creator_id: user.id) }
+ let_it_be(:project2) { create(:project, creator_id: user.id) }
+
+ let_it_be(:group) { create(:group).tap { |group| group.add_owner(user) } }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+
+ let_it_be(:shared_runner, reload: true) { create(:ci_runner, :instance, description: 'Shared runner') }
+ let_it_be(:project_runner, reload: true) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
+ let_it_be(:two_projects_runner) { create(:ci_runner, :project, description: 'Two projects runner', projects: [project, project2]) }
+ let_it_be(:group_runner_a) { create(:ci_runner, :group, description: 'Group runner A', groups: [group]) }
+ let_it_be(:group_runner_b) { create(:ci_runner, :group, description: 'Group runner B', groups: [subgroup]) }
+
+ before_all do
+ group.add_guest(group_guest)
+ group.add_reporter(group_reporter)
+ group.add_developer(group_developer)
+ group.add_maintainer(group_maintainer)
+ project.add_maintainer(user)
+ project2.add_maintainer(user)
+ project.add_reporter(user2)
end
describe 'GET /runners' do
@@ -603,10 +602,10 @@ describe API::Runners do
describe 'GET /runners/:id/jobs' do
let_it_be(:job_1) { create(:ci_build) }
- let!(:job_2) { create(:ci_build, :running, runner: shared_runner, project: project) }
- let!(:job_3) { create(:ci_build, :failed, runner: shared_runner, project: project) }
- let!(:job_4) { create(:ci_build, :running, runner: project_runner, project: project) }
- let!(:job_5) { create(:ci_build, :failed, runner: project_runner, project: project) }
+ let_it_be(:job_2) { create(:ci_build, :running, runner: shared_runner, project: project) }
+ let_it_be(:job_3) { create(:ci_build, :failed, runner: shared_runner, project: project) }
+ let_it_be(:job_4) { create(:ci_build, :running, runner: project_runner, project: project) }
+ let_it_be(:job_5) { create(:ci_build, :failed, runner: project_runner, project: project) }
context 'admin user' do
context 'when runner exists' do
@@ -952,7 +951,7 @@ describe API::Runners do
describe 'POST /projects/:id/runners' do
context 'authorized user' do
- let(:project_runner2) { create(:ci_runner, :project, projects: [project2]) }
+ let_it_be(:project_runner2) { create(:ci_runner, :project, projects: [project2]) }
it 'enables specific runner' do
expect do
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 3e30dc537e4..b779de85ce3 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -90,7 +90,7 @@ describe API::Snippets do
describe 'GET /snippets/:id/raw' do
let_it_be(:author) { create(:user) }
- let_it_be(:snippet) { create(:personal_snippet, :private, author: author) }
+ let_it_be(:snippet) { create(:personal_snippet, :repository, :private, author: author) }
it 'requires authentication' do
get api("/snippets/#{snippet.id}", nil)
@@ -103,7 +103,6 @@ describe API::Snippets do
expect(response).to have_gitlab_http_status(:ok)
expect(response.content_type).to eq 'text/plain'
- expect(response.body).to eq(snippet.content)
end
it 'forces attachment content disposition' do
@@ -134,6 +133,12 @@ describe API::Snippets do
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it_behaves_like 'snippet blob content' do
+ let_it_be(:snippet_with_empty_repo) { create(:personal_snippet, :empty_repo, :private, author: author) }
+
+ subject { get api("/snippets/#{snippet.id}/raw", snippet.author) }
+ end
end
describe 'GET /snippets/:id' do
@@ -155,7 +160,7 @@ describe API::Snippets do
expect(json_response['title']).to eq(private_snippet.title)
expect(json_response['description']).to eq(private_snippet.description)
- expect(json_response['file_name']).to eq(private_snippet.file_name)
+ expect(json_response['file_name']).to eq(private_snippet.file_name_on_repo)
expect(json_response['visibility']).to eq(private_snippet.visibility)
expect(json_response['ssh_url_to_repo']).to eq(private_snippet.ssh_url_to_repo)
expect(json_response['http_url_to_repo']).to eq(private_snippet.http_url_to_repo)
@@ -424,6 +429,32 @@ describe API::Snippets do
end
end
+ context "when admin" do
+ let(:admin) { create(:admin) }
+ let(:token) { create(:personal_access_token, user: admin, scopes: [:sudo]) }
+
+ subject do
+ put api("/snippets/#{snippet.id}", admin, personal_access_token: token), params: { visibility: 'private', sudo: user.id }
+ end
+
+ context 'when sudo is defined' do
+ it 'returns 200 and updates snippet visibility' do
+ expect(snippet.visibility).not_to eq('private')
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(json_response["visibility"]).to eq 'private'
+ end
+
+ it 'does not commit data' do
+ expect_any_instance_of(SnippetRepository).not_to receive(:multi_files_action)
+
+ subject
+ end
+ end
+ end
+
def update_snippet(snippet_id: snippet.id, params: {}, requester: user)
put api("/snippets/#{snippet_id}", requester), params: params
end
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb
index b0a963db684..88c277f4e08 100644
--- a/spec/requests/api/terraform/state_spec.rb
+++ b/spec/requests/api/terraform/state_spec.rb
@@ -3,95 +3,231 @@
require 'spec_helper'
describe API::Terraform::State do
- def auth_header_for(user)
- auth_header = ActionController::HttpAuthentication::Basic.encode_credentials(
- user.username,
- create(:personal_access_token, user: user).token
- )
- { 'HTTP_AUTHORIZATION' => auth_header }
- end
+ let_it_be(:project) { create(:project) }
+ let_it_be(:developer) { create(:user, developer_projects: [project]) }
+ let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) }
+
+ let!(:state) { create(:terraform_state, :with_file, project: project) }
- let!(:project) { create(:project) }
- let(:developer) { create(:user) }
- let(:maintainer) { create(:user) }
- let(:state_name) { 'state' }
+ let(:current_user) { maintainer }
+ let(:auth_header) { basic_auth_header(current_user) }
+ let(:project_id) { project.id }
+ let(:state_name) { state.name }
+ let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}" }
before do
- project.add_maintainer(maintainer)
+ stub_terraform_state_object_storage(Terraform::StateUploader)
end
describe 'GET /projects/:id/terraform/state/:name' do
- it 'returns 401 if user is not authenticated' do
- headers = { 'HTTP_AUTHORIZATION' => 'failing_token' }
- get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: headers
+ subject(:request) { get api(state_path), headers: auth_header }
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
+ context 'without authentication' do
+ let(:auth_header) { basic_auth_header('failing_token') }
- it 'returns terraform state belonging to a project of given state name' do
- get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(maintainer)
+ it 'returns 401 if user is not authenticated' do
+ request
- expect(response).to have_gitlab_http_status(:not_implemented)
- expect(response.body).to eq('not implemented')
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
end
- it 'returns not found if the project does not exists' do
- get api("/projects/0000/terraform/state/#{state_name}"), headers: auth_header_for(maintainer)
+ context 'with maintainer permissions' do
+ let(:current_user) { maintainer }
+
+ it 'returns terraform state belonging to a project of given state name' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.body).to eq(state.file.read)
+ end
+
+ context 'for a project that does not exist' do
+ let(:project_id) { '0000' }
+
+ it 'returns not found' do
+ request
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
end
- it 'returns forbidden if the user cannot access the state' do
- project.add_developer(developer)
- get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(developer)
+ context 'with developer permissions' do
+ let(:current_user) { developer }
+
+ it 'returns forbidden if the user cannot access the state' do
+ request
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
end
describe 'POST /projects/:id/terraform/state/:name' do
+ let(:params) { { 'instance': 'example-instance' } }
+
+ subject(:request) { post api(state_path), headers: auth_header, as: :json, params: params }
+
context 'when terraform state with a given name is already present' do
- it 'updates the state' do
- post api("/projects/#{project.id}/terraform/state/#{state_name}"),
- params: '{ "instance": "example-instance" }',
- headers: { 'Content-Type' => 'text/plain' }.merge(auth_header_for(maintainer))
+ context 'with maintainer permissions' do
+ let(:current_user) { maintainer }
- expect(response).to have_gitlab_http_status(:not_implemented)
- expect(response.body).to eq('not implemented')
+ it 'updates the state' do
+ expect { request }.to change { Terraform::State.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
- it 'returns forbidden if the user cannot access the state' do
- project.add_developer(developer)
- get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(developer)
+ context 'without body' do
+ let(:params) { nil }
- expect(response).to have_gitlab_http_status(:forbidden)
+ it 'returns no content if no body is provided' do
+ request
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+
+ context 'with developer permissions' do
+ let(:current_user) { developer }
+
+ it 'returns forbidden' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
end
context 'when there is no terraform state of a given name' do
- it 'creates a new state' do
- post api("/projects/#{project.id}/terraform/state/example2"),
- headers: auth_header_for(maintainer),
- params: '{ "database": "example-database" }'
+ let(:state_name) { 'example2' }
+
+ context 'with maintainer permissions' do
+ let(:current_user) { maintainer }
+
+ it 'creates a new state' do
+ expect { request }.to change { Terraform::State.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'without body' do
+ let(:params) { nil }
+
+ it 'returns no content if no body is provided' do
+ request
- expect(response).to have_gitlab_http_status(:not_implemented)
- expect(response.body).to eq('not implemented')
+ expect(response).to have_gitlab_http_status(:no_content)
+ end
+ end
+
+ context 'with developer permissions' do
+ let(:current_user) { developer }
+
+ it 'returns forbidden' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
end
end
end
describe 'DELETE /projects/:id/terraform/state/:name' do
- it 'deletes the state' do
- delete api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(maintainer)
+ subject(:request) { delete api(state_path), headers: auth_header }
+
+ context 'with maintainer permissions' do
+ let(:current_user) { maintainer }
+
+ it 'deletes the state' do
+ expect { request }.to change { Terraform::State.count }.by(-1)
- expect(response).to have_gitlab_http_status(:not_implemented)
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with developer permissions' do
+ let(:current_user) { developer }
+
+ it 'returns forbidden' do
+ expect { request }.to change { Terraform::State.count }.by(0)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ describe 'PUT /projects/:id/terraform/state/:name/lock' do
+ let(:params) do
+ {
+ ID: '123-456',
+ Version: '0.1',
+ Operation: 'OperationTypePlan',
+ Info: '',
+ Who: "#{current_user.username}",
+ Created: Time.now.utc.iso8601(6),
+ Path: ''
+ }
+ end
+
+ subject(:request) { post api("#{state_path}/lock"), headers: auth_header, params: params }
+
+ it 'locks the terraform state' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
end
+ end
+
+ describe 'DELETE /projects/:id/terraform/state/:name/lock' do
+ before do
+ state.lock_xid = '123-456'
+ state.save!
+ end
+
+ subject(:request) { delete api("#{state_path}/lock"), headers: auth_header, params: params }
- it 'returns forbidden if the user cannot access the state' do
- project.add_developer(developer)
- get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(developer)
+ context 'with the correct lock id' do
+ let(:params) { { ID: '123-456' } }
- expect(response).to have_gitlab_http_status(:forbidden)
+ it 'removes the terraform state lock' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with no lock id (force-unlock)' do
+ let(:params) { {} }
+
+ it 'removes the terraform state lock' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'with an incorrect lock id' do
+ let(:params) { { ID: '456-789' } }
+
+ it 'returns an error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:conflict)
+ end
+ end
+
+ context 'with a longer than 255 character lock id' do
+ let(:params) { { ID: '0' * 256 } }
+
+ it 'returns an error' do
+ request
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
end
end