~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
Email Notifications for Branch Merge Proposals
==============================================

Subscribers to any of the branches involved in the merge proposal get
notifications.


Subscription
------------

When subscribers subscribe to branches, they can specify what level of
notification they would like to receive.

    >>> from zope.security.proxy import removeSecurityProxy
    >>> from difflib import unified_diff
    >>> from lp.code.enums import (
    ...     BranchSubscriptionDiffSize, BranchSubscriptionNotificationLevel,
    ...     CodeReviewNotificationLevel)
    >>> from lp.code.interfaces.branchmergeproposal import (
    ...     IBranchMergeProposalJobSource)
    >>> from lp.code.model.diff import PreviewDiff
    >>> from lp.code.tests.helpers import (
    ...     make_merge_proposal_without_reviewers)
    >>> from lp.testing.mail_helpers import pop_notifications
    >>> import transaction
    >>> login('test@canonical.com')
    >>> diff_bytes = ''.join(unified_diff('', 'c'))
    >>> preview_diff = PreviewDiff.create(diff_bytes, u'a', u'b', None, None)
    >>> # Make Librarian happy.
    >>> transaction.commit()
    >>> target_owner = factory.makePerson(email='target_owner@example.com')
    >>> target_branch = factory.makeBranch(owner=target_owner)
    >>> bmp = make_merge_proposal_without_reviewers(
    ...     factory, target_branch=target_branch)
    >>> source_subscriber = factory.makePerson(
    ...     email='source@example.com', displayname='Source Subscriber')
    >>> _unused = bmp.source_branch.subscribe(source_subscriber,
    ...     BranchSubscriptionNotificationLevel.NOEMAIL,
    ...     BranchSubscriptionDiffSize.NODIFF,
    ...     CodeReviewNotificationLevel.STATUS, source_subscriber)
    >>> target_subscriber = factory.makePerson(
    ...     email='target@example.com', displayname='Target Subscriber')
    >>> target_subscription = bmp.target_branch.subscribe(target_subscriber,
    ...     BranchSubscriptionNotificationLevel.NOEMAIL,
    ...     BranchSubscriptionDiffSize.NODIFF,
    ...     CodeReviewNotificationLevel.FULL, target_subscriber)

The owners of the branches are subscribed when the branches are created.
We know the target_owner from when the target branch was created above.
    >>> source_owner = bmp.source_branch.owner


Notification Recipients
-----------------------

Recipients are determined using getNotificationRecipients.

    >>> recipients = bmp.getNotificationRecipients(
    ...     CodeReviewNotificationLevel.STATUS)

Subscribers to all related branches are candidates.

    >>> all_subscribers = set([
    ...     source_owner, target_owner, source_subscriber, target_subscriber])
    >>> all_subscribers == set(recipients.keys())
    True

Only subscribers whose level is >= the minimum level are selected.

    >>> full_subscribers = set([
    ...     source_owner, target_owner, target_subscriber])
    >>> recipients = bmp.getNotificationRecipients(
    ...     CodeReviewNotificationLevel.FULL)
    >>> full_subscribers == set(recipients.keys())
    True

Now we will unsubscribe the branch owners to simplify the rest of the test.

    >>> bmp.source_branch.unsubscribe(source_owner, source_owner)
    >>> bmp.target_branch.unsubscribe(target_owner, target_owner)
    >>> recipients = bmp.getNotificationRecipients(
    ...     CodeReviewNotificationLevel.FULL)

The value assigned to the recipient is a utility class to generate useful
values for the email headers and footers.

    >>> [reason] = recipients.values()
    >>> print reason.mail_header
    Subscriber
    >>> print reason.getReason()
    You are subscribed to branch ...


E-mail
------

Jobs for notifications are automagically generated when the merge proposal
is created.  When those jobs are run, the email is sent from the registrant.

    >>> #clear any existing notifications
    >>> source_branch = bmp.source_branch
    >>> factory.makeRevisionsForBranch(source_branch, count=1)
    >>> target_branch = bmp.target_branch
    >>> # Login to delete the proposal.
    >>> login('admin@canonical.com')
    >>> bmp.deleteProposal()
    >>> notifications = pop_notifications()
    >>> registrant = factory.makePerson(
    ...     displayname="Eric", email="eric@example.com")
    >>> # To avoid needing to access branches, pre-populate diffs.
    >>> bmp = source_branch.addLandingTarget(
    ...     registrant, target_branch, needs_review=True)
    >>> removeSecurityProxy(bmp).preview_diff = preview_diff
    >>> # Fake the update preview diff as done.
    >>> bmp.next_preview_diff_job.start()
    >>> bmp.next_preview_diff_job.complete()
    >>> [job] = list(getUtility(IBranchMergeProposalJobSource).iterReady())
    >>> job.run()
    >>> notifications = pop_notifications(
    ...     sort_key=lambda n: n.get('X-Envelope-To'))

An email is sent to subscribers of either branch and the default reviewer.

    >>> for notification in notifications:
    ...     print notification['X-Envelope-To']
    source@example.com
    target@example.com
    target_owner@example.com

    >>> notification = notifications[0]
    >>> print notification['From']
    Eric <eric@example.com>
    >>> print notification['Subject']
    [Merge] lp://dev/~person-name... into lp://dev/~person-name...
    >>> print notification['X-Launchpad-Project']
    product-name...
    >>> print notification['X-Launchpad-Branch']
    ~person-name...
    >>> print notification['X-Launchpad-Message-Rationale']
    Subscriber
    >>> print notification.get_payload(decode=True)
    Eric has proposed merging
    lp://dev/~person-name...into lp://dev/~person-name...
    --
    http://code.launchpad.dev/~person-name...
    You are subscribed to branch ...


If there is an initial commit message or reviewers then they are also included
in the email.

    >>> bob = factory.makePerson(
    ...     name="bob", displayname="Bob the Builder",
    ...     email="bob@example.com")
    >>> mary = factory.makePerson(
    ...     name="mary", displayname="Mary Jones",
    ...     email="mary@example.com")
    >>> reviewers = ((bob, None), (mary, 'ui'))
    >>> from textwrap import dedent
    >>> initial_comment = dedent("""\
    ...     This is the initial commit message.
    ...
    ...     It is included in the initial email sent out.
    ...     """)
    >>> bmp.deleteProposal()
    >>> bmp = source_branch.addLandingTarget(
    ...     registrant, target_branch,
    ...     description=initial_comment, review_requests=reviewers,
    ...     needs_review=True)
    >>> removeSecurityProxy(bmp).preview_diff = preview_diff
    >>> # Fake the update preview diff as done.
    >>> bmp.next_preview_diff_job.start()
    >>> bmp.next_preview_diff_job.complete()
    >>> [job] = list(getUtility(IBranchMergeProposalJobSource).iterReady())
    >>> job.run()
    >>> notifications = pop_notifications(
    ...     sort_key=lambda n: n.get('X-Envelope-To'))
    >>> for notification in notifications:
    ...     print "%s, %s" % (
    ...         notification['X-Envelope-To'],
    ...         notification['X-Launchpad-Message-Rationale'])
    bob@example.com, Reviewer
    mary@example.com, Reviewer
    source@example.com, Subscriber
    target@example.com, Subscriber
    >>> notification = notifications[0]
    >>> print notification.get_payload()[0].get_payload(decode=True)
    Eric has proposed merging
    lp://dev/~person-name...into lp://dev/~person-name...
    <BLANKLINE>
    Requested reviews:
        Bob the Builder (bob)
        Mary Jones (mary): ui
    <BLANKLINE>
    For more details, see:
    http://code.launchpad.dev/~person-name...
    <BLANKLINE>
    This is the initial commit message.
    <BLANKLINE>
    It is included in the initial email sent out.
    <BLANKLINE>
    --
    http://code.launchpad.dev/~...