~launchpad-pqm/launchpad/devel

8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
5575.1.4 by Aaron Bentley
Implement objects for code review messages
3
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
4
"""Unit tests for CodeReviewComment"""
5575.1.4 by Aaron Bentley
Implement objects for code review messages
5
9742.8.9 by Aaron Bentley
Fix quote_text_as_email tests.
6
from textwrap import dedent
5575.1.37 by Aaron Bentley
get tests passing
7
14604.1.1 by Curtis Hovey
Separate test-authoring classes from test-running classes.
8
from lp.testing.layers import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
9
    DatabaseFunctionalLayer,
10
    LaunchpadFunctionalLayer,
11
    )
8555.2.9 by Tim Penhey
Move CodeReviewVote enum.
12
from lp.code.enums import CodeReviewVote
13
from lp.code.event.branchmergeproposal import NewCodeReviewCommentEvent
9742.8.9 by Aaron Bentley
Fix quote_text_as_email tests.
14
from lp.code.model.codereviewcomment import quote_text_as_email
14550.1.1 by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad
15
from lp.services.messages.model.message import MessageSet
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
16
from lp.testing import (
17
    TestCase,
18
    TestCaseWithFactory,
19
    )
5575.1.4 by Aaron Bentley
Implement objects for code review messages
20
7362.12.24 by Jonathan Lange
Restore change that somehow got lost.
21
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
22
class TestCodeReviewComment(TestCaseWithFactory):
5575.1.4 by Aaron Bentley
Implement objects for code review messages
23
7334.4.12 by Tim Penhey
Fix the TestCodeReviewComment tests.
24
    layer = DatabaseFunctionalLayer
5575.1.4 by Aaron Bentley
Implement objects for code review messages
25
26
    def setUp(self):
7334.4.12 by Tim Penhey
Fix the TestCodeReviewComment tests.
27
        TestCaseWithFactory.setUp(self, 'admin@canonical.com')
7362.12.24 by Jonathan Lange
Restore change that somehow got lost.
28
        source = self.factory.makeProductBranch(title='source-branch')
29
        target = self.factory.makeProductBranch(
6475.2.22 by Barry Warsaw
mergeRF
30
            product=source.product, title='target-branch')
31
        self.bmp = source.addLandingTarget(source.owner, target)
6334.3.14 by Aaron Bentley
Fix failing tests
32
        self.submitter = self.factory.makePerson(password='password')
33
        self.reviewer = self.factory.makePerson(password='password')
5575.1.37 by Aaron Bentley
get tests passing
34
        self.bmp2 = self.factory.makeBranchMergeProposal()
5575.1.4 by Aaron Bentley
Implement objects for code review messages
35
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
36
    def test_createRootComment(self):
37
        comment = self.bmp.createComment(
5575.1.29 by Aaron Bentley
Update from review comments
38
            self.submitter, 'Message subject', 'Message content')
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
39
        self.assertEqual(None, comment.vote)
40
        self.assertEqual(None, comment.vote_tag)
41
        self.assertEqual(self.submitter, comment.message.owner)
42
        self.assertEqual('Message subject', comment.message.subject)
43
        self.assertEqual('Message content', comment.message.chunks[0].content)
5575.1.4 by Aaron Bentley
Implement objects for code review messages
44
6475.2.22 by Barry Warsaw
mergeRF
45
    def test_createRootCommentNoSubject(self):
46
        comment = self.bmp.createComment(
47
            self.submitter, None, 'Message content')
48
        self.assertEqual(None, comment.vote)
49
        self.assertEqual(None, comment.vote_tag)
50
        self.assertEqual(self.submitter, comment.message.owner)
7362.12.33 by Jonathan Lange
NEVER used hardwired strings for factory-produced objects.
51
        self.assertEqual(
52
            'Re: [Merge] %s into %s' % (
53
                self.bmp.source_branch.bzr_identity,
54
                self.bmp.target_branch.bzr_identity), comment.message.subject)
6475.2.22 by Barry Warsaw
mergeRF
55
        self.assertEqual('Message content', comment.message.chunks[0].content)
56
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
57
    def test_createReplyComment(self):
58
        comment = self.bmp.createComment(
5575.1.29 by Aaron Bentley
Update from review comments
59
            self.submitter, 'Message subject', 'Message content')
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
60
        reply = self.bmp.createComment(
5575.1.29 by Aaron Bentley
Update from review comments
61
            self.reviewer, 'Reply subject', 'Reply content',
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
62
            CodeReviewVote.ABSTAIN, 'My tag', comment)
63
        self.assertEqual(comment.message.id, reply.message.parent.id)
64
        self.assertEqual(comment.message, reply.message.parent)
5575.1.5 by Aaron Bentley
Add tests and assertions
65
        self.assertEqual('Reply subject', reply.message.subject)
66
        self.assertEqual('Reply content', reply.message.chunks[0].content)
5575.1.47 by Aaron Bentley
Change CodeReviewVote values to DISAPPROVE, APPROVE, ABSTAIN
67
        self.assertEqual(CodeReviewVote.ABSTAIN, reply.vote)
7055.6.21 by Tim Penhey
Fix the tests that didn't take into account the lowercasing of the review type.
68
        self.assertEqual('my tag', reply.vote_tag)
5575.1.5 by Aaron Bentley
Add tests and assertions
69
6475.2.22 by Barry Warsaw
mergeRF
70
    def test_createReplyCommentNoSubject(self):
71
        comment = self.bmp.createComment(
72
            self.submitter, 'Message subject', 'Message content')
73
        reply = self.bmp.createComment(
74
            self.reviewer, subject=None, parent=comment)
75
        self.assertEqual('Re: Message subject', reply.message.subject)
76
77
    def test_createReplyCommentNoSubjectExistingRe(self):
78
        comment = self.bmp.createComment(
79
            self.submitter, 'Re: Message subject', 'Message content')
80
        reply = self.bmp.createComment(
81
            self.reviewer, subject=None, parent=comment)
82
        self.assertEqual('Re: Message subject', reply.message.subject)
83
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
84
    def test_createNoParentComment(self):
11733.1.4 by Tim Penhey
Add an extra event for the needs review, but keep the same functionality for now.
85
        self.bmp.createComment(
5575.1.29 by Aaron Bentley
Update from review comments
86
            self.submitter, 'Message subject', 'Message content')
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
87
        new_comment = self.bmp.createComment(
5575.1.47 by Aaron Bentley
Change CodeReviewVote values to DISAPPROVE, APPROVE, ABSTAIN
88
            self.reviewer, 'New subject', 'New content',
89
            CodeReviewVote.ABSTAIN)
6821.4.4 by Aaron Bentley
Use root_message_id, not root_comment, as in-reply-to id
90
        self.assertEqual(None, new_comment.message.parent)
5575.1.5 by Aaron Bentley
Add tests and assertions
91
5575.1.43 by Aaron Bentley
Style and doc updates
92
    def test_replyWithWrongMergeProposal(self):
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
93
        comment = self.bmp.createComment(
5575.1.29 by Aaron Bentley
Update from review comments
94
            self.submitter, 'Message subject', 'Message content')
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
95
        self.assertRaises(AssertionError, self.bmp2.createComment,
5575.1.20 by Aaron Bentley
Turn CodeReviewMessage.vote into an enum
96
                          self.reviewer, 'Reply subject', 'Reply content',
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
97
                          CodeReviewVote.ABSTAIN, 'My tag', comment)
5575.1.4 by Aaron Bentley
Implement objects for code review messages
98
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
99
    def test_createCommentFromMessage(self):
100
        """Creating a CodeReviewComment from a message works."""
6334.6.11 by Aaron Bentley
Improve testing of createMessageFromMessage
101
        message = self.factory.makeMessage(owner=self.submitter)
8970.5.1 by Aaron Bentley
Ensure no codereviewcomment is created if subject missing.
102
        comment = self.bmp.createCommentFromMessage(
103
            message, None, None, original_email=None, _validate=False)
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
104
        self.assertEqual(message, comment.message)
6334.6.11 by Aaron Bentley
Improve testing of createMessageFromMessage
105
6334.6.36 by Aaron Bentley
Rename code review messages to code review comments
106
    def test_createCommentFromMessageNotifies(self):
107
        """Creating a CodeReviewComment should trigger a notification."""
6334.6.11 by Aaron Bentley
Improve testing of createMessageFromMessage
108
        message = self.factory.makeMessage()
6334.6.12 by Aaron Bentley
merge with notify-codereview2
109
        self.assertNotifies(
7334.4.12 by Tim Penhey
Fix the TestCodeReviewComment tests.
110
            NewCodeReviewCommentEvent, self.bmp.createCommentFromMessage,
8970.5.1 by Aaron Bentley
Ensure no codereviewcomment is created if subject missing.
111
            message, None, None, original_email=None, _validate=False)
5608.11.38 by Aaron Bentley
Implement graph_dict
112
6334.6.9 by Aaron Bentley
Implement message creation from email handler
113
7407.1.8 by Tim Penhey
Add tests for getAttachments.
114
class TestCodeReviewCommentGetAttachments(TestCaseWithFactory):
115
    """Test the getAttachments method of code review comments."""
116
117
    # We need the librarian for storing the messages.
118
    layer = LaunchpadFunctionalLayer
119
120
    def setUp(self):
121
        TestCaseWithFactory.setUp(self, 'admin@canonical.com')
122
        self.bmp = self.factory.makeBranchMergeProposal()
123
124
    def test_getAttachments_no_attachments(self):
125
        # If there are no attachments, the getAttachments should return two
126
        # empty lists.
127
        comment = self.bmp.createComment(
128
            self.bmp.registrant, 'Subject', content='Some content')
7407.1.13 by Tim Penhey
Update following review.
129
        self.assertEqual(([], []), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
130
131
    def _makeCommentFromEmailWithAttachment(self, filename, content_type):
132
        # Make an email message with an attachment, and create a code
133
        # review comment from it.
134
        msg = self.factory.makeEmailMessage(
135
            body='This is the body of the email.',
136
            attachments=[
137
                (filename, content_type, 'Attachment body')])
138
        message = MessageSet().fromEmail(msg.as_string())
139
        return self.bmp.createCommentFromMessage(message, None, None, msg)
140
141
    def test_getAttachments_text_plain_are_displayed(self):
142
        # text/plain attachments are displayed.
143
        comment = self._makeCommentFromEmailWithAttachment(
144
            'some.txt', 'text/plain')
145
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
146
        self.assertEqual(([attachment.blob], []), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
147
148
    def test_getAttachments_text_xdiff_are_displayed(self):
149
        # text/x-diff attachments are displayed.
150
        comment = self._makeCommentFromEmailWithAttachment(
151
            'some.txt', 'text/x-diff')
152
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
153
        self.assertEqual(([attachment.blob], []), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
154
155
    def test_getAttachments_text_xpatch_are_displayed(self):
156
        # text/x-patch attachments are displayed.
157
        comment = self._makeCommentFromEmailWithAttachment(
158
            'some.txt', 'text/x-patch')
159
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
160
        self.assertEqual(([attachment.blob], []), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
161
162
    def test_getAttachments_others(self):
163
        # Attachments with other content types are not considered display
164
        # attachments.
165
        comment = self._makeCommentFromEmailWithAttachment(
166
            'some.txt', 'application/octet-stream')
167
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
168
        self.assertEqual(([], [attachment.blob]), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
169
170
        comment = self._makeCommentFromEmailWithAttachment(
171
            'pic.jpg', 'image/jpeg')
172
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
173
        self.assertEqual(([], [attachment.blob]), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
174
175
    def test_getAttachments_diff_or_patch_filename_overrides(self):
176
        # If the filename ends with .diff or .patch, then we consider these
177
        # attachments good even if attached with the wrong content type.
178
        comment = self._makeCommentFromEmailWithAttachment(
179
            'some.diff', 'application/octet-stream')
180
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
181
        self.assertEqual(([attachment.blob], []), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
182
183
        comment = self._makeCommentFromEmailWithAttachment(
184
            'some.patch', 'application/octet-stream')
185
        email_body, attachment = comment.message.chunks
7407.1.13 by Tim Penhey
Update following review.
186
        self.assertEqual(([attachment.blob], []), comment.getAttachments())
7407.1.8 by Tim Penhey
Add tests for getAttachments.
187
188
9742.8.9 by Aaron Bentley
Fix quote_text_as_email tests.
189
class TestQuoteTextAsEmail(TestCase):
190
    """Test the quote_text_as_email helper method."""
191
192
    def test_empty_string(self):
193
        # Nothing just gives us an empty string.
194
        self.assertEqual('', quote_text_as_email(''))
195
196
    def test_none_string(self):
197
        # If None is passed the quoted text is an empty string.
198
        self.assertEqual('', quote_text_as_email(None))
199
200
    def test_whitespace_string(self):
201
        # Just whitespace gives us an empty string.
202
        self.assertEqual('', quote_text_as_email('  \t '))
203
204
    def test_long_string(self):
205
        # Long lines are wrapped.
206
        long_line = ('This is a very long line that needs to be wrapped '
207
                     'onto more than one line given a short length.')
208
        self.assertEqual(
209
            dedent("""\
210
                > This is a very long line that needs to
211
                > be wrapped onto more than one line
212
                > given a short length."""),
213
            quote_text_as_email(long_line, 40))
214
215
    def test_code_sample(self):
216
        # Initial whitespace is not trimmed.
217
        code = """\
218
    def test_whitespace_string(self):
219
        # Nothing just gives us the prefix.
220
        self.assertEqual('', wrap_text('  \t '))"""
221
        self.assertEqual(
222
            dedent("""\
223
                >     def test_whitespace_string(self):
224
                >         # Nothing just gives us the prefix.
225
                >         self.assertEqual('', wrap_text('         '))"""),
226
            quote_text_as_email(code, 60))
227
228
    def test_empty_line_mid_string(self):
229
        # Lines in the middle of the string are quoted too.
230
        value = dedent("""\
231
            This is the first line.
232
233
            This is the second line.
234
            """)
235
        expected = dedent("""\
236
            > This is the first line.
237
            > 
238
            > This is the second line.""")
239
        self.assertEqual(expected, quote_text_as_email(value))
240
241
    def test_trailing_whitespace(self):
242
        # Trailing whitespace is removed.
243
        self.assertEqual('>   foo', quote_text_as_email('  foo  \n '))