Merge lp:~cjwatson/launchpad/separate-build-notifications into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 17636
Proposed branch: lp:~cjwatson/launchpad/separate-build-notifications
Merge into: lp:launchpad
Diff against target: 537 lines (+180/-108)
4 files modified
lib/lp/soyuz/doc/build-failedtoupload-workflow.txt (+17/-1)
lib/lp/soyuz/emailtemplates/build-notification.txt (+2/-0)
lib/lp/soyuz/model/binarypackagebuild.py (+18/-13)
lib/lp/soyuz/tests/test_build_notify.py (+143/-94)
To merge this branch: bzr merge lp:~cjwatson/launchpad/separate-build-notifications
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+264606@code.launchpad.net

Commit message

Add a rationale to build mail notifications.

Description of the change

Add a rationale to build mail notifications.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/soyuz/doc/build-failedtoupload-workflow.txt'
--- lib/lp/soyuz/doc/build-failedtoupload-workflow.txt 2015-02-17 07:37:36 +0000
+++ lib/lp/soyuz/doc/build-failedtoupload-workflow.txt 2015-07-13 16:09:27 +0000
@@ -67,7 +67,7 @@
6767
68Note that the generated notification contain the 'extra_info' content:68Note that the generated notification contain the 'extra_info' content:
6969
70 >>> build_notification = notifications.pop(0)70 >>> build_notification = notifications[0]
7171
72 >>> build_notification['Subject']72 >>> build_notification['Subject']
73 '[Build #22] i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE'73 '[Build #22] i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE'
@@ -103,3 +103,19 @@
103 i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE103 i386 build of cdrkit 1.0 in ubuntu breezy-autotest RELEASE
104 http://launchpad.dev/ubuntu/+source/cdrkit/1.0/+build/22104 http://launchpad.dev/ubuntu/+source/cdrkit/1.0/+build/22
105 <BLANKLINE>105 <BLANKLINE>
106 You are receiving this email because you are a buildd administrator.
107 <BLANKLINE>
108
109The other notifications are similar except for the footer.
110
111 >>> print notifications[1].get_payload()
112 <BLANKLINE>
113 ...
114 You are receiving this email because you are a buildd administrator.
115 <BLANKLINE>
116 >>> print notifications[2].get_payload()
117 <BLANKLINE>
118 ...
119 You are receiving this email because you created this version of this
120 package.
121 <BLANKLINE>
106122
=== modified file 'lib/lp/soyuz/emailtemplates/build-notification.txt'
--- lib/lp/soyuz/emailtemplates/build-notification.txt 2011-12-18 23:10:57 +0000
+++ lib/lp/soyuz/emailtemplates/build-notification.txt 2015-07-13 16:09:27 +0000
@@ -18,3 +18,5 @@
18--18--
19%(build_title)s19%(build_title)s
20%(build_url)s20%(build_url)s
21
22%(reason)s
2123
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2015-07-08 16:05:11 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2015-07-13 16:09:27 +0000
@@ -17,6 +17,7 @@
17 attrgetter,17 attrgetter,
18 itemgetter,18 itemgetter,
19 )19 )
20import textwrap
2021
21import apt_pkg22import apt_pkg
22import pytz23import pytz
@@ -702,7 +703,7 @@
702 def notify(self, extra_info=None):703 def notify(self, extra_info=None):
703 """See `IPackageBuild`.704 """See `IPackageBuild`.
704705
705 If config.buildmaster.build_notification is disable, simply706 If config.buildmaster.send_build_notification is disabled, simply
706 return.707 return.
707708
708 If config.builddmaster.notify_owner is enabled and SPR.creator709 If config.builddmaster.notify_owner is enabled and SPR.creator
@@ -722,7 +723,7 @@
722 if self.status == BuildStatus.FULLYBUILT:723 if self.status == BuildStatus.FULLYBUILT:
723 return724 return
724725
725 recipients = set()726 recipients = {}
726727
727 fromaddress = format_address(728 fromaddress = format_address(
728 config.builddmaster.default_sender_name,729 config.builddmaster.default_sender_name,
@@ -764,12 +765,13 @@
764 # notified if they are the PPA owner or in the PPA team.765 # notified if they are the PPA owner or in the PPA team.
765 # (see bug 375757)766 # (see bug 375757)
766 # Non-PPA notifications inform the creator regardless.767 # Non-PPA notifications inform the creator regardless.
767 recipients = recipients.union(768 for recipient in get_contact_email_addresses(creator):
768 get_contact_email_addresses(creator))769 recipients.setdefault(
770 recipient, "you created this version of this package")
769 dsc_key = self.source_package_release.dscsigningkey771 dsc_key = self.source_package_release.dscsigningkey
770 if dsc_key:772 if dsc_key:
771 recipients = recipients.union(773 for recipient in get_contact_email_addresses(dsc_key.owner):
772 get_contact_email_addresses(dsc_key.owner))774 recipients.setdefault(recipient, "you signed this package")
773775
774 # Modify notification contents according to the targeted archive.776 # Modify notification contents according to the targeted archive.
775 # 'Archive Tag', 'Subject' and 'Source URL' are customized for PPA.777 # 'Archive Tag', 'Subject' and 'Source URL' are customized for PPA.
@@ -780,12 +782,13 @@
780 subject = "[Build #%d] %s" % (self.id, self.title)782 subject = "[Build #%d] %s" % (self.id, self.title)
781 if not self.archive.is_ppa:783 if not self.archive.is_ppa:
782 buildd_admins = getUtility(ILaunchpadCelebrities).buildd_admin784 buildd_admins = getUtility(ILaunchpadCelebrities).buildd_admin
783 recipients = recipients.union(785 for recipient in get_contact_email_addresses(buildd_admins):
784 get_contact_email_addresses(buildd_admins))786 recipients.setdefault(
787 recipient, "you are a buildd administrator")
785 source_url = canonical_url(self.distributionsourcepackagerelease)788 source_url = canonical_url(self.distributionsourcepackagerelease)
786 else:789 else:
787 recipients = recipients.union(790 for recipient in get_contact_email_addresses(self.archive.owner):
788 get_contact_email_addresses(self.archive.owner))791 recipients.setdefault(recipient, "you own this archive")
789 # For PPAs we run the risk of having no available contact_address,792 # For PPAs we run the risk of having no available contact_address,
790 # for instance, when both, SPR.creator and Archive.owner have793 # for instance, when both, SPR.creator and Archive.owner have
791 # not enabled it.794 # not enabled it.
@@ -803,7 +806,7 @@
803 # pocket build. We don't build SECURITY yet :(806 # pocket build. We don't build SECURITY yet :(
804807
805 # XXX cprov 2006-08-02: find out a way to glue parameters reported808 # XXX cprov 2006-08-02: find out a way to glue parameters reported
806 # with the state in the build worflow, maybe by having an809 # with the state in the build workflow, maybe by having an
807 # IBuild.statusReport property, which could also be used in the810 # IBuild.statusReport property, which could also be used in the
808 # respective page template.811 # respective page template.
809 if self.status in [812 if self.status in [
@@ -852,9 +855,11 @@
852 'archive_tag': self.archive.reference,855 'archive_tag': self.archive.reference,
853 'component_tag': self.current_component.name,856 'component_tag': self.current_component.name,
854 }857 }
855 message = template % replacements
856858
857 for toaddress in recipients:859 for toaddress, reason in recipients.items():
860 replacements['reason'] = textwrap.fill(
861 'You are receiving this email because %s.' % reason, width=72)
862 message = template % replacements
858 simple_sendmail(863 simple_sendmail(
859 fromaddress, toaddress, subject, message,864 fromaddress, toaddress, subject, message,
860 headers=extra_headers)865 headers=extra_headers)
861866
=== modified file 'lib/lp/soyuz/tests/test_build_notify.py'
--- lib/lp/soyuz/tests/test_build_notify.py 2015-04-20 15:59:52 +0000
+++ lib/lp/soyuz/tests/test_build_notify.py 2015-07-13 16:09:27 +0000
@@ -4,6 +4,7 @@
4__metaclass__ = type4__metaclass__ = type
55
6from datetime import timedelta6from datetime import timedelta
7from operator import itemgetter
7from textwrap import dedent8from textwrap import dedent
89
9from zope.component import getUtility10from zope.component import getUtility
@@ -27,6 +28,18 @@
27from lp.testing.sampledata import ADMIN_EMAIL28from lp.testing.sampledata import ADMIN_EMAIL
2829
2930
31REASONS = {
32 "creator": (
33 "You are receiving this email because you created this version of "
34 "this\npackage."),
35 "signer": "You are receiving this email because you signed this package.",
36 "buildd-admin": (
37 "You are receiving this email because you are a buildd "
38 "administrator."),
39 "owner": "You are receiving this email because you own this archive.",
40 }
41
42
30class TestBuildNotify(TestCaseWithFactory):43class TestBuildNotify(TestCaseWithFactory):
3144
32 layer = LaunchpadFunctionalLayer45 layer = LaunchpadFunctionalLayer
@@ -51,6 +64,7 @@
51 'launchpad-buildd-admins')64 'launchpad-buildd-admins')
52 self.buildd_admins_email = []65 self.buildd_admins_email = []
53 with person_logged_in(self.admin):66 with person_logged_in(self.admin):
67 self.ppa_owner_email = self.ppa.owner.preferredemail.email
54 self.publisher = SoyuzTestPublisher()68 self.publisher = SoyuzTestPublisher()
55 self.publisher.prepareBreezyAutotest()69 self.publisher.prepareBreezyAutotest()
56 self.distroseries.nominatedarchindep = self.das70 self.distroseries.nominatedarchindep = self.das
@@ -83,21 +97,22 @@
83 build.buildqueue_record.builder = self.builder97 build.buildqueue_record.builder = self.builder
84 self.builds.append(build)98 self.builds.append(build)
8599
86 def _assert_mail_is_correct(self, build, notification, ppa=False):100 def _assert_mail_is_correct(self, build, notification, recipient, reason,
101 ppa=False):
87 # Assert that the mail sent (which is in notification), matches102 # Assert that the mail sent (which is in notification), matches
88 # the data from the build103 # the data from the build
89 self.assertEquals('test@example.com',104 self.assertEqual(recipient, notification['To'])
90 notification['X-Creator-Recipient'])105 self.assertEqual(
91 self.assertEquals(106 'test@example.com', notification['X-Creator-Recipient'])
107 self.assertEqual(
92 self.das.architecturetag, notification['X-Launchpad-Build-Arch'])108 self.das.architecturetag, notification['X-Launchpad-Build-Arch'])
93 self.assertEquals(109 self.assertEqual('main', notification['X-Launchpad-Build-Component'])
94 'main', notification['X-Launchpad-Build-Component'])110 self.assertEqual(
95 self.assertEquals(
96 build.status.name, notification['X-Launchpad-Build-State'])111 build.status.name, notification['X-Launchpad-Build-State'])
97 self.assertEquals(112 self.assertEqual(
98 build.archive.reference, notification['X-Launchpad-Archive'])113 build.archive.reference, notification['X-Launchpad-Archive'])
99 if ppa and build.archive.distribution.name == u'ubuntu':114 if ppa and build.archive.distribution.name == u'ubuntu':
100 self.assertEquals(115 self.assertEqual(
101 get_ppa_reference(self.ppa), notification['X-Launchpad-PPA'])116 get_ppa_reference(self.ppa), notification['X-Launchpad-PPA'])
102 body = notification.get_payload(decode=True)117 body = notification.get_payload(decode=True)
103 build_log = 'None'118 build_log = 'None'
@@ -105,10 +120,10 @@
105 source = 'not available'120 source = 'not available'
106 else:121 else:
107 source = canonical_url(build.distributionsourcepackagerelease)122 source = canonical_url(build.distributionsourcepackagerelease)
108 builder = canonical_url(build.builder)
109 if build.status == BuildStatus.BUILDING:123 if build.status == BuildStatus.BUILDING:
110 duration = 'not finished'124 duration = 'not finished'
111 build_log = 'see builder page'125 build_log = 'see builder page'
126 builder = canonical_url(build.builder)
112 elif (127 elif (
113 build.status == BuildStatus.SUPERSEDED or128 build.status == BuildStatus.SUPERSEDED or
114 build.status == BuildStatus.NEEDSBUILD):129 build.status == BuildStatus.NEEDSBUILD):
@@ -122,6 +137,7 @@
122 else:137 else:
123 duration = DurationFormatterAPI(138 duration = DurationFormatterAPI(
124 build.duration).approximateduration()139 build.duration).approximateduration()
140 builder = canonical_url(build.builder)
125 expected_body = dedent("""141 expected_body = dedent("""
126 * Source Package: %s142 * Source Package: %s
127 * Version: %s143 * Version: %s
@@ -147,59 +163,61 @@
147 build.source_package_release.version, self.das.architecturetag,163 build.source_package_release.version, self.das.architecturetag,
148 build.archive.reference, build.status.title, duration, build_log,164 build.archive.reference, build.status.title, duration, build_log,
149 builder, source, build.title, canonical_url(build)))165 builder, source, build.title, canonical_url(build)))
150 self.assertEquals(expected_body, body)166 expected_body += "\n" + REASONS[reason] + "\n"
151167 self.assertEqual(expected_body, body)
152 def test_notify_buildd_admins(self):168
153 # A build will cause an e-mail to be sent out to the buildd-admins,169 def _assert_mails_are_correct(self, build, reasons, ppa=False):
154 # for primary archive builds.170 notifications = pop_notifications()
155 self.create_builds(self.archive)171 reasons = sorted(reasons, key=itemgetter(0))
156 build = self.builds[BuildStatus.FAILEDTOBUILD.value]172 for notification, (recipient, reason) in zip(notifications, reasons):
157 build.notify()173 self._assert_mail_is_correct(
158 expected_emails = self.buildd_admins_email + ['test@example.com']174 build, notification, recipient, reason, ppa=ppa)
159 notifications = pop_notifications()
160 actual_emails = [n['To'] for n in notifications]
161 self.assertEquals(expected_emails, actual_emails)
162
163 def test_ppa_does_not_notify_buildd_admins(self):
164 # A build for a PPA does not notify the buildd admins.
165 self.create_builds(self.ppa)
166 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
167 build.notify()
168 notifications = pop_notifications()
169 # An e-mail is sent to the archive owner, as well as the creator
170 self.assertEquals(2, len(notifications))
171175
172 def test_notify_failed_to_build(self):176 def test_notify_failed_to_build(self):
173 # An e-mail is sent to the source package creator on build failures.177 # For primary archive builds, a build failure notifies the buildd
178 # admins and the source package creator.
174 self.create_builds(self.archive)179 self.create_builds(self.archive)
175 build = self.builds[BuildStatus.FAILEDTOBUILD.value]180 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
176 build.notify()181 build.notify()
177 notification = pop_notifications()[1]182 expected_reasons = [
178 self._assert_mail_is_correct(build, notification)183 (email, "buildd-admin") for email in self.buildd_admins_email]
184 expected_reasons.append(("test@example.com", "creator"))
185 self._assert_mails_are_correct(build, expected_reasons)
179186
180 def test_notify_failed_to_build_ppa(self):187 def test_notify_failed_to_build_ppa(self):
181 # An e-mail is sent to the source package creator on build failures.188 # For PPA builds, a build failure notifies the source package signer
182 self.create_builds(archive=self.ppa)189 # and the archive owner, but not the buildd admins.
190 self.create_builds(self.ppa)
183 build = self.builds[BuildStatus.FAILEDTOBUILD.value]191 build = self.builds[BuildStatus.FAILEDTOBUILD.value]
184 build.notify()192 build.notify()
185 notification = pop_notifications()[1]193 expected_reasons = [
186 self._assert_mail_is_correct(build, notification, ppa=True)194 ("test@example.com", "signer"),
195 (self.ppa_owner_email, "owner"),
196 ]
197 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
187198
188 def test_notify_needs_building(self):199 def test_notify_needs_building(self):
189 # We can notify the creator when the build is needing to be built.200 # We can notify the creator and buildd admins when a build needs to
201 # be built.
190 self.create_builds(self.archive)202 self.create_builds(self.archive)
191 build = self.builds[BuildStatus.NEEDSBUILD.value]203 build = self.builds[BuildStatus.NEEDSBUILD.value]
192 build.notify()204 build.notify()
193 notification = pop_notifications()[1]205 expected_reasons = [
194 self._assert_mail_is_correct(build, notification)206 (email, "buildd-admin") for email in self.buildd_admins_email]
207 expected_reasons.append(("test@example.com", "creator"))
208 self._assert_mails_are_correct(build, expected_reasons)
195209
196 def test_notify_needs_building_ppa(self):210 def test_notify_needs_building_ppa(self):
197 # We can notify the creator when the build is needing to be built.211 # We can notify the signer and the archive owner when a build needs
212 # to be built.
198 self.create_builds(self.ppa)213 self.create_builds(self.ppa)
199 build = self.builds[BuildStatus.NEEDSBUILD.value]214 build = self.builds[BuildStatus.NEEDSBUILD.value]
200 build.notify()215 build.notify()
201 notification = pop_notifications()[1]216 expected_reasons = [
202 self._assert_mail_is_correct(build, notification, ppa=True)217 ("test@example.com", "signer"),
218 (self.ppa_owner_email, "owner"),
219 ]
220 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
203221
204 def test_notify_successfully_built(self):222 def test_notify_successfully_built(self):
205 # Successful builds don't notify anyone.223 # Successful builds don't notify anyone.
@@ -209,90 +227,121 @@
209 self.assertEqual([], pop_notifications())227 self.assertEqual([], pop_notifications())
210228
211 def test_notify_dependency_wait(self):229 def test_notify_dependency_wait(self):
212 # We can notify the creator when the build can't find a dependency.230 # We can notify the creator and buildd admins when a build can't
231 # find a dependency.
213 self.create_builds(self.archive)232 self.create_builds(self.archive)
214 build = self.builds[BuildStatus.MANUALDEPWAIT.value]233 build = self.builds[BuildStatus.MANUALDEPWAIT.value]
215 build.notify()234 build.notify()
216 notification = pop_notifications()[1]235 expected_reasons = [
217 self._assert_mail_is_correct(build, notification)236 (email, "buildd-admin") for email in self.buildd_admins_email]
237 expected_reasons.append(("test@example.com", "creator"))
238 self._assert_mails_are_correct(build, expected_reasons)
218239
219 def test_notify_dependency_wait_ppa(self):240 def test_notify_dependency_wait_ppa(self):
220 # We can notify the creator when the build can't find a dependency.241 # We can notify the signer and the archive owner when the build
242 # can't find a dependency.
221 self.create_builds(self.ppa)243 self.create_builds(self.ppa)
222 build = self.builds[BuildStatus.MANUALDEPWAIT.value]244 build = self.builds[BuildStatus.MANUALDEPWAIT.value]
223 build.notify()245 build.notify()
224 notification = pop_notifications()[1]246 expected_reasons = [
225 self._assert_mail_is_correct(build, notification, ppa=True)247 ("test@example.com", "signer"),
248 (self.ppa_owner_email, "owner"),
249 ]
250 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
226251
227 def test_notify_chroot_problem(self):252 def test_notify_chroot_problem(self):
228 # We can notify the creator when the builder the build attempted to253 # We can notify the creator and buildd admins when the builder a
229 # be built on has an internal problem.254 # build attempted to be built on has an internal problem.
230 self.create_builds(self.archive)255 self.create_builds(self.archive)
231 build = self.builds[BuildStatus.CHROOTWAIT.value]256 build = self.builds[BuildStatus.CHROOTWAIT.value]
232 build.notify()257 build.notify()
233 notification = pop_notifications()[1]258 expected_reasons = [
234 self._assert_mail_is_correct(build, notification)259 (email, "buildd-admin") for email in self.buildd_admins_email]
260 expected_reasons.append(("test@example.com", "creator"))
261 self._assert_mails_are_correct(build, expected_reasons)
235262
236 def test_notify_chroot_problem_ppa(self):263 def test_notify_chroot_problem_ppa(self):
237 # We can notify the creator when the builder the build attempted to264 # We can notify the signer and the archive owner when the builder a
238 # be built on has an internal problem.265 # build attempted to be built on has an internal problem.
239 self.create_builds(self.ppa)266 self.create_builds(self.ppa)
240 build = self.builds[BuildStatus.CHROOTWAIT.value]267 build = self.builds[BuildStatus.CHROOTWAIT.value]
241 build.notify()268 build.notify()
242 notification = pop_notifications()[1]269 expected_reasons = [
243 self._assert_mail_is_correct(build, notification, ppa=True)270 ("test@example.com", "signer"),
271 (self.ppa_owner_email, "owner"),
272 ]
273 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
244274
245 def test_notify_build_for_superseded_source(self):275 def test_notify_build_for_superseded_source(self):
246 # We can notify the creator when the source package had a newer276 # We can notify the creator and buildd admins when the source
247 # version uploaded before this build had a chance to be dispatched.277 # package had a newer version uploaded before this build had a
278 # chance to be dispatched.
248 self.create_builds(self.archive)279 self.create_builds(self.archive)
249 build = self.builds[BuildStatus.SUPERSEDED.value]280 build = self.builds[BuildStatus.SUPERSEDED.value]
250 build.notify()281 build.notify()
251 notification = pop_notifications()[1]282 expected_reasons = [
252 self._assert_mail_is_correct(build, notification)283 (email, "buildd-admin") for email in self.buildd_admins_email]
284 expected_reasons.append(("test@example.com", "creator"))
285 self._assert_mails_are_correct(build, expected_reasons)
253286
254 def test_notify_build_for_superseded_source_ppa(self):287 def test_notify_build_for_superseded_source_ppa(self):
255 # We can notify the creator when the source package had a newer288 # We can notify the signer and the archive owner when the source
256 # version uploaded before this build had a chance to be dispatched.289 # package had a newer version uploaded before this build had a
290 # chance to be dispatched.
257 self.create_builds(self.ppa)291 self.create_builds(self.ppa)
258 build = self.builds[BuildStatus.SUPERSEDED.value]292 build = self.builds[BuildStatus.SUPERSEDED.value]
259 build.notify()293 build.notify()
260 notification = pop_notifications()[1]294 expected_reasons = [
261 self._assert_mail_is_correct(build, notification, ppa=True)295 ("test@example.com", "signer"),
296 (self.ppa_owner_email, "owner"),
297 ]
298 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
262299
263 def test_notify_currently_building(self):300 def test_notify_currently_building(self):
264 # We can notify the creator when the build is currently building.301 # We can notify the creator and buildd admins when the build is
302 # currently building.
265 self.create_builds(self.archive)303 self.create_builds(self.archive)
266 build = self.builds[BuildStatus.BUILDING.value]304 build = self.builds[BuildStatus.BUILDING.value]
267 build.notify()305 build.notify()
268 notification = pop_notifications()[1]306 expected_reasons = [
269 self._assert_mail_is_correct(build, notification)307 (email, "buildd-admin") for email in self.buildd_admins_email]
308 expected_reasons.append(("test@example.com", "creator"))
309 self._assert_mails_are_correct(build, expected_reasons)
270310
271 def test_notify_currently_building_ppa(self):311 def test_notify_currently_building_ppa(self):
272 # We can notify the creator when the build is currently building.312 # We can notify the signer and the archive owner when the build is
313 # currently building.
273 self.create_builds(self.ppa)314 self.create_builds(self.ppa)
274 build = self.builds[BuildStatus.BUILDING.value]315 build = self.builds[BuildStatus.BUILDING.value]
275 build.notify()316 build.notify()
276 notification = pop_notifications()[1]317 expected_reasons = [
277 self._assert_mail_is_correct(build, notification, ppa=True)318 ("test@example.com", "signer"),
319 (self.ppa_owner_email, "owner"),
320 ]
321 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
278322
279 def test_notify_uploading_build(self):323 def test_notify_uploading_build(self):
280 # We can notify the creator when the build has completed, and binary324 # We can notify the creator and buildd admins when the build has
281 # packages are being uploaded by the builder.325 # completed, and binary packages are being uploaded by the builder.
282 self.create_builds(self.archive)326 self.create_builds(self.archive)
283 build = self.builds[BuildStatus.UPLOADING.value]327 build = self.builds[BuildStatus.UPLOADING.value]
284 build.notify()328 build.notify()
285 notification = pop_notifications()[1]329 expected_reasons = [
286 self._assert_mail_is_correct(build, notification)330 (email, "buildd-admin") for email in self.buildd_admins_email]
331 expected_reasons.append(("test@example.com", "creator"))
332 self._assert_mails_are_correct(build, expected_reasons)
287333
288 def test_notify_uploading_build_ppa(self):334 def test_notify_uploading_build_ppa(self):
289 # We can notify the creator when the build has completed, and binary335 # We can notify the signer and the archive owner when the build has
290 # packages are being uploaded by the builder.336 # completed, and binary packages are being uploaded by the builder.
291 self.create_builds(self.ppa)337 self.create_builds(self.ppa)
292 build = self.builds[BuildStatus.UPLOADING.value]338 build = self.builds[BuildStatus.UPLOADING.value]
293 build.notify()339 build.notify()
294 notification = pop_notifications()[1]340 expected_reasons = [
295 self._assert_mail_is_correct(build, notification, ppa=True)341 ("test@example.com", "signer"),
342 (self.ppa_owner_email, "owner"),
343 ]
344 self._assert_mails_are_correct(build, expected_reasons, ppa=True)
296345
297 def test_copied_into_ppa_does_not_spam(self):346 def test_copied_into_ppa_does_not_spam(self):
298 # When a package is copied into a PPA, we don't send mail to the347 # When a package is copied into a PPA, we don't send mail to the
@@ -304,10 +353,10 @@
304 self.distroseries, PackagePublishingPocket.RELEASE, self.ppa)353 self.distroseries, PackagePublishingPocket.RELEASE, self.ppa)
305 [ppa_build] = ppa_spph.createMissingBuilds()354 [ppa_build] = ppa_spph.createMissingBuilds()
306 ppa_build.notify()355 ppa_build.notify()
307 notifications = pop_notifications()356 self._assert_mails_are_correct(
308 self.assertEquals(1, len(notifications))357 ppa_build, [(self.ppa_owner_email, "owner")], ppa=True)
309358
310 def test_notify_owner_supresses_mail(self):359 def test_notify_owner_suppresses_mail(self):
311 # When the 'notify_owner' config option is False, we don't send mail360 # When the 'notify_owner' config option is False, we don't send mail
312 # to the owner of the SPR.361 # to the owner of the SPR.
313 self.create_builds(self.archive)362 self.create_builds(self.archive)
@@ -319,13 +368,13 @@
319 """)368 """)
320 config.push('notify_owner', notify_owner)369 config.push('notify_owner', notify_owner)
321 build.notify()370 build.notify()
322 notifications = pop_notifications()371 self._assert_mails_are_correct(
323 actual_emails = [n['To'] for n in notifications]372 build,
324 self.assertEquals(self.buildd_admins_email, actual_emails)373 [(email, "buildd-admin") for email in self.buildd_admins_email])
325 # And undo what we just did.374 # And undo what we just did.
326 config.pop('notify_owner')375 config.pop('notify_owner')
327376
328 def test_build_notification_supresses_mail(self):377 def test_build_notification_suppresses_mail(self):
329 # When the 'build_notification' config option is False, we don't378 # When the 'build_notification' config option is False, we don't
330 # send any mail at all.379 # send any mail at all.
331 self.create_builds(self.archive)380 self.create_builds(self.archive)
@@ -337,12 +386,12 @@
337 config.push('send_build_notification', send_build_notification)386 config.push('send_build_notification', send_build_notification)
338 build.notify()387 build.notify()
339 notifications = pop_notifications()388 notifications = pop_notifications()
340 self.assertEquals(0, len(notifications))389 self.assertEqual(0, len(notifications))
341 # And undo what we just did.390 # And undo what we just did.
342 config.pop('send_build_notification')391 config.pop('send_build_notification')
343392
344 def test_sponsored_upload_notification(self):393 def test_sponsored_upload_notification(self):
345 # If the signing key is different to the creator, they are both394 # If the signing key is different from the creator, they are both
346 # notified.395 # notified.
347 sponsor = self.factory.makePerson('sponsor@example.com')396 sponsor = self.factory.makePerson('sponsor@example.com')
348 key = self.factory.makeGPGKey(owner=sponsor)397 key = self.factory.makeGPGKey(owner=sponsor)
@@ -352,8 +401,8 @@
352 # Push past the security proxy401 # Push past the security proxy
353 removeSecurityProxy(spr).dscsigningkey = key402 removeSecurityProxy(spr).dscsigningkey = key
354 build.notify()403 build.notify()
355 notifications = pop_notifications()404 expected_reasons = [
356 expected_emails = self.buildd_admins_email + [405 (email, "buildd-admin") for email in self.buildd_admins_email]
357 'sponsor@example.com', 'test@example.com']406 expected_reasons.append(("test@example.com", "creator"))
358 actual_emails = [n['To'] for n in notifications]407 expected_reasons.append(("sponsor@example.com", "signer"))
359 self.assertEquals(expected_emails, actual_emails)408 self._assert_mails_are_correct(build, expected_reasons)