gitlab-org--gitlab-foss/spec/services/notification_service_spec.rb
Sean McGivern e94c1028c1 Deserialise existing custom notification settings
Create a post-deployment migration to update all existing notification settings
with at least one custom level enabled to the new format. Also handle the same
conversion when updating settings, to catch any stragglers.
2017-06-15 15:15:13 +01:00

1562 lines
56 KiB
Ruby

require 'spec_helper'
describe NotificationService, services: true do
include EmailHelpers
let(:notification) { NotificationService.new }
let(:assignee) { create(:user) }
around(:each) do |example|
perform_enqueued_jobs do
example.run
end
end
shared_examples 'notifications for new mentions' do
def send_notifications(*new_mentions)
reset_delivered_emails!
notification.send(notification_method, mentionable, new_mentions, @u_disabled)
end
it 'sends no emails when no new mentions are present' do
send_notifications
should_not_email_anyone
end
it 'emails new mentions with a watch level higher than participant' do
send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global)
should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global)
end
it 'does not email new mentions with a watch level equal to or less than participant' do
send_notifications(@u_participating, @u_mentioned)
should_not_email_anyone
end
end
# Next shared examples are intended to test notifications of "participants"
#
# they take the following parameters:
# * issuable
# * notification trigger
# * participant
#
shared_examples 'participating by note notification' do
it 'emails the participant' do
create(:note_on_issue, noteable: issuable, project_id: project.id, note: 'anything', author: participant)
notification_trigger
should_email(participant)
end
end
shared_examples 'participating by assignee notification' do
it 'emails the participant' do
if issuable.is_a?(Issue)
issuable.assignees << participant
else
issuable.update_attribute(:assignee, participant)
end
notification_trigger
should_email(participant)
end
end
shared_examples 'participating by author notification' do
it 'emails the participant' do
issuable.author = participant
notification_trigger
should_email(participant)
end
end
shared_examples_for 'participating notifications' do
it_should_behave_like 'participating by note notification'
it_should_behave_like 'participating by author notification'
it_should_behave_like 'participating by assignee notification'
end
describe 'Keys' do
describe '#new_key' do
let!(:key) { create(:personal_key) }
it { expect(notification.new_key(key)).to be_truthy }
it 'sends email to key owner' do
expect{ notification.new_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
end
end
end
describe 'Email' do
describe '#new_email' do
let!(:email) { create(:email) }
it { expect(notification.new_email(email)).to be_truthy }
it 'sends email to email owner' do
expect{ notification.new_email(email) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
end
end
end
describe 'Notes' do
context 'issue note' do
let(:project) { create(:empty_project, :private) }
let(:issue) { create(:issue, project: project, assignees: [assignee]) }
let(:mentioned_issue) { create(:issue, assignees: issue.assignees) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced, @outsider also') }
before do
build_team(note.project)
project.add_master(issue.author)
project.add_master(assignee)
project.add_master(note.author)
create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@subscribed_participant cc this guy')
update_custom_notification(:new_note, @u_guest_custom, resource: project)
update_custom_notification(:new_note, @u_custom_global)
end
describe '#new_note' do
it do
add_users_with_subscription(note.project, issue)
# Ensure create SentNotification by noteable = issue 6 times, not noteable = note
expect(SentNotification).to receive(:record).with(issue, any_args).exactly(8).times
reset_delivered_emails!
notification.new_note(note)
should_email(@u_watcher)
should_email(note.noteable.author)
should_email(note.noteable.assignees.first)
should_email(@u_custom_global)
should_email(@u_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_email(@subscribed_participant)
should_not_email(@u_guest_custom)
should_not_email(@u_guest_watcher)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@unsubscriber)
should_not_email(@u_outsider_mentioned)
should_not_email(@u_lazy_participant)
end
it "emails the note author if they've opted into notifications about their activity" do
add_users_with_subscription(note.project, issue)
note.author.notified_of_own_activity = true
reset_delivered_emails!
notification.new_note(note)
should_email(note.author)
end
it 'filters out "mentioned in" notes' do
mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author)
expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
end
context 'participating' do
context 'by note' do
before do
reset_delivered_emails!
note.author = @u_lazy_participant
note.save
notification.new_note(note)
end
it { should_not_email(@u_lazy_participant) }
end
end
end
describe 'new note on issue in project that belongs to a group' do
let(:group) { create(:group) }
before do
note.project.namespace_id = group.id
note.project.group.add_user(@u_watcher, GroupMember::MASTER)
note.project.group.add_user(@u_custom_global, GroupMember::MASTER)
note.project.save
@u_watcher.notification_settings_for(note.project).participating!
@u_watcher.notification_settings_for(note.project.group).global!
update_custom_notification(:new_note, @u_custom_global)
reset_delivered_emails!
end
it do
notification.new_note(note)
should_email(note.noteable.author)
should_email(note.noteable.assignees.first)
should_email(@u_mentioned)
should_email(@u_custom_global)
should_not_email(@u_guest_custom)
should_not_email(@u_guest_watcher)
should_not_email(@u_watcher)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
end
end
context 'confidential issue note' do
let(:project) { create(:empty_project, :public) }
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, author: author, assignees: [assignee]) }
let(:note) { create(:note_on_issue, noteable: confidential_issue, project: project, note: "#{author.to_reference} #{assignee.to_reference} #{non_member.to_reference} #{member.to_reference} #{admin.to_reference}") }
let(:guest_watcher) { create_user_with_notification(:watch, "guest-watcher-confidential") }
it 'filters out users that can not read the issue' do
project.add_developer(member)
project.add_guest(guest)
expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times
reset_delivered_emails!
notification.new_note(note)
should_not_email(non_member)
should_not_email(guest)
should_not_email(guest_watcher)
should_email(author)
should_email(assignee)
should_email(member)
should_email(admin)
end
end
context 'issue note mention' do
let(:project) { create(:empty_project, :public) }
let(:issue) { create(:issue, project: project, assignees: [assignee]) }
let(:mentioned_issue) { create(:issue, assignees: issue.assignees) }
let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@all mentioned') }
before do
build_team(note.project)
note.project.add_master(note.author)
reset_delivered_emails!
end
describe '#new_note' do
it 'notifies the team members' do
notification.new_note(note)
# Notify all team members
note.project.team.members.each do |member|
# User with disabled notification should not be notified
next if member.id == @u_disabled.id
# Author should not be notified
next if member.id == note.author.id
should_email(member)
end
should_email(@u_guest_watcher)
should_email(note.noteable.author)
should_email(note.noteable.assignees.first)
should_not_email(note.author)
should_email(@u_mentioned)
should_not_email(@u_disabled)
should_email(@u_not_mentioned)
end
it 'filters out "mentioned in" notes' do
mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author)
expect(Notify).not_to receive(:note_issue_email)
notification.new_note(mentioned_note)
end
end
end
context 'project snippet note' do
let(:project) { create(:empty_project, :public) }
let(:snippet) { create(:project_snippet, project: project, author: create(:user)) }
let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') }
before do
build_team(note.project)
note.project.add_master(note.author)
reset_delivered_emails!
end
describe '#new_note' do
it 'notifies the team members' do
notification.new_note(note)
# Notify all team members
note.project.team.members.each do |member|
# User with disabled notification should not be notified
next if member.id == @u_disabled.id
# Author should not be notified
next if member.id == note.author.id
should_email(member)
end
# it emails custom global users on mention
should_email(@u_custom_global)
should_email(@u_guest_watcher)
should_email(note.noteable.author)
should_not_email(note.author)
should_email(@u_mentioned)
should_not_email(@u_disabled)
should_email(@u_not_mentioned)
end
end
end
context 'personal snippet note' do
let(:snippet) { create(:personal_snippet, :public, author: @u_snippet_author) }
let(:note) { create(:note_on_personal_snippet, noteable: snippet, note: '@mentioned note', author: @u_note_author) }
before do
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participant = create_global_setting_for(create(:user), :participating)
@u_disabled = create_global_setting_for(create(:user), :disabled)
@u_mentioned = create_global_setting_for(create(:user, username: 'mentioned'), :mention)
@u_mentioned_level = create_global_setting_for(create(:user, username: 'participator'), :mention)
@u_note_author = create(:user, username: 'note_author')
@u_snippet_author = create(:user, username: 'snippet_author')
@u_not_mentioned = create_global_setting_for(create(:user, username: 'regular'), :participating)
reset_delivered_emails!
end
let!(:notes) do
[
create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_watcher),
create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_participant),
create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_mentioned),
create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_disabled),
create(:note_on_personal_snippet, noteable: snippet, note: 'note', author: @u_note_author)
]
end
describe '#new_note' do
it 'notifies the participants' do
notification.new_note(note)
# it emails participants
should_email(@u_watcher)
should_email(@u_participant)
should_email(@u_watcher)
should_email(@u_snippet_author)
# it emails mentioned users
should_email(@u_mentioned)
# it does not email participants with mention notification level
should_not_email(@u_mentioned_level)
# it does not email note author
should_not_email(@u_note_author)
end
end
end
context 'commit note' do
let(:project) { create(:project, :public, :repository) }
let(:note) { create(:note_on_commit, project: project) }
before do
build_team(note.project)
reset_delivered_emails!
allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer)
update_custom_notification(:new_note, @u_guest_custom, resource: project)
update_custom_notification(:new_note, @u_custom_global)
end
describe '#new_note, #perform_enqueued_jobs' do
it do
notification.new_note(note)
should_email(@u_guest_watcher)
should_email(@u_custom_global)
should_email(@u_guest_custom)
should_email(@u_committer)
should_email(@u_watcher)
should_not_email(@u_mentioned)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it do
note.update_attribute(:note, '@mention referenced')
notification.new_note(note)
should_email(@u_guest_watcher)
should_email(@u_committer)
should_email(@u_watcher)
should_email(@u_mentioned)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it do
@u_committer = create_global_setting_for(@u_committer, :mention)
notification.new_note(note)
should_not_email(@u_committer)
end
end
end
context "merge request diff note" do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: project, assignee: user) }
let(:note) { create(:diff_note_on_merge_request, project: project, noteable: merge_request) }
before do
build_team(note.project)
project.add_master(merge_request.author)
project.add_master(merge_request.assignee)
end
describe '#new_note' do
it "records sent notifications" do
# Ensure create SentNotification by noteable = merge_request 6 times, not noteable = note
expect(SentNotification).to receive(:record_note).with(note, any_args).exactly(3).times.and_call_original
notification.new_note(note)
expect(SentNotification.last.in_reply_to_discussion_id).to eq(note.discussion_id)
end
end
end
end
describe 'Issues' do
let(:group) { create(:group) }
let(:project) { create(:empty_project, :public, namespace: group) }
let(:another_project) { create(:empty_project, :public, namespace: group) }
let(:issue) { create :issue, project: project, assignees: [assignee], description: 'cc @participant' }
before do
build_team(issue.project)
build_group(issue.project)
add_users_with_subscription(issue.project, issue)
reset_delivered_emails!
update_custom_notification(:new_issue, @u_guest_custom, resource: project)
update_custom_notification(:new_issue, @u_custom_global)
end
describe '#new_issue' do
it do
notification.new_issue(issue, @u_disabled)
should_email(assignee)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_email(@u_participant_mentioned)
should_email(@g_global_watcher)
should_email(@g_watcher)
should_not_email(@u_mentioned)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it do
create_global_setting_for(issue.assignees.first, :mention)
notification.new_issue(issue, @u_disabled)
should_not_email(issue.assignees.first)
end
it "emails the author if they've opted into notifications about their activity" do
issue.author.notified_of_own_activity = true
notification.new_issue(issue, issue.author)
should_email(issue.author)
end
it "doesn't email the author if they haven't opted into notifications about their activity" do
notification.new_issue(issue, issue.author)
should_not_email(issue.author)
end
it "emails subscribers of the issue's labels" do
user_1 = create(:user)
user_2 = create(:user)
user_3 = create(:user)
user_4 = create(:user)
label = create(:label, project: project, issues: [issue])
group_label = create(:group_label, group: group, issues: [issue])
issue.reload
label.toggle_subscription(user_1, project)
group_label.toggle_subscription(user_2, project)
group_label.toggle_subscription(user_3, another_project)
group_label.toggle_subscription(user_4)
notification.new_issue(issue, @u_disabled)
should_email(user_1)
should_email(user_2)
should_not_email(user_3)
should_email(user_4)
end
context 'confidential issues' do
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignees: [assignee]) }
it "emails subscribers of the issue's labels that can read the issue" do
project.add_developer(member)
project.add_guest(guest)
label = create(:label, project: project, issues: [confidential_issue])
confidential_issue.reload
label.toggle_subscription(non_member, project)
label.toggle_subscription(author, project)
label.toggle_subscription(assignee, project)
label.toggle_subscription(member, project)
label.toggle_subscription(guest, project)
label.toggle_subscription(admin, project)
reset_delivered_emails!
notification.new_issue(confidential_issue, @u_disabled)
should_not_email(@u_guest_watcher)
should_not_email(non_member)
should_not_email(author)
should_not_email(guest)
should_email(assignee)
should_email(member)
should_email(admin)
end
end
end
describe '#new_mentions_in_issue' do
let(:notification_method) { :new_mentions_in_issue }
let(:mentionable) { issue }
include_examples 'notifications for new mentions'
end
describe '#reassigned_issue' do
before do
update_custom_notification(:reassign_issue, @u_guest_custom, resource: project)
update_custom_notification(:reassign_issue, @u_custom_global)
end
it 'emails new assignee' do
notification.reassigned_issue(issue, @u_disabled, [assignee])
should_email(issue.assignees.first)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it 'emails previous assignee even if he has the "on mention" notif level' do
issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_disabled, [@u_watcher])
should_email(@u_mentioned)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@u_custom_global)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it 'emails new assignee even if he has the "on mention" notif level' do
issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_disabled, [@u_mentioned])
expect(issue.assignees.first).to be @u_mentioned
should_email(issue.assignees.first)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@u_custom_global)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it 'emails new assignee' do
issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_disabled, [@u_mentioned])
expect(issue.assignees.first).to be @u_mentioned
should_email(issue.assignees.first)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@u_custom_global)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it 'does not email new assignee if they are the current user' do
issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_mentioned, [@u_mentioned])
expect(issue.assignees.first).to be @u_mentioned
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@u_custom_global)
should_not_email(issue.assignees.first)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { issue }
let(:notification_trigger) { notification.reassigned_issue(issue, @u_disabled, [assignee]) }
end
end
describe '#relabeled_issue' do
let(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1', issues: [issue]) }
let(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') }
let(:label_1) { create(:label, project: project, title: 'Label 1', issues: [issue]) }
let(:label_2) { create(:label, project: project, title: 'Label 2') }
let!(:subscriber_to_group_label_1) { create(:user) { |u| group_label_1.toggle_subscription(u, project) } }
let!(:subscriber_1_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u, project) } }
let!(:subscriber_2_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u) } }
let!(:subscriber_to_group_label_2_on_another_project) { create(:user) { |u| group_label_2.toggle_subscription(u, another_project) } }
let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } }
let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } }
it "emails subscribers of the issue's added labels only" do
notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled)
should_not_email(subscriber_to_label_1)
should_not_email(subscriber_to_group_label_1)
should_not_email(subscriber_to_group_label_2_on_another_project)
should_email(subscriber_1_to_group_label_2)
should_email(subscriber_2_to_group_label_2)
should_email(subscriber_to_label_2)
end
it "emails the current user if they've opted into notifications about their activity" do
subscriber_to_label_2.notified_of_own_activity = true
notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2)
should_email(subscriber_to_label_2)
end
it "doesn't email the current user if they haven't opted into notifications about their activity" do
notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2)
should_not_email(subscriber_to_label_2)
end
it "doesn't send email to anyone but subscribers of the given labels" do
notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled)
should_not_email(issue.assignees.first)
should_not_email(issue.author)
should_not_email(@u_watcher)
should_not_email(@u_guest_watcher)
should_not_email(@u_participant_mentioned)
should_not_email(@subscriber)
should_not_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(subscriber_to_label_1)
should_not_email(subscriber_to_group_label_1)
should_not_email(subscriber_to_group_label_2_on_another_project)
should_email(subscriber_1_to_group_label_2)
should_email(subscriber_2_to_group_label_2)
should_email(subscriber_to_label_2)
end
context 'confidential issues' do
let(:author) { create(:user) }
let(:assignee) { create(:user) }
let(:non_member) { create(:user) }
let(:member) { create(:user) }
let(:guest) { create(:user) }
let(:admin) { create(:admin) }
let(:confidential_issue) { create(:issue, :confidential, project: project, title: 'Confidential issue', author: author, assignees: [assignee]) }
let!(:label_1) { create(:label, project: project, issues: [confidential_issue]) }
let!(:label_2) { create(:label, project: project) }
it "emails subscribers of the issue's labels that can read the issue" do
project.add_developer(member)
project.add_guest(guest)
label_2.toggle_subscription(non_member, project)
label_2.toggle_subscription(author, project)
label_2.toggle_subscription(assignee, project)
label_2.toggle_subscription(member, project)
label_2.toggle_subscription(guest, project)
label_2.toggle_subscription(admin, project)
reset_delivered_emails!
notification.relabeled_issue(confidential_issue, [label_2], @u_disabled)
should_not_email(non_member)
should_not_email(guest)
should_email(author)
should_email(assignee)
should_email(member)
should_email(admin)
end
end
end
describe '#close_issue' do
before do
update_custom_notification(:close_issue, @u_guest_custom, resource: project)
update_custom_notification(:close_issue, @u_custom_global)
end
it 'sends email to issue assignee and issue author' do
notification.close_issue(issue, @u_disabled)
should_email(issue.assignees.first)
should_email(issue.author)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { issue }
let(:notification_trigger) { notification.close_issue(issue, @u_disabled) }
end
end
describe '#reopen_issue' do
before do
update_custom_notification(:reopen_issue, @u_guest_custom, resource: project)
update_custom_notification(:reopen_issue, @u_custom_global)
end
it 'sends email to issue notification recipients' do
notification.reopen_issue(issue, @u_disabled)
should_email(issue.assignees.first)
should_email(issue.author)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { issue }
let(:notification_trigger) { notification.reopen_issue(issue, @u_disabled) }
end
end
describe '#issue_moved' do
let(:new_issue) { create(:issue) }
it 'sends email to issue notification recipients' do
notification.issue_moved(issue, new_issue, @u_disabled)
should_email(issue.assignees.first)
should_email(issue.author)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { issue }
let(:notification_trigger) { notification.issue_moved(issue, new_issue, @u_disabled) }
end
end
end
describe 'Merge Requests' do
let(:group) { create(:group) }
let(:project) { create(:project, :public, :repository, namespace: group) }
let(:another_project) { create(:empty_project, :public, namespace: group) }
let(:merge_request) { create :merge_request, source_project: project, assignee: create(:user), description: 'cc @participant' }
before do
build_team(merge_request.target_project)
add_users_with_subscription(merge_request.target_project, merge_request)
update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:new_merge_request, @u_custom_global)
reset_delivered_emails!
end
describe '#new_merge_request' do
before do
update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:new_merge_request, @u_custom_global)
end
it do
notification.new_merge_request(merge_request, @u_disabled)
should_email(merge_request.assignee)
should_email(@u_watcher)
should_email(@watcher_and_subscriber)
should_email(@u_participant_mentioned)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it "emails the author if they've opted into notifications about their activity" do
merge_request.author.notified_of_own_activity = true
notification.new_merge_request(merge_request, merge_request.author)
should_email(merge_request.author)
end
it "doesn't email the author if they haven't opted into notifications about their activity" do
notification.new_merge_request(merge_request, merge_request.author)
should_not_email(merge_request.author)
end
it "emails subscribers of the merge request's labels" do
user_1 = create(:user)
user_2 = create(:user)
user_3 = create(:user)
user_4 = create(:user)
label = create(:label, project: project, merge_requests: [merge_request])
group_label = create(:group_label, group: group, merge_requests: [merge_request])
label.toggle_subscription(user_1, project)
group_label.toggle_subscription(user_2, project)
group_label.toggle_subscription(user_3, another_project)
group_label.toggle_subscription(user_4)
notification.new_merge_request(merge_request, @u_disabled)
should_email(user_1)
should_email(user_2)
should_not_email(user_3)
should_email(user_4)
end
context 'participating' do
it_should_behave_like 'participating by assignee notification' do
let(:participant) { create(:user, username: 'user-participant')}
let(:issuable) { merge_request }
let(:notification_trigger) { notification.new_merge_request(merge_request, @u_disabled) }
end
it_should_behave_like 'participating by note notification' do
let(:participant) { create(:user, username: 'user-participant')}
let(:issuable) { merge_request }
let(:notification_trigger) { notification.new_merge_request(merge_request, @u_disabled) }
end
context 'by author' do
let(:participant) { create(:user, username: 'user-participant')}
before do
merge_request.author = participant
merge_request.save
notification.new_merge_request(merge_request, @u_disabled)
end
it { should_not_email(participant) }
end
end
end
describe '#new_mentions_in_merge_request' do
let(:notification_method) { :new_mentions_in_merge_request }
let(:mentionable) { merge_request }
include_examples 'notifications for new mentions'
end
describe '#reassigned_merge_request' do
before do
update_custom_notification(:reassign_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:reassign_merge_request, @u_custom_global)
end
it do
notification.reassigned_merge_request(merge_request, merge_request.author)
should_email(merge_request.assignee)
should_email(@u_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
let(:notification_trigger) { notification.reassigned_merge_request(merge_request, @u_disabled) }
end
end
describe '#relabel_merge_request' do
let(:group_label_1) { create(:group_label, group: group, title: 'Group Label 1', merge_requests: [merge_request]) }
let(:group_label_2) { create(:group_label, group: group, title: 'Group Label 2') }
let(:label_1) { create(:label, project: project, title: 'Label 1', merge_requests: [merge_request]) }
let(:label_2) { create(:label, project: project, title: 'Label 2') }
let!(:subscriber_to_group_label_1) { create(:user) { |u| group_label_1.toggle_subscription(u, project) } }
let!(:subscriber_1_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u, project) } }
let!(:subscriber_2_to_group_label_2) { create(:user) { |u| group_label_2.toggle_subscription(u) } }
let!(:subscriber_to_group_label_2_on_another_project) { create(:user) { |u| group_label_2.toggle_subscription(u, another_project) } }
let!(:subscriber_to_label_1) { create(:user) { |u| label_1.toggle_subscription(u, project) } }
let!(:subscriber_to_label_2) { create(:user) { |u| label_2.toggle_subscription(u, project) } }
it "emails subscribers of the merge request's added labels only" do
notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled)
should_not_email(subscriber_to_label_1)
should_not_email(subscriber_to_group_label_1)
should_not_email(subscriber_to_group_label_2_on_another_project)
should_email(subscriber_1_to_group_label_2)
should_email(subscriber_2_to_group_label_2)
should_email(subscriber_to_label_2)
end
it "doesn't send email to anyone but subscribers of the given labels" do
notification.relabeled_merge_request(merge_request, [group_label_2, label_2], @u_disabled)
should_not_email(merge_request.assignee)
should_not_email(merge_request.author)
should_not_email(@u_watcher)
should_not_email(@u_participant_mentioned)
should_not_email(@subscriber)
should_not_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_lazy_participant)
should_not_email(subscriber_to_label_1)
should_not_email(subscriber_to_group_label_1)
should_not_email(subscriber_to_group_label_2_on_another_project)
should_email(subscriber_1_to_group_label_2)
should_email(subscriber_2_to_group_label_2)
should_email(subscriber_to_label_2)
end
end
describe '#closed_merge_request' do
before do
update_custom_notification(:close_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:close_merge_request, @u_custom_global)
end
it do
notification.close_mr(merge_request, @u_disabled)
should_email(merge_request.assignee)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
let(:notification_trigger) { notification.close_mr(merge_request, @u_disabled) }
end
end
describe '#merged_merge_request' do
before do
update_custom_notification(:merge_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:merge_merge_request, @u_custom_global)
end
it do
notification.merge_mr(merge_request, @u_disabled)
should_email(merge_request.assignee)
should_email(@u_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_email(@u_guest_watcher)
should_email(@u_custom_global)
should_email(@u_guest_custom)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it "notifies the merger when the pipeline succeeds is true" do
merge_request.merge_when_pipeline_succeeds = true
notification.merge_mr(merge_request, @u_watcher)
should_email(@u_watcher)
end
it "does not notify the merger when the pipeline succeeds is false" do
merge_request.merge_when_pipeline_succeeds = false
notification.merge_mr(merge_request, @u_watcher)
should_not_email(@u_watcher)
end
it "notifies the merger when the pipeline succeeds is false but they've opted into notifications about their activity" do
merge_request.merge_when_pipeline_succeeds = false
@u_watcher.notified_of_own_activity = true
notification.merge_mr(merge_request, @u_watcher)
should_email(@u_watcher)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
let(:notification_trigger) { notification.merge_mr(merge_request, @u_disabled) }
end
end
describe '#reopen_merge_request' do
before do
update_custom_notification(:reopen_merge_request, @u_guest_custom, resource: project)
update_custom_notification(:reopen_merge_request, @u_custom_global)
end
it do
notification.reopen_mr(merge_request, @u_disabled)
should_email(merge_request.assignee)
should_email(@u_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_custom_global)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
let(:notification_trigger) { notification.reopen_mr(merge_request, @u_disabled) }
end
end
describe "#resolve_all_discussions" do
it do
notification.resolve_all_discussions(merge_request, @u_disabled)
should_email(merge_request.assignee)
should_email(@u_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_email(@u_guest_watcher)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it_behaves_like 'participating notifications' do
let(:participant) { create(:user, username: 'user-participant') }
let(:issuable) { merge_request }
let(:notification_trigger) { notification.resolve_all_discussions(merge_request, @u_disabled) }
end
end
end
describe 'Projects' do
let(:project) { create(:empty_project) }
before do
build_team(project)
reset_delivered_emails!
end
describe '#project_was_moved' do
it do
notification.project_was_moved(project, "gitlab/gitlab")
should_email(@u_watcher)
should_email(@u_participating)
should_email(@u_lazy_participant)
should_email(@u_custom_global)
should_not_email(@u_guest_watcher)
should_not_email(@u_guest_custom)
should_not_email(@u_disabled)
end
end
describe '#project_exported' do
it do
notification.project_exported(project, @u_disabled)
should_only_email(@u_disabled)
end
end
describe '#project_not_exported' do
it do
notification.project_not_exported(project, @u_disabled, ['error'])
should_only_email(@u_disabled)
end
end
end
describe 'GroupMember' do
describe '#decline_group_invite' do
let(:creator) { create(:user) }
let(:group) { create(:group) }
let(:member) { create(:user) }
before(:each) do
group.add_owner(creator)
group.add_developer(member, creator)
end
it do
group_member = group.members.first
expect do
notification.decline_group_invite(group_member)
end.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end
end
describe 'ProjectMember' do
describe '#decline_group_invite' do
let(:project) { create(:empty_project) }
let(:member) { create(:user) }
before(:each) do
project.add_developer(member, current_user: project.owner)
end
it do
project_member = project.members.first
expect do
notification.decline_project_invite(project_member)
end.to change { ActionMailer::Base.deliveries.size }.by(1)
end
end
end
context 'guest user in private project' do
let(:private_project) { create(:empty_project, :private) }
let(:guest) { create(:user) }
let(:developer) { create(:user) }
let(:assignee) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: private_project, assignee: assignee) }
let(:merge_request1) { create(:merge_request, source_project: private_project, assignee: assignee, description: "cc @#{guest.username}") }
let(:note) { create(:note, noteable: merge_request, project: private_project) }
before do
private_project.add_developer(assignee)
private_project.add_developer(developer)
private_project.add_guest(guest)
ActionMailer::Base.deliveries.clear
end
it 'filters out guests when new note is created' do
expect(SentNotification).to receive(:record).with(merge_request, any_args).exactly(1).times
notification.new_note(note)
should_not_email(guest)
should_email(assignee)
end
it 'filters out guests when new merge request is created' do
notification.new_merge_request(merge_request1, @u_disabled)
should_not_email(guest)
should_email(assignee)
end
it 'filters out guests when merge request is closed' do
notification.close_mr(merge_request, developer)
should_not_email(guest)
should_email(assignee)
end
it 'filters out guests when merge request is reopened' do
notification.reopen_mr(merge_request, developer)
should_not_email(guest)
should_email(assignee)
end
it 'filters out guests when merge request is merged' do
notification.merge_mr(merge_request, developer)
should_not_email(guest)
should_email(assignee)
end
end
describe 'Pipelines' do
describe '#pipeline_finished' do
let(:project) { create(:project, :public, :repository) }
let(:u_member) { create(:user) }
let(:u_watcher) { create_user_with_notification(:watch, 'watcher') }
let(:u_custom_notification_unset) do
create_user_with_notification(:custom, 'custom_unset')
end
let(:u_custom_notification_enabled) do
user = create_user_with_notification(:custom, 'custom_enabled')
update_custom_notification(:success_pipeline, user, resource: project)
update_custom_notification(:failed_pipeline, user, resource: project)
user
end
let(:u_custom_notification_disabled) do
user = create_user_with_notification(:custom, 'custom_disabled')
update_custom_notification(:success_pipeline, user, resource: project, value: false)
update_custom_notification(:failed_pipeline, user, resource: project, value: false)
user
end
let(:commit) { project.commit }
def create_pipeline(user, status)
create(:ci_pipeline, status,
project: project,
user: user,
ref: 'refs/heads/master',
sha: commit.id,
before_sha: '00000000')
end
before do
project.add_master(u_member)
project.add_master(u_watcher)
project.add_master(u_custom_notification_unset)
project.add_master(u_custom_notification_enabled)
project.add_master(u_custom_notification_disabled)
reset_delivered_emails!
end
context 'with a successful pipeline' do
context 'when the creator has default settings' do
before do
pipeline = create_pipeline(u_member, :success)
notification.pipeline_finished(pipeline)
end
it 'notifies nobody' do
should_not_email_anyone
end
end
context 'when the creator has watch set' do
before do
pipeline = create_pipeline(u_watcher, :success)
notification.pipeline_finished(pipeline)
end
it 'notifies nobody' do
should_not_email_anyone
end
end
context 'when the creator has custom notifications, but without any set' do
before do
pipeline = create_pipeline(u_custom_notification_unset, :success)
notification.pipeline_finished(pipeline)
end
it 'notifies nobody' do
should_not_email_anyone
end
end
context 'when the creator has custom notifications disabled' do
before do
pipeline = create_pipeline(u_custom_notification_disabled, :success)
notification.pipeline_finished(pipeline)
end
it 'notifies nobody' do
should_not_email_anyone
end
end
context 'when the creator has custom notifications enabled' do
before do
pipeline = create_pipeline(u_custom_notification_enabled, :success)
notification.pipeline_finished(pipeline)
end
it 'emails only the creator' do
should_only_email(u_custom_notification_enabled, kind: :bcc)
end
end
end
context 'with a failed pipeline' do
context 'when the creator has no custom notification set' do
before do
pipeline = create_pipeline(u_member, :failed)
notification.pipeline_finished(pipeline)
end
it 'emails only the creator' do
should_only_email(u_member, kind: :bcc)
end
end
context 'when the creator has watch set' do
before do
pipeline = create_pipeline(u_watcher, :failed)
notification.pipeline_finished(pipeline)
end
it 'emails only the creator' do
should_only_email(u_watcher, kind: :bcc)
end
end
context 'when the creator has custom notifications, but without any set' do
before do
pipeline = create_pipeline(u_custom_notification_unset, :failed)
notification.pipeline_finished(pipeline)
end
it 'emails only the creator' do
should_only_email(u_custom_notification_unset, kind: :bcc)
end
end
context 'when the creator has custom notifications disabled' do
before do
pipeline = create_pipeline(u_custom_notification_disabled, :failed)
notification.pipeline_finished(pipeline)
end
it 'notifies nobody' do
should_not_email_anyone
end
end
context 'when the creator has custom notifications set' do
before do
pipeline = create_pipeline(u_custom_notification_enabled, :failed)
notification.pipeline_finished(pipeline)
end
it 'emails only the creator' do
should_only_email(u_custom_notification_enabled, kind: :bcc)
end
end
context 'when the creator has no read_build access' do
before do
pipeline = create_pipeline(u_member, :failed)
project.update(public_builds: false)
project.team.truncate
notification.pipeline_finished(pipeline)
end
it 'does not send emails' do
should_not_email_anyone
end
end
end
end
end
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
@u_participant_mentioned = create_global_setting_for(create(:user, username: 'participant'), :participating)
@u_disabled = create_global_setting_for(create(:user), :disabled)
@u_mentioned = create_global_setting_for(create(:user, username: 'mention'), :mention)
@u_committer = create(:user, username: 'committer')
@u_not_mentioned = create_global_setting_for(create(:user, username: 'regular'), :participating)
@u_outsider_mentioned = create(:user, username: 'outsider')
@u_custom_global = create_global_setting_for(create(:user, username: 'custom_global'), :custom)
# User to be participant by default
# This user does not contain any record in notification settings table
# It should be treated with a :participating notification_level
@u_lazy_participant = create(:user, username: 'lazy-participant')
@u_guest_watcher = create_user_with_notification(:watch, 'guest_watching')
@u_guest_custom = create_user_with_notification(:custom, 'guest_custom')
project.add_master(@u_watcher)
project.add_master(@u_participating)
project.add_master(@u_participant_mentioned)
project.add_master(@u_disabled)
project.add_master(@u_mentioned)
project.add_master(@u_committer)
project.add_master(@u_not_mentioned)
project.add_master(@u_lazy_participant)
project.add_master(@u_custom_global)
end
# Users in the project's group but not part of project's team
# with different notification settings
def build_group(project)
group = create(:group, :public)
project.group = group
# Group member: global=disabled, group=watch
@g_watcher = create_user_with_notification(:watch, 'group_watcher', project.group)
@g_watcher.notification_settings_for(nil).disabled!
# Group member: global=watch, group=global
@g_global_watcher = create_global_setting_for(create(:user), :watch)
group.add_users([@g_watcher, @g_global_watcher], :master)
group
end
def create_global_setting_for(user, level)
setting = user.global_notification_setting
setting.level = level
setting.save
user
end
def create_user_with_notification(level, username, resource = project)
user = create(:user, username: username)
setting = user.notification_settings_for(resource)
setting.level = level
setting.save
user
end
# Create custom notifications
# When resource is nil it means global notification
def update_custom_notification(event, user, resource: nil, value: true)
setting = user.notification_settings_for(resource)
setting.update!(event => value)
end
def add_users_with_subscription(project, issuable)
@subscriber = create :user
@unsubscriber = create :user
@subscribed_participant = create_global_setting_for(create(:user, username: 'subscribed_participant'), :participating)
@watcher_and_subscriber = create_global_setting_for(create(:user), :watch)
project.add_master(@subscribed_participant)
project.add_master(@subscriber)
project.add_master(@unsubscriber)
project.add_master(@watcher_and_subscriber)
issuable.subscriptions.create(user: @subscriber, project: project, subscribed: true)
issuable.subscriptions.create(user: @subscribed_participant, project: project, subscribed: true)
issuable.subscriptions.create(user: @unsubscriber, project: project, subscribed: false)
# Make the watcher a subscriber to detect dupes
issuable.subscriptions.create(user: @watcher_and_subscriber, project: project, subscribed: true)
end
end