diff options
Diffstat (limited to 'spec/requests/api/graphql/mutations')
11 files changed, 639 insertions, 68 deletions
diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb new file mode 100644 index 00000000000..5b5b2ec8788 --- /dev/null +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Create an alert issue from an alert' do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:alert) { create(:alert_management_alert, project: project) } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: alert.iid.to_s + } + graphql_mutation(:create_alert_issue, variables, + <<~QL + clientMutationId + errors + alert { + iid + issueIid + } + issue { + iid + title + } + QL + ) + end + + let(:mutation_response) { graphql_mutation_response(:create_alert_issue) } + + before do + project.add_developer(user) + end + + context 'when there is no issue associated with the alert' do + it 'creates an alert issue' do + post_graphql_mutation(mutation, current_user: user) + + new_issue = Issue.last! + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response.slice('alert', 'issue')).to eq( + 'alert' => { + 'iid' => alert.iid.to_s, + 'issueIid' => new_issue.iid.to_s + }, + 'issue' => { + 'iid' => new_issue.iid.to_s, + 'title' => new_issue.title + } + ) + end + end + + context 'when there is an issue already associated with the alert' do + before do + AlertManagement::CreateAlertIssueService.new(alert, user).execute + end + + it 'responds with an error' do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response.slice('errors', 'issue')).to eq( + 'errors' => ['An issue already exists'], + 'issue' => nil + ) + end + end +end diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb new file mode 100644 index 00000000000..6663281e093 --- /dev/null +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Setting assignees of an alert' do + include GraphqlHelpers + + let_it_be(:project) { create(:project) } + let_it_be(:current_user) { create(:user) } + let_it_be(:alert) { create(:alert_management_alert, project: project) } + let(:input) { { assignee_usernames: [current_user.username] } } + + let(:mutation) do + graphql_mutation( + :alert_set_assignees, + { project_path: project.full_path, iid: alert.iid.to_s }.merge(input), + <<~QL + clientMutationId + errors + alert { + assignees { + nodes { + username + } + } + } + QL + ) + end + + let(:mutation_response) { graphql_mutation_response(:alert_set_assignees) } + + before_all do + project.add_developer(current_user) + end + + it 'updates the assignee of the alert' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['alert']['assignees']['nodes'].first['username']).to eq(current_user.username) + expect(alert.reload.assignees).to contain_exactly(current_user) + end + + context 'with operation_mode specified' do + let(:input) do + { + assignee_usernames: [current_user.username], + operation_mode: Types::MutationOperationModeEnum.enum[:remove] + } + end + + before do + alert.assignees = [current_user] + end + + it 'updates the assignee of the alert' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['alert']['assignees']['nodes']).to be_empty + expect(alert.reload.assignees).to be_empty + end + end +end diff --git a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb index fe50468134c..2a470bda689 100644 --- a/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb +++ b/spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb @@ -15,16 +15,16 @@ describe 'Setting the status of an alert' do project_path: project.full_path, iid: alert.iid.to_s } - graphql_mutation(:update_alert_status, variables.merge(input), - <<~QL - clientMutationId - errors - alert { - iid - status - } - QL - ) + graphql_mutation(:update_alert_status, variables.merge(input)) do + <<~QL + clientMutationId + errors + alert { + iid + status + } + QL + end end let(:mutation_response) { graphql_mutation_response(:update_alert_status) } diff --git a/spec/requests/api/graphql/mutations/commits/create_spec.rb b/spec/requests/api/graphql/mutations/commits/create_spec.rb new file mode 100644 index 00000000000..10a69932948 --- /dev/null +++ b/spec/requests/api/graphql/mutations/commits/create_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Creation of a new commit' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:project) { create(:project, :public, :repository) } + let(:input) { { project_path: project.full_path, branch: branch, message: message, actions: actions } } + let(:branch) { 'master' } + let(:message) { 'Commit message' } + let(:actions) do + [ + { + action: 'CREATE', + filePath: 'NEW_FILE.md', + content: 'Hello' + } + ] + end + + let(:mutation) { graphql_mutation(:commit_create, input) } + let(:mutation_response) { graphql_mutation_response(:commit_create) } + + context 'the user is not allowed to create a commit' do + it_behaves_like 'a mutation that returns top-level errors', + errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action'] + end + + context 'when user has permissions to create a commit' do + before do + project.add_developer(current_user) + end + + it 'creates a new commit' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['commit']).to include( + 'title' => message + ) + end + + context 'when branch is not correct' do + let(:branch) { 'unknown' } + + it_behaves_like 'a mutation that returns errors in the response', + errors: ['You can only create or edit files when you are on a branch'] + end + end +end diff --git a/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb new file mode 100644 index 00000000000..bc256a08f00 --- /dev/null +++ b/spec/requests/api/graphql/mutations/container_expiration_policy/update_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Updating the container expiration policy' do + include GraphqlHelpers + using RSpec::Parameterized::TableSyntax + + let_it_be(:project, reload: true) { create(:project) } + let_it_be(:user) { create(:user) } + + let(:container_expiration_policy) { project.container_expiration_policy.reload } + let(:params) do + { + project_path: project.full_path, + cadence: 'EVERY_THREE_MONTHS', + keep_n: 'ONE_HUNDRED_TAGS', + older_than: 'FOURTEEN_DAYS' + } + end + let(:mutation) do + graphql_mutation(:update_container_expiration_policy, params, + <<~QL + containerExpirationPolicy { + cadence + keepN + nameRegexKeep + nameRegex + olderThan + } + errors + QL + ) + end + let(:mutation_response) { graphql_mutation_response(:update_container_expiration_policy) } + let(:container_expiration_policy_response) { mutation_response['containerExpirationPolicy'] } + + RSpec.shared_examples 'returning a success' do + it_behaves_like 'returning response status', :success + + it 'returns the updated container expiration policy' do + subject + + expect(mutation_response['errors']).to be_empty + expect(container_expiration_policy_response['cadence']).to eq(params[:cadence]) + expect(container_expiration_policy_response['keepN']).to eq(params[:keep_n]) + expect(container_expiration_policy_response['olderThan']).to eq(params[:older_than]) + end + end + + RSpec.shared_examples 'updating the container expiration policy' do + it_behaves_like 'updating the container expiration policy attributes', mode: :update, from: { cadence: '1d', keep_n: 10, older_than: '90d' }, to: { cadence: '3month', keep_n: 100, older_than: '14d' } + + it_behaves_like 'returning a success' + end + + RSpec.shared_examples 'denying access to container expiration policy' do + it_behaves_like 'not creating the container expiration policy' + + it_behaves_like 'returning response status', :success + + it 'returns no response' do + subject + + expect(mutation_response).to be_nil + end + end + + describe 'post graphql mutation' do + subject { post_graphql_mutation(mutation, current_user: user) } + + context 'with existing container expiration policy' do + where(:user_role, :shared_examples_name) do + :maintainer | 'updating the container expiration policy' + :developer | 'updating the container expiration policy' + :reporter | 'denying access to container expiration policy' + :guest | 'denying access to container expiration policy' + :anonymous | 'denying access to container expiration policy' + end + + with_them do + before do + project.send("add_#{user_role}", user) unless user_role == :anonymous + end + + it_behaves_like params[:shared_examples_name] + end + end + + context 'without existing container expiration policy' do + let_it_be(:project, reload: true) { create(:project, :without_container_expiration_policy) } + + where(:user_role, :shared_examples_name) do + :maintainer | 'creating the container expiration policy' + :developer | 'creating the container expiration policy' + :reporter | 'denying access to container expiration policy' + :guest | 'denying access to container expiration policy' + :anonymous | 'denying access to container expiration policy' + end + + with_them do + before do + project.send("add_#{user_role}", user) unless user_role == :anonymous + end + + it_behaves_like params[:shared_examples_name] + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb new file mode 100644 index 00000000000..95e967c039d --- /dev/null +++ b/spec/requests/api/graphql/mutations/discussions/toggle_resolve_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Toggling the resolve status of a discussion' do + include GraphqlHelpers + + let_it_be(:project) { create(:project, :public, :repository) } + let_it_be(:noteable) { create(:merge_request, source_project: project) } + let(:discussion) do + create(:diff_note_on_merge_request, noteable: noteable, project: project).to_discussion + end + let(:mutation) do + graphql_mutation(:discussion_toggle_resolve, { id: discussion.to_global_id.to_s, resolve: true }) + end + let(:mutation_response) { graphql_mutation_response(:discussion_toggle_resolve) } + + context 'when the user does not have permission' do + let_it_be(:current_user) { create(:user) } + + it_behaves_like 'a mutation that returns top-level errors', + errors: ["The resource that you are attempting to access does not exist or you don't have permission to perform this action"] + end + + context 'when user has permission' do + let_it_be(:current_user) { create(:user, developer_projects: [project]) } + + it 'returns the discussion without errors', :aggregate_failures do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response).to include( + 'discussion' => be_present, + 'errors' => be_empty + ) + end + + context 'when an error is encountered' do + before do + allow_next_instance_of(::Discussions::ResolveService) do |service| + allow(service).to receive(:execute).and_raise(ActiveRecord::RecordNotSaved) + end + end + + it_behaves_like 'a mutation that returns errors in the response', + errors: ['Discussion failed to be resolved'] + end + end +end diff --git a/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb new file mode 100644 index 00000000000..be0d843d5ff --- /dev/null +++ b/spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb @@ -0,0 +1,104 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Importing Jira Users' do + include JiraServiceHelper + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let(:project_path) { project.full_path } + let(:start_at) { 7 } + + let(:mutation) do + variables = { + start_at: start_at, + project_path: project_path + } + + graphql_mutation(:jira_import_users, variables) + end + + def mutation_response + graphql_mutation_response(:jira_import_users) + end + + def jira_import + mutation_response['jiraUsers'] + end + + context 'with anonymous user' do + let(:current_user) { nil } + + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + end + + context 'with user without permissions' do + let(:current_user) { user } + + before do + project.add_developer(current_user) + end + + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + end + + context 'when the user has permissions' do + let(:current_user) { user } + + before do + project.add_maintainer(current_user) + end + + context 'when the project path is invalid' do + let(:project_path) { 'foobar' } + + it 'returns an an error' do + post_graphql_mutation(mutation, current_user: current_user) + + errors = json_response['errors'] + + expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) + end + end + + context 'when all params and permissions are ok' do + let(:importer) { instance_double(JiraImport::UsersImporter) } + + before do + expect(JiraImport::UsersImporter).to receive(:new).with(current_user, project, 7) + .and_return(importer) + end + + context 'when service returns a successful response' do + it 'returns imported users' do + users = [{ jira_account_id: '12a', jira_display_name: 'user 1' }] + result = ServiceResponse.success(payload: users) + + expect(importer).to receive(:execute).and_return(result) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(jira_import.length).to eq(1) + expect(jira_import.first['jiraAccountId']).to eq('12a') + expect(jira_import.first['jiraDisplayName']).to eq('user 1') + end + end + + context 'when service returns an error response' do + it 'returns an error messaege' do + result = ServiceResponse.error(message: 'Some error') + + expect(importer).to receive(:execute).and_return(result) + + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['errors']).to eq(['Some error']) + 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 84110098400..296d33aec5d 100644 --- a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb +++ b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb @@ -29,10 +29,6 @@ describe 'Starting a Jira Import' do end context 'when the user does not have permission' do - before do - stub_feature_flags(jira_issue_import: true) - end - shared_examples 'Jira import does not start' do it 'does not start the Jira import' do post_graphql_mutation(mutation, current_user: current_user) @@ -83,53 +79,39 @@ describe 'Starting a Jira Import' do end end - context 'when feature jira_issue_import feature flag is disabled' do - before do - stub_feature_flags(jira_issue_import: false) - end - - it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira import feature is disabled.'] + context 'when project has no Jira service' do + it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira integration not configured.'] end - context 'when feature jira_issue_import feature flag is enabled' do + context 'when when project has Jira service' do + let!(:service) { create(:jira_service, project: project) } + before do - stub_feature_flags(jira_issue_import: true) - end + project.reload - context 'when project has no Jira service' do - it_behaves_like 'a mutation that returns errors in the response', errors: ['Jira integration not configured.'] + stub_jira_service_test end - context 'when when project has Jira service' do - let!(:service) { create(:jira_service, project: project) } + context 'when issues feature are disabled' do + let_it_be(:project, reload: true) { create(:project, :issues_disabled) } - before do - project.reload - - stub_jira_service_test - 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 + 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) { '' } + 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 + 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 - it 'schedules a Jira import' do - post_graphql_mutation(mutation, current_user: current_user) + context 'when Jira import successfully scheduled' do + it 'schedules a Jira import' do + post_graphql_mutation(mutation, current_user: current_user) - expect(jira_import['jiraProjectKey']).to eq 'AA' - expect(jira_import['scheduledBy']['username']).to eq current_user.username - expect(project.latest_jira_import).not_to be_nil - expect(project.latest_jira_import).to be_scheduled - end + expect(jira_import['jiraProjectKey']).to eq 'AA' + expect(jira_import['scheduledBy']['username']).to eq current_user.username + expect(project.latest_jira_import).not_to be_nil + expect(project.latest_jira_import).to be_scheduled 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 new file mode 100644 index 00000000000..5c63f655f1d --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/create_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Creation of a new merge request' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let(:project) { create(:project, :public, :repository) } + let(:input) do + { + project_path: project.full_path, + title: title, + source_branch: source_branch, + target_branch: target_branch + } + end + let(:title) { 'MergeRequest' } + let(:source_branch) { 'new_branch' } + let(:target_branch) { 'master' } + + let(:mutation) { graphql_mutation(:merge_request_create, input) } + let(:mutation_response) { graphql_mutation_response(:merge_request_create) } + + context 'the user is not allowed to create a branch' do + it_behaves_like 'a mutation that returns top-level errors', + errors: ['The resource that you are attempting to access does not exist or you don\'t have permission to perform this action'] + end + + context 'when user has permissions to create a merge request' do + before do + project.add_developer(current_user) + end + + it 'creates a new merge request' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']).to include( + 'title' => title + ) + end + + context 'when source branch is equal to the target branch' do + let(:source_branch) { target_branch } + + it_behaves_like 'a mutation that returns errors in the response', + errors: ['Branch conflict You can\'t use same project/branch for source and target'] + end + end +end diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb new file mode 100644 index 00000000000..217f538c53e --- /dev/null +++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Mutations::Metrics::Dashboard::Annotations::Delete do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:project) { create(:project, :private, :repository) } + let_it_be(:environment) { create(:environment, project: project) } + let_it_be(:annotation) { create(:metrics_dashboard_annotation, environment: environment) } + let(:mutation) do + variables = { + id: GitlabSchema.id_from_object(annotation).to_s + } + + graphql_mutation(:delete_annotation, variables) + end + + def mutation_response + graphql_mutation_response(:delete_annotation) + end + + specify { expect(described_class).to require_graphql_authorizations(:delete_metrics_dashboard_annotation) } + + context 'when the user has permission to delete the annotation' do + before do + project.add_developer(current_user) + end + + context 'with valid params' do + it 'deletes the annotation' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.to change { Metrics::Dashboard::Annotation.count }.by(-1) + end + end + + context 'with invalid params' do + let(:mutation) do + variables = { + id: 'invalid_id' + } + + graphql_mutation(:delete_annotation, variables) + end + + it_behaves_like 'a mutation that returns top-level errors', errors: ['invalid_id is not a valid GitLab id.'] + end + + context 'when the delete fails' do + let(:service_response) { { message: 'Annotation has not been deleted', status: :error, last_step: :delete } } + + before do + allow_next_instance_of(Metrics::Dashboard::Annotations::DeleteService) do |delete_service| + allow(delete_service).to receive(:execute).and_return(service_response) + end + end + it 'returns the error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['errors']).to eq([service_response[:message]]) + end + end + end + + context 'when the user does not have permission to delete the annotation' do + before do + project.add_reporter(current_user) + end + + it_behaves_like 'a mutation that returns top-level errors', errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] + + it 'does not delete the annotation' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + end.not_to change { Metrics::Dashboard::Annotation.count } + end + end +end diff --git a/spec/requests/api/graphql/mutations/snippets/create_spec.rb b/spec/requests/api/graphql/mutations/snippets/create_spec.rb index e1e5fe22887..9052f54b171 100644 --- a/spec/requests/api/graphql/mutations/snippets/create_spec.rb +++ b/spec/requests/api/graphql/mutations/snippets/create_spec.rb @@ -109,31 +109,21 @@ describe 'Creating a Snippet' do context 'when the project path is invalid' do let(:project_path) { 'foobar' } - it 'returns an an error' do - subject - errors = json_response['errors'] - - expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) - end + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] end context 'when the feature is disabled' do - it 'returns an an error' do + before do project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::DISABLED) - - subject - errors = json_response['errors'] - - expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR) end + + it_behaves_like 'a mutation that returns top-level errors', + errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] end end - context 'when there are ActiveRecord validation errors' do - let(:title) { '' } - - it_behaves_like 'a mutation that returns errors in the response', errors: ["Title can't be blank"] - + shared_examples 'does not create snippet' do it 'does not create the Snippet' do expect do subject @@ -147,7 +137,21 @@ describe 'Creating a Snippet' do end end - context 'when there uploaded files' do + context 'when there are ActiveRecord validation errors' do + let(:title) { '' } + + it_behaves_like 'a mutation that returns errors in the response', errors: ["Title can't be blank"] + it_behaves_like 'does not create snippet' + end + + context 'when there non ActiveRecord errors' do + let(:file_name) { 'invalid://file/path' } + + it_behaves_like 'a mutation that returns errors in the response', errors: ['Repository Error creating the snippet - Invalid file name'] + it_behaves_like 'does not create snippet' + end + + context 'when there are uploaded files' do shared_examples 'expected files argument' do |file_value, expected_value| let(:uploaded_files) { file_value } |