summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2008-05-25 17:31:50 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2008-05-25 17:31:50 +0000
commit0c8105277070c20590d22a0d4b8fc1f09ce981d9 (patch)
treecc03ec6f7da3f6b6b1ebd8c4a73f36276050b1a8
parentd99dc4070a925fabc050ae61c21c64548730a636 (diff)
downloadredmine-0c8105277070c20590d22a0d4b8fc1f09ce981d9.tar.gz
redmine-0c8105277070c20590d22a0d4b8fc1f09ce981d9.zip
Adds a rake task to send reminders. An email is sent to each user with a list of the issues due in the next days, if any.
See @rake -D redmine:send_reminders@ for options. git-svn-id: http://redmine.rubyforge.org/svn/trunk@1459 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/models/mailer.rb31
-rw-r--r--app/views/mailer/reminder.text.html.rhtml9
-rw-r--r--app/views/mailer/reminder.text.plain.rhtml7
-rw-r--r--lang/bg.yml2
-rw-r--r--lang/cs.yml2
-rw-r--r--lang/da.yml2
-rw-r--r--lang/de.yml2
-rw-r--r--lang/en.yml2
-rw-r--r--lang/es.yml2
-rw-r--r--lang/fi.yml2
-rw-r--r--lang/fr.yml2
-rw-r--r--lang/he.yml2
-rw-r--r--lang/hu.yml2
-rw-r--r--lang/it.yml2
-rw-r--r--lang/ja.yml2
-rw-r--r--lang/ko.yml2
-rw-r--r--lang/lt.yml2
-rw-r--r--lang/nl.yml2
-rw-r--r--lang/no.yml2
-rw-r--r--lang/pl.yml2
-rw-r--r--lang/pt-br.yml2
-rw-r--r--lang/pt.yml2
-rw-r--r--lang/ro.yml2
-rw-r--r--lang/ru.yml2
-rw-r--r--lang/sr.yml2
-rw-r--r--lang/sv.yml2
-rw-r--r--lang/th.yml2
-rw-r--r--lang/uk.yml2
-rw-r--r--lang/zh-tw.yml2
-rw-r--r--lang/zh.yml4
-rw-r--r--lib/tasks/reminder.rake39
-rw-r--r--test/fixtures/issues.yml2
-rw-r--r--test/unit/mailer_test.rb9
33 files changed, 151 insertions, 2 deletions
diff --git a/app/models/mailer.rb b/app/models/mailer.rb
index 6fc879a15..aae374268 100644
--- a/app/models/mailer.rb
+++ b/app/models/mailer.rb
@@ -51,6 +51,15 @@ class Mailer < ActionMailer::Base
:issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
end
+ def reminder(user, issues, days)
+ set_language_if_valid user.language
+ recipients user.mail
+ subject l(:mail_subject_reminder, issues.size)
+ body :issues => issues,
+ :days => days,
+ :issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'issues.due_date', :sort_order => 'asc')
+ end
+
def document_added(document)
redmine_headers 'Project' => document.project.identifier
recipients document.project.recipients
@@ -144,6 +153,28 @@ class Mailer < ActionMailer::Base
(bcc.nil? || bcc.empty?)
super
end
+
+ # Sends reminders to issue assignees
+ # Available options:
+ # * :days => how many days in the future to remind about (defaults to 7)
+ # * :tracker => id of tracker for filtering issues (defaults to all trackers)
+ # * :project => id or identifier of project to process (defaults to all projects)
+ def self.reminders(options={})
+ days = options[:days] || 7
+ project = options[:project] ? Project.find(options[:project]) : nil
+ tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
+
+ s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ? AND #{Issue.table_name}.assigned_to_id IS NOT NULL", false, days.day.from_now.to_date]
+ s << "#{Issue.table_name}.project_id = #{project.id}" if project
+ s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
+
+ issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker],
+ :conditions => s.conditions
+ ).group_by(&:assigned_to)
+ issues_by_assignee.each do |assignee, issues|
+ deliver_reminder(assignee, issues, days) unless assignee.nil?
+ end
+ end
private
def initialize_defaults(method_name)
diff --git a/app/views/mailer/reminder.text.html.rhtml b/app/views/mailer/reminder.text.html.rhtml
new file mode 100644
index 000000000..1e33fbe43
--- /dev/null
+++ b/app/views/mailer/reminder.text.html.rhtml
@@ -0,0 +1,9 @@
+<p><%= l(:mail_body_reminder, @issues.size, @days) %></p>
+
+<ul>
+<% @issues.each do |issue| -%>
+ <li><%=h "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %></li>
+<% end -%>
+</ul>
+
+<p><%= link_to l(:label_issue_view_all), @issues_url %></p>
diff --git a/app/views/mailer/reminder.text.plain.rhtml b/app/views/mailer/reminder.text.plain.rhtml
new file mode 100644
index 000000000..7e6a2e585
--- /dev/null
+++ b/app/views/mailer/reminder.text.plain.rhtml
@@ -0,0 +1,7 @@
+<%= l(:mail_body_reminder, @issues.size, @days) %>:
+
+<% @issues.each do |issue| -%>
+* <%= "#{issue.project} - #{issue.tracker} ##{issue.id}: #{issue.subject}" %>
+<% end -%>
+
+<%= @issues_url %>
diff --git a/lang/bg.yml b/lang/bg.yml
index 224177d94..10198bce9 100644
--- a/lang/bg.yml
+++ b/lang/bg.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "Обектът не съществува или не мож
label_planning: Планиране
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/cs.yml b/lang/cs.yml
index 2126553fc..f90068b3b 100644
--- a/lang/cs.yml
+++ b/lang/cs.yml
@@ -624,3 +624,5 @@ error_scm_annotate: "Položka neexistuje nebo nemůže být komentována."
label_planning: Plánování
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/da.yml b/lang/da.yml
index 3b5ca07d3..ca4ddeb46 100644
--- a/lang/da.yml
+++ b/lang/da.yml
@@ -621,3 +621,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planlægning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/de.yml b/lang/de.yml
index 273bad277..fa33e1c74 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -620,3 +620,5 @@ enumeration_doc_categories: Dokumentenkategorien
enumeration_activities: Aktivitäten (Zeiterfassung)
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/en.yml b/lang/en.yml
index 892a63a1b..f9e07d211 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -91,6 +91,8 @@ mail_body_account_information_external: You can use your "%s" account to log in.
mail_body_account_information: Your account information
mail_subject_account_activation_request: %s account activation request
mail_body_account_activation_request: 'A new user (%s) has registered. His account is pending your approval:'
+mail_subject_reminder: "%d issue(s) due in the next days"
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
gui_validation_error: 1 error
gui_validation_error_plural: %d errors
diff --git a/lang/es.yml b/lang/es.yml
index 26009d716..3d8c0a931 100644
--- a/lang/es.yml
+++ b/lang/es.yml
@@ -622,3 +622,5 @@ error_scm_annotate: "No existe la entrada o no ha podido ser anotada"
label_planning: Planificación
text_subprojects_destroy_warning: 'Sus subprojectos: %s también se eliminarán'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/fi.yml b/lang/fi.yml
index 908ef1283..60428bb49 100644
--- a/lang/fi.yml
+++ b/lang/fi.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "Merkintää ei ole tai siihen ei voi lisätä selityksiä."
label_planning: Suunnittelu
text_subprojects_destroy_warning: 'Tämän alaprojekti(t): %s tullaan myös poistamaan.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/fr.yml b/lang/fr.yml
index 8827edd5e..8b002bbf6 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -91,6 +91,8 @@ mail_body_account_information_external: Vous pouvez utiliser votre compte "%s" p
mail_body_account_information: Paramètres de connexion de votre compte
mail_subject_account_activation_request: "Demande d'activation d'un compte %s"
mail_body_account_activation_request: "Un nouvel utilisateur (%s) s'est inscrit. Son compte nécessite votre approbation:"
+mail_subject_reminder: "%d demande(s) arrivent à échéance"
+mail_body_reminder: "%d demande(s) qui vous sont assignées arrivent à échéance dans les %d prochains jours:"
gui_validation_error: 1 erreur
gui_validation_error_plural: %d erreurs
diff --git a/lang/he.yml b/lang/he.yml
index 71bc5e5b3..f972ef62a 100644
--- a/lang/he.yml
+++ b/lang/he.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "הכניסה לא קיימת או שלא ניתן לתאר
label_planning: תכנון
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/hu.yml b/lang/hu.yml
index c3feaf16d..f6d312ce0 100644
--- a/lang/hu.yml
+++ b/lang/hu.yml
@@ -620,3 +620,5 @@ default_activity_development: Fejlesztés
enumeration_issue_priorities: Feladat prioritások
enumeration_doc_categories: Dokumentum kategóriák
enumeration_activities: Tevékenységek (idő rögzítés)
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/it.yml b/lang/it.yml
index 1771e1bda..a8646644a 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/ja.yml b/lang/ja.yml
index 84f27a2eb..d119cf55e 100644
--- a/lang/ja.yml
+++ b/lang/ja.yml
@@ -620,3 +620,5 @@ error_scm_annotate: "エントリが存在しない、もしくはアノテー
label_planning: 計画
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/ko.yml b/lang/ko.yml
index ee94c4025..98dde9e06 100644
--- a/lang/ko.yml
+++ b/lang/ko.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/lt.yml b/lang/lt.yml
index bc2faaa00..6b791d87b 100644
--- a/lang/lt.yml
+++ b/lang/lt.yml
@@ -621,3 +621,5 @@ label_planning: Planavimas
text_subprojects_destroy_warning: 'Šis(ie) subprojektas(ai): %s taip pat bus ištrintas(i).'
label_and_its_subprojects: %s projektas ir jo subprojektai
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/nl.yml b/lang/nl.yml
index 01755137a..c36eaecd5 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -620,3 +620,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/no.yml b/lang/no.yml
index 13fcd24f5..5e788dc50 100644
--- a/lang/no.yml
+++ b/lang/no.yml
@@ -620,3 +620,5 @@ default_activity_development: Utvikling
enumeration_issue_priorities: Sakssprioriteringer
enumeration_doc_categories: Dokument-kategorier
enumeration_activities: Aktiviteter (tidssporing)
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/pl.yml b/lang/pl.yml
index c83173ca2..4476c2160 100644
--- a/lang/pl.yml
+++ b/lang/pl.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "Wpis nie istnieje lub nie można do niego dodawać adnotacj
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/pt-br.yml b/lang/pt-br.yml
index 9714df2ca..9b3d23151 100644
--- a/lang/pt-br.yml
+++ b/lang/pt-br.yml
@@ -619,3 +619,5 @@ label_planning: Planejamento
text_subprojects_destroy_warning: 'Seu(s) subprojeto(s): %s também serão excluídos.'
label_age: Age
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/pt.yml b/lang/pt.yml
index 9d0586775..c56b76a0b 100644
--- a/lang/pt.yml
+++ b/lang/pt.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/ro.yml b/lang/ro.yml
index 01127f5c5..30464569b 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -619,3 +619,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/ru.yml b/lang/ru.yml
index 4f0a4bb7b..f4fd92037 100644
--- a/lang/ru.yml
+++ b/lang/ru.yml
@@ -623,3 +623,5 @@ error_scm_annotate: "Данные отсутствуют или не могут
label_planning: Планирование
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/sr.yml b/lang/sr.yml
index 263bd1187..53e4f9f2c 100644
--- a/lang/sr.yml
+++ b/lang/sr.yml
@@ -620,3 +620,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/sv.yml b/lang/sv.yml
index 5b0c57acf..248f3dcba 100644
--- a/lang/sv.yml
+++ b/lang/sv.yml
@@ -620,3 +620,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/th.yml b/lang/th.yml
index 643ee01c6..444316aa1 100644
--- a/lang/th.yml
+++ b/lang/th.yml
@@ -622,3 +622,5 @@ enumeration_issue_priorities: ความสำคัญของปัญห
enumeration_doc_categories: ประเภทเอกสาร
enumeration_activities: กิจกรรม (ใช้ในการติดตามเวลา)
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/uk.yml b/lang/uk.yml
index e61fc5b38..3cc60ef48 100644
--- a/lang/uk.yml
+++ b/lang/uk.yml
@@ -621,3 +621,5 @@ error_scm_annotate: "The entry does not exist or can not be annotated."
label_planning: Planning
text_subprojects_destroy_warning: 'Its subproject(s): %s will be also deleted.'
label_and_its_subprojects: %s and its subprojects
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/zh-tw.yml b/lang/zh-tw.yml
index 1bdc7d715..3926113b2 100644
--- a/lang/zh-tw.yml
+++ b/lang/zh-tw.yml
@@ -620,3 +620,5 @@ default_activity_development: 開發
enumeration_issue_priorities: 項目優先權
enumeration_doc_categories: 文件分類
enumeration_activities: 活動 (時間追蹤)
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
diff --git a/lang/zh.yml b/lang/zh.yml
index ac5eb456d..ffd7cd811 100644
--- a/lang/zh.yml
+++ b/lang/zh.yml
@@ -619,4 +619,6 @@ default_activity_development: 开发
enumeration_issue_priorities: 问题优先级
enumeration_doc_categories: 文档类别
-enumeration_activities: 活动(时间跟踪) \ No newline at end of file
+enumeration_activities: 活动(时间跟踪)mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
+mail_subject_reminder: "%d issue(s) due in the next days"
+mail_body_reminder: "%d issue(s) that are assigned to you are due in the next %d days:"
diff --git a/lib/tasks/reminder.rake b/lib/tasks/reminder.rake
new file mode 100644
index 000000000..73844fb79
--- /dev/null
+++ b/lib/tasks/reminder.rake
@@ -0,0 +1,39 @@
+# redMine - project management software
+# Copyright (C) 2008 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+desc <<-END_DESC
+Send reminders about issues due in the next days.
+
+Available options:
+ * days => number of days to remind about (defaults to 7)
+ * tracker => id of tracker (defaults to all trackers)
+ * project => id or identifier of project (defaults to all projects)
+
+Example:
+ rake redmine:send_reminders days=7 RAILS_ENV="production"
+END_DESC
+
+namespace :redmine do
+ task :send_reminders => :environment do
+ options = {}
+ options[:days] = ENV['days'].to_i if ENV['days']
+ options[:project] = ENV['project'] if ENV['project']
+ options[:tracker] = ENV['tracker'].to_i if ENV['tracker']
+
+ Mailer.reminders(options)
+ end
+end
diff --git a/test/fixtures/issues.yml b/test/fixtures/issues.yml
index c037624ae..9d3287c6f 100644
--- a/test/fixtures/issues.yml
+++ b/test/fixtures/issues.yml
@@ -42,7 +42,7 @@ issues_003:
category_id:
description: Error 281 is encountered when saving a recipe
tracker_id: 1
- assigned_to_id:
+ assigned_to_id: 3
author_id: 2
status_id: 1
start_date: <%= 1.day.from_now.to_date.to_s(:db) %>
diff --git a/test/unit/mailer_test.rb b/test/unit/mailer_test.rb
index 64648b94c..8cf6c1423 100644
--- a/test/unit/mailer_test.rb
+++ b/test/unit/mailer_test.rb
@@ -116,4 +116,13 @@ class MailerTest < Test::Unit::TestCase
assert Mailer.deliver_register(token)
end
end
+
+ def test_reminders
+ ActionMailer::Base.deliveries.clear
+ Mailer.reminders(:days => 42)
+ assert_equal 1, ActionMailer::Base.deliveries.size
+ mail = ActionMailer::Base.deliveries.last
+ assert mail.bcc.include?('dlopper@somenet.foo')
+ assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
+ end
end