diff --git a/app/graphql/mutations/merge_requests/remove_attention_request.rb b/app/graphql/mutations/merge_requests/remove_attention_request.rb new file mode 100644 index 0000000000000000000000000000000000000000..ab8617edb36ff2b468aa50dea0d0087ed8f67e37 --- /dev/null +++ b/app/graphql/mutations/merge_requests/remove_attention_request.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mutations + module MergeRequests + class RemoveAttentionRequest < Base + graphql_name 'MergeRequestRemoveAttentionRequest' + + argument :user_id, ::Types::GlobalIDType[::User], + loads: Types::UserType, + required: true, + description: <<~DESC + User ID of the user for attention request removal. + DESC + + def resolve(project_path:, iid:, user:) + merge_request = authorized_find!(project_path: project_path, iid: iid) + + result = ::MergeRequests::RemoveAttentionRequestedService.new( + project: merge_request.project, + current_user: current_user, + merge_request: merge_request, + user: user + ).execute + + { + merge_request: merge_request, + errors: Array(result[:message]) + } + end + end + end +end diff --git a/app/graphql/mutations/merge_requests/request_attention.rb b/app/graphql/mutations/merge_requests/request_attention.rb new file mode 100644 index 0000000000000000000000000000000000000000..9a9f2f4cd435b656e9bb0a465819b8abd3586787 --- /dev/null +++ b/app/graphql/mutations/merge_requests/request_attention.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Mutations + module MergeRequests + class RequestAttention < Base + graphql_name 'MergeRequestRequestAttention' + + argument :user_id, ::Types::GlobalIDType[::User], + loads: Types::UserType, + required: true, + description: <<~DESC + User ID of the user to request attention. + DESC + + def resolve(project_path:, iid:, user:) + merge_request = authorized_find!(project_path: project_path, iid: iid) + + result = ::MergeRequests::RequestAttentionService.new( + project: merge_request.project, + current_user: current_user, + merge_request: merge_request, + user: user + ).execute + + { + merge_request: merge_request, + errors: Array(result[:message]) + } + end + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 47d58e5dc60f9fa20cb44abcdc3016728b5abd7b..d6bfe6bf052dbd3a2b2783fae0511d1a0b015ee0 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -69,6 +69,8 @@ class MutationType < BaseObject mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true mount_mutation Mutations::MergeRequests::SetAssignees mount_mutation Mutations::MergeRequests::ReviewerRereview + mount_mutation Mutations::MergeRequests::RequestAttention, feature_flag: :mr_attention_requests + mount_mutation Mutations::MergeRequests::RemoveAttentionRequest, feature_flag: :mr_attention_requests mount_mutation Mutations::MergeRequests::ToggleAttentionRequested, feature_flag: :mr_attention_requests mount_mutation Mutations::Metrics::Dashboard::Annotations::Create mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index d197c13378ae43e27cb32fe621bcc8d3d8d829a6..8d06a1020c423c993f8d9e14c622b6d3596db782 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -256,7 +256,7 @@ def remove_all_attention_requests(merge_request) def remove_attention_requested(merge_request) return unless merge_request.attention_requested_enabled? - ::MergeRequests::RemoveAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request).execute + ::MergeRequests::RemoveAttentionRequestedService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: current_user).execute end end end diff --git a/app/services/merge_requests/remove_attention_requested_service.rb b/app/services/merge_requests/remove_attention_requested_service.rb index a32a80714710cdd53f071c19288768b4596e001a..8a410fda69184c7c09408ced917918d7b6853e76 100644 --- a/app/services/merge_requests/remove_attention_requested_service.rb +++ b/app/services/merge_requests/remove_attention_requested_service.rb @@ -2,22 +2,26 @@ module MergeRequests class RemoveAttentionRequestedService < MergeRequests::BaseService - attr_accessor :merge_request + attr_accessor :merge_request, :user - def initialize(project:, current_user:, merge_request:) + def initialize(project:, current_user:, merge_request:, user:) super(project: project, current_user: current_user) @merge_request = merge_request + @user = user end def execute return error("Invalid permissions") unless can?(current_user, :update_merge_request, merge_request) if reviewer || assignee + return success if reviewer&.reviewed? || assignee&.reviewed? + update_state(reviewer) update_state(assignee) - current_user.invalidate_attention_requested_count + user.invalidate_attention_requested_count + create_remove_attention_request_note success else @@ -28,15 +32,19 @@ def execute private def assignee - merge_request.find_assignee(current_user) + @assignee ||= merge_request.find_assignee(user) end def reviewer - merge_request.find_reviewer(current_user) + @reviewer ||= merge_request.find_reviewer(user) end def update_state(reviewer_or_assignee) reviewer_or_assignee&.update(state: :reviewed) end + + def create_remove_attention_request_note + SystemNoteService.remove_attention_request(merge_request, merge_request.project, current_user, user) + end end end diff --git a/app/services/merge_requests/request_attention_service.rb b/app/services/merge_requests/request_attention_service.rb new file mode 100644 index 0000000000000000000000000000000000000000..07e9996f87b134bdbb9e10482b94faf36bc11602 --- /dev/null +++ b/app/services/merge_requests/request_attention_service.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module MergeRequests + class RequestAttentionService < MergeRequests::BaseService + attr_accessor :merge_request, :user + + def initialize(project:, current_user:, merge_request:, user:) + super(project: project, current_user: current_user) + + @merge_request = merge_request + @user = user + end + + def execute + return error("Invalid permissions") unless can?(current_user, :update_merge_request, merge_request) + + if reviewer || assignee + return success if reviewer&.attention_requested? || assignee&.attention_requested? + + update_state(reviewer) + update_state(assignee) + + user.invalidate_attention_requested_count + create_attention_request_note + notity_user + + if current_user.id != user.id + remove_attention_requested(merge_request) + end + + success + else + error("User is not a reviewer or assignee of the merge request") + end + end + + private + + def notity_user + notification_service.async.attention_requested_of_merge_request(merge_request, current_user, user) + todo_service.create_attention_requested_todo(merge_request, current_user, user) + end + + def create_attention_request_note + SystemNoteService.request_attention(merge_request, merge_request.project, current_user, user) + end + + def assignee + @assignee ||= merge_request.find_assignee(user) + end + + def reviewer + @reviewer ||= merge_request.find_reviewer(user) + end + + def update_state(reviewer_or_assignee) + reviewer_or_assignee&.update(state: :attention_requested, updated_state_by: current_user) + end + end +end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b88e52456c1f4a01b3372ab43d2b0cb11ad6a0cf..34e1e6f73972775b4ca919bf4f5e24d11d8ae28d 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -3459,6 +3459,52 @@ Input type: `MergeRequestCreateInput` | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. | +### `Mutation.mergeRequestRemoveAttentionRequest` + +Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. + +Input type: `MergeRequestRemoveAttentionRequestInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `iid` | [`String!`](#string) | IID of the merge request to mutate. | +| `projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. | +| `userId` | [`UserID!`](#userid) | User ID of the user for attention request removal. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| `mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. | + +### `Mutation.mergeRequestRequestAttention` + +Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. + +Input type: `MergeRequestRequestAttentionInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `iid` | [`String!`](#string) | IID of the merge request to mutate. | +| `projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. | +| `userId` | [`UserID!`](#userid) | User ID of the user to request attention. | + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | +| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | +| `mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. | + ### `Mutation.mergeRequestReviewerRereview` Input type: `MergeRequestReviewerRereviewInput` diff --git a/spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..e96f8ce55395d775493d1e243d132aa739a58ec7 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/request_attention_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Request attention' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request, reviewers: [user]) } + let_it_be(:project) { merge_request.project } + + let(:input) { { user_id: global_id_of(user) } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_request_attention, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_request_attention) + end + + def mutation_errors + mutation_response['errors'] + end + + before_all do + project.add_developer(current_user) + project.add_developer(user) + end + + it 'is successful' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).to be_empty + end + + context 'when current user is not allowed to update the merge request' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + end + + context 'when user is not a reviewer' do + let(:input) { { user_id: global_id_of(create(:user)) } } + + it 'returns an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).not_to be_empty + end + end +end diff --git a/spec/requests/api/graphql/mutations/remove_attention_request_spec.rb b/spec/requests/api/graphql/mutations/remove_attention_request_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..0a7ced1a30ce18946748d366aad1bda87fe94bb4 --- /dev/null +++ b/spec/requests/api/graphql/mutations/remove_attention_request_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Remove attention request' do + include GraphqlHelpers + + let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request, reviewers: [user]) } + let_it_be(:project) { merge_request.project } + + let(:input) { { user_id: global_id_of(user) } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid.to_s + } + graphql_mutation(:merge_request_remove_attention_request, variables.merge(input), + <<-QL.strip_heredoc + clientMutationId + errors + QL + ) + end + + def mutation_response + graphql_mutation_response(:merge_request_remove_attention_request) + end + + def mutation_errors + mutation_response['errors'] + end + + before_all do + project.add_developer(current_user) + project.add_developer(user) + end + + it 'is successful' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).to be_empty + end + + context 'when current user is not allowed to update the merge request' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + end + + context 'when user is not a reviewer' do + let(:input) { { user_id: global_id_of(create(:user)) } } + + it 'returns an error' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_errors).not_to be_empty + end + end +end diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb index 9b064da44b845f2f798d57b73c24821b121c986b..4d20d62b864403d01be17712d7e1ce948428dbf8 100644 --- a/spec/services/merge_requests/approval_service_spec.rb +++ b/spec/services/merge_requests/approval_service_spec.rb @@ -61,7 +61,7 @@ it 'removes attention requested state' do expect(MergeRequests::RemoveAttentionRequestedService).to receive(:new) - .with(project: project, current_user: user, merge_request: merge_request) + .with(project: project, current_user: user, merge_request: merge_request, user: user) .and_call_original service.execute(merge_request) diff --git a/spec/services/merge_requests/handle_assignees_change_service_spec.rb b/spec/services/merge_requests/handle_assignees_change_service_spec.rb index 26f53f55b0fcfd59ee8438ff956346a7b7b4b7af..34c598e6cc0830117e72445b1848e1283c71bc63 100644 --- a/spec/services/merge_requests/handle_assignees_change_service_spec.rb +++ b/spec/services/merge_requests/handle_assignees_change_service_spec.rb @@ -89,7 +89,7 @@ def execute it 'removes attention requested state' do expect(MergeRequests::RemoveAttentionRequestedService).to receive(:new) - .with(project: project, current_user: user, merge_request: merge_request) + .with(project: project, current_user: user, merge_request: merge_request, user: user) .and_call_original execute diff --git a/spec/services/merge_requests/remove_attention_requested_service_spec.rb b/spec/services/merge_requests/remove_attention_requested_service_spec.rb index 450204ebfdde988fb5cc3236ff474974361b3ba8..576049b9f1b58ce3ef4cd661af7259f393580348 100644 --- a/spec/services/merge_requests/remove_attention_requested_service_spec.rb +++ b/spec/services/merge_requests/remove_attention_requested_service_spec.rb @@ -3,64 +3,112 @@ require 'spec_helper' RSpec.describe MergeRequests::RemoveAttentionRequestedService do - let(:current_user) { create(:user) } - let(:merge_request) { create(:merge_request, reviewers: [current_user], assignees: [current_user]) } - let(:reviewer) { merge_request.find_reviewer(current_user) } - let(:assignee) { merge_request.find_assignee(current_user) } + let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:assignee_user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request, reviewers: [user], assignees: [assignee_user]) } + + let(:reviewer) { merge_request.find_reviewer(user) } + let(:assignee) { merge_request.find_assignee(assignee_user) } let(:project) { merge_request.project } - let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request) } + + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: user + ) + end + let(:result) { service.execute } before do + allow(SystemNoteService).to receive(:remove_attention_request) + project.add_developer(current_user) + project.add_developer(user) end describe '#execute' do - context 'invalid permissions' do - let(:service) { described_class.new(project: project, current_user: create(:user), merge_request: merge_request) } + context 'when current user cannot update merge request' do + let(:service) do + described_class.new( + project: project, + current_user: create(:user), + merge_request: merge_request, + user: user + ) + end it 'returns an error' do expect(result[:status]).to eq :error end end - context 'reviewer does not exist' do - let(:service) { described_class.new(project: project, current_user: create(:user), merge_request: merge_request) } + context 'when user is not a reviewer nor assignee' do + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: create(:user) + ) + end it 'returns an error' do expect(result[:status]).to eq :error end end - context 'reviewer exists' do + context 'when user is a reviewer' do + before do + reviewer.update!(state: :attention_requested) + end + it 'returns success' do expect(result[:status]).to eq :success end - it 'updates reviewers state' do + it 'updates reviewer state' do service.execute reviewer.reload expect(reviewer.state).to eq 'reviewed' end + it 'creates a remove attention request system note' do + expect(SystemNoteService) + .to receive(:remove_attention_request) + .with(merge_request, merge_request.project, current_user, user) + + service.execute + end + it_behaves_like 'invalidates attention request cache' do - let(:users) { [current_user] } + let(:users) { [user] } end end - context 'assignee exists' do - let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request) } + context 'when user is an assignee' do + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: assignee_user + ) + end before do - assignee.update!(state: :reviewed) + assignee.update!(state: :attention_requested) end it 'returns success' do expect(result[:status]).to eq :success end - it 'updates assignees state' do + it 'updates assignee state' do service.execute assignee.reload @@ -68,14 +116,40 @@ end it_behaves_like 'invalidates attention request cache' do - let(:users) { [current_user] } + let(:users) { [assignee_user] } + end + + it 'creates a remove attention request system note' do + expect(SystemNoteService) + .to receive(:remove_attention_request) + .with(merge_request, merge_request.project, current_user, assignee_user) + + service.execute end end - context 'assignee is the same as reviewer' do - let(:merge_request) { create(:merge_request, reviewers: [current_user], assignees: [current_user]) } - let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request) } - let(:assignee) { merge_request.find_assignee(current_user) } + context 'when user is an assignee and reviewer at the same time' do + let_it_be(:merge_request) { create(:merge_request, reviewers: [user], assignees: [user]) } + + let(:assignee) { merge_request.find_assignee(user) } + + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: user + ) + end + + before do + reviewer.update!(state: :attention_requested) + assignee.update!(state: :attention_requested) + end + + it 'returns success' do + expect(result[:status]).to eq :success + end it 'updates reviewers and assignees state' do service.execute @@ -86,5 +160,24 @@ expect(assignee.state).to eq 'reviewed' end end + + context 'when state is already not attention_requested' do + before do + reviewer.update!(state: :reviewed) + end + + it 'does not change state' do + service.execute + reviewer.reload + + expect(reviewer.state).to eq 'reviewed' + end + + it 'does not create a remove attention request system note' do + expect(SystemNoteService).not_to receive(:remove_attention_request) + + service.execute + end + end end end diff --git a/spec/services/merge_requests/request_attention_service_spec.rb b/spec/services/merge_requests/request_attention_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..813a8150625c90c76ac7aa301f95712dbeedc34a --- /dev/null +++ b/spec/services/merge_requests/request_attention_service_spec.rb @@ -0,0 +1,220 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe MergeRequests::RequestAttentionService do + let_it_be(:current_user) { create(:user) } + let_it_be(:user) { create(:user) } + let_it_be(:assignee_user) { create(:user) } + let_it_be(:merge_request) { create(:merge_request, reviewers: [user], assignees: [assignee_user]) } + + let(:reviewer) { merge_request.find_reviewer(user) } + let(:assignee) { merge_request.find_assignee(assignee_user) } + let(:project) { merge_request.project } + + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: user + ) + end + + let(:result) { service.execute } + let(:todo_svc) { instance_double('TodoService') } + let(:notification_svc) { instance_double('NotificationService') } + + before do + allow(service).to receive(:todo_service).and_return(todo_svc) + allow(service).to receive(:notification_service).and_return(notification_svc) + allow(todo_svc).to receive(:create_attention_requested_todo) + allow(notification_svc).to receive_message_chain(:async, :attention_requested_of_merge_request) + allow(SystemNoteService).to receive(:request_attention) + + project.add_developer(current_user) + project.add_developer(user) + end + + describe '#execute' do + context 'when current user cannot update merge request' do + let(:service) do + described_class.new( + project: project, + current_user: create(:user), + merge_request: merge_request, + user: user + ) + end + + it 'returns an error' do + expect(result[:status]).to eq :error + end + end + + context 'when user is not a reviewer nor assignee' do + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: create(:user) + ) + end + + it 'returns an error' do + expect(result[:status]).to eq :error + end + end + + context 'when user is a reviewer' do + before do + reviewer.update!(state: :reviewed) + end + + it 'returns success' do + expect(result[:status]).to eq :success + end + + it 'updates reviewers state' do + service.execute + reviewer.reload + + expect(reviewer.state).to eq 'attention_requested' + end + + it 'adds who toggled attention' do + service.execute + reviewer.reload + + expect(reviewer.updated_state_by).to eq current_user + end + + it 'creates a new todo for the reviewer' do + expect(todo_svc).to receive(:create_attention_requested_todo).with(merge_request, current_user, user) + + service.execute + end + + it 'sends email to reviewer' do + expect(notification_svc) + .to receive_message_chain(:async, :attention_requested_of_merge_request) + .with(merge_request, current_user, user) + + service.execute + end + + it 'removes attention requested state' do + expect(MergeRequests::RemoveAttentionRequestedService).to receive(:new) + .with(project: project, current_user: current_user, merge_request: merge_request, user: current_user) + .and_call_original + + service.execute + end + + it_behaves_like 'invalidates attention request cache' do + let(:users) { [user] } + end + end + + context 'when user is an assignee' do + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: assignee_user + ) + end + + before do + assignee.update!(state: :reviewed) + end + + it 'returns success' do + expect(result[:status]).to eq :success + end + + it 'updates assignees state' do + service.execute + assignee.reload + + expect(assignee.state).to eq 'attention_requested' + end + + it 'creates a new todo for the reviewer' do + expect(todo_svc).to receive(:create_attention_requested_todo).with(merge_request, current_user, assignee_user) + + service.execute + end + + it 'creates a request attention system note' do + expect(SystemNoteService) + .to receive(:request_attention) + .with(merge_request, merge_request.project, current_user, assignee_user) + + service.execute + end + + it 'removes attention requested state' do + expect(MergeRequests::RemoveAttentionRequestedService).to receive(:new) + .with(project: project, current_user: current_user, merge_request: merge_request, user: current_user) + .and_call_original + + service.execute + end + + it_behaves_like 'invalidates attention request cache' do + let(:users) { [assignee_user] } + end + end + + context 'when user is an assignee and reviewer at the same time' do + let_it_be(:merge_request) { create(:merge_request, reviewers: [user], assignees: [user]) } + + let(:assignee) { merge_request.find_assignee(user) } + + let(:service) do + described_class.new( + project: project, + current_user: current_user, + merge_request: merge_request, + user: user + ) + end + + before do + reviewer.update!(state: :reviewed) + assignee.update!(state: :reviewed) + end + + it 'updates reviewers and assignees state' do + service.execute + reviewer.reload + assignee.reload + + expect(reviewer.state).to eq 'attention_requested' + expect(assignee.state).to eq 'attention_requested' + end + end + + context 'when state is attention_requested' do + before do + reviewer.update!(state: :attention_requested) + end + + it 'does not change state' do + service.execute + reviewer.reload + + expect(reviewer.state).to eq 'attention_requested' + end + + it 'does not create a new todo for the reviewer' do + expect(todo_svc).not_to receive(:create_attention_requested_todo).with(merge_request, current_user, user) + + service.execute + end + end + end +end diff --git a/spec/services/merge_requests/toggle_attention_requested_service_spec.rb b/spec/services/merge_requests/toggle_attention_requested_service_spec.rb index dcaac5d26996a4f2f29436dc7d98c37071938e10..20bc536b21e7c18483a65991d93cab7925703ec6 100644 --- a/spec/services/merge_requests/toggle_attention_requested_service_spec.rb +++ b/spec/services/merge_requests/toggle_attention_requested_service_spec.rb @@ -80,7 +80,7 @@ it 'removes attention requested state' do expect(MergeRequests::RemoveAttentionRequestedService).to receive(:new) - .with(project: project, current_user: current_user, merge_request: merge_request) + .with(project: project, current_user: current_user, merge_request: merge_request, user: current_user) .and_call_original service.execute @@ -129,7 +129,7 @@ it 'removes attention requested state' do expect(MergeRequests::RemoveAttentionRequestedService).to receive(:new) - .with(project: project, current_user: current_user, merge_request: merge_request) + .with(project: project, current_user: current_user, merge_request: merge_request, user: current_user) .and_call_original service.execute