Merge lp:~michael.nelson/rnr-server/731736-other-origins into lp:rnr-server
- 731736-other-origins
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Michael Nelson | ||||
Approved revision: | 154 | ||||
Merged at revision: | 151 | ||||
Proposed branch: | lp:~michael.nelson/rnr-server/731736-other-origins | ||||
Merge into: | lp:rnr-server | ||||
Diff against target: |
571 lines (+277/-103) 9 files modified
django_project/config_dev/config/main.cfg (+8/-0) django_project/config_dev/schema.py (+1/-0) src/reviewsapp/api/handlers.py (+3/-1) src/reviewsapp/forms.py (+45/-38) src/reviewsapp/models/reviews.py (+6/-0) src/reviewsapp/tests/test_handlers.py (+42/-3) src/reviewsapp/tests/test_rnrclient.py (+8/-3) src/reviewsapp/tests/test_utilities.py (+120/-43) src/reviewsapp/utilities.py (+44/-15) |
||||
To merge this branch: | bzr merge lp:~michael.nelson/rnr-server/731736-other-origins | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Danny Tamez (community) | Approve | ||
Review via email: mp+53007@code.launchpad.net |
Commit message
[r=zematynnad][bug=731376] Enable reviews of packages in partner and extras.
Description of the change
Overview
========
This branch is fixing bug 731376 by enabling reviews for origins other than 'ubuntu'.
To test: follow the README and then `make test`
This branch will not land until the next one, dealing with the issue identified below, is ready.
Details for the next branch
=======
The main issue is verifying the origin for a new review when we don't yet have a record of that origin.
For 'ubuntu' and 'canonical' it is simple enough - we can use the LP API to verify that a binary with the given package name is published in the repository for the distroseries and arch. For extras it's also possible to verify (differently) using the API.
But we cannot generalise the origin for public PPAs currently, as the published origin is ambiguous (I created bug 733170). So I've just left that for the moment.
For the commercial PPAs, we cannot verify that the specified origin is correct, as LP doesn't let us query private PPAs for binaries :). So currently (in this branch) submitting a review for 'linux' in the fluendo-dvd origin would work, as would submitting a review for 'vendetta-online' in the fluendo-dvd origin, as would 'fluendo-dvd' in the correct origin, but with 'hoary' as the distroseries etc.
Sooo, we need a solution. The solutions I can think of are:
1) Pull and cache the staging/production sca available apps to get the data (currently this will also pull the icons, but we could later add a separate url on sca for just the data we need, if necessary)
2) (Temporary solution): Encode the binary publishings (ie. ppa-name, arch, distroseries) for each of the commercial apps in the settings (and update settings each time a new app is added).
...
So I'll probably go ahead with 1 (Update: checked with achuni and he agrees).
Michael Nelson (michael.nelson) wrote : | # |
On Mon, Mar 14, 2011 at 6:46 PM, Danny Tamez <email address hidden>wrote:
> Review: Approve
> Michael - great works as always. Approved but what would you think of
> adding a test that is the negative test for
> "test_create_
> test that when lp returns False that the item and repo are not created.
>
Yeah - good find... I'll add the test.
Thanks again!
M
- 154. By Michael Nelson
-
Added test ensuring new repositories are not created when lp_verify returns false.
Preview Diff
1 | === modified file 'django_project/config_dev/config/main.cfg' | |||
2 | --- django_project/config_dev/config/main.cfg 2011-03-11 21:09:10 +0000 | |||
3 | +++ django_project/config_dev/config/main.cfg 2011-03-15 09:36:33 +0000 | |||
4 | @@ -98,6 +98,14 @@ | |||
5 | 98 | cache_review_stats_seconds = 14400 | 98 | cache_review_stats_seconds = 14400 |
6 | 99 | token_cache_expiry_hours = 4 | 99 | token_cache_expiry_hours = 4 |
7 | 100 | reviewsapp_media_root = src/reviewsapp/media | 100 | reviewsapp_media_root = src/reviewsapp/media |
8 | 101 | commercial_origins = lp-ppa-commercial-ppa-uploaders-fluendo-dvd | ||
9 | 102 | lp-ppa-commercial-ppa-uploaders-fluendo-plugins | ||
10 | 103 | lp-ppa-commercial-ppa-uploaders-fluendo-wmv-plugins | ||
11 | 104 | lp-ppa-commercial-ppa-uploaders-brukkon | ||
12 | 105 | lp-ppa-commercial-ppa-uploaders-vendetta-online | ||
13 | 106 | lp-ppa-commercial-ppa-uploaders-world-of-goo | ||
14 | 107 | lp-ppa-commercial-ppa-uploaders-illumination | ||
15 | 108 | |||
16 | 101 | 109 | ||
17 | 102 | [sso_api] | 110 | [sso_api] |
18 | 103 | sso_api_service_root = https://login.staging.ubuntu.com/api/1.0 | 111 | sso_api_service_root = https://login.staging.ubuntu.com/api/1.0 |
19 | 104 | 112 | ||
20 | === modified file 'django_project/config_dev/schema.py' | |||
21 | --- django_project/config_dev/schema.py 2011-03-10 22:56:54 +0000 | |||
22 | +++ django_project/config_dev/schema.py 2011-03-15 09:36:33 +0000 | |||
23 | @@ -56,6 +56,7 @@ | |||
24 | 56 | rnr.token_cache_expiry_hours = IntConfigOption(default=4) | 56 | rnr.token_cache_expiry_hours = IntConfigOption(default=4) |
25 | 57 | rnr.reviewsapp_media_root = StringConfigOption() | 57 | rnr.reviewsapp_media_root = StringConfigOption() |
26 | 58 | rnr.allow_multiple_reviews_for_testing = BoolConfigOption(default=False) | 58 | rnr.allow_multiple_reviews_for_testing = BoolConfigOption(default=False) |
27 | 59 | rnr.commercial_origins = LinesConfigOption(item=StringConfigOption()) | ||
28 | 59 | 60 | ||
29 | 60 | # Launchpad | 61 | # Launchpad |
30 | 61 | launchpad = ConfigSection() | 62 | launchpad = ConfigSection() |
31 | 62 | 63 | ||
32 | === modified file 'src/reviewsapp/api/handlers.py' | |||
33 | --- src/reviewsapp/api/handlers.py 2011-03-01 12:24:51 +0000 | |||
34 | +++ src/reviewsapp/api/handlers.py 2011-03-15 09:36:33 +0000 | |||
35 | @@ -80,7 +80,7 @@ | |||
36 | 80 | 80 | ||
37 | 81 | class RetrieveReviewHandler(BaseHandler): | 81 | class RetrieveReviewHandler(BaseHandler): |
38 | 82 | allowed_methods = ('GET',) | 82 | allowed_methods = ('GET',) |
40 | 83 | 83 | ||
41 | 84 | def read(self, request, review_id): | 84 | def read(self, request, review_id): |
42 | 85 | try: | 85 | try: |
43 | 86 | review = Review.objects.get(id=review_id) | 86 | review = Review.objects.get(id=review_id) |
44 | @@ -95,6 +95,8 @@ | |||
45 | 95 | 'id', | 95 | 'id', |
46 | 96 | 'package_name', | 96 | 'package_name', |
47 | 97 | 'app_name', | 97 | 'app_name', |
48 | 98 | 'origin', | ||
49 | 99 | 'distroseries', | ||
50 | 98 | 'language', | 100 | 'language', |
51 | 99 | 'date_created', | 101 | 'date_created', |
52 | 100 | 'rating', | 102 | 'rating', |
53 | 101 | 103 | ||
54 | === modified file 'src/reviewsapp/forms.py' | |||
55 | --- src/reviewsapp/forms.py 2011-03-09 11:45:47 +0000 | |||
56 | +++ src/reviewsapp/forms.py 2011-03-15 09:36:33 +0000 | |||
57 | @@ -88,8 +88,7 @@ | |||
58 | 88 | if not self.is_valid(): | 88 | if not self.is_valid(): |
59 | 89 | return self.cleaned_data | 89 | return self.cleaned_data |
60 | 90 | 90 | ||
63 | 91 | self._validate_and_populate_software_item() | 91 | self._validate_software_item_and_repository() |
62 | 92 | self._validate_and_populate_repository() | ||
64 | 93 | 92 | ||
65 | 94 | if not settings.ALLOW_MULTIPLE_REVIEWS_FOR_TESTING: | 93 | if not settings.ALLOW_MULTIPLE_REVIEWS_FOR_TESTING: |
66 | 95 | # Ensure a user cannot submit a second review for the same app | 94 | # Ensure a user cannot submit a second review for the same app |
67 | @@ -117,52 +116,60 @@ | |||
68 | 117 | self.cleaned_data['architecture'] = architecture | 116 | self.cleaned_data['architecture'] = architecture |
69 | 118 | return arch_tag | 117 | return arch_tag |
70 | 119 | 118 | ||
73 | 120 | def _validate_and_populate_software_item(self): | 119 | def _validate_software_item_and_repository(self): |
74 | 121 | """Add the software item to the cleaned data, creating if necessary. | 120 | """Add the software item and repository to the cleaned data. |
75 | 121 | |||
76 | 122 | If they do not exist in the db, we check the validity and | ||
77 | 123 | create them if they are valid. | ||
78 | 122 | """ | 124 | """ |
79 | 123 | package_name = self.cleaned_data['package_name'] | 125 | package_name = self.cleaned_data['package_name'] |
80 | 124 | app_name = self.cleaned_data['app_name'] | 126 | app_name = self.cleaned_data['app_name'] |
81 | 127 | origin = self.cleaned_data['origin'] | ||
82 | 125 | distroseries = self.cleaned_data['distroseries'] | 128 | distroseries = self.cleaned_data['distroseries'] |
83 | 126 | arch_tag=self.cleaned_data['arch_tag'] | 129 | arch_tag=self.cleaned_data['arch_tag'] |
93 | 127 | review_count = Review.objects.filter( | 130 | |
94 | 128 | softwareitem__package_name=package_name, | 131 | require_lp_check = False |
95 | 129 | softwareitem__app_name=app_name, | 132 | repository = None |
96 | 130 | repository__distroseries=distroseries, | 133 | # First we grab the repository - if it exists. |
97 | 131 | architecture__tag=arch_tag).count() | 134 | try: |
98 | 132 | if review_count == 0: | 135 | repository = Repository.objects.get( |
99 | 133 | # This is the first review for this software_item and distroseries | 136 | origin=origin, distroseries=distroseries) |
100 | 134 | if not WebServices().lp_verify_packagename_in_distro( | 137 | except Repository.DoesNotExist: |
101 | 135 | package_name, distroseries, arch_tag=arch_tag): | 138 | require_lp_check = True |
102 | 139 | |||
103 | 140 | if repository is not None: | ||
104 | 141 | # Check whether there are any existing reviews for this | ||
105 | 142 | # software in the repository with the given arch. | ||
106 | 143 | review_count = Review.objects.filter( | ||
107 | 144 | softwareitem__package_name=package_name, | ||
108 | 145 | softwareitem__app_name=app_name, | ||
109 | 146 | repository=repository, | ||
110 | 147 | architecture__tag=arch_tag).count() | ||
111 | 148 | if review_count == 0: | ||
112 | 149 | # This is the first review for this software_item in the | ||
113 | 150 | # repository with the given arch. | ||
114 | 151 | require_lp_check = True | ||
115 | 152 | |||
116 | 153 | if require_lp_check: | ||
117 | 154 | valid_in_lp = WebServices().lp_verify_packagename_in_repository( | ||
118 | 155 | package_name, origin, distroseries, arch_tag=arch_tag) | ||
119 | 156 | |||
120 | 157 | if not valid_in_lp: | ||
121 | 136 | raise forms.ValidationError( | 158 | raise forms.ValidationError( |
124 | 137 | ': package {0} not in {1} for {2}'.format( | 159 | ': package {0} not in {1} {2} for {2}'.format( |
125 | 138 | package_name, distroseries, arch_tag)) | 160 | package_name, origin, distroseries, arch_tag)) |
126 | 161 | |||
127 | 162 | # Now we know the given data corresponds to a published binary | ||
128 | 163 | # on launchpad, so we can populate the cleaned data, creating | ||
129 | 164 | # the repository and software_item records if necessary. | ||
130 | 165 | repository = repository or Repository.objects.create( | ||
131 | 166 | origin=origin, distroseries=distroseries) | ||
132 | 167 | self.cleaned_data['repository'] = repository | ||
133 | 168 | |||
134 | 139 | software_item, created = SoftwareItem.objects.get_or_create( | 169 | software_item, created = SoftwareItem.objects.get_or_create( |
135 | 140 | package_name=package_name, app_name=app_name) | 170 | package_name=package_name, app_name=app_name) |
136 | 141 | |||
137 | 142 | self.cleaned_data['software_item'] = software_item | 171 | self.cleaned_data['software_item'] = software_item |
138 | 143 | 172 | ||
139 | 144 | def _validate_and_populate_repository(self): | ||
140 | 145 | """Add the repository to the cleaned data, creating if necessary.""" | ||
141 | 146 | # Find the origin/distroseries for the software item. | ||
142 | 147 | origin = self.cleaned_data['origin'] | ||
143 | 148 | distroseries = self.cleaned_data['distroseries'] | ||
144 | 149 | try: | ||
145 | 150 | repository = Repository.objects.get( | ||
146 | 151 | origin=origin, | ||
147 | 152 | distroseries=distroseries) | ||
148 | 153 | except Repository.DoesNotExist: | ||
149 | 154 | # The software repository does not yet exist, probably because | ||
150 | 155 | # this is the first review for this origin/distroseries. That's | ||
151 | 156 | # okay, create a record for it in our database. | ||
152 | 157 | # | ||
153 | 158 | # XXX 2010-03-03 bug=688043 Verify with Launchpad first. | ||
154 | 159 | repository = Repository( | ||
155 | 160 | origin=origin, | ||
156 | 161 | distroseries=distroseries) | ||
157 | 162 | repository.save() | ||
158 | 163 | |||
159 | 164 | self.cleaned_data['repository'] = repository | ||
160 | 165 | |||
161 | 166 | 173 | ||
162 | 167 | class ReviewModerationFilterForm(forms.Form): | 174 | class ReviewModerationFilterForm(forms.Form): |
163 | 168 | """Filtering options for review moderation list.""" | 175 | """Filtering options for review moderation list.""" |
164 | 169 | 176 | ||
165 | === modified file 'src/reviewsapp/models/reviews.py' | |||
166 | --- src/reviewsapp/models/reviews.py 2011-03-09 08:27:34 +0000 | |||
167 | +++ src/reviewsapp/models/reviews.py 2011-03-15 09:36:33 +0000 | |||
168 | @@ -270,6 +270,12 @@ | |||
169 | 270 | def app_name(self): | 270 | def app_name(self): |
170 | 271 | return self.softwareitem.app_name | 271 | return self.softwareitem.app_name |
171 | 272 | 272 | ||
172 | 273 | def origin(self): | ||
173 | 274 | return self.repository.origin | ||
174 | 275 | |||
175 | 276 | def distroseries(self): | ||
176 | 277 | return self.repository.distroseries | ||
177 | 278 | |||
178 | 273 | def package_app_name(self): | 279 | def package_app_name(self): |
179 | 274 | app_name = self.app_name() | 280 | app_name = self.app_name() |
180 | 275 | if app_name: | 281 | if app_name: |
181 | 276 | 282 | ||
182 | === modified file 'src/reviewsapp/tests/test_handlers.py' | |||
183 | --- src/reviewsapp/tests/test_handlers.py 2011-03-09 11:49:10 +0000 | |||
184 | +++ src/reviewsapp/tests/test_handlers.py 2011-03-15 09:36:33 +0000 | |||
185 | @@ -51,6 +51,7 @@ | |||
186 | 51 | SubmitUsefulnessHandler, | 51 | SubmitUsefulnessHandler, |
187 | 52 | ) | 52 | ) |
188 | 53 | from reviewsapp.models import ( | 53 | from reviewsapp.models import ( |
189 | 54 | Repository, | ||
190 | 54 | Review, | 55 | Review, |
191 | 55 | ReviewModerationFlag, | 56 | ReviewModerationFlag, |
192 | 56 | SoftwareItem, | 57 | SoftwareItem, |
193 | @@ -475,7 +476,7 @@ | |||
194 | 475 | } | 476 | } |
195 | 476 | super(SubmitReviewHandlerTestCase, self).setUp() | 477 | super(SubmitReviewHandlerTestCase, self).setUp() |
196 | 477 | 478 | ||
198 | 478 | def _post_new_review(self, data, user=None): | 479 | def _post_new_review(self, data, user=None, lp_verify_result=True): |
199 | 479 | # Post a review as an authenticated user. | 480 | # Post a review as an authenticated user. |
200 | 480 | url = reverse('rnr-api-reviews') | 481 | url = reverse('rnr-api-reviews') |
201 | 481 | 482 | ||
202 | @@ -485,11 +486,11 @@ | |||
203 | 485 | 486 | ||
204 | 486 | is_authed_fn = 'reviewsapp.auth.SSOOAuthAuthentication.is_authenticated' | 487 | is_authed_fn = 'reviewsapp.auth.SSOOAuthAuthentication.is_authenticated' |
205 | 487 | lp_verify_fn = ('reviewsapp.utilities.WebServices.' | 488 | lp_verify_fn = ('reviewsapp.utilities.WebServices.' |
207 | 488 | 'lp_verify_packagename_in_distro') | 489 | 'lp_verify_packagename_in_repository') |
208 | 489 | with patch(is_authed_fn) as mock_is_authenticated: | 490 | with patch(is_authed_fn) as mock_is_authenticated: |
209 | 490 | mock_is_authenticated.return_value = True | 491 | mock_is_authenticated.return_value = True |
210 | 491 | with patch(lp_verify_fn) as mock_lp_verify_method: | 492 | with patch(lp_verify_fn) as mock_lp_verify_method: |
212 | 492 | mock_lp_verify_method.return_value = True | 493 | mock_lp_verify_method.return_value = lp_verify_result |
213 | 493 | response = self.client.post( | 494 | response = self.client.post( |
214 | 494 | url, data=data, content_type='application/json') | 495 | url, data=data, content_type='application/json') |
215 | 495 | return response | 496 | return response |
216 | @@ -520,6 +521,44 @@ | |||
217 | 520 | response_dict = simplejson.loads(response.content) | 521 | response_dict = simplejson.loads(response.content) |
218 | 521 | self.assertEqual('inkscape', response_dict['package_name']) | 522 | self.assertEqual('inkscape', response_dict['package_name']) |
219 | 522 | 523 | ||
220 | 524 | def test_creates_repository_and_software_item(self): | ||
221 | 525 | # Submitting a review for a software item or repository of which | ||
222 | 526 | # we don't yet have a record will create those records (after | ||
223 | 527 | # validating that the corresponding binary exists on Launchpad). | ||
224 | 528 | required_data = self.required_data | ||
225 | 529 | required_data['distroseries'] = 'doesntexist' | ||
226 | 530 | self.assertEqual(0, Repository.objects.filter( | ||
227 | 531 | origin='ubuntu', distroseries='doesntexist').count()) | ||
228 | 532 | self.assertEqual(0, SoftwareItem.objects.filter( | ||
229 | 533 | package_name='inkscape').count()) | ||
230 | 534 | |||
231 | 535 | response = self._post_new_review(simplejson.dumps(self.required_data)) | ||
232 | 536 | |||
233 | 537 | self.assertEqual(1, Repository.objects.filter( | ||
234 | 538 | origin='ubuntu', distroseries='doesntexist').count()) | ||
235 | 539 | self.assertEqual(1, SoftwareItem.objects.filter( | ||
236 | 540 | package_name='inkscape').count()) | ||
237 | 541 | |||
238 | 542 | def test_bad_repository(self): | ||
239 | 543 | # If lp_verify returns false a bad request status is returned, | ||
240 | 544 | # and no new repositories are created. | ||
241 | 545 | required_data = self.required_data | ||
242 | 546 | required_data['distroseries'] = 'doesntexist' | ||
243 | 547 | self.assertEqual(0, Repository.objects.filter( | ||
244 | 548 | origin='ubuntu', distroseries='doesntexist').count()) | ||
245 | 549 | self.assertEqual(0, SoftwareItem.objects.filter( | ||
246 | 550 | package_name='inkscape').count()) | ||
247 | 551 | |||
248 | 552 | response = self._post_new_review( | ||
249 | 553 | simplejson.dumps(self.required_data), lp_verify_result=False) | ||
250 | 554 | |||
251 | 555 | self.assertEqual(httplib.BAD_REQUEST, response.status_code) | ||
252 | 556 | self.assertEqual(0, Repository.objects.filter( | ||
253 | 557 | origin='ubuntu', distroseries='doesntexist').count()) | ||
254 | 558 | self.assertEqual(0, SoftwareItem.objects.filter( | ||
255 | 559 | package_name='inkscape').count()) | ||
256 | 560 | |||
257 | 561 | |||
258 | 523 | def test_max_length_summary(self): | 562 | def test_max_length_summary(self): |
259 | 524 | # Posting with a summary > 80 chars results in a bad request. | 563 | # Posting with a summary > 80 chars results in a bad request. |
260 | 525 | data = self.required_data | 564 | data = self.required_data |
261 | 526 | 565 | ||
262 | === modified file 'src/reviewsapp/tests/test_rnrclient.py' | |||
263 | --- src/reviewsapp/tests/test_rnrclient.py 2011-03-09 08:27:34 +0000 | |||
264 | +++ src/reviewsapp/tests/test_rnrclient.py 2011-03-15 09:36:33 +0000 | |||
265 | @@ -154,6 +154,8 @@ | |||
266 | 154 | reviews[0].reviewer_username) | 154 | reviews[0].reviewer_username) |
267 | 155 | self.assertEqual(review.reviewer_displayname(), | 155 | self.assertEqual(review.reviewer_displayname(), |
268 | 156 | reviews[0].reviewer_displayname) | 156 | reviews[0].reviewer_displayname) |
269 | 157 | self.assertEqual(review.origin(), reviews[0].origin) | ||
270 | 158 | self.assertEqual(review.distroseries(), reviews[0].distroseries) | ||
271 | 157 | 159 | ||
272 | 158 | def test_get_reviews_for_app(self): | 160 | def test_get_reviews_for_app(self): |
273 | 159 | software_item = self.factory.makeSoftwareItem(app_name='something') | 161 | software_item = self.factory.makeSoftwareItem(app_name='something') |
274 | @@ -202,9 +204,11 @@ | |||
275 | 202 | review = self.factory.makeReview() | 204 | review = self.factory.makeReview() |
276 | 203 | 205 | ||
277 | 204 | api = RatingsAndReviewsAPI() | 206 | api = RatingsAndReviewsAPI() |
279 | 205 | review_request = api.get_review(review_id=review.id) | 207 | review_detail = api.get_review(review_id=review.id) |
280 | 206 | 208 | ||
282 | 207 | self.assertEqual(review_request.id, review.id) | 209 | self.assertEqual(review_detail.id, review.id) |
283 | 210 | self.assertEqual(review_detail.origin, review.origin()) | ||
284 | 211 | self.assertEqual(review_detail.distroseries, review.distroseries()) | ||
285 | 208 | invalidate_paginated_reviews_for(review) | 212 | invalidate_paginated_reviews_for(review) |
286 | 209 | 213 | ||
287 | 210 | 214 | ||
288 | @@ -222,7 +226,8 @@ | |||
289 | 222 | auth = OAuthAuthorizer(token.token, token.token_secret, consumer.key, | 226 | auth = OAuthAuthorizer(token.token, token.token_secret, consumer.key, |
290 | 223 | consumer.secret) | 227 | consumer.secret) |
291 | 224 | lp_verify_packagename_method = ( | 228 | lp_verify_packagename_method = ( |
293 | 225 | 'reviewsapp.utilities.WebServices.lp_verify_packagename_in_distro') | 229 | 'reviewsapp.utilities.WebServices.' |
294 | 230 | 'lp_verify_packagename_in_repository') | ||
295 | 226 | 231 | ||
296 | 227 | if mock_verify_package is None: | 232 | if mock_verify_package is None: |
297 | 228 | mock_verify_package = Mock() | 233 | mock_verify_package = Mock() |
298 | 229 | 234 | ||
299 | === modified file 'src/reviewsapp/tests/test_utilities.py' | |||
300 | --- src/reviewsapp/tests/test_utilities.py 2011-03-10 22:56:54 +0000 | |||
301 | +++ src/reviewsapp/tests/test_utilities.py 2011-03-15 09:36:33 +0000 | |||
302 | @@ -100,58 +100,135 @@ | |||
303 | 100 | class LaunchpadTestCase(TestCaseWithFactory): | 100 | class LaunchpadTestCase(TestCaseWithFactory): |
304 | 101 | def setUp(self): | 101 | def setUp(self): |
305 | 102 | super(LaunchpadTestCase, self).setUp() | 102 | super(LaunchpadTestCase, self).setUp() |
306 | 103 | self._request = HttpRequest() | ||
307 | 104 | self._request.user = self.factory.makeUser() | ||
308 | 105 | self.binary = Mock() | ||
309 | 106 | self.mock_lp = Mock() | ||
310 | 107 | archive = self.mock_lp.load.return_value | ||
311 | 108 | archive.getPublishedBinaries.return_value = [self.binary] | ||
312 | 109 | self.client.login(username=self._request.user.username, password='test') | ||
313 | 110 | # Force web_services to recreate the the Launchpad service root | 103 | # Force web_services to recreate the the Launchpad service root |
314 | 111 | WebServices._launchpad_service = None | 104 | WebServices._launchpad_service = None |
315 | 112 | 105 | ||
331 | 113 | @patch('reviewsapp.utilities.Launchpad') | 106 | def _make_mock_launchpad(self, binary_name='mypkg'): |
332 | 114 | def test_valid_package(self, mock_launchpad): | 107 | mock_ubuntu = Mock() |
333 | 115 | # A valid package in a distroseries can be found. | 108 | mock_ubuntu.self_link = 'http://lpapi/ubuntu' |
334 | 116 | mock_launchpad.login_anonymously.return_value = self.mock_lp | 109 | |
335 | 117 | self.binary.binary_package_name = 'apt' | 110 | mock_launchpad = Mock() |
336 | 118 | ws = WebServices() | 111 | mock_launchpad.distributions = dict(ubuntu=mock_ubuntu) |
337 | 119 | 112 | app_review_board = Mock() | |
338 | 120 | self.assertTrue(ws.lp_verify_packagename_in_distro("apt", "lucid", | 113 | mock_launchpad.people = {'app-review-board': app_review_board} |
339 | 121 | 'i386')) | 114 | |
340 | 122 | 115 | mock_archive = Mock() | |
341 | 123 | @patch('reviewsapp.utilities.Launchpad') | 116 | mock_ubuntu.getArchive.return_value = mock_archive |
342 | 124 | def test_invalid_distro(self, mock_launchpad): | 117 | app_review_board.getPPAByName.return_value = mock_archive |
343 | 125 | # Invalid distroseries (assuming we never call a release 'xxx') cannot | 118 | |
344 | 126 | # be found. | 119 | mock_binary = Mock() |
345 | 127 | mock_launchpad.login_anonymously.return_value = self.mock_lp | 120 | mock_binary.binary_package_name = binary_name |
346 | 121 | mock_archive.getPublishedBinaries.return_value = [mock_binary] | ||
347 | 122 | return (mock_launchpad, mock_ubuntu, mock_archive) | ||
348 | 123 | |||
349 | 124 | def _verify_package(self, mock_lp_api, package_name, | ||
350 | 125 | distro, series, arch_tag): | ||
351 | 126 | with patch('reviewsapp.utilities.Launchpad') as mock_launchpad: | ||
352 | 127 | mock_launchpad.login_anonymously.return_value = mock_lp_api | ||
353 | 128 | return WebServices().lp_verify_packagename_in_repository( | ||
354 | 129 | package_name, distro, series, arch_tag) | ||
355 | 130 | |||
356 | 131 | def test_package_in_ubuntu(self): | ||
357 | 132 | lp_api, ubuntu, archive = self._make_mock_launchpad() | ||
358 | 133 | |||
359 | 134 | result = self._verify_package(lp_api, 'mypkg', 'ubuntu', 'lucid', | ||
360 | 135 | 'i386') | ||
361 | 136 | |||
362 | 137 | ubuntu.getArchive.assert_called_with(name='primary') | ||
363 | 138 | archive.getPublishedBinaries.assert_called_with( | ||
364 | 139 | binary_name='mypkg', | ||
365 | 140 | distro_arch_series='http://lpapi/ubuntu/lucid/i386', | ||
366 | 141 | status='Published', exact_match=True) | ||
367 | 142 | self.assertTrue(result) | ||
368 | 143 | |||
369 | 144 | def test_package_not_in_ubuntu(self): | ||
370 | 145 | lp_api, ubuntu, archive = self._make_mock_launchpad( | ||
371 | 146 | binary_name='other_pkg') | ||
372 | 147 | |||
373 | 148 | result = self._verify_package(lp_api, 'mypkg', 'ubuntu', 'lucid', | ||
374 | 149 | 'i386') | ||
375 | 150 | |||
376 | 151 | ubuntu.getArchive.assert_called_with(name='primary') | ||
377 | 152 | archive.getPublishedBinaries.assert_called_with( | ||
378 | 153 | binary_name='mypkg', | ||
379 | 154 | distro_arch_series='http://lpapi/ubuntu/lucid/i386', | ||
380 | 155 | status='Published', exact_match=True) | ||
381 | 156 | self.assertFalse(result) | ||
382 | 157 | |||
383 | 158 | def test_package_in_partner(self): | ||
384 | 159 | lp_api, ubuntu, archive = self._make_mock_launchpad() | ||
385 | 160 | |||
386 | 161 | result = self._verify_package(lp_api, 'mypkg', 'canonical', 'lucid', | ||
387 | 162 | 'i386') | ||
388 | 163 | |||
389 | 164 | ubuntu.getArchive.assert_called_with(name='partner') | ||
390 | 165 | archive.getPublishedBinaries.assert_called_with( | ||
391 | 166 | binary_name='mypkg', | ||
392 | 167 | distro_arch_series='http://lpapi/ubuntu/lucid/i386', | ||
393 | 168 | status='Published', exact_match=True) | ||
394 | 169 | self.assertTrue(result) | ||
395 | 170 | |||
396 | 171 | def test_package_in_extras(self): | ||
397 | 172 | lp_api, ubuntu, archive = self._make_mock_launchpad() | ||
398 | 173 | |||
399 | 174 | result = self._verify_package(lp_api, 'mypkg', | ||
400 | 175 | 'lp-ppa-app-review-board', 'lucid', 'i386') | ||
401 | 176 | |||
402 | 177 | app_review_board = lp_api.people['app-review-board'] | ||
403 | 178 | app_review_board.getPPAByName.assert_called_with(name='ppa') | ||
404 | 179 | archive.getPublishedBinaries.assert_called_with( | ||
405 | 180 | binary_name='mypkg', | ||
406 | 181 | distro_arch_series='http://lpapi/ubuntu/lucid/i386', | ||
407 | 182 | status='Published', exact_match=True) | ||
408 | 183 | self.assertTrue(result) | ||
409 | 184 | |||
410 | 185 | def test_commercial_package(self): | ||
411 | 186 | lp_api, ubuntu, archive = self._make_mock_launchpad() | ||
412 | 187 | |||
413 | 188 | with patch_settings(COMMERCIAL_ORIGINS=['lp-ppa-commercial-one']): | ||
414 | 189 | result = self._verify_package(lp_api, 'anything', | ||
415 | 190 | 'lp-ppa-commercial-one', 'anything', 'anything') | ||
416 | 191 | |||
417 | 192 | # XXX The result should not always be true... we need a way to | ||
418 | 193 | # know the content of commercial ppas. | ||
419 | 194 | self.assertTrue(result) | ||
420 | 195 | |||
421 | 196 | def test_unknown_origin(self): | ||
422 | 197 | lp_api, ubuntu, archive = self._make_mock_launchpad() | ||
423 | 198 | |||
424 | 199 | result = self._verify_package(lp_api, 'mypkg', | ||
425 | 200 | 'lp-ppa-unknown', 'lucid', 'i386') | ||
426 | 201 | |||
427 | 202 | # Currently we only support primary, partner, extras and the | ||
428 | 203 | # specified commercial origins. | ||
429 | 204 | self.assertEqual(0, archive.getPublishedBinaries.call_count) | ||
430 | 205 | self.assertFalse(result) | ||
431 | 206 | |||
432 | 207 | def test_invalid_series_or_arch(self): | ||
433 | 208 | # An invalid series or arch raises a 400 Bad Request. | ||
434 | 209 | lp_api, ubuntu, archive = self._make_mock_launchpad() | ||
435 | 128 | response = Mock() | 210 | response = Mock() |
452 | 129 | response.status = 404 | 211 | response.status = 400 |
453 | 130 | mock_launchpad.load.side_effect = HTTPError(response, 'content') | 212 | archive.getPublishedBinaries.side_effect = HTTPError( |
454 | 131 | ws = WebServices() | 213 | response, 'content') |
455 | 132 | 214 | ||
456 | 133 | self.assertFalse(ws.lp_verify_packagename_in_distro("apt", "xxx", | 215 | result = self._verify_package(lp_api, 'mypkg', 'ubuntu', 'lucid', |
457 | 134 | 'i386')) | 216 | 'i386') |
458 | 135 | 217 | ||
459 | 136 | @patch('reviewsapp.utilities.Launchpad') | 218 | self.assertFalse(result) |
444 | 137 | def test_invalid_package(self, mock_launchpad): | ||
445 | 138 | # Invalid package (assuming no one creates such a package) in a valid | ||
446 | 139 | # distroseries cannot be found. | ||
447 | 140 | mock_launchpad.login_anonymously.return_value = self.mock_lp | ||
448 | 141 | ws = WebServices() | ||
449 | 142 | |||
450 | 143 | self.assertFalse(ws.lp_verify_packagename_in_distro( | ||
451 | 144 | "***xxx***", "lucid", 'i386')) | ||
460 | 145 | 219 | ||
461 | 146 | @patch('reviewsapp.utilities.Launchpad') | 220 | @patch('reviewsapp.utilities.Launchpad') |
462 | 147 | def test_doesnt_sign_in_each_time(self, mock_launchpad): | 221 | def test_doesnt_sign_in_each_time(self, mock_launchpad): |
464 | 148 | mock_launchpad.login_anonymously.return_value = self.mock_lp | 222 | lp_api, ubuntu, archive = self._make_mock_launchpad() |
465 | 223 | mock_launchpad.login_anonymously.return_value = lp_api | ||
466 | 149 | ws = WebServices() | 224 | ws = WebServices() |
468 | 150 | ws.lp_verify_packagename_in_distro("foobar", "lucid", 'i386') | 225 | ws.lp_verify_packagename_in_repository("foobar", 'ubuntu', |
469 | 226 | "lucid", 'i386') | ||
470 | 151 | self.assertTrue(mock_launchpad.login_anonymously.called) | 227 | self.assertTrue(mock_launchpad.login_anonymously.called) |
471 | 152 | mock_launchpad.login_anonymously.reset_mock() | 228 | mock_launchpad.login_anonymously.reset_mock() |
472 | 153 | 229 | ||
474 | 154 | ws.lp_verify_packagename_in_distro("foobar", "lucid", 'i386') | 230 | ws.lp_verify_packagename_in_repository("foobar", 'ubuntu', |
475 | 231 | "lucid", 'i386') | ||
476 | 155 | 232 | ||
477 | 156 | self.assertFalse(mock_launchpad.login_anonymously.called) | 233 | self.assertFalse(mock_launchpad.login_anonymously.called) |
478 | 157 | 234 | ||
479 | @@ -163,8 +240,8 @@ | |||
480 | 163 | del os.environ['HOME'] | 240 | del os.environ['HOME'] |
481 | 164 | ws = WebServices() | 241 | ws = WebServices() |
482 | 165 | # SyntaxError is raised when we fail to parse our dummy wadl | 242 | # SyntaxError is raised when we fail to parse our dummy wadl |
485 | 166 | self.assertRaises(SyntaxError, ws.lp_verify_packagename_in_distro, | 243 | self.assertRaises(SyntaxError, ws.lp_verify_packagename_in_repository, |
486 | 167 | "foobar", "lucid", 'i386') | 244 | "foobar", "ubuntu", "lucid", 'i386') |
487 | 168 | 245 | ||
488 | 169 | 246 | ||
489 | 170 | class InvalidatePaginatedReviewsTestCase(TestCaseWithFactory): | 247 | class InvalidatePaginatedReviewsTestCase(TestCaseWithFactory): |
490 | 171 | 248 | ||
491 | === modified file 'src/reviewsapp/utilities.py' | |||
492 | --- src/reviewsapp/utilities.py 2011-03-10 11:02:01 +0000 | |||
493 | +++ src/reviewsapp/utilities.py 2011-03-15 09:36:33 +0000 | |||
494 | @@ -157,33 +157,62 @@ | |||
495 | 157 | result.update(api.accounts.me()) | 157 | result.update(api.accounts.me()) |
496 | 158 | return result | 158 | return result |
497 | 159 | 159 | ||
500 | 160 | def lp_verify_packagename_in_distro(self, pkgname, distroseries, | 160 | def lp_verify_packagename_in_repository(self, pkgname, origin, distroseries, |
501 | 161 | arch_tag): | 161 | arch_tag): |
502 | 162 | """Verify that a given package exists in a distroseries. | 162 | """Verify that a given package exists in a distroseries. |
503 | 163 | 163 | ||
504 | 164 | :param pkgname: The name of the package to check. | 164 | :param pkgname: The name of the package to check. |
505 | 165 | :param origin: The repository origin identifier. | ||
506 | 165 | :param distroseries: The name of distribution series. | 166 | :param distroseries: The name of distribution series. |
507 | 166 | :param arch_tag: The machine architecture (i386, amd64...) | 167 | :param arch_tag: The machine architecture (i386, amd64...) |
508 | 167 | :return: Whether the package was found or not. | 168 | :return: Whether the package was found or not. |
509 | 168 | :rtype: bool | 169 | :rtype: bool |
510 | 169 | """ | 170 | """ |
513 | 170 | # FIXME: is there a better way to get the root_uri from LP? | 171 | ubuntu = self.launchpad_service.distributions['ubuntu'] |
514 | 171 | ubuntu_uri = str(self.launchpad_service._root_uri) + "ubuntu/" | 172 | if origin == 'ubuntu': |
515 | 173 | archive = ubuntu.getArchive(name='primary') | ||
516 | 174 | elif origin == 'canonical': | ||
517 | 175 | archive = ubuntu.getArchive(name='partner') | ||
518 | 176 | elif origin =='lp-ppa-app-review-board': | ||
519 | 177 | app_review_board = self.launchpad_service.people[ | ||
520 | 178 | 'app-review-board'] | ||
521 | 179 | archive = app_review_board.getPPAByName(name='ppa') | ||
522 | 180 | elif origin in settings.COMMERCIAL_ORIGINS: | ||
523 | 181 | # XXX 2011-03-11 michaeln bug=733170 PPA origins are | ||
524 | 182 | # currently ambiguous. | ||
525 | 183 | # We should check this in a way that doesn't require | ||
526 | 184 | # settings, either pulling the data from SCA, or request | ||
527 | 185 | # that LP updates IArchive to allow public access to | ||
528 | 186 | # getPublishedBinaries() for commercial PPAs (but not | ||
529 | 187 | # private-only). | ||
530 | 188 | # XXX - chat with achuni about options - dict in settings? | ||
531 | 189 | # Perhaps for now just a non-configglue setting with a | ||
532 | 190 | # complex dict: | ||
533 | 191 | # 'lp-ppa-commercial-ppa-uploaders-fluendo-dvd': { | ||
534 | 192 | # 'package_name': 'fluendo-dvd', | ||
535 | 193 | # 'releases': { | ||
536 | 194 | # 'natty': ['i386', 'amd64'], | ||
537 | 195 | # ... | ||
538 | 196 | # And then we can make this available on SCA as json/api. | ||
539 | 197 | return True | ||
540 | 198 | else: | ||
541 | 199 | return False | ||
542 | 200 | |||
543 | 201 | # We now have the archive, check for the distroseries and the | ||
544 | 202 | # package. | ||
545 | 203 | distro_arch_series = ubuntu.self_link + '/{0}/{1}'.format( | ||
546 | 204 | distroseries, arch_tag) | ||
547 | 172 | try: | 205 | try: |
551 | 173 | release = self.launchpad_service.load(ubuntu_uri + distroseries) | 206 | binaries = archive.getPublishedBinaries( |
552 | 174 | distro_arch = self.launchpad_service.load( | 207 | binary_name=pkgname, distro_arch_series=distro_arch_series, |
553 | 175 | ubuntu_uri + '{0}/{1}'.format(distroseries, arch_tag)) | 208 | status='Published', exact_match=True) |
554 | 176 | except HTTPError as error: | 209 | except HTTPError as error: |
559 | 177 | if error.response.status == 404: | 210 | if error.response.status == 400: |
560 | 178 | # Either the release or distro_arch does not exist. | 211 | # A bad request is raised if the distro arch series we |
561 | 179 | # Obviously, this does not verify! Any other status is | 212 | # indicate does not exist. Obviously, this does not |
562 | 180 | # unexpected. | 213 | # verify! Any other status is unexpected. |
563 | 181 | return False | 214 | return False |
564 | 182 | raise | 215 | raise |
565 | 183 | archive = self.launchpad_service.load(ubuntu_uri + '+archive/primary') | ||
566 | 184 | binaries = archive.getPublishedBinaries( | ||
567 | 185 | binary_name=pkgname, distro_arch_series=distro_arch, | ||
568 | 186 | exact_match=True) | ||
569 | 187 | for binary in binaries: | 216 | for binary in binaries: |
570 | 188 | if binary.binary_package_name == pkgname: | 217 | if binary.binary_package_name == pkgname: |
571 | 189 | return True | 218 | return True |
Michael - great works as always. Approved but what would you think of adding a test that is the negative test for "test_create_ repository_ and_software_ item"? As it would be good to also test that when lp returns False that the item and repo are not created.