Commit a8fcaaf1 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'pipeline-notifications' into 'master'

Integrate CI emails into notification system

Closes #21930

See merge request !6342
parents 940cb3db 3e75e453
...@@ -74,4 +74,13 @@ module NotificationsHelper ...@@ -74,4 +74,13 @@ module NotificationsHelper
return unless notification_setting.source_type return unless notification_setting.source_type
hidden_field_tag "#{notification_setting.source_type.downcase}_id", notification_setting.source_id hidden_field_tag "#{notification_setting.source_type.downcase}_id", notification_setting.source_id
end end
def notification_event_name(event)
case event
when :success_pipeline
'Successful pipeline'
else
event.to_s.humanize
end
end
end end
module Emails module Emails
module Pipelines module Pipelines
def pipeline_success_email(pipeline, to) def pipeline_success_email(pipeline, recipients)
pipeline_mail(pipeline, to, 'succeeded') pipeline_mail(pipeline, recipients, 'succeeded')
end end
def pipeline_failed_email(pipeline, to) def pipeline_failed_email(pipeline, recipients)
pipeline_mail(pipeline, to, 'failed') pipeline_mail(pipeline, recipients, 'failed')
end end
private private
def pipeline_mail(pipeline, to, status) def pipeline_mail(pipeline, recipients, status)
@project = pipeline.project @project = pipeline.project
@pipeline = pipeline @pipeline = pipeline
@merge_request = pipeline.merge_requests.first @merge_request = pipeline.merge_requests.first
add_headers add_headers
mail(to: to, subject: pipeline_subject(status), skip_premailer: true) do |format| # We use bcc here because we don't want to generate this emails for a
# thousand times. This could be potentially expensive in a loop, and
# recipients would contain all project watchers so it could be a lot.
mail(bcc: recipients,
subject: pipeline_subject(status),
skip_premailer: true) do |format|
format.html { render layout: false } format.html { render layout: false }
format.text format.text
end end
......
...@@ -81,6 +81,12 @@ module Ci ...@@ -81,6 +81,12 @@ module Ci
PipelineHooksWorker.perform_async(id) PipelineHooksWorker.perform_async(id)
end end
end end
after_transition any => [:success, :failed] do |pipeline|
pipeline.run_after_commit do
PipelineNotificationWorker.perform_async(pipeline.id)
end
end
end end
# ref can't be HEAD or SHA, can only be branch/tag name # ref can't be HEAD or SHA, can only be branch/tag name
...@@ -109,6 +115,11 @@ module Ci ...@@ -109,6 +115,11 @@ module Ci
project.id project.id
end end
# For now the only user who participates is the user who triggered
def participants(_current_user = nil)
Array(user)
end
def valid_commit_sha def valid_commit_sha
if self.sha == Gitlab::Git::BLANK_SHA if self.sha == Gitlab::Git::BLANK_SHA
self.errors.add(:sha, " cant be 00000000 (branch removal)") self.errors.add(:sha, " cant be 00000000 (branch removal)")
......
...@@ -32,7 +32,9 @@ class NotificationSetting < ActiveRecord::Base ...@@ -32,7 +32,9 @@ class NotificationSetting < ActiveRecord::Base
:reopen_merge_request, :reopen_merge_request,
:close_merge_request, :close_merge_request,
:reassign_merge_request, :reassign_merge_request,
:merge_merge_request :merge_merge_request,
:failed_pipeline,
:success_pipeline
] ]
store :events, accessors: EMAIL_EVENTS, coder: JSON store :events, accessors: EMAIL_EVENTS, coder: JSON
......
class PipelinesEmailService < Service class PipelinesEmailService < Service
prop_accessor :recipients prop_accessor :recipients
boolean_accessor :add_pusher
boolean_accessor :notify_only_broken_pipelines boolean_accessor :notify_only_broken_pipelines
validates :recipients, validates :recipients, presence: true, if: :activated?
presence: true,
if: ->(s) { s.activated? && !s.add_pusher? }
def initialize_properties def initialize_properties
self.properties ||= { notify_only_broken_pipelines: true } self.properties ||= { notify_only_broken_pipelines: true }
...@@ -34,8 +31,8 @@ class PipelinesEmailService < Service ...@@ -34,8 +31,8 @@ class PipelinesEmailService < Service
return unless all_recipients.any? return unless all_recipients.any?
pipeline = Ci::Pipeline.find(data[:object_attributes][:id]) pipeline_id = data[:object_attributes][:id]
Ci::SendPipelineNotificationService.new(pipeline).execute(all_recipients) PipelineNotificationWorker.new.perform(pipeline_id, all_recipients)
end end
def can_test? def can_test?
...@@ -57,9 +54,6 @@ class PipelinesEmailService < Service ...@@ -57,9 +54,6 @@ class PipelinesEmailService < Service
{ type: 'textarea', { type: 'textarea',
name: 'recipients', name: 'recipients',
placeholder: 'Emails separated by comma' }, placeholder: 'Emails separated by comma' },
{ type: 'checkbox',
name: 'add_pusher',
label: 'Add pusher to recipients list' },
{ type: 'checkbox', { type: 'checkbox',
name: 'notify_only_broken_pipelines' }, name: 'notify_only_broken_pipelines' },
] ]
...@@ -85,12 +79,6 @@ class PipelinesEmailService < Service ...@@ -85,12 +79,6 @@ class PipelinesEmailService < Service
end end
def retrieve_recipients(data) def retrieve_recipients(data)
all_recipients = recipients.to_s.split(',').reject(&:blank?) recipients.to_s.split(',').reject(&:blank?)
if add_pusher? && data[:user].try(:[], :email)
all_recipients << data[:user][:email]
end
all_recipients
end end
end end
...@@ -5,7 +5,7 @@ module Ci ...@@ -5,7 +5,7 @@ module Ci
# If we can't read build we should also not have that # If we can't read build we should also not have that
# ability when looking at this in context of commit_status # ability when looking at this in context of commit_status
%w(read create update admin).each do |rule| %w[read create update admin].each do |rule|
cannot! :"#{rule}_commit_status" unless can? :"#{rule}_build" cannot! :"#{rule}_commit_status" unless can? :"#{rule}_build"
end end
end end
......
module Ci
class PipelinePolicy < BuildPolicy
end
end
module Ci
class SendPipelineNotificationService
attr_reader :pipeline
def initialize(new_pipeline)
@pipeline = new_pipeline
end
def execute(recipients)
email_template = "pipeline_#{pipeline.status}_email"
return unless Notify.respond_to?(email_template)
recipients.each do |to|
Notify.public_send(email_template, pipeline, to).deliver_later
end
end
end
end
...@@ -312,6 +312,22 @@ class NotificationService ...@@ -312,6 +312,22 @@ class NotificationService
mailer.project_was_not_exported_email(current_user, project, errors).deliver_later mailer.project_was_not_exported_email(current_user, project, errors).deliver_later
end end
def pipeline_finished(pipeline, recipients = nil)
email_template = "pipeline_#{pipeline.status}_email"
return unless mailer.respond_to?(email_template)
recipients ||= build_recipients(
pipeline,
pipeline.project,
nil, # The acting user, who won't be added to recipients
action: pipeline.status).map(&:notification_email)
if recipients.any?
mailer.public_send(email_template, pipeline, recipients).deliver_later
end
end
protected protected
# Get project/group users with CUSTOM notification level # Get project/group users with CUSTOM notification level
...@@ -475,9 +491,14 @@ class NotificationService ...@@ -475,9 +491,14 @@ class NotificationService
end end
def reject_users_without_access(recipients, target) def reject_users_without_access(recipients, target)
return recipients unless target.is_a?(Issuable) ability = case target
when Issuable
:"read_#{target.to_ability_name}"
when Ci::Pipeline
:read_build # We have build trace in pipeline emails
end
ability = :"read_#{target.to_ability_name}" return recipients unless ability
recipients.select do |user| recipients.select do |user|
user.can?(ability, target) user.can?(ability, target)
...@@ -624,6 +645,6 @@ class NotificationService ...@@ -624,6 +645,6 @@ class NotificationService
# Build event key to search on custom notification level # Build event key to search on custom notification level
# Check NotificationSetting::EMAIL_EVENTS # Check NotificationSetting::EMAIL_EVENTS
def build_custom_key(action, object) def build_custom_key(action, object)
"#{action}_#{object.class.name.underscore}".to_sym "#{action}_#{object.class.model_name.name.underscore}".to_sym
end end
end end
...@@ -27,5 +27,5 @@ ...@@ -27,5 +27,5 @@
%label{ for: field_id } %label{ for: field_id }
= check_box("notification_setting", event, id: field_id, class: "js-custom-notification-event", checked: notification_setting.events[event]) = check_box("notification_setting", event, id: field_id, class: "js-custom-notification-event", checked: notification_setting.events[event])
%strong %strong
= event.to_s.humanize = notification_event_name(event)
= icon("spinner spin", class: "custom-notification-event-loading") = icon("spinner spin", class: "custom-notification-event-loading")
class PipelineNotificationWorker
include Sidekiq::Worker
include PipelineQueue
def perform(pipeline_id, recipients = nil)
pipeline = Ci::Pipeline.find_by(id: pipeline_id)
return unless pipeline
NotificationService.new.pipeline_finished(pipeline, recipients)
end
end
---
title: Add CI notifications. Who triggered a pipeline would receive an email after
the pipeline is succeeded or failed. Users could also update notification settings
accordingly
merge_request: 6342
author:
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
**Valid notification levels** **Valid notification levels**
The notification levels are defined in the `NotificationSetting::level` model enumeration. Currently, these levels are recognized: The notification levels are defined in the `NotificationSetting.level` model enumeration. Currently, these levels are recognized:
``` ```
disabled disabled
...@@ -28,6 +28,8 @@ reopen_merge_request ...@@ -28,6 +28,8 @@ reopen_merge_request
close_merge_request close_merge_request
reassign_merge_request reassign_merge_request
merge_merge_request merge_merge_request
failed_pipeline
success_pipeline
``` ```
## Global notification settings ## Global notification settings
...@@ -77,6 +79,8 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab ...@@ -77,6 +79,8 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab
| `close_merge_request` | boolean | no | Enable/disable this notification | | `close_merge_request` | boolean | no | Enable/disable this notification |
| `reassign_merge_request` | boolean | no | Enable/disable this notification | | `reassign_merge_request` | boolean | no | Enable/disable this notification |
| `merge_merge_request` | boolean | no | Enable/disable this notification | | `merge_merge_request` | boolean | no | Enable/disable this notification |
| `failed_pipeline` | boolean | no | Enable/disable this notification |
| `success_pipeline` | boolean | no | Enable/disable this notification |
Example response: Example response:
...@@ -141,6 +145,8 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab ...@@ -141,6 +145,8 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab
| `close_merge_request` | boolean | no | Enable/disable this notification | | `close_merge_request` | boolean | no | Enable/disable this notification |
| `reassign_merge_request` | boolean | no | Enable/disable this notification | | `reassign_merge_request` | boolean | no | Enable/disable this notification |
| `merge_merge_request` | boolean | no | Enable/disable this notification | | `merge_merge_request` | boolean | no | Enable/disable this notification |
| `failed_pipeline` | boolean | no | Enable/disable this notification |
| `success_pipeline` | boolean | no | Enable/disable this notification |
Example responses: Example responses:
...@@ -161,7 +167,9 @@ Example responses: ...@@ -161,7 +167,9 @@ Example responses:
"reopen_merge_request": false, "reopen_merge_request": false,
"close_merge_request": false, "close_merge_request": false,
"reassign_merge_request": false, "reassign_merge_request": false,
"merge_merge_request": false "merge_merge_request": false,
"failed_pipeline": false,
"success_pipeline": false
} }
} }
``` ```
......
...@@ -66,6 +66,7 @@ Below is the table of events users can be notified of: ...@@ -66,6 +66,7 @@ Below is the table of events users can be notified of:
In all of the below cases, the notification will be sent to: In all of the below cases, the notification will be sent to:
- Participants: - Participants:
- the author and assignee of the issue/merge request - the author and assignee of the issue/merge request
- the author of the pipeline
- authors of comments on the issue/merge request - authors of comments on the issue/merge request
- anyone mentioned by `@username` in the issue/merge request title or description - anyone mentioned by `@username` in the issue/merge request title or description
- anyone mentioned by `@username` in any of the comments on the issue/merge request - anyone mentioned by `@username` in any of the comments on the issue/merge request
...@@ -88,6 +89,8 @@ In all of the below cases, the notification will be sent to: ...@@ -88,6 +89,8 @@ In all of the below cases, the notification will be sent to:
| Reopen merge request | | | Reopen merge request | |
| Merge merge request | | | Merge merge request | |
| New comment | The above, plus anyone mentioned by `@username` in the comment, with notification level "Mention" or higher | | New comment | The above, plus anyone mentioned by `@username` in the comment, with notification level "Mention" or higher |
| Failed pipeline | The above, plus the author of the pipeline |
| Successful pipeline | The above, plus the author of the pipeline |
In addition, if the title or description of an Issue or Merge Request is In addition, if the title or description of an Issue or Merge Request is
......
...@@ -524,4 +524,78 @@ describe Ci::Pipeline, models: true do ...@@ -524,4 +524,78 @@ describe Ci::Pipeline, models: true do
expect(pipeline.merge_requests).to be_empty expect(pipeline.merge_requests).to be_empty
end end
end end
describe 'notifications when pipeline success or failed' do
let(:project) { create(:project) }
let(:pipeline) do
create(:ci_pipeline,
project: project,
sha: project.commit('master').sha,
user: create(:user))
end
before do
reset_delivered_emails!
project.team << [pipeline.user, Gitlab::Access::DEVELOPER]
perform_enqueued_jobs do
pipeline.enqueue
pipeline.run
end
end
shared_examples 'sending a notification' do
it 'sends an email' do
should_only_email(pipeline.user, kind: :bcc)
end
end
shared_examples 'not sending any notification' do
it 'does not send any email' do
should_not_email_anyone
end
end
context 'with success pipeline' do
before do
perform_enqueued_jobs do
pipeline.succeed
end
end
it_behaves_like 'sending a notification'
end
context 'with failed pipeline' do
before do
perform_enqueued_jobs do
pipeline.drop
end
end
it_behaves_like 'sending a notification'
end
context 'with skipped pipeline' do
before do
perform_enqueued_jobs do
pipeline.skip
end
end
it_behaves_like 'not sending any notification'
end
context 'with cancelled pipeline' do
before do
perform_enqueued_jobs do
pipeline.cancel
end
end
it_behaves_like 'not sending any notification'
end
end
end end
...@@ -13,7 +13,7 @@ describe PipelinesEmailService do ...@@ -13,7 +13,7 @@ describe PipelinesEmailService do
end end
before do before do
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
describe 'Validations' do describe 'Validations' do
...@@ -23,14 +23,6 @@ describe PipelinesEmailService do ...@@ -23,14 +23,6 @@ describe PipelinesEmailService do
end end
it { is_expected.to validate_presence_of(:recipients) } it { is_expected.to validate_presence_of(:recipients) }
context 'when pusher is added' do
before do
subject.add_pusher = true
end
it { is_expected.not_to validate_presence_of(:recipients) }
end
end end
context 'when service is inactive' do context 'when service is inactive' do
...@@ -66,8 +58,7 @@ describe PipelinesEmailService do ...@@ -66,8 +58,7 @@ describe PipelinesEmailService do
end end
it 'sends email' do it 'sends email' do
sent_to = ActionMailer::Base.deliveries.flat_map(&:to) should_only_email(double(notification_email: recipient), kind: :bcc)
expect(sent_to).to contain_exactly(recipient)
end end
end end
...@@ -79,7 +70,7 @@ describe PipelinesEmailService do ...@@ -79,7 +70,7 @@ describe PipelinesEmailService do
end end
it 'does not send email' do it 'does not send email' do
expect(ActionMailer::Base.deliveries).to be_empty should_not_email_anyone
end end
end end
......
require 'spec_helper'
describe Ci::SendPipelineNotificationService, services: true do
let(:pipeline) do
create(:ci_pipeline,
project: project,
sha: project.commit('master').sha,
user: user,
status: status)
end
let(:project) { create(:project) }
let(:user) { create(:user) }
subject{ described_class.new(pipeline) }
describe '#execute' do
before do
reset_delivered_emails!
end
shared_examples 'sending emails' do
it 'sends an email to pipeline user' do
perform_enqueued_jobs do
subject.execute([user.email])
end
email = ActionMailer::Base.deliveries.last
expect(email.subject).to include(email_subject)
expect(email.to).to eq([user.email])
end
end
context 'with success pipeline' do
let(:status) { 'success' }
let(:email_subject) { "Pipeline ##{pipeline.id} has succeeded" }
it_behaves_like 'sending emails'
end
context 'with failed pipeline' do
let(:status) { 'failed' }
let(:email_subject) { "Pipeline ##{pipeline.id} has failed" }
it_behaves_like 'sending emails'
end
end
end
...@@ -17,7 +17,7 @@ describe NotificationService, services: true do ...@@ -17,7 +17,7 @@ describe NotificationService, services: true do
it 'sends no emails when no new mentions are present' do it 'sends no emails when no new mentions are present' do
send_notifications send_notifications
expect(ActionMailer::Base.deliveries).to be_empty should_not_email_anyone
end end
it 'emails new mentions with a watch level higher than participant' do it 'emails new mentions with a watch level higher than participant' do
...@@ -27,7 +27,7 @@ describe NotificationService, services: true do ...@@ -27,7 +27,7 @@ describe NotificationService, services: true do
it 'does not email new mentions with a watch level equal to or less than participant' do it 'does not email new mentions with a watch level equal to or less than participant' do
send_notifications(@u_participating, @u_mentioned) send_notifications(@u_participating, @u_mentioned)
expect(ActionMailer::Base.deliveries).to be_empty should_not_email_anyone
end end
end end
...@@ -79,7 +79,7 @@ describe NotificationService, services: true do ...@@ -79,7 +79,7 @@ describe NotificationService, services: true do
# Ensure create SentNotification by noteable = issue 6 times, not noteable = note # Ensure create SentNotification by noteable = issue 6 times, not noteable = note
expect(SentNotification).to receive(:record).with(issue, any_args).exactly(8).times expect(SentNotification).to receive(:record).with(issue, any_args).exactly(8).times
ActionMailer::Base.deliveries.clear reset_delivered_emails!
notification.new_note(note) notification.new_note(note)
...@@ -111,7 +111,7 @@ describe NotificationService, services: true do ...@@ -111,7 +111,7 @@ describe NotificationService, services: true do
context 'participating' do context 'participating' do
context 'by note' do context 'by note' do
before do before do
ActionMailer::Base.deliveries.clear reset_delivered_emails!
note.author = @u_lazy_participant note.author = @u_lazy_participant
note.save note.save
notification.new_note(note) notification.new_note(note)
...@@ -134,7 +134,7 @@ describe NotificationService, services: true do ...@@ -134,7 +134,7 @@ describe NotificationService, services: true do
@u_watcher.notification_settings_for(note.project).participating! @u_watcher.notification_settings_for(note.project).participating!
@u_watcher.notification_settings_for(note.project.group).global! @u_watcher.notification_settings_for(note.project.group).global!
update_custom_notification(:new_note, @u_custom_global) update_custom_notification(:new_note, @u_custom_global)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
it do it do
...@@ -173,7 +173,7 @@ describe NotificationService, services: true do ...@@ -173,7 +173,7 @@ describe NotificationService, services: true do
expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times expect(SentNotification).to receive(:record).with(confidential_issue, any_args).exactly(4).times
ActionMailer::Base.deliveries.clear reset_delivered_emails!
notification.new_note(note) notification.new_note(note)
...@@ -196,7 +196,7 @@ describe NotificationService, services: true do ...@@ -196,7 +196,7 @@ describe NotificationService, services: true do
before do before do
build_team(note.project) build_team(note.project)
note.project.team << [note.author, :master] note.project.team << [note.author, :master]
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
describe '#new_note' do describe '#new_note' do
...@@ -238,7 +238,7 @@ describe NotificationService, services: true do ...@@ -238,7 +238,7 @@ describe NotificationService, services: true do
before do before do
build_team(note.project) build_team(note.project)
note.project.team << [note.author, :master] note.project.team << [note.author, :master]
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
describe '#new_note' do describe '#new_note' do
...@@ -273,7 +273,7 @@ describe NotificationService, services: true do ...@@ -273,7 +273,7 @@ describe NotificationService, services: true do
before do before do
build_team(note.project) build_team(note.project)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer)
update_custom_notification(:new_note, @u_guest_custom, project) update_custom_notification(:new_note, @u_guest_custom, project)
update_custom_notification(:new_note, @u_custom_global) update_custom_notification(:new_note, @u_custom_global)
...@@ -348,7 +348,7 @@ describe NotificationService, services: true do ...@@ -348,7 +348,7 @@ describe NotificationService, services: true do
before do before do
build_team(issue.project) build_team(issue.project)
add_users_with_subscription(issue.project, issue) add_users_with_subscription(issue.project, issue)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
update_custom_notification(:new_issue, @u_guest_custom, project) update_custom_notification(:new_issue, @u_guest_custom, project)
update_custom_notification(:new_issue, @u_custom_global) update_custom_notification(:new_issue, @u_custom_global)
end end
...@@ -408,7 +408,7 @@ describe NotificationService, services: true do ...@@ -408,7 +408,7 @@ describe NotificationService, services: true do
label.toggle_subscription(guest) label.toggle_subscription(guest)
label.toggle_subscription(admin) label.toggle_subscription(admin)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
notification.new_issue(confidential_issue, @u_disabled) notification.new_issue(confidential_issue, @u_disabled)
...@@ -604,7 +604,7 @@ describe NotificationService, services: true do ...@@ -604,7 +604,7 @@ describe NotificationService, services: true do
label_2.toggle_subscription(guest) label_2.toggle_subscription(guest)
label_2.toggle_subscription(admin) label_2.toggle_subscription(admin)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
notification.relabeled_issue(confidential_issue, [label_2], @u_disabled) notification.relabeled_issue(confidential_issue, [label_2], @u_disabled)
...@@ -733,7 +733,7 @@ describe NotificationService, services: true do ...@@ -733,7 +733,7 @@ describe NotificationService, services: true do
add_users_with_subscription(merge_request.target_project, merge_request) add_users_with_subscription(merge_request.target_project, merge_request)
update_custom_notification(:new_merge_request, @u_guest_custom, project) update_custom_notification(:new_merge_request, @u_guest_custom, project)
update_custom_notification(:new_merge_request, @u_custom_global) update_custom_notification(:new_merge_request, @u_custom_global)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
describe '#new_merge_request' do describe '#new_merge_request' do
...@@ -1111,7 +1111,7 @@ describe NotificationService, services: true do ...@@ -1111,7 +1111,7 @@ describe NotificationService, services: true do
before do before do
build_team(project) build_team(project)
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
describe '#project_was_moved' do describe '#project_was_moved' do
......
module EmailHelpers module EmailHelpers
def sent_to_user?(user) def sent_to_user?(user, recipients = email_recipients)
ActionMailer::Base.deliveries.map(&:to).flatten.count(user.email) == 1 recipients.include?(user.notification_email)
end end
def reset_delivered_emails! def reset_delivered_emails!
ActionMailer::Base.deliveries.clear ActionMailer::Base.deliveries.clear
end end
def should_only_email(*users) def should_only_email(*users, kind: :to)
users.each {|user| should_email(user) } recipients = email_recipients(kind: kind)
recipients = ActionMailer::Base.deliveries.flat_map(&:to)
users.each { |user| should_email(user, recipients) }
expect(recipients.count).to eq(users.count) expect(recipients.count).to eq(users.count)
end end
def should_email(user) def should_email(user, recipients = email_recipients)
expect(sent_to_user?(user)).to be_truthy expect(sent_to_user?(user, recipients)).to be_truthy
end
def should_not_email(user, recipients = email_recipients)
expect(sent_to_user?(user, recipients)).to be_falsey
end
def should_not_email_anyone
expect(ActionMailer::Base.deliveries).to be_empty
end end
def should_not_email(user) def email_recipients(kind: :to)
expect(sent_to_user?(user)).to be_falsey ActionMailer::Base.deliveries.flat_map(&kind)
end end
end end
...@@ -7,7 +7,7 @@ shared_context 'gitlab email notification' do ...@@ -7,7 +7,7 @@ shared_context 'gitlab email notification' do
let(:new_user_address) { 'newguy@example.com' } let(:new_user_address) { 'newguy@example.com' }
before do before do
ActionMailer::Base.deliveries.clear reset_delivered_emails!
email = recipient.emails.create(email: "notifications@example.com") email = recipient.emails.create(email: "notifications@example.com")
recipient.update_attribute(:notification_email, email.email) recipient.update_attribute(:notification_email, email.email)
stub_incoming_email_setting(enabled: true, address: "reply+%{key}@#{Gitlab.config.gitlab.host}") stub_incoming_email_setting(enabled: true, address: "reply+%{key}@#{Gitlab.config.gitlab.host}")
......
...@@ -24,7 +24,7 @@ describe BuildEmailWorker do ...@@ -24,7 +24,7 @@ describe BuildEmailWorker do
end end
it "gracefully handles an input SMTP error" do it "gracefully handles an input SMTP error" do
ActionMailer::Base.deliveries.clear reset_delivered_emails!
allow(Notify).to receive(:build_success_email).and_raise(Net::SMTPFatalError) allow(Notify).to receive(:build_success_email).and_raise(Net::SMTPFatalError)
subject.perform(build.id, [user.email], data.stringify_keys) subject.perform(build.id, [user.email], data.stringify_keys)
......
...@@ -87,7 +87,7 @@ describe EmailsOnPushWorker do ...@@ -87,7 +87,7 @@ describe EmailsOnPushWorker do
context "when there is an SMTP error" do context "when there is an SMTP error" do
before do before do
ActionMailer::Base.deliveries.clear reset_delivered_emails!
allow(Notify).to receive(:repository_push_email).and_raise(Net::SMTPFatalError) allow(Notify).to receive(:repository_push_email).and_raise(Net::SMTPFatalError)
allow(subject).to receive_message_chain(:logger, :info) allow(subject).to receive_message_chain(:logger, :info)
perform perform
...@@ -112,7 +112,7 @@ describe EmailsOnPushWorker do ...@@ -112,7 +112,7 @@ describe EmailsOnPushWorker do
original.call(Mail.new(mail.encoded)) original.call(Mail.new(mail.encoded))
end end
ActionMailer::Base.deliveries.clear reset_delivered_emails!
end end
it "sends the mail to each of the recipients" do it "sends the mail to each of the recipients" do
......
require 'spec_helper'
describe PipelineNotificationWorker do
let(:pipeline) do
create(:ci_pipeline,
project: project,
sha: project.commit('master').sha,
user: pusher,
status: status)
end
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:pusher) { user }
let(:watcher) { pusher }
describe '#execute' do
before do
reset_delivered_emails!
pipeline.project.team << [pusher, Gitlab::Access::DEVELOPER]
end
context 'when watcher has developer access' do
before do
pipeline.project.team << [watcher, Gitlab::Access::DEVELOPER]
end
shared_examples 'sending emails' do
it 'sends emails' do
perform_enqueued_jobs do
subject.perform(pipeline.id)
end
emails = ActionMailer::Base.deliveries
actual = emails.flat_map(&:bcc).sort
expected_receivers = receivers.map(&:email).uniq.sort
expect(actual).to eq(expected_receivers)
expect(emails.size).to eq(1)
expect(emails.last.subject).to include(email_subject)
end
end
context 'with success pipeline' do
let(:status) { 'success' }
let(:email_subject) { "Pipeline ##{pipeline.id} has succeeded" }
let(:receivers) { [pusher, watcher] }
it_behaves_like 'sending emails'
context 'with pipeline from someone else' do
let(:pusher) { create(:user) }
let(:watcher) { user }
context 'with success pipeline notification on' do
before do
watcher.global_notification_setting.
update(level: 'custom', success_pipeline: true)
end
it_behaves_like 'sending emails'
end
context 'with success pipeline notification off' do
let(:receivers) { [pusher] }
before do
watcher.global_notification_setting.
update(level: 'custom', success_pipeline: false)
end
it_behaves_like 'sending emails'
end
end
context 'with failed pipeline' do
let(:status) { 'failed' }
let(:email_subject) { "Pipeline ##{pipeline.id} has failed" }
it_behaves_like 'sending emails'
context 'with pipeline from someone else' do
let(:pusher) { create(:user) }
let(:watcher) { user }
context 'with failed pipeline notification on' do
before do
watcher.global_notification_setting.
update(level: 'custom', failed_pipeline: true)
end
it_behaves_like 'sending emails'
end
context 'with failed pipeline notification off' do
let(:receivers) { [pusher] }
before do
watcher.global_notification_setting.
update(level: 'custom', failed_pipeline: false)
end
it_behaves_like 'sending emails'
end
end
end
end
end
context 'when watcher has no read_build access' do
let(:status) { 'failed' }
let(:email_subject) { "Pipeline ##{pipeline.id} has failed" }
let(:watcher) { create(:user) }
before do
pipeline.project.team << [watcher, Gitlab::Access::GUEST]
watcher.global_notification_setting.
update(level: 'custom', failed_pipeline: true)
perform_enqueued_jobs do
subject.perform(pipeline.id)
end
end
it 'does not send emails' do
should_only_email(pusher, kind: :bcc)
end
end
end
end
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