diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-21 15:21:10 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-21 15:21:10 +0000 |
commit | e33f87ac0fabaab468ce4b457996cc0f1b1bb648 (patch) | |
tree | 8bf0de72a9acac014cfdaddab7d463b208294af2 /spec/requests | |
parent | 5baf990db20a75078684702782c24399ef9eb0fa (diff) | |
download | gitlab-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.rb | 7 | ||||
-rw-r--r-- | spec/requests/api/graphql/boards/board_lists_query_spec.rb | 137 | ||||
-rw-r--r-- | spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb | 109 | ||||
-rw-r--r-- | spec/requests/api/graphql/mutations/jira_import/start_spec.rb | 14 | ||||
-rw-r--r-- | spec/requests/api/graphql/project/jira_import_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/internal/base_spec.rb | 118 | ||||
-rw-r--r-- | spec/requests/api/issues/post_projects_issues_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/issues/put_projects_issues_spec.rb | 9 | ||||
-rw-r--r-- | spec/requests/api/markdown_spec.rb | 2 | ||||
-rw-r--r-- | spec/requests/api/merge_requests_spec.rb | 16 | ||||
-rw-r--r-- | spec/requests/api/project_milestones_spec.rb | 8 | ||||
-rw-r--r-- | spec/requests/api/project_snippets_spec.rb | 11 | ||||
-rw-r--r-- | spec/requests/api/project_statistics_spec.rb | 8 | ||||
-rw-r--r-- | spec/requests/api/projects_spec.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/runners_spec.rb | 67 | ||||
-rw-r--r-- | spec/requests/api/snippets_spec.rb | 37 | ||||
-rw-r--r-- | spec/requests/api/terraform/state_spec.rb | 238 |
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 |