Commit 25b6738c authored by Alexandru Croitor's avatar Alexandru Croitor

Fix missing system notes and system events on create issue

This fixes missing events when milestone or assignees are set
together with setting the epic on issue creation. This was
happening due to epic_issues relationship being saved before issue
changes were handled, which also triggered an issue.save which
would then lose issue changes. This moves the save of epic_issue
relationship to happen at issue.save time.

Changelog: fixed
MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61746
parent 15448f95
...@@ -184,7 +184,7 @@ class IssuableBaseService < ::BaseProjectService ...@@ -184,7 +184,7 @@ class IssuableBaseService < ::BaseProjectService
params[:assignee_ids] = process_assignee_ids(params, extra_assignee_ids: issuable.assignee_ids.to_a) params[:assignee_ids] = process_assignee_ids(params, extra_assignee_ids: issuable.assignee_ids.to_a)
end end
issuable.assign_attributes(params) issuable.assign_attributes(allowed_create_params(params))
before_create(issuable) before_create(issuable)
...@@ -194,6 +194,7 @@ class IssuableBaseService < ::BaseProjectService ...@@ -194,6 +194,7 @@ class IssuableBaseService < ::BaseProjectService
if issuable_saved if issuable_saved
create_system_notes(issuable, is_update: false) unless skip_system_notes create_system_notes(issuable, is_update: false) unless skip_system_notes
handle_changes(issuable, { params: params })
after_create(issuable) after_create(issuable)
execute_hooks(issuable) execute_hooks(issuable)
...@@ -233,7 +234,7 @@ class IssuableBaseService < ::BaseProjectService ...@@ -233,7 +234,7 @@ class IssuableBaseService < ::BaseProjectService
assign_requested_assignees(issuable) assign_requested_assignees(issuable)
if issuable.changed? || params.present? if issuable.changed? || params.present?
issuable.assign_attributes(params) issuable.assign_attributes(allowed_update_params(params))
if has_title_or_description_changed?(issuable) if has_title_or_description_changed?(issuable)
issuable.assign_attributes(last_edited_at: Time.current, last_edited_by: current_user) issuable.assign_attributes(last_edited_at: Time.current, last_edited_by: current_user)
...@@ -260,7 +261,7 @@ class IssuableBaseService < ::BaseProjectService ...@@ -260,7 +261,7 @@ class IssuableBaseService < ::BaseProjectService
issuable, old_labels: old_associations[:labels], old_milestone: old_associations[:milestone] issuable, old_labels: old_associations[:labels], old_milestone: old_associations[:milestone]
) )
handle_changes(issuable, old_associations: old_associations) handle_changes(issuable, old_associations: old_associations, params: params)
new_assignees = issuable.assignees.to_a new_assignees = issuable.assignees.to_a
affected_assignees = (old_associations[:assignees] + new_assignees) - (old_associations[:assignees] & new_assignees) affected_assignees = (old_associations[:assignees] + new_assignees) - (old_associations[:assignees] & new_assignees)
...@@ -505,6 +506,14 @@ class IssuableBaseService < ::BaseProjectService ...@@ -505,6 +506,14 @@ class IssuableBaseService < ::BaseProjectService
def update_timestamp?(issuable) def update_timestamp?(issuable)
issuable.changes.keys != ["relative_position"] issuable.changes.keys != ["relative_position"]
end end
def allowed_create_params(params)
params
end
def allowed_update_params(params)
params
end
end end
IssuableBaseService.prepend_mod_with('IssuableBaseService') IssuableBaseService.prepend_mod_with('IssuableBaseService')
...@@ -40,6 +40,20 @@ module Issues ...@@ -40,6 +40,20 @@ module Issues
super super
end end
def handle_changes(issue, options)
super
old_associations = options.fetch(:old_associations, {})
old_assignees = old_associations.fetch(:assignees, [])
handle_assignee_changes(issue, old_assignees)
end
def handle_assignee_changes(issue, old_assignees)
return if issue.assignees == old_assignees
create_assignee_note(issue, old_assignees)
end
def resolve_discussions_with_issue(issue) def resolve_discussions_with_issue(issue)
return if discussions_to_resolve.empty? return if discussions_to_resolve.empty?
......
...@@ -43,6 +43,7 @@ module Issues ...@@ -43,6 +43,7 @@ module Issues
end end
def handle_changes(issue, options) def handle_changes(issue, options)
super
old_associations = options.fetch(:old_associations, {}) old_associations = options.fetch(:old_associations, {})
old_labels = old_associations.fetch(:labels, []) old_labels = old_associations.fetch(:labels, [])
old_mentioned_users = old_associations.fetch(:mentioned_users, []) old_mentioned_users = old_associations.fetch(:mentioned_users, [])
......
...@@ -27,6 +27,33 @@ module MergeRequests ...@@ -27,6 +27,33 @@ module MergeRequests
enqueue_jira_connect_messages_for(merge_request) enqueue_jira_connect_messages_for(merge_request)
end end
def handle_changes(merge_request, options)
old_associations = options.fetch(:old_associations, {})
old_assignees = old_associations.fetch(:assignees, [])
old_reviewers = old_associations.fetch(:reviewers, [])
handle_assignees_change(merge_request, old_assignees) if merge_request.assignees != old_assignees
handle_reviewers_change(merge_request, old_reviewers) if merge_request.reviewers != old_reviewers
end
def handle_assignees_change(merge_request, old_assignees)
MergeRequests::HandleAssigneesChangeService
.new(project: project, current_user: current_user)
.async_execute(merge_request, old_assignees)
end
def handle_reviewers_change(merge_request, old_reviewers)
affected_reviewers = (old_reviewers + merge_request.reviewers) - (old_reviewers & merge_request.reviewers)
create_reviewer_note(merge_request, old_reviewers)
notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers)
todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers)
invalidate_cache_counts(merge_request, users: affected_reviewers.compact)
new_reviewers = merge_request.reviewers - old_reviewers
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
end
def cleanup_environments(merge_request) def cleanup_environments(merge_request)
Ci::StopEnvironmentsService.new(merge_request.source_project, current_user) Ci::StopEnvironmentsService.new(merge_request.source_project, current_user)
.execute_for_merge_request(merge_request) .execute_for_merge_request(merge_request)
......
...@@ -15,6 +15,7 @@ module MergeRequests ...@@ -15,6 +15,7 @@ module MergeRequests
end end
def handle_changes(merge_request, options) def handle_changes(merge_request, options)
super
old_associations = options.fetch(:old_associations, {}) old_associations = options.fetch(:old_associations, {})
old_labels = old_associations.fetch(:labels, []) old_labels = old_associations.fetch(:labels, [])
old_mentioned_users = old_associations.fetch(:mentioned_users, []) old_mentioned_users = old_associations.fetch(:mentioned_users, [])
...@@ -31,8 +32,6 @@ module MergeRequests ...@@ -31,8 +32,6 @@ module MergeRequests
end end
handle_target_branch_change(merge_request) handle_target_branch_change(merge_request)
handle_assignees_change(merge_request, old_assignees) if merge_request.assignees != old_assignees
handle_reviewers_change(merge_request, old_reviewers) if merge_request.reviewers != old_reviewers
handle_milestone_change(merge_request) handle_milestone_change(merge_request)
handle_draft_status_change(merge_request, changed_fields) handle_draft_status_change(merge_request, changed_fields)
...@@ -220,24 +219,6 @@ module MergeRequests ...@@ -220,24 +219,6 @@ module MergeRequests
end end
end end
def handle_assignees_change(merge_request, old_assignees)
MergeRequests::HandleAssigneesChangeService
.new(project: project, current_user: current_user)
.async_execute(merge_request, old_assignees)
end
def handle_reviewers_change(merge_request, old_reviewers)
affected_reviewers = (old_reviewers + merge_request.reviewers) - (old_reviewers & merge_request.reviewers)
create_reviewer_note(merge_request, old_reviewers)
notification_service.async.changed_reviewer_of_merge_request(merge_request, current_user, old_reviewers)
todo_service.reassigned_reviewable(merge_request, current_user, old_reviewers)
invalidate_cache_counts(merge_request, users: affected_reviewers.compact)
new_reviewers = merge_request.reviewers - old_reviewers
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
end
def create_branch_change_note(issuable, branch_type, event_type, old_branch, new_branch) def create_branch_change_note(issuable, branch_type, event_type, old_branch, new_branch)
SystemNoteService.change_branch( SystemNoteService.change_branch(
issuable, issuable.project, current_user, branch_type, event_type, issuable, issuable.project, current_user, branch_type, event_type,
......
...@@ -58,5 +58,15 @@ module EE ...@@ -58,5 +58,15 @@ module EE
end end
end end
end end
override :allowed_create_params
def allowed_create_params(params)
super(params).except(:epic)
end
override :allowed_update_params
def allowed_update_params(params)
super(params).except(:epic)
end
end end
end end
...@@ -7,21 +7,16 @@ module EE ...@@ -7,21 +7,16 @@ module EE
class EpicAssignmentError < ::ArgumentError; end class EpicAssignmentError < ::ArgumentError; end
def handle_epic(issue) def filter_epic(issue)
return unless epic_param_present? return unless epic_param_present?
epic = epic_param(issue) epic = epic_param(issue)
result = epic ? assign_epic(issue, epic) : unassign_epic(issue)
issue.reload_epic
if result[:status] == :error params[:confidential] = true if !issue.persisted? && epic&.confidential
raise EpicAssignmentError, result[:message] params[:epic] = epic
end
end end
def assign_epic(issue, epic) def assign_epic(issue, epic)
issue.confidential = true if !issue.persisted? && epic.confidential
had_epic = issue.epic.present? had_epic = issue.epic.present?
link_params = { target_issuable: issue, skip_epic_dates_update: true } link_params = { target_issuable: issue, skip_epic_dates_update: true }
...@@ -86,6 +81,32 @@ module EE ...@@ -86,6 +81,32 @@ module EE
params[:sprint_id] = '' unless iteration params[:sprint_id] = '' unless iteration
end end
override :handle_changes
def handle_changes(issue, options)
handle_epic_change(issue, options)
end
def handle_epic_change(issue, options)
return unless epic_param_present?
old_epic = issue.epic
epic = options.dig(:params, :epic)
return if !epic && !old_epic
result = if old_epic && !epic
unassign_epic(issue)
else
assign_epic(issue, epic)
end
issue.reload_epic
if result[:status] == :error
raise EpicAssignmentError, result[:message]
end
end
end end
end end
end end
...@@ -7,7 +7,7 @@ module EE ...@@ -7,7 +7,7 @@ module EE
override :filter_params override :filter_params
def filter_params(issue) def filter_params(issue)
handle_epic(issue) filter_epic(issue)
super super
end end
......
...@@ -10,7 +10,7 @@ module EE ...@@ -10,7 +10,7 @@ module EE
def filter_params(issue) def filter_params(issue)
params.delete(:sprint_id) unless can_admin_issuable?(issue) params.delete(:sprint_id) unless can_admin_issuable?(issue)
handle_epic(issue) filter_epic(issue)
filter_iteration filter_iteration
super super
......
...@@ -167,6 +167,11 @@ RSpec.describe 'Issues > Bulk edit issues' do ...@@ -167,6 +167,11 @@ RSpec.describe 'Issues > Bulk edit issues' do
end end
context 'at group level' do context 'at group level' do
before do
# avoid raising QueryLimiting exception for bulk inserts
stub_const("::Gitlab::QueryLimiting::Transaction::THRESHOLD", 110)
end
it_behaves_like 'bulk edit option in sidebar', :group it_behaves_like 'bulk edit option in sidebar', :group
it_behaves_like 'bulk edit epic', :group it_behaves_like 'bulk edit epic', :group
it_behaves_like 'bulk edit health status', :group it_behaves_like 'bulk edit health status', :group
......
...@@ -4,9 +4,9 @@ require 'spec_helper' ...@@ -4,9 +4,9 @@ require 'spec_helper'
RSpec.describe Issues::CreateService do RSpec.describe Issues::CreateService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:project) { create(:project, group: group) }
let(:project) { create(:project, group: group) }
let(:user) { create(:user) }
let(:params) { { title: 'Awesome issue', description: 'please fix', weight: 9 } } let(:params) { { title: 'Awesome issue', description: 'please fix', weight: 9 } }
let(:service) { described_class.new(project: project, current_user: user, params: params) } let(:service) { described_class.new(project: project, current_user: user, params: params) }
...@@ -67,11 +67,17 @@ RSpec.describe Issues::CreateService do ...@@ -67,11 +67,17 @@ RSpec.describe Issues::CreateService do
end end
context 'with epic and milestone in commands only' do context 'with epic and milestone in commands only' do
let(:milestone) { create(:milestone, group: group, start_date: Date.today, due_date: 7.days.from_now) } let_it_be(:milestone) { create(:milestone, group: group, start_date: Date.today, due_date: 7.days.from_now) }
let_it_be(:assignee_user1) { create(:user) }
before do
project.add_guest(assignee_user1)
end
let(:params) do let(:params) do
{ {
title: 'Awesome issue', title: 'Awesome issue',
description: %(/epic #{epic.to_reference}\n/milestone #{milestone.to_reference}") description: %(/epic #{epic.to_reference}\n/milestone #{milestone.to_reference}\n/assign #{assignee_user1.to_reference})
} }
end end
...@@ -83,6 +89,20 @@ RSpec.describe Issues::CreateService do ...@@ -83,6 +89,20 @@ RSpec.describe Issues::CreateService do
expect(epic.reload.start_date).to eq(milestone.start_date) expect(epic.reload.start_date).to eq(milestone.start_date)
expect(epic.due_date).to eq(milestone.due_date) expect(epic.due_date).to eq(milestone.due_date)
end end
it 'generates system notes for adding an epic and milestone' do
expect { service.execute }.to change(Note, :count).by(3).and(change(ResourceMilestoneEvent, :count).by(1))
end
context 'when assigning epic raises an exception' do
let(:mock_service) { double('service', execute: { status: :error, message: 'failed to assign epic' }) }
it 'assigns the issue passed to the provided epic' do
expect(EpicIssues::CreateService).to receive(:new).and_return(mock_service)
expect { service.execute }.to raise_error(EE::Issues::BaseService::EpicAssignmentError, 'failed to assign epic')
end
end
end end
context 'when adding a public issue to confidential epic' do context 'when adding a public issue to confidential epic' do
......
...@@ -4,9 +4,10 @@ require 'spec_helper' ...@@ -4,9 +4,10 @@ require 'spec_helper'
RSpec.describe Issues::UpdateService do RSpec.describe Issues::UpdateService do
let_it_be(:group) { create(:group) } let_it_be(:group) { create(:group) }
let_it_be_with_reload(:project) { create(:project, group: group) }
let_it_be_with_reload(:issue) { create(:issue, project: project) }
let_it_be(:epic) { create(:epic, group: group) }
let(:project) { create(:project, group: group) }
let(:issue) { create(:issue, project: project) }
let(:author) { issue.author } let(:author) { issue.author }
let(:user) { author } let(:user) { author }
...@@ -20,12 +21,12 @@ RSpec.describe Issues::UpdateService do ...@@ -20,12 +21,12 @@ RSpec.describe Issues::UpdateService do
end end
context 'refresh epic dates' do context 'refresh epic dates' do
let_it_be(:epic) { create(:epic) } before do
issue.update!(epic: epic)
let(:issue) { create(:issue, epic: epic, project: project) } end
context 'updating milestone' do context 'updating milestone' do
let(:milestone) { create(:milestone, project: project) } let_it_be(:milestone) { create(:milestone, project: project) }
it 'calls UpdateDatesService' do it 'calls UpdateDatesService' do
expect(Epics::UpdateDatesService).to receive(:new).with([epic]).and_call_original.twice expect(Epics::UpdateDatesService).to receive(:new).with([epic]).and_call_original.twice
...@@ -36,7 +37,7 @@ RSpec.describe Issues::UpdateService do ...@@ -36,7 +37,7 @@ RSpec.describe Issues::UpdateService do
end end
context 'updating iteration' do context 'updating iteration' do
let(:iteration) { create(:iteration, group: group) } let_it_be(:iteration) { create(:iteration, group: group) }
context 'when issue does not already have an iteration' do context 'when issue does not already have an iteration' do
it 'calls NotificationService#changed_iteration_issue' do it 'calls NotificationService#changed_iteration_issue' do
...@@ -49,7 +50,7 @@ RSpec.describe Issues::UpdateService do ...@@ -49,7 +50,7 @@ RSpec.describe Issues::UpdateService do
end end
context 'when issue already has an iteration' do context 'when issue already has an iteration' do
let(:old_iteration) { create(:iteration, group: group) } let_it_be(:old_iteration) { create(:iteration, group: group) }
before do before do
update_issue(iteration: old_iteration) update_issue(iteration: old_iteration)
...@@ -96,7 +97,7 @@ RSpec.describe Issues::UpdateService do ...@@ -96,7 +97,7 @@ RSpec.describe Issues::UpdateService do
context 'updating weight' do context 'updating weight' do
before do before do
project.add_maintainer(user) project.add_maintainer(user)
issue.update(weight: 3) issue.update!(weight: 3)
end end
context 'when weight is integer' do context 'when weight is integer' do
...@@ -158,20 +159,20 @@ RSpec.describe Issues::UpdateService do ...@@ -158,20 +159,20 @@ RSpec.describe Issues::UpdateService do
end end
context 'group iterations' do context 'group iterations' do
let(:iteration) { create(:iteration, group: group) } let_it_be(:iteration) { create(:iteration, group: group) }
it_behaves_like 'creates iteration resource event' it_behaves_like 'creates iteration resource event'
end end
context 'project iterations' do context 'project iterations' do
let(:iteration) { create(:iteration, :skip_project_validation, project: project) } let_it_be(:iteration) { create(:iteration, :skip_project_validation, project: project) }
it_behaves_like 'creates iteration resource event' it_behaves_like 'creates iteration resource event'
end end
end end
context 'changing issue_type' do context 'changing issue_type' do
let!(:sla_setting) { create(:project_incident_management_setting, :sla_enabled, project: project) } let_it_be(:sla_setting) { create(:project_incident_management_setting, :sla_enabled, project: project) }
before do before do
stub_licensed_features(incident_sla: true, quality_management: true) stub_licensed_features(incident_sla: true, quality_management: true)
...@@ -186,8 +187,8 @@ RSpec.describe Issues::UpdateService do ...@@ -186,8 +187,8 @@ RSpec.describe Issues::UpdateService do
end end
context 'from incident to issue' do context 'from incident to issue' do
let(:issue) { create(:incident, project: project) } let_it_be(:issue) { create(:incident, project: project) }
let!(:sla) { create(:issuable_sla, issue: issue) } let_it_be(:sla) { create(:issuable_sla, issue: issue) }
it 'does not remove the SLA or create a new one' do it 'does not remove the SLA or create a new one' do
expect { update_issue(issue_type: 'issue') }.not_to change(IssuableSla, :count) expect { update_issue(issue_type: 'issue') }.not_to change(IssuableSla, :count)
...@@ -212,10 +213,12 @@ RSpec.describe Issues::UpdateService do ...@@ -212,10 +213,12 @@ RSpec.describe Issues::UpdateService do
end end
context 'without sufficient permissions' do context 'without sufficient permissions' do
let(:user) { create(:user) } let_it_be(:guest) { create(:user) }
let(:user) { guest }
before do before do
project.add_guest(user) project.add_guest(guest)
end end
it 'excludes the issue type param' do it 'excludes the issue type param' do
...@@ -230,9 +233,9 @@ RSpec.describe Issues::UpdateService do ...@@ -230,9 +233,9 @@ RSpec.describe Issues::UpdateService do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
end end
let(:epic) { create(:epic, group: group) } let(:params) { { epic: epic } }
subject { update_issue(epic: epic) } subject { update_issue(params) }
context 'when a user does not have permissions to assign an epic' do context 'when a user does not have permissions to assign an epic' do
it 'raises an exception' do it 'raises an exception' do
...@@ -282,7 +285,7 @@ RSpec.describe Issues::UpdateService do ...@@ -282,7 +285,7 @@ RSpec.describe Issues::UpdateService do
end end
context 'when issue belongs to another epic' do context 'when issue belongs to another epic' do
let(:epic2) { create(:epic, group: group) } let_it_be(:epic2) { create(:epic, group: group) }
before do before do
issue.update!(epic: epic2) issue.update!(epic: epic2)
...@@ -308,6 +311,52 @@ RSpec.describe Issues::UpdateService do ...@@ -308,6 +311,52 @@ RSpec.describe Issues::UpdateService do
subject subject
end end
end end
context 'when updating issue epic and milestone and assignee attributes' do
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:assignee_user1) { create(:user) }
let(:params) { { epic: epic, milestone: milestone, assignees: [assignee_user1] } }
before do
project.add_guest(assignee_user1)
end
it 'assigns the issue passed to the provided epic' do
expect do
subject
issue.reload
end.to change { issue.epic }.from(nil).to(epic)
.and(change { issue.milestone }.from(nil).to(milestone))
.and(change(ResourceMilestoneEvent, :count).by(1))
.and(change(Note, :count).by(3))
end
context 'when milestone and epic attributes are changed from description' do
let(:params) { { description: %(/epic #{epic.to_reference}\n/milestone #{milestone.to_reference}\n/assign #{assignee_user1.to_reference}) } }
it 'assigns the issue passed to the provided epic' do
expect do
subject
issue.reload
end.to change { issue.epic }.from(nil).to(epic)
.and(change { issue.assignees }.from([]).to([assignee_user1]))
.and(change { issue.milestone }.from(nil).to(milestone))
.and(change(ResourceMilestoneEvent, :count).by(1))
.and(change(Note, :count).by(4))
end
end
context 'when assigning epic raises an exception' do
let(:mock_service) { double('service', execute: { status: :error, message: 'failed to assign epic' }) }
it 'assigns the issue passed to the provided epic' do
expect(EpicIssues::CreateService).to receive(:new).and_return(mock_service)
expect { subject }.to raise_error(EE::Issues::BaseService::EpicAssignmentError, 'failed to assign epic')
end
end
end
end end
end end
...@@ -316,8 +365,6 @@ RSpec.describe Issues::UpdateService do ...@@ -316,8 +365,6 @@ RSpec.describe Issues::UpdateService do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
end end
let(:epic) { create(:epic, group: group) }
subject { update_issue(epic: nil) } subject { update_issue(epic: nil) }
context 'when a user has permissions to assign an epic' do context 'when a user has permissions to assign an epic' do
...@@ -338,7 +385,9 @@ RSpec.describe Issues::UpdateService do ...@@ -338,7 +385,9 @@ RSpec.describe Issues::UpdateService do
end end
context 'when issue belongs to an epic' do context 'when issue belongs to an epic' do
let!(:epic_issue) { create(:epic_issue, issue: issue, epic: epic)} before do
issue.update!(epic: epic)
end
it 'unassigns the epic' do it 'unassigns the epic' do
expect { subject }.to change { issue.reload.epic }.from(epic).to(nil) expect { subject }.to change { issue.reload.epic }.from(epic).to(nil)
...@@ -346,8 +395,7 @@ RSpec.describe Issues::UpdateService do ...@@ -346,8 +395,7 @@ RSpec.describe Issues::UpdateService do
it 'calls EpicIssues::DestroyService' do it 'calls EpicIssues::DestroyService' do
link_sevice = double link_sevice = double
expect(EpicIssues::DestroyService).to receive(:new).with(EpicIssue.last, user) expect(EpicIssues::DestroyService).to receive(:new).with(EpicIssue.last, user).and_return(link_sevice)
.and_return(link_sevice)
expect(link_sevice).to receive(:execute).and_return({ status: :success }) expect(link_sevice).to receive(:execute).and_return({ status: :success })
subject subject
...@@ -362,10 +410,8 @@ RSpec.describe Issues::UpdateService do ...@@ -362,10 +410,8 @@ RSpec.describe Issues::UpdateService do
context 'but EpicIssues::DestroyService returns failure', :aggregate_failures do context 'but EpicIssues::DestroyService returns failure', :aggregate_failures do
it 'does not send usage data for removed epic action' do it 'does not send usage data for removed epic action' do
link_sevice = double link_sevice = double
expect(EpicIssues::DestroyService).to receive(:new).with(EpicIssue.last, user) expect(EpicIssues::DestroyService).to receive(:new).with(EpicIssue.last, user).and_return(link_sevice)
.and_return(link_sevice)
expect(link_sevice).to receive(:execute).and_return({ status: :failure }) expect(link_sevice).to receive(:execute).and_return({ status: :failure })
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_removed_from_epic_action) expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_removed_from_epic_action)
subject subject
...@@ -382,17 +428,16 @@ RSpec.describe Issues::UpdateService do ...@@ -382,17 +428,16 @@ RSpec.describe Issues::UpdateService do
it_behaves_like 'issue with epic_id parameter' do it_behaves_like 'issue with epic_id parameter' do
let(:execute) { described_class.new(project: project, current_user: user, params: params).execute(issue) } let(:execute) { described_class.new(project: project, current_user: user, params: params).execute(issue) }
let(:epic) { create(:epic, group: group) }
end end
context 'when epic_id is nil' do context 'when epic_id is nil' do
before do before do
stub_licensed_features(epics: true) stub_licensed_features(epics: true)
group.add_maintainer(user) group.add_maintainer(user)
issue.update!(epic: epic)
end end
let(:epic) { create(:epic, group: group) } let(:epic_issue) { issue.epic_issue }
let!(:epic_issue) { create(:epic_issue, epic: epic, issue: issue) }
let(:params) { { epic_id: nil } } let(:params) { { epic_id: nil } }
subject { update_issue(params) } subject { update_issue(params) }
...@@ -403,8 +448,7 @@ RSpec.describe Issues::UpdateService do ...@@ -403,8 +448,7 @@ RSpec.describe Issues::UpdateService do
it 'calls EpicIssues::DestroyService' do it 'calls EpicIssues::DestroyService' do
link_sevice = double link_sevice = double
expect(EpicIssues::DestroyService).to receive(:new).with(epic_issue, user) expect(EpicIssues::DestroyService).to receive(:new).with(epic_issue, user).and_return(link_sevice)
.and_return(link_sevice)
expect(link_sevice).to receive(:execute).and_return({ status: :success }) expect(link_sevice).to receive(:execute).and_return({ status: :success })
subject subject
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment