Merge ~cjwatson/launchpad:faster-bugs-webservice-tests into launchpad:master
- Git
- lp:~cjwatson/launchpad
- faster-bugs-webservice-tests
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 69c5e5b29fe41ac8d8b6341e84d01ffbc3a1a935 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:faster-bugs-webservice-tests |
Merge into: | launchpad:master |
Diff against target: |
757 lines (+223/-175) 6 files modified
lib/lp/bugs/browser/tests/test_bugattachment_file_access.py (+53/-46) lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py (+65/-65) lib/lp/bugs/browser/tests/test_structuralsubscription.py (+43/-26) lib/lp/bugs/model/tests/test_bugtask.py (+23/-18) lib/lp/bugs/tests/test_bug_messages_webservice.py (+33/-20) lib/lp/testing/__init__.py (+6/-0) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tom Wardill (community) | Approve | ||
Review via email: mp+375798@code.launchpad.net |
Commit message
Stop using launchpadlib in bugs webservice tests
Description of the change
Port the bugs webservice tests to use in-process webservice calls rather than launchpadlib and AppServerLayer. While the code is a bit longer as a result, it's easier to debug and substantially faster: this change takes the test time for these test suites from 106 seconds to 22 seconds on my laptop.
Similarly, I downgraded a couple of bug subscription filter test suites from LaunchpadFuncti
To post a comment you must log in.
Revision history for this message
Tom Wardill (twom) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py b/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py | |||
2 | index f43e922..30e6cca 100644 | |||
3 | --- a/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py | |||
4 | +++ b/lib/lp/bugs/browser/tests/test_bugattachment_file_access.py | |||
5 | @@ -1,15 +1,16 @@ | |||
7 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2019 Canonical Ltd. This software is licensed under the |
8 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
9 | 3 | 3 | ||
10 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
11 | 5 | 5 | ||
12 | 6 | import re | 6 | import re |
14 | 7 | from urlparse import ( | 7 | |
15 | 8 | import requests | ||
16 | 9 | from six.moves.urllib.parse import ( | ||
17 | 8 | parse_qs, | 10 | parse_qs, |
18 | 9 | urlparse, | 11 | urlparse, |
19 | 12 | urlunparse, | ||
20 | 10 | ) | 13 | ) |
21 | 11 | |||
22 | 12 | from lazr.restfulclient.errors import NotFound as RestfulNotFound | ||
23 | 13 | import transaction | 14 | import transaction |
24 | 14 | from zope.component import ( | 15 | from zope.component import ( |
25 | 15 | getMultiAdapter, | 16 | getMultiAdapter, |
26 | @@ -17,24 +18,27 @@ from zope.component import ( | |||
27 | 17 | ) | 18 | ) |
28 | 18 | from zope.publisher.interfaces import NotFound | 19 | from zope.publisher.interfaces import NotFound |
29 | 19 | from zope.security.interfaces import Unauthorized | 20 | from zope.security.interfaces import Unauthorized |
30 | 20 | from zope.security.management import endInteraction | ||
31 | 21 | 21 | ||
32 | 22 | from lp.bugs.browser.bugattachment import BugAttachmentFileNavigation | 22 | from lp.bugs.browser.bugattachment import BugAttachmentFileNavigation |
33 | 23 | from lp.services.config import config | ||
34 | 23 | from lp.services.librarian.interfaces import ILibraryFileAliasWithParent | 24 | from lp.services.librarian.interfaces import ILibraryFileAliasWithParent |
36 | 24 | from lp.services.webapp.interfaces import ILaunchBag | 25 | from lp.services.webapp.interfaces import ( |
37 | 26 | ILaunchBag, | ||
38 | 27 | OAuthPermission, | ||
39 | 28 | ) | ||
40 | 25 | from lp.services.webapp.publisher import RedirectionView | 29 | from lp.services.webapp.publisher import RedirectionView |
41 | 26 | from lp.services.webapp.servers import LaunchpadTestRequest | 30 | from lp.services.webapp.servers import LaunchpadTestRequest |
42 | 27 | from lp.testing import ( | 31 | from lp.testing import ( |
44 | 28 | launchpadlib_for, | 32 | api_url, |
45 | 29 | login_person, | 33 | login_person, |
46 | 34 | logout, | ||
47 | 30 | TestCaseWithFactory, | 35 | TestCaseWithFactory, |
48 | 31 | ws_object, | ||
49 | 32 | ) | 36 | ) |
53 | 33 | from lp.testing.layers import ( | 37 | from lp.testing.layers import LaunchpadFunctionalLayer |
54 | 34 | AppServerLayer, | 38 | from lp.testing.pages import ( |
55 | 35 | LaunchpadFunctionalLayer, | 39 | LaunchpadWebServiceCaller, |
56 | 40 | webservice_for_person, | ||
57 | 36 | ) | 41 | ) |
58 | 37 | from lp.testing.pages import LaunchpadWebServiceCaller | ||
59 | 38 | 42 | ||
60 | 39 | 43 | ||
61 | 40 | class TestAccessToBugAttachmentFiles(TestCaseWithFactory): | 44 | class TestAccessToBugAttachmentFiles(TestCaseWithFactory): |
62 | @@ -119,7 +123,7 @@ class TestAccessToBugAttachmentFiles(TestCaseWithFactory): | |||
63 | 119 | class TestWebserviceAccessToBugAttachmentFiles(TestCaseWithFactory): | 123 | class TestWebserviceAccessToBugAttachmentFiles(TestCaseWithFactory): |
64 | 120 | """Tests access to bug attachments via the webservice.""" | 124 | """Tests access to bug attachments via the webservice.""" |
65 | 121 | 125 | ||
67 | 122 | layer = AppServerLayer | 126 | layer = LaunchpadFunctionalLayer |
68 | 123 | 127 | ||
69 | 124 | def setUp(self): | 128 | def setUp(self): |
70 | 125 | super(TestWebserviceAccessToBugAttachmentFiles, self).setUp() | 129 | super(TestWebserviceAccessToBugAttachmentFiles, self).setUp() |
71 | @@ -127,48 +131,41 @@ class TestWebserviceAccessToBugAttachmentFiles(TestCaseWithFactory): | |||
72 | 127 | getUtility(ILaunchBag).clear() | 131 | getUtility(ILaunchBag).clear() |
73 | 128 | login_person(self.bug_owner) | 132 | login_person(self.bug_owner) |
74 | 129 | self.bug = self.factory.makeBug(owner=self.bug_owner) | 133 | self.bug = self.factory.makeBug(owner=self.bug_owner) |
76 | 130 | self.bugattachment = self.factory.makeBugAttachment( | 134 | self.factory.makeBugAttachment( |
77 | 131 | bug=self.bug, filename='foo.txt', data='file content') | 135 | bug=self.bug, filename='foo.txt', data='file content') |
78 | 136 | self.bug_url = api_url(self.bug) | ||
79 | 132 | 137 | ||
80 | 133 | def test_anon_access_to_public_bug_attachment(self): | 138 | def test_anon_access_to_public_bug_attachment(self): |
81 | 134 | # Attachments of public bugs can be accessed by anonymous users. | 139 | # Attachments of public bugs can be accessed by anonymous users. |
91 | 135 | # | 140 | logout() |
92 | 136 | # Need to endInteraction() because launchpadlib_for_anonymous() will | 141 | webservice = LaunchpadWebServiceCaller( |
93 | 137 | # setup a new one. | 142 | 'test', '', default_api_version='devel') |
94 | 138 | endInteraction() | 143 | ws_bug = self.getWebserviceJSON(webservice, self.bug_url) |
95 | 139 | launchpad = launchpadlib_for('test', None, version='devel') | 144 | ws_bug_attachment = self.getWebserviceJSON( |
96 | 140 | ws_bug = ws_object(launchpad, self.bug) | 145 | webservice, ws_bug['attachments_collection_link'])['entries'][0] |
97 | 141 | ws_bugattachment = ws_bug.attachments[0] | 146 | response = webservice.get(ws_bug_attachment['data_link']) |
98 | 142 | self.assertEqual( | 147 | self.assertEqual(303, response.status) |
99 | 143 | 'file content', ws_bugattachment.data.open().read()) | 148 | response = requests.get(response.getHeader('Location')) |
100 | 149 | response.raise_for_status() | ||
101 | 150 | self.assertEqual(b'file content', response.content) | ||
102 | 144 | 151 | ||
103 | 145 | def test_user_access_to_private_bug_attachment(self): | 152 | def test_user_access_to_private_bug_attachment(self): |
104 | 146 | # Users having access to private bugs can also read attachments | 153 | # Users having access to private bugs can also read attachments |
105 | 147 | # of these bugs. | 154 | # of these bugs. |
106 | 148 | self.bug.setPrivate(True, self.bug_owner) | 155 | self.bug.setPrivate(True, self.bug_owner) |
107 | 149 | other_user = self.factory.makePerson() | 156 | other_user = self.factory.makePerson() |
124 | 150 | launchpad = launchpadlib_for('test', self.bug_owner, version='devel') | 157 | webservice = webservice_for_person( |
125 | 151 | ws_bug = ws_object(launchpad, self.bug) | 158 | self.bug_owner, permission=OAuthPermission.READ_PRIVATE) |
126 | 152 | ws_bugattachment = ws_bug.attachments[0] | 159 | ws_bug = self.getWebserviceJSON(webservice, self.bug_url) |
127 | 153 | 160 | ws_bug_attachment = self.getWebserviceJSON( | |
128 | 154 | # The attachment contains a link to a HostedBytes resource; | 161 | webservice, ws_bug['attachments_collection_link'])['entries'][0] |
129 | 155 | # the response to a GET request of this URL is a redirect to a | 162 | response = webservice.get(ws_bug_attachment['data_link']) |
114 | 156 | # Librarian URL. We cannot simply access these Librarian URLs | ||
115 | 157 | # for restricted Librarian files because the host name used in | ||
116 | 158 | # the URLs is different for each file, and our test envireonment | ||
117 | 159 | # does not support wildcard DNS, and because the Launchpadlib | ||
118 | 160 | # browser automatically follows redirects. | ||
119 | 161 | # LaunchpadWebServiceCaller, on the other hand, gives us | ||
120 | 162 | # access to a raw HTTPResonse object. | ||
121 | 163 | webservice = LaunchpadWebServiceCaller( | ||
122 | 164 | 'launchpad-library', 'salgado-change-anything') | ||
123 | 165 | response = webservice.get(ws_bugattachment.data._wadl_resource._url) | ||
130 | 166 | self.assertEqual(303, response.status) | 163 | self.assertEqual(303, response.status) |
131 | 167 | 164 | ||
132 | 168 | # The Librarian URL has, for our test case, the form | 165 | # The Librarian URL has, for our test case, the form |
133 | 169 | # "https://NNNN.restricted.launchpad.test:PORT/NNNN/foo.txt?token=..." | 166 | # "https://NNNN.restricted.launchpad.test:PORT/NNNN/foo.txt?token=..." |
134 | 170 | # where NNNN and PORT are integers. | 167 | # where NNNN and PORT are integers. |
136 | 171 | parsed_url = urlparse(response.getHeader('location')) | 168 | parsed_url = urlparse(response.getHeader('Location')) |
137 | 172 | self.assertEqual('https', parsed_url.scheme) | 169 | self.assertEqual('https', parsed_url.scheme) |
138 | 173 | mo = re.search( | 170 | mo = re.search( |
139 | 174 | r'^i\d+\.restricted\..+:\d+$', parsed_url.netloc) | 171 | r'^i\d+\.restricted\..+:\d+$', parsed_url.netloc) |
140 | @@ -178,10 +175,20 @@ class TestWebserviceAccessToBugAttachmentFiles(TestCaseWithFactory): | |||
141 | 178 | params = parse_qs(parsed_url.query) | 175 | params = parse_qs(parsed_url.query) |
142 | 179 | self.assertEqual(['token'], params.keys()) | 176 | self.assertEqual(['token'], params.keys()) |
143 | 180 | 177 | ||
144 | 178 | # Our test environment does not support wildcard DNS. Work around | ||
145 | 179 | # this. | ||
146 | 180 | librarian_netloc = '%s:%d' % ( | ||
147 | 181 | config.librarian.download_host, config.librarian.download_port) | ||
148 | 182 | url = urlunparse( | ||
149 | 183 | ('http', librarian_netloc, parsed_url.path, parsed_url.params, | ||
150 | 184 | parsed_url.query, parsed_url.fragment)) | ||
151 | 185 | response = requests.get(url, headers={'Host': parsed_url.netloc}) | ||
152 | 186 | response.raise_for_status() | ||
153 | 187 | self.assertEqual(b'file content', response.content) | ||
154 | 188 | |||
155 | 181 | # If a user which cannot access the private bug itself tries to | 189 | # If a user which cannot access the private bug itself tries to |
162 | 182 | # to access the attachment, an NotFound error is raised. | 190 | # to access the attachment, we deny its existence. |
163 | 183 | other_launchpad = launchpadlib_for( | 191 | other_webservice = webservice_for_person( |
164 | 184 | 'test_unauthenticated', other_user, version='devel') | 192 | other_user, permission=OAuthPermission.READ_PRIVATE) |
165 | 185 | self.assertRaises( | 193 | response = other_webservice.get(ws_bug_attachment['data_link']) |
166 | 186 | RestfulNotFound, other_launchpad._browser.get, | 194 | self.assertEqual(404, response.status) |
161 | 187 | ws_bugattachment.data._wadl_resource._url) | ||
167 | diff --git a/lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py b/lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py | |||
168 | index cd5cacb..1ae3b1e 100644 | |||
169 | --- a/lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py | |||
170 | +++ b/lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py | |||
171 | @@ -1,18 +1,15 @@ | |||
173 | 1 | # Copyright 2010-2012 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2019 Canonical Ltd. This software is licensed under the |
174 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
175 | 3 | 3 | ||
176 | 4 | """Tests for bug subscription filter browser code.""" | 4 | """Tests for bug subscription filter browser code.""" |
177 | 5 | 5 | ||
178 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
179 | 7 | 7 | ||
181 | 8 | from functools import partial | 8 | import json |
182 | 9 | from urlparse import urlparse | 9 | from urlparse import urlparse |
183 | 10 | 10 | ||
184 | 11 | from lazr.restfulclient.errors import BadRequest | ||
185 | 12 | from lxml import html | 11 | from lxml import html |
186 | 13 | from storm.exceptions import LostObjectError | ||
187 | 14 | from testtools.matchers import StartsWith | 12 | from testtools.matchers import StartsWith |
188 | 15 | import transaction | ||
189 | 16 | 13 | ||
190 | 17 | from lp.app.enums import InformationType | 14 | from lp.app.enums import InformationType |
191 | 18 | from lp.bugs.browser.structuralsubscription import ( | 15 | from lp.bugs.browser.structuralsubscription import ( |
192 | @@ -23,21 +20,19 @@ from lp.bugs.interfaces.bugtask import ( | |||
193 | 23 | BugTaskImportance, | 20 | BugTaskImportance, |
194 | 24 | BugTaskStatus, | 21 | BugTaskStatus, |
195 | 25 | ) | 22 | ) |
196 | 23 | from lp.services.webapp.interfaces import OAuthPermission | ||
197 | 26 | from lp.services.webapp.publisher import canonical_url | 24 | from lp.services.webapp.publisher import canonical_url |
198 | 27 | from lp.services.webapp.servers import LaunchpadTestRequest | 25 | from lp.services.webapp.servers import LaunchpadTestRequest |
199 | 28 | from lp.testing import ( | 26 | from lp.testing import ( |
200 | 29 | anonymous_logged_in, | 27 | anonymous_logged_in, |
201 | 28 | api_url, | ||
202 | 30 | login_person, | 29 | login_person, |
203 | 31 | normalize_whitespace, | 30 | normalize_whitespace, |
204 | 32 | person_logged_in, | 31 | person_logged_in, |
205 | 33 | TestCaseWithFactory, | 32 | TestCaseWithFactory, |
206 | 34 | ws_object, | ||
207 | 35 | ) | ||
208 | 36 | from lp.testing.layers import ( | ||
209 | 37 | AppServerLayer, | ||
210 | 38 | DatabaseFunctionalLayer, | ||
211 | 39 | LaunchpadFunctionalLayer, | ||
212 | 40 | ) | 33 | ) |
213 | 34 | from lp.testing.layers import DatabaseFunctionalLayer | ||
214 | 35 | from lp.testing.pages import webservice_for_person | ||
215 | 41 | from lp.testing.views import create_initialized_view | 36 | from lp.testing.views import create_initialized_view |
216 | 42 | 37 | ||
217 | 43 | 38 | ||
218 | @@ -53,12 +48,14 @@ class TestBugSubscriptionFilterBase: | |||
219 | 53 | self.owner, self.owner) | 48 | self.owner, self.owner) |
220 | 54 | self.initial_filter = self.subscription.bug_filters.one() | 49 | self.initial_filter = self.subscription.bug_filters.one() |
221 | 55 | self.subscription_filter = self.subscription.newBugFilter() | 50 | self.subscription_filter = self.subscription.newBugFilter() |
222 | 51 | self.subscription_url = api_url(self.subscription) | ||
223 | 52 | self.subscription_filter_url = api_url(self.subscription_filter) | ||
224 | 56 | 53 | ||
225 | 57 | 54 | ||
226 | 58 | class TestBugSubscriptionFilterNavigation( | 55 | class TestBugSubscriptionFilterNavigation( |
227 | 59 | TestBugSubscriptionFilterBase, TestCaseWithFactory): | 56 | TestBugSubscriptionFilterBase, TestCaseWithFactory): |
228 | 60 | 57 | ||
230 | 61 | layer = LaunchpadFunctionalLayer | 58 | layer = DatabaseFunctionalLayer |
231 | 62 | 59 | ||
232 | 63 | def test_canonical_url(self): | 60 | def test_canonical_url(self): |
233 | 64 | url = urlparse(canonical_url(self.subscription_filter)) | 61 | url = urlparse(canonical_url(self.subscription_filter)) |
234 | @@ -80,35 +77,34 @@ class TestBugSubscriptionFilterNavigation( | |||
235 | 80 | class TestBugSubscriptionFilterAPI( | 77 | class TestBugSubscriptionFilterAPI( |
236 | 81 | TestBugSubscriptionFilterBase, TestCaseWithFactory): | 78 | TestBugSubscriptionFilterBase, TestCaseWithFactory): |
237 | 82 | 79 | ||
239 | 83 | layer = AppServerLayer | 80 | layer = DatabaseFunctionalLayer |
240 | 84 | 81 | ||
241 | 85 | def test_visible_attributes(self): | 82 | def test_visible_attributes(self): |
242 | 86 | # Bug subscription filters are not private objects. All attributes are | 83 | # Bug subscription filters are not private objects. All attributes are |
243 | 87 | # visible to everyone. | 84 | # visible to everyone. |
250 | 88 | transaction.commit() | 85 | webservice = webservice_for_person(self.factory.makePerson()) |
251 | 89 | # Create a service for a new person. | 86 | ws_subscription = self.getWebserviceJSON( |
252 | 90 | service = self.factory.makeLaunchpadService() | 87 | webservice, self.subscription_url) |
253 | 91 | get_ws_object = partial(ws_object, service) | 88 | ws_subscription_filter = self.getWebserviceJSON( |
254 | 92 | ws_subscription = get_ws_object(self.subscription) | 89 | webservice, self.subscription_filter_url) |
249 | 93 | ws_subscription_filter = get_ws_object(self.subscription_filter) | ||
255 | 94 | self.assertEqual( | 90 | self.assertEqual( |
258 | 95 | ws_subscription.self_link, | 91 | ws_subscription["self_link"], |
259 | 96 | ws_subscription_filter.structural_subscription_link) | 92 | ws_subscription_filter["structural_subscription_link"]) |
260 | 97 | self.assertEqual( | 93 | self.assertEqual( |
261 | 98 | self.subscription_filter.find_all_tags, | 94 | self.subscription_filter.find_all_tags, |
263 | 99 | ws_subscription_filter.find_all_tags) | 95 | ws_subscription_filter["find_all_tags"]) |
264 | 100 | self.assertEqual( | 96 | self.assertEqual( |
265 | 101 | self.subscription_filter.description, | 97 | self.subscription_filter.description, |
267 | 102 | ws_subscription_filter.description) | 98 | ws_subscription_filter["description"]) |
268 | 103 | self.assertEqual( | 99 | self.assertEqual( |
269 | 104 | list(self.subscription_filter.statuses), | 100 | list(self.subscription_filter.statuses), |
271 | 105 | ws_subscription_filter.statuses) | 101 | ws_subscription_filter["statuses"]) |
272 | 106 | self.assertEqual( | 102 | self.assertEqual( |
273 | 107 | list(self.subscription_filter.importances), | 103 | list(self.subscription_filter.importances), |
275 | 108 | ws_subscription_filter.importances) | 104 | ws_subscription_filter["importances"]) |
276 | 109 | self.assertEqual( | 105 | self.assertEqual( |
277 | 110 | list(self.subscription_filter.tags), | 106 | list(self.subscription_filter.tags), |
279 | 111 | ws_subscription_filter.tags) | 107 | ws_subscription_filter["tags"]) |
280 | 112 | 108 | ||
281 | 113 | def test_structural_subscription_cannot_be_modified(self): | 109 | def test_structural_subscription_cannot_be_modified(self): |
282 | 114 | # Bug filters cannot be moved from one structural subscription to | 110 | # Bug filters cannot be moved from one structural subscription to |
283 | @@ -117,15 +113,14 @@ class TestBugSubscriptionFilterAPI( | |||
284 | 117 | user = self.factory.makePerson(name=u"baz") | 113 | user = self.factory.makePerson(name=u"baz") |
285 | 118 | with person_logged_in(self.owner): | 114 | with person_logged_in(self.owner): |
286 | 119 | user_subscription = self.structure.addBugSubscription(user, user) | 115 | user_subscription = self.structure.addBugSubscription(user, user) |
296 | 120 | transaction.commit() | 116 | user_subscription_url = api_url(user_subscription) |
297 | 121 | # Create a service for the structure owner. | 117 | webservice = webservice_for_person( |
298 | 122 | service = self.factory.makeLaunchpadService(self.owner) | 118 | self.owner, permission=OAuthPermission.WRITE_PUBLIC) |
299 | 123 | get_ws_object = partial(ws_object, service) | 119 | response = webservice.patch( |
300 | 124 | ws_user_subscription = get_ws_object(user_subscription) | 120 | self.subscription_filter_url, "application/json", |
301 | 125 | ws_subscription_filter = get_ws_object(self.subscription_filter) | 121 | json.dumps( |
302 | 126 | ws_subscription_filter.structural_subscription = ws_user_subscription | 122 | {"structural_subscription_link": user_subscription_url})) |
303 | 127 | error = self.assertRaises(BadRequest, ws_subscription_filter.lp_save) | 123 | self.assertEqual(400, response.status) |
295 | 128 | self.assertEqual(400, error.response.status) | ||
304 | 129 | self.assertEqual( | 124 | self.assertEqual( |
305 | 130 | self.subscription, | 125 | self.subscription, |
306 | 131 | self.subscription_filter.structural_subscription) | 126 | self.subscription_filter.structural_subscription) |
307 | @@ -134,14 +129,12 @@ class TestBugSubscriptionFilterAPI( | |||
308 | 134 | class TestBugSubscriptionFilterAPIModifications( | 129 | class TestBugSubscriptionFilterAPIModifications( |
309 | 135 | TestBugSubscriptionFilterBase, TestCaseWithFactory): | 130 | TestBugSubscriptionFilterBase, TestCaseWithFactory): |
310 | 136 | 131 | ||
312 | 137 | layer = AppServerLayer | 132 | layer = DatabaseFunctionalLayer |
313 | 138 | 133 | ||
314 | 139 | def setUp(self): | 134 | def setUp(self): |
315 | 140 | super(TestBugSubscriptionFilterAPIModifications, self).setUp() | 135 | super(TestBugSubscriptionFilterAPIModifications, self).setUp() |
320 | 141 | transaction.commit() | 136 | self.webservice = webservice_for_person( |
321 | 142 | self.service = self.factory.makeLaunchpadService(self.owner) | 137 | self.owner, permission=OAuthPermission.WRITE_PUBLIC) |
318 | 143 | self.ws_subscription_filter = ws_object( | ||
319 | 144 | self.service, self.subscription_filter) | ||
322 | 145 | 138 | ||
323 | 146 | def test_modify_tags_fields(self): | 139 | def test_modify_tags_fields(self): |
324 | 147 | # Two tags-related fields - find_all_tags and tags - can be | 140 | # Two tags-related fields - find_all_tags and tags - can be |
325 | @@ -154,11 +147,14 @@ class TestBugSubscriptionFilterAPIModifications( | |||
326 | 154 | self.assertFalse(self.subscription_filter.exclude_any_tags) | 147 | self.assertFalse(self.subscription_filter.exclude_any_tags) |
327 | 155 | self.assertEqual(set(), self.subscription_filter.tags) | 148 | self.assertEqual(set(), self.subscription_filter.tags) |
328 | 156 | 149 | ||
334 | 157 | # Modify, save, and start a new transaction. | 150 | # Apply changes. |
335 | 158 | self.ws_subscription_filter.find_all_tags = True | 151 | response = self.webservice.patch( |
336 | 159 | self.ws_subscription_filter.tags = ["foo", "-bar", "*", "-*"] | 152 | self.subscription_filter_url, "application/json", |
337 | 160 | self.ws_subscription_filter.lp_save() | 153 | json.dumps({ |
338 | 161 | transaction.begin() | 154 | "find_all_tags": True, |
339 | 155 | "tags": ["foo", "-bar", "*", "-*"], | ||
340 | 156 | })) | ||
341 | 157 | self.assertEqual(209, response.status) | ||
342 | 162 | 158 | ||
343 | 163 | # Updated state. | 159 | # Updated state. |
344 | 164 | self.assertTrue(self.subscription_filter.find_all_tags) | 160 | self.assertTrue(self.subscription_filter.find_all_tags) |
345 | @@ -170,13 +166,13 @@ class TestBugSubscriptionFilterAPIModifications( | |||
346 | 170 | 166 | ||
347 | 171 | def test_modify_description(self): | 167 | def test_modify_description(self): |
348 | 172 | # The description can be modified. | 168 | # The description can be modified. |
351 | 173 | self.assertEqual( | 169 | self.assertIsNone(self.subscription_filter.description) |
350 | 174 | None, self.subscription_filter.description) | ||
352 | 175 | 170 | ||
357 | 176 | # Modify, save, and start a new transaction. | 171 | # Apply changes. |
358 | 177 | self.ws_subscription_filter.description = u"It's late." | 172 | response = self.webservice.patch( |
359 | 178 | self.ws_subscription_filter.lp_save() | 173 | self.subscription_filter_url, "application/json", |
360 | 179 | transaction.begin() | 174 | json.dumps({"description": u"It's late."})) |
361 | 175 | self.assertEqual(209, response.status) | ||
362 | 180 | 176 | ||
363 | 181 | # Updated state. | 177 | # Updated state. |
364 | 182 | self.assertEqual( | 178 | self.assertEqual( |
365 | @@ -186,10 +182,11 @@ class TestBugSubscriptionFilterAPIModifications( | |||
366 | 186 | # The statuses field can be modified. | 182 | # The statuses field can be modified. |
367 | 187 | self.assertEqual(set(), self.subscription_filter.statuses) | 183 | self.assertEqual(set(), self.subscription_filter.statuses) |
368 | 188 | 184 | ||
373 | 189 | # Modify, save, and start a new transaction. | 185 | # Apply changes. |
374 | 190 | self.ws_subscription_filter.statuses = ["New", "Triaged"] | 186 | response = self.webservice.patch( |
375 | 191 | self.ws_subscription_filter.lp_save() | 187 | self.subscription_filter_url, "application/json", |
376 | 192 | transaction.begin() | 188 | json.dumps({"statuses": ["New", "Triaged"]})) |
377 | 189 | self.assertEqual(209, response.status) | ||
378 | 193 | 190 | ||
379 | 194 | # Updated state. | 191 | # Updated state. |
380 | 195 | self.assertEqual( | 192 | self.assertEqual( |
381 | @@ -200,10 +197,11 @@ class TestBugSubscriptionFilterAPIModifications( | |||
382 | 200 | # The importances field can be modified. | 197 | # The importances field can be modified. |
383 | 201 | self.assertEqual(set(), self.subscription_filter.importances) | 198 | self.assertEqual(set(), self.subscription_filter.importances) |
384 | 202 | 199 | ||
389 | 203 | # Modify, save, and start a new transaction. | 200 | # Apply changes. |
390 | 204 | self.ws_subscription_filter.importances = ["Low", "High"] | 201 | response = self.webservice.patch( |
391 | 205 | self.ws_subscription_filter.lp_save() | 202 | self.subscription_filter_url, "application/json", |
392 | 206 | transaction.begin() | 203 | json.dumps({"importances": ["Low", "High"]})) |
393 | 204 | self.assertEqual(209, response.status) | ||
394 | 207 | 205 | ||
395 | 208 | # Updated state. | 206 | # Updated state. |
396 | 209 | self.assertEqual( | 207 | self.assertEqual( |
397 | @@ -212,11 +210,13 @@ class TestBugSubscriptionFilterAPIModifications( | |||
398 | 212 | 210 | ||
399 | 213 | def test_delete(self): | 211 | def test_delete(self): |
400 | 214 | # Subscription filters can be deleted. | 212 | # Subscription filters can be deleted. |
406 | 215 | self.ws_subscription_filter.lp_delete() | 213 | self.assertContentEqual( |
407 | 216 | transaction.begin() | 214 | [self.initial_filter, self.subscription_filter], |
408 | 217 | self.assertRaises( | 215 | self.subscription.bug_filters) |
409 | 218 | LostObjectError, getattr, self.subscription_filter, | 216 | response = self.webservice.delete(self.subscription_filter_url) |
410 | 219 | "find_all_tags") | 217 | self.assertEqual(200, response.status) |
411 | 218 | self.assertContentEqual( | ||
412 | 219 | [self.initial_filter], self.subscription.bug_filters) | ||
413 | 220 | 220 | ||
414 | 221 | 221 | ||
415 | 222 | class TestBugSubscriptionFilterView( | 222 | class TestBugSubscriptionFilterView( |
416 | @@ -489,7 +489,7 @@ class TestBugSubscriptionFilterEditView( | |||
417 | 489 | class TestBugSubscriptionFilterAdvancedFeatures(TestCaseWithFactory): | 489 | class TestBugSubscriptionFilterAdvancedFeatures(TestCaseWithFactory): |
418 | 490 | """A base class for testing advanced structural subscription features.""" | 490 | """A base class for testing advanced structural subscription features.""" |
419 | 491 | 491 | ||
421 | 492 | layer = LaunchpadFunctionalLayer | 492 | layer = DatabaseFunctionalLayer |
422 | 493 | 493 | ||
423 | 494 | def setUp(self): | 494 | def setUp(self): |
424 | 495 | super(TestBugSubscriptionFilterAdvancedFeatures, self).setUp() | 495 | super(TestBugSubscriptionFilterAdvancedFeatures, self).setUp() |
425 | diff --git a/lib/lp/bugs/browser/tests/test_structuralsubscription.py b/lib/lp/bugs/browser/tests/test_structuralsubscription.py | |||
426 | index db341b5..ff2b160 100644 | |||
427 | --- a/lib/lp/bugs/browser/tests/test_structuralsubscription.py | |||
428 | +++ b/lib/lp/bugs/browser/tests/test_structuralsubscription.py | |||
429 | @@ -1,11 +1,10 @@ | |||
431 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2019 Canonical Ltd. This software is licensed under the |
432 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
433 | 3 | 3 | ||
434 | 4 | """Tests for structural subscription traversal.""" | 4 | """Tests for structural subscription traversal.""" |
435 | 5 | 5 | ||
436 | 6 | from urlparse import urlparse | 6 | from urlparse import urlparse |
437 | 7 | 7 | ||
438 | 8 | import transaction | ||
439 | 9 | from zope.publisher.interfaces import NotFound | 8 | from zope.publisher.interfaces import NotFound |
440 | 10 | 9 | ||
441 | 11 | from lp.registry.browser.distribution import DistributionNavigation | 10 | from lp.registry.browser.distribution import DistributionNavigation |
442 | @@ -17,19 +16,18 @@ from lp.registry.browser.milestone import MilestoneNavigation | |||
443 | 17 | from lp.registry.browser.product import ProductNavigation | 16 | from lp.registry.browser.product import ProductNavigation |
444 | 18 | from lp.registry.browser.productseries import ProductSeriesNavigation | 17 | from lp.registry.browser.productseries import ProductSeriesNavigation |
445 | 19 | from lp.registry.browser.project import ProjectNavigation | 18 | from lp.registry.browser.project import ProjectNavigation |
446 | 19 | from lp.services.webapp.interfaces import OAuthPermission | ||
447 | 20 | from lp.services.webapp.publisher import canonical_url | 20 | from lp.services.webapp.publisher import canonical_url |
448 | 21 | from lp.testing import ( | 21 | from lp.testing import ( |
449 | 22 | api_url, | ||
450 | 22 | FakeLaunchpadRequest, | 23 | FakeLaunchpadRequest, |
451 | 23 | login, | 24 | login, |
452 | 24 | logout, | 25 | logout, |
453 | 25 | person_logged_in, | 26 | person_logged_in, |
454 | 26 | TestCaseWithFactory, | 27 | TestCaseWithFactory, |
455 | 27 | ws_object, | ||
456 | 28 | ) | ||
457 | 29 | from lp.testing.layers import ( | ||
458 | 30 | AppServerLayer, | ||
459 | 31 | DatabaseFunctionalLayer, | ||
460 | 32 | ) | 28 | ) |
461 | 29 | from lp.testing.layers import DatabaseFunctionalLayer | ||
462 | 30 | from lp.testing.pages import webservice_for_person | ||
463 | 33 | from lp.testing.views import create_initialized_view | 31 | from lp.testing.views import create_initialized_view |
464 | 34 | 32 | ||
465 | 35 | 33 | ||
466 | @@ -217,7 +215,7 @@ class TestSourcePackageStructuralSubscribersPortletView( | |||
467 | 217 | 215 | ||
468 | 218 | class TestStructuralSubscriptionAPI(TestCaseWithFactory): | 216 | class TestStructuralSubscriptionAPI(TestCaseWithFactory): |
469 | 219 | 217 | ||
471 | 220 | layer = AppServerLayer | 218 | layer = DatabaseFunctionalLayer |
472 | 221 | 219 | ||
473 | 222 | def setUp(self): | 220 | def setUp(self): |
474 | 223 | super(TestStructuralSubscriptionAPI, self).setUp() | 221 | super(TestStructuralSubscriptionAPI, self).setUp() |
475 | @@ -228,40 +226,59 @@ class TestStructuralSubscriptionAPI(TestCaseWithFactory): | |||
476 | 228 | self.subscription = self.structure.addBugSubscription( | 226 | self.subscription = self.structure.addBugSubscription( |
477 | 229 | self.owner, self.owner) | 227 | self.owner, self.owner) |
478 | 230 | self.initial_filter = self.subscription.bug_filters[0] | 228 | self.initial_filter = self.subscription.bug_filters[0] |
484 | 231 | transaction.commit() | 229 | self.subscription_url = api_url(self.subscription) |
485 | 232 | self.service = self.factory.makeLaunchpadService(self.owner) | 230 | self.initial_filter_url = api_url(self.initial_filter) |
486 | 233 | self.ws_subscription = ws_object(self.service, self.subscription) | 231 | self.webservice = webservice_for_person( |
487 | 234 | self.ws_subscription_filter = ws_object( | 232 | self.owner, permission=OAuthPermission.WRITE_PUBLIC) |
483 | 235 | self.service, self.initial_filter) | ||
488 | 236 | 233 | ||
489 | 237 | def test_newBugFilter(self): | 234 | def test_newBugFilter(self): |
490 | 238 | # New bug subscription filters can be created with newBugFilter(). | 235 | # New bug subscription filters can be created with newBugFilter(). |
492 | 239 | ws_subscription_filter = self.ws_subscription.newBugFilter() | 236 | ws_subscription = self.getWebserviceJSON( |
493 | 237 | self.webservice, self.subscription_url) | ||
494 | 238 | response = self.webservice.named_post( | ||
495 | 239 | self.subscription_url, "newBugFilter") | ||
496 | 240 | self.assertEqual(201, response.status) | ||
497 | 241 | ws_subscription_filter = self.getWebserviceJSON( | ||
498 | 242 | self.webservice, response.getHeader("Location")) | ||
499 | 240 | self.assertEqual( | 243 | self.assertEqual( |
500 | 241 | "bug_subscription_filter", | 244 | "bug_subscription_filter", |
502 | 242 | urlparse(ws_subscription_filter.resource_type_link).fragment) | 245 | urlparse(ws_subscription_filter["resource_type_link"]).fragment) |
503 | 243 | self.assertEqual( | 246 | self.assertEqual( |
506 | 244 | ws_subscription_filter.structural_subscription.self_link, | 247 | ws_subscription["self_link"], |
507 | 245 | self.ws_subscription.self_link) | 248 | ws_subscription_filter["structural_subscription_link"]) |
508 | 246 | 249 | ||
509 | 247 | def test_bug_filters(self): | 250 | def test_bug_filters(self): |
510 | 248 | # The bug_filters property is a collection of IBugSubscriptionFilter | 251 | # The bug_filters property is a collection of IBugSubscriptionFilter |
511 | 249 | # instances previously created by newBugFilter(). | 252 | # instances previously created by newBugFilter(). |
516 | 250 | bug_filter_links = lambda: set( | 253 | ws_subscription = self.getWebserviceJSON( |
517 | 251 | bug_filter.self_link for bug_filter in ( | 254 | self.webservice, self.subscription_url) |
518 | 252 | self.ws_subscription.bug_filters)) | 255 | ws_initial_filter = self.getWebserviceJSON( |
519 | 253 | initial_filter_link = self.ws_subscription_filter.self_link | 256 | self.webservice, self.initial_filter_url) |
520 | 257 | |||
521 | 258 | def bug_filter_links(): | ||
522 | 259 | ws_bug_filters = self.getWebserviceJSON( | ||
523 | 260 | self.webservice, | ||
524 | 261 | ws_subscription["bug_filters_collection_link"]) | ||
525 | 262 | return {entry["self_link"] for entry in ws_bug_filters["entries"]} | ||
526 | 263 | |||
527 | 264 | initial_filter_link = ws_initial_filter["self_link"] | ||
528 | 254 | self.assertContentEqual( | 265 | self.assertContentEqual( |
529 | 255 | [initial_filter_link], bug_filter_links()) | 266 | [initial_filter_link], bug_filter_links()) |
530 | 256 | # A new filter appears in the bug_filters collection. | 267 | # A new filter appears in the bug_filters collection. |
532 | 257 | ws_subscription_filter1 = self.ws_subscription.newBugFilter() | 268 | response = self.webservice.named_post( |
533 | 269 | self.subscription_url, "newBugFilter") | ||
534 | 270 | self.assertEqual(201, response.status) | ||
535 | 271 | ws_subscription_filter1_link = response.getHeader("Location") | ||
536 | 258 | self.assertContentEqual( | 272 | self.assertContentEqual( |
538 | 259 | [ws_subscription_filter1.self_link, initial_filter_link], | 273 | [ws_subscription_filter1_link, initial_filter_link], |
539 | 260 | bug_filter_links()) | 274 | bug_filter_links()) |
540 | 261 | # A second new filter also appears in the bug_filters collection. | 275 | # A second new filter also appears in the bug_filters collection. |
542 | 262 | ws_subscription_filter2 = self.ws_subscription.newBugFilter() | 276 | response = self.webservice.named_post( |
543 | 277 | self.subscription_url, "newBugFilter") | ||
544 | 278 | self.assertEqual(201, response.status) | ||
545 | 279 | ws_subscription_filter2_link = response.getHeader("Location") | ||
546 | 263 | self.assertContentEqual( | 280 | self.assertContentEqual( |
549 | 264 | [ws_subscription_filter1.self_link, | 281 | [ws_subscription_filter1_link, |
550 | 265 | ws_subscription_filter2.self_link, | 282 | ws_subscription_filter2_link, |
551 | 266 | initial_filter_link], | 283 | initial_filter_link], |
552 | 267 | bug_filter_links()) | 284 | bug_filter_links()) |
553 | diff --git a/lib/lp/bugs/model/tests/test_bugtask.py b/lib/lp/bugs/model/tests/test_bugtask.py | |||
554 | index c2874ba..7466f7e 100644 | |||
555 | --- a/lib/lp/bugs/model/tests/test_bugtask.py | |||
556 | +++ b/lib/lp/bugs/model/tests/test_bugtask.py | |||
557 | @@ -10,7 +10,6 @@ import subprocess | |||
558 | 10 | import unittest | 10 | import unittest |
559 | 11 | 11 | ||
560 | 12 | from lazr.lifecycle.snapshot import Snapshot | 12 | from lazr.lifecycle.snapshot import Snapshot |
561 | 13 | from lazr.restfulclient.errors import Unauthorized | ||
562 | 14 | from storm.store import Store | 13 | from storm.store import Store |
563 | 15 | from testtools.matchers import Equals | 14 | from testtools.matchers import Equals |
564 | 16 | from testtools.testcase import ExpectedException | 15 | from testtools.testcase import ExpectedException |
565 | @@ -86,12 +85,16 @@ from lp.services.log.logger import DevNullLogger | |||
566 | 86 | from lp.services.propertycache import get_property_cache | 85 | from lp.services.propertycache import get_property_cache |
567 | 87 | from lp.services.searchbuilder import any | 86 | from lp.services.searchbuilder import any |
568 | 88 | from lp.services.webapp.authorization import check_permission | 87 | from lp.services.webapp.authorization import check_permission |
570 | 89 | from lp.services.webapp.interfaces import ILaunchBag | 88 | from lp.services.webapp.interfaces import ( |
571 | 89 | ILaunchBag, | ||
572 | 90 | OAuthPermission, | ||
573 | 91 | ) | ||
574 | 90 | from lp.services.webapp.snapshot import notify_modified | 92 | from lp.services.webapp.snapshot import notify_modified |
575 | 91 | from lp.soyuz.interfaces.archive import ArchivePurpose | 93 | from lp.soyuz.interfaces.archive import ArchivePurpose |
576 | 92 | from lp.testing import ( | 94 | from lp.testing import ( |
577 | 93 | admin_logged_in, | 95 | admin_logged_in, |
578 | 94 | ANONYMOUS, | 96 | ANONYMOUS, |
579 | 97 | api_url, | ||
580 | 95 | EventRecorder, | 98 | EventRecorder, |
581 | 96 | feature_flags, | 99 | feature_flags, |
582 | 97 | login, | 100 | login, |
583 | @@ -103,15 +106,14 @@ from lp.testing import ( | |||
584 | 103 | StormStatementRecorder, | 106 | StormStatementRecorder, |
585 | 104 | TestCase, | 107 | TestCase, |
586 | 105 | TestCaseWithFactory, | 108 | TestCaseWithFactory, |
587 | 106 | ws_object, | ||
588 | 107 | ) | 109 | ) |
589 | 108 | from lp.testing.fakemethod import FakeMethod | 110 | from lp.testing.fakemethod import FakeMethod |
590 | 109 | from lp.testing.layers import ( | 111 | from lp.testing.layers import ( |
591 | 110 | AppServerLayer, | ||
592 | 111 | CeleryJobLayer, | 112 | CeleryJobLayer, |
593 | 112 | DatabaseFunctionalLayer, | 113 | DatabaseFunctionalLayer, |
594 | 113 | ) | 114 | ) |
595 | 114 | from lp.testing.matchers import HasQueryCount | 115 | from lp.testing.matchers import HasQueryCount |
596 | 116 | from lp.testing.pages import webservice_for_person | ||
597 | 115 | 117 | ||
598 | 116 | 118 | ||
599 | 117 | BugData = namedtuple("BugData", ['owner', 'distro', 'distro_release', | 119 | BugData = namedtuple("BugData", ['owner', 'distro', 'distro_release', |
600 | @@ -2953,29 +2955,32 @@ class TestValidateNewTarget(TestCaseWithFactory, ValidateTargetMixin): | |||
601 | 2953 | class TestWebservice(TestCaseWithFactory): | 2955 | class TestWebservice(TestCaseWithFactory): |
602 | 2954 | """Tests for the webservice.""" | 2956 | """Tests for the webservice.""" |
603 | 2955 | 2957 | ||
605 | 2956 | layer = AppServerLayer | 2958 | layer = DatabaseFunctionalLayer |
606 | 2957 | 2959 | ||
607 | 2958 | def test_delete_bugtask(self): | 2960 | def test_delete_bugtask(self): |
608 | 2959 | """Test that a bugtask can be deleted.""" | 2961 | """Test that a bugtask can be deleted.""" |
609 | 2960 | owner = self.factory.makePerson() | 2962 | owner = self.factory.makePerson() |
610 | 2961 | some_person = self.factory.makePerson() | 2963 | some_person = self.factory.makePerson() |
615 | 2962 | db_bug = self.factory.makeBug() | 2964 | bug = self.factory.makeBug() |
616 | 2963 | db_bugtask = self.factory.makeBugTask(bug=db_bug, owner=owner) | 2965 | bugtask = self.factory.makeBugTask(bug=bug, owner=owner) |
617 | 2964 | transaction.commit() | 2966 | bugtask_url = api_url(bugtask) |
614 | 2965 | logout() | ||
618 | 2966 | 2967 | ||
619 | 2967 | # It will fail for an unauthorised user. | 2968 | # It will fail for an unauthorised user. |
623 | 2968 | launchpad = self.factory.makeLaunchpadService(some_person) | 2969 | webservice = webservice_for_person( |
624 | 2969 | bugtask = ws_object(launchpad, db_bugtask) | 2970 | some_person, permission=OAuthPermission.WRITE_PUBLIC, |
625 | 2970 | self.assertRaises(Unauthorized, bugtask.lp_delete) | 2971 | default_api_version="devel") |
626 | 2972 | response = webservice.delete(bugtask_url) | ||
627 | 2973 | self.assertEqual(401, response.status) | ||
628 | 2974 | |||
629 | 2975 | webservice = webservice_for_person( | ||
630 | 2976 | owner, permission=OAuthPermission.WRITE_PUBLIC, | ||
631 | 2977 | default_api_version="devel") | ||
632 | 2978 | response = webservice.delete(bugtask_url) | ||
633 | 2979 | self.assertEqual(200, response.status) | ||
634 | 2971 | 2980 | ||
635 | 2972 | launchpad = self.factory.makeLaunchpadService(owner) | ||
636 | 2973 | bugtask = ws_object(launchpad, db_bugtask) | ||
637 | 2974 | bugtask.lp_delete() | ||
638 | 2975 | transaction.commit() | ||
639 | 2976 | # Check the delete really worked. | 2981 | # Check the delete really worked. |
642 | 2977 | with person_logged_in(removeSecurityProxy(db_bug).owner): | 2982 | with person_logged_in(removeSecurityProxy(bug).owner): |
643 | 2978 | self.assertEqual([db_bug.default_bugtask], db_bug.bugtasks) | 2983 | self.assertEqual([bug.default_bugtask], bug.bugtasks) |
644 | 2979 | 2984 | ||
645 | 2980 | 2985 | ||
646 | 2981 | class TestBugTaskUserHasBugSupervisorPrivileges(TestCaseWithFactory): | 2986 | class TestBugTaskUserHasBugSupervisorPrivileges(TestCaseWithFactory): |
647 | diff --git a/lib/lp/bugs/tests/test_bug_messages_webservice.py b/lib/lp/bugs/tests/test_bug_messages_webservice.py | |||
648 | index feec1a7..5faae0e 100644 | |||
649 | --- a/lib/lp/bugs/tests/test_bug_messages_webservice.py | |||
650 | +++ b/lib/lp/bugs/tests/test_bug_messages_webservice.py | |||
651 | @@ -1,4 +1,4 @@ | |||
653 | 1 | # Copyright 2011 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2011-2019 Canonical Ltd. This software is licensed under the |
654 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
655 | 3 | 3 | ||
656 | 4 | """Webservice unit tests related to Launchpad Bug messages.""" | 4 | """Webservice unit tests related to Launchpad Bug messages.""" |
657 | @@ -24,36 +24,43 @@ from lp.testing import ( | |||
658 | 24 | logout, | 24 | logout, |
659 | 25 | person_logged_in, | 25 | person_logged_in, |
660 | 26 | TestCaseWithFactory, | 26 | TestCaseWithFactory, |
661 | 27 | WebServiceTestCase, | ||
662 | 28 | ) | 27 | ) |
663 | 29 | from lp.testing.layers import ( | 28 | from lp.testing.layers import ( |
664 | 30 | DatabaseFunctionalLayer, | 29 | DatabaseFunctionalLayer, |
665 | 31 | LaunchpadFunctionalLayer, | 30 | LaunchpadFunctionalLayer, |
666 | 32 | ) | 31 | ) |
668 | 33 | from lp.testing.pages import LaunchpadWebServiceCaller | 32 | from lp.testing.pages import ( |
669 | 33 | LaunchpadWebServiceCaller, | ||
670 | 34 | webservice_for_person, | ||
671 | 35 | ) | ||
672 | 34 | 36 | ||
673 | 35 | 37 | ||
675 | 36 | class TestMessageTraversal(WebServiceTestCase): | 38 | class TestMessageTraversal(TestCaseWithFactory): |
676 | 37 | """Tests safe traversal of bugs. | 39 | """Tests safe traversal of bugs. |
677 | 38 | 40 | ||
678 | 39 | See bug 607438.""" | 41 | See bug 607438.""" |
679 | 40 | 42 | ||
680 | 43 | layer = LaunchpadFunctionalLayer | ||
681 | 44 | |||
682 | 41 | def test_message_with_attachments(self): | 45 | def test_message_with_attachments(self): |
685 | 42 | bugowner = self.factory.makePerson() | 46 | bug = self.factory.makeBug() |
684 | 43 | bug = self.factory.makeBug(owner=bugowner) | ||
686 | 44 | # Traversal over bug messages attachments has no errors. | 47 | # Traversal over bug messages attachments has no errors. |
687 | 45 | expected_messages = [] | 48 | expected_messages = [] |
689 | 46 | with person_logged_in(bugowner): | 49 | with person_logged_in(bug.owner): |
690 | 47 | for i in range(3): | 50 | for i in range(3): |
691 | 48 | att = self.factory.makeBugAttachment(bug) | 51 | att = self.factory.makeBugAttachment(bug) |
692 | 49 | expected_messages.append(att.message.subject) | 52 | expected_messages.append(att.message.subject) |
700 | 50 | 53 | bug_url = api_url(bug) | |
701 | 51 | lp_user = self.factory.makePerson() | 54 | |
702 | 52 | lp_bug = self.wsObject(bug, lp_user) | 55 | webservice = webservice_for_person(self.factory.makePerson()) |
703 | 53 | 56 | ws_bug = self.getWebserviceJSON(webservice, bug_url) | |
704 | 54 | attachments = lp_bug.attachments | 57 | ws_bug_attachments = self.getWebserviceJSON( |
705 | 55 | messages = [a.message.subject for a in attachments | 58 | webservice, ws_bug['attachments_collection_link']) |
706 | 56 | if a.message is not None] | 59 | messages = [ |
707 | 60 | self.getWebserviceJSON( | ||
708 | 61 | webservice, attachment['message_link'])['subject'] | ||
709 | 62 | for attachment in ws_bug_attachments['entries'] | ||
710 | 63 | if attachment['message_link'] is not None] | ||
711 | 57 | self.assertContentEqual( | 64 | self.assertContentEqual( |
712 | 58 | messages, | 65 | messages, |
713 | 59 | expected_messages) | 66 | expected_messages) |
714 | @@ -67,15 +74,21 @@ class TestMessageTraversal(WebServiceTestCase): | |||
715 | 67 | message_2 = self.factory.makeMessage() | 74 | message_2 = self.factory.makeMessage() |
716 | 68 | message_2.parent = message_1 | 75 | message_2.parent = message_1 |
717 | 69 | bug = self.factory.makeBug() | 76 | bug = self.factory.makeBug() |
719 | 70 | bug.linkMessage(message_2) | 77 | with person_logged_in(bug.owner): |
720 | 78 | bug.linkMessage(message_2) | ||
721 | 79 | bug_url = api_url(bug) | ||
722 | 80 | message_2_url = api_url(message_2) | ||
723 | 71 | user = self.factory.makePerson() | 81 | user = self.factory.makePerson() |
726 | 72 | lp_bug = self.wsObject(bug, user) | 82 | webservice = webservice_for_person(user) |
727 | 73 | for lp_message in lp_bug.messages: | 83 | ws_bug = self.getWebserviceJSON(webservice, bug_url) |
728 | 84 | ws_bug_messages = self.getWebserviceJSON( | ||
729 | 85 | webservice, ws_bug['messages_collection_link']) | ||
730 | 86 | for ws_message in ws_bug_messages['entries']: | ||
731 | 74 | # An IIndexedMessage's representation. | 87 | # An IIndexedMessage's representation. |
733 | 75 | self.assertIs(None, lp_message.parent) | 88 | self.assertIsNone(ws_message['parent_link']) |
734 | 76 | # An IMessage's representation. | 89 | # An IMessage's representation. |
737 | 77 | lp_message = self.wsObject(message_2, user) | 90 | ws_message = self.getWebserviceJSON(webservice, message_2_url) |
738 | 78 | self.assertIs(None, lp_message.parent) | 91 | self.assertIsNone(ws_message['parent_link']) |
739 | 79 | 92 | ||
740 | 80 | 93 | ||
741 | 81 | class TestBugMessage(TestCaseWithFactory): | 94 | class TestBugMessage(TestCaseWithFactory): |
742 | diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py | |||
743 | index 6a0d9b2..747930d 100644 | |||
744 | --- a/lib/lp/testing/__init__.py | |||
745 | +++ b/lib/lp/testing/__init__.py | |||
746 | @@ -827,6 +827,12 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures): | |||
747 | 827 | "\n\n".join(str(n) for n in notifications))) | 827 | "\n\n".join(str(n) for n in notifications))) |
748 | 828 | return notifications | 828 | return notifications |
749 | 829 | 829 | ||
750 | 830 | def getWebserviceJSON(self, webservice, url): | ||
751 | 831 | """Get the JSON representation of a webservice object given its URL.""" | ||
752 | 832 | response = webservice.get(url) | ||
753 | 833 | self.assertEqual(200, response.status) | ||
754 | 834 | return response.jsonBody() | ||
755 | 835 | |||
756 | 830 | 836 | ||
757 | 831 | class TestCaseWithFactory(TestCase): | 837 | class TestCaseWithFactory(TestCase): |
758 | 832 | 838 |