Merge lp:~sinzui/launchpad/package-link-validation-1 into lp:launchpad/db-devel
- package-link-validation-1
- Merge into db-devel
Proposed by
Curtis Hovey
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~sinzui/launchpad/package-link-validation-1 |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
394 lines 6 files modified
lib/lp/registry/browser/packaging.py (+35/-7) lib/lp/registry/browser/tests/productseries-views.txt (+52/-15) lib/lp/registry/doc/sourcepackage.txt (+0/-15) lib/lp/registry/interfaces/packaging.py (+12/-4) lib/lp/registry/model/packaging.py (+17/-7) lib/lp/registry/tests/test_packaging.py (+92/-9) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/package-link-validation-1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | code | Approve | |
Review via email: mp+13578@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote : | # |
Revision history for this message
Eleanor Berger (intellectronica) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/registry/browser/packaging.py' | |||
2 | --- lib/lp/registry/browser/packaging.py 2009-10-16 15:00:55 +0000 | |||
3 | +++ lib/lp/registry/browser/packaging.py 2009-10-19 21:23:14 +0000 | |||
4 | @@ -14,6 +14,7 @@ | |||
5 | 14 | IPackaging, IPackagingUtil) | 14 | IPackaging, IPackagingUtil) |
6 | 15 | from canonical.launchpad.webapp import canonical_url | 15 | from canonical.launchpad.webapp import canonical_url |
7 | 16 | from canonical.launchpad.webapp.launchpadform import action, LaunchpadFormView | 16 | from canonical.launchpad.webapp.launchpadform import action, LaunchpadFormView |
8 | 17 | from canonical.launchpad.webapp.menu import structured | ||
9 | 17 | 18 | ||
10 | 18 | 19 | ||
11 | 19 | class PackagingAddView(LaunchpadFormView): | 20 | class PackagingAddView(LaunchpadFormView): |
12 | @@ -27,17 +28,45 @@ | |||
13 | 27 | 28 | ||
14 | 28 | page_title = label | 29 | page_title = label |
15 | 29 | 30 | ||
16 | 31 | @property | ||
17 | 32 | def next_url(self): | ||
18 | 33 | """See `LaunchpadFormView`.""" | ||
19 | 34 | return canonical_url(self.context) | ||
20 | 35 | |||
21 | 36 | cancel_url = next_url | ||
22 | 37 | |||
23 | 30 | def validate(self, data): | 38 | def validate(self, data): |
24 | 31 | productseries = self.context | 39 | productseries = self.context |
26 | 32 | sourcepackagename = data['sourcepackagename'] | 40 | sourcepackagename = data.get('sourcepackagename', None) |
27 | 33 | distroseries = data['distroseries'] | 41 | distroseries = data['distroseries'] |
32 | 34 | packaging = data['packaging'] | 42 | if sourcepackagename is None: |
33 | 35 | 43 | message = "You must choose the source package name." | |
34 | 36 | if getUtility(IPackagingUtil).packagingEntryExists( | 44 | self.setFieldError('sourcepackagename', message) |
35 | 37 | productseries, sourcepackagename, distroseries): | 45 | packaging_util = getUtility(IPackagingUtil) |
36 | 46 | if packaging_util.packagingEntryExists( | ||
37 | 47 | productseries=productseries, | ||
38 | 48 | sourcepackagename=sourcepackagename, | ||
39 | 49 | distroseries=distroseries): | ||
40 | 50 | # The series packaging conflicts with itself. | ||
41 | 38 | self.addError(_( | 51 | self.addError(_( |
43 | 39 | "This series is already packaged in %s" % | 52 | "This series is already packaged in %s." % |
44 | 40 | distroseries.displayname)) | 53 | distroseries.displayname)) |
45 | 54 | elif packaging_util.packagingEntryExists( | ||
46 | 55 | sourcepackagename=sourcepackagename, | ||
47 | 56 | distroseries=distroseries): | ||
48 | 57 | # The series package conflicts with another series. | ||
49 | 58 | sourcepackage = distroseries.getSourcePackage( | ||
50 | 59 | sourcepackagename.name) | ||
51 | 60 | self.addError(structured( | ||
52 | 61 | 'The <a href="%s">%s</a> package in %s is already linked to ' | ||
53 | 62 | 'another series.' % | ||
54 | 63 | (canonical_url(sourcepackage), | ||
55 | 64 | sourcepackagename.name, | ||
56 | 65 | distroseries.displayname))) | ||
57 | 66 | else: | ||
58 | 67 | # The distroseries and sourcepackagename are not already linked | ||
59 | 68 | # to this series, or any other series. | ||
60 | 69 | pass | ||
61 | 41 | 70 | ||
62 | 42 | @action('Continue', name='continue') | 71 | @action('Continue', name='continue') |
63 | 43 | def continue_action(self, action, data): | 72 | def continue_action(self, action, data): |
64 | @@ -45,4 +74,3 @@ | |||
65 | 45 | getUtility(IPackagingUtil).createPackaging( | 74 | getUtility(IPackagingUtil).createPackaging( |
66 | 46 | productseries, data['sourcepackagename'], data['distroseries'], | 75 | productseries, data['sourcepackagename'], data['distroseries'], |
67 | 47 | data['packaging'], owner=self.user) | 76 | data['packaging'], owner=self.user) |
68 | 48 | self.next_url = canonical_url(self.context) | ||
69 | 49 | 77 | ||
70 | === modified file 'lib/lp/registry/browser/tests/productseries-views.txt' | |||
71 | --- lib/lp/registry/browser/tests/productseries-views.txt 2009-10-02 13:08:35 +0000 | |||
72 | +++ lib/lp/registry/browser/tests/productseries-views.txt 2009-10-19 21:23:14 +0000 | |||
73 | @@ -358,7 +358,7 @@ | |||
74 | 358 | Linking packages | 358 | Linking packages |
75 | 359 | ---------------- | 359 | ---------------- |
76 | 360 | 360 | ||
78 | 361 | Distrobution sourcepackages can be linked to product series using the | 361 | Distro series sourcepackages can be linked to product series using the |
79 | 362 | +addpackage named view. | 362 | +addpackage named view. |
80 | 363 | 363 | ||
81 | 364 | >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu | 364 | >>> ubuntu = getUtility(ILaunchpadCelebrities).ubuntu |
82 | @@ -385,6 +385,9 @@ | |||
83 | 385 | >>> print view.field_names | 385 | >>> print view.field_names |
84 | 386 | ['distroseries', 'sourcepackagename', 'packaging'] | 386 | ['distroseries', 'sourcepackagename', 'packaging'] |
85 | 387 | 387 | ||
86 | 388 | >>> print view.cancel_url | ||
87 | 389 | http://launchpad.dev/hot/hotter | ||
88 | 390 | |||
89 | 388 | >>> form = { | 391 | >>> form = { |
90 | 389 | ... 'field.distroseries': 'ubuntu/hoary', | 392 | ... 'field.distroseries': 'ubuntu/hoary', |
91 | 390 | ... 'field.sourcepackagename': 'hot', | 393 | ... 'field.sourcepackagename': 'hot', |
92 | @@ -399,17 +402,51 @@ | |||
93 | 399 | ... print package.name | 402 | ... print package.name |
94 | 400 | hot | 403 | hot |
95 | 401 | 404 | ||
110 | 402 | It is an error to link a series to the same package twice. | 405 | >>> transaction.commit() |
111 | 403 | 406 | ||
112 | 404 | >>> form = { | 407 | It is an error to link a series to the same package and distro series twice. |
113 | 405 | ... 'field.distroseries': 'ubuntu/hoary', | 408 | |
114 | 406 | ... 'field.sourcepackagename': 'hot', | 409 | >>> form = { |
115 | 407 | ... 'field.packaging': 'Primary Product', | 410 | ... 'field.distroseries': 'ubuntu/hoary', |
116 | 408 | ... 'field.actions.continue': 'Continue', | 411 | ... 'field.sourcepackagename': 'hot', |
117 | 409 | ... } | 412 | ... 'field.packaging': 'Primary Product', |
118 | 410 | >>> view = create_initialized_view( | 413 | ... 'field.actions.continue': 'Continue', |
119 | 411 | ... productseries, '+addpackage', form=form) | 414 | ... } |
120 | 412 | >>> for error in view.errors: | 415 | >>> view = create_initialized_view( |
121 | 413 | ... print error | 416 | ... productseries, '+addpackage', form=form) |
122 | 414 | This series is already packaged in Hoary | 417 | >>> for error in view.errors: |
123 | 415 | 418 | ... print error | |
124 | 419 | This series is already packaged in Hoary. | ||
125 | 420 | |||
126 | 421 | Once a distro series sourcepackage is linked to a product series, no other | ||
127 | 422 | product series can link to it. | ||
128 | 423 | |||
129 | 424 | >>> other_productseries = factory.makeProductSeries( | ||
130 | 425 | ... product=product, name='hotest') | ||
131 | 426 | >>> form = { | ||
132 | 427 | ... 'field.distroseries': 'ubuntu/hoary', | ||
133 | 428 | ... 'field.sourcepackagename': 'hot', | ||
134 | 429 | ... 'field.packaging': 'Primary Product', | ||
135 | 430 | ... 'field.actions.continue': 'Continue', | ||
136 | 431 | ... } | ||
137 | 432 | >>> view = create_initialized_view( | ||
138 | 433 | ... other_productseries, '+addpackage', form=form) | ||
139 | 434 | >>> for error in view.errors: | ||
140 | 435 | ... print error | ||
141 | 436 | The <a href=".../hoary/+source/hot">hot</a> package in Hoary is already | ||
142 | 437 | linked to another series. | ||
143 | 438 | |||
144 | 439 | A source package name must be provided. | ||
145 | 440 | |||
146 | 441 | >>> form = { | ||
147 | 442 | ... 'field.distroseries': 'ubuntu/hoary', | ||
148 | 443 | ... 'field.sourcepackagename': '', | ||
149 | 444 | ... 'field.packaging': 'Primary Product', | ||
150 | 445 | ... 'field.actions.continue': 'Continue', | ||
151 | 446 | ... } | ||
152 | 447 | >>> view = create_initialized_view( | ||
153 | 448 | ... productseries, '+addpackage', form=form) | ||
154 | 449 | >>> for error in view.errors: | ||
155 | 450 | ... print error | ||
156 | 451 | ('sourcepackagename', u'Source Package Name', RequiredMissing()) | ||
157 | 452 | You must choose the source package name. | ||
158 | 416 | 453 | ||
159 | === modified file 'lib/lp/registry/doc/sourcepackage.txt' | |||
160 | --- lib/lp/registry/doc/sourcepackage.txt 2009-10-16 20:33:56 +0000 | |||
161 | +++ lib/lp/registry/doc/sourcepackage.txt 2009-10-19 21:23:14 +0000 | |||
162 | @@ -453,21 +453,6 @@ | |||
163 | 453 | >>> print warty_firefox.direct_packaging.productseries.title | 453 | >>> print warty_firefox.direct_packaging.productseries.title |
164 | 454 | Mozilla Firefox trunk series | 454 | Mozilla Firefox trunk series |
165 | 455 | 455 | ||
166 | 456 | If multiple product series link to a sourcepackage, direct_packaging | ||
167 | 457 | returns the last packaging added: | ||
168 | 458 | |||
169 | 459 | >>> from lp.registry.interfaces.product import IProductSet | ||
170 | 460 | >>> firefox_product = getUtility(IProductSet).getByName('firefox') | ||
171 | 461 | >>> firefox_trunk = firefox_product.getSeries('trunk') | ||
172 | 462 | >>> PackagingUtil().createPackaging( | ||
173 | 463 | ... sourcepackagename=firefox, | ||
174 | 464 | ... distroseries=hoary, | ||
175 | 465 | ... productseries=firefox_trunk, | ||
176 | 466 | ... packaging=PackagingType.PRIME, | ||
177 | 467 | ... owner=foobar) | ||
178 | 468 | >>> print hoary_firefox_one.direct_packaging.productseries.title | ||
179 | 469 | Mozilla Firefox trunk series | ||
180 | 470 | |||
181 | 471 | 456 | ||
182 | 472 | Release History | 457 | Release History |
183 | 473 | --------------- | 458 | --------------- |
184 | 474 | 459 | ||
185 | === modified file 'lib/lp/registry/interfaces/packaging.py' | |||
186 | --- lib/lp/registry/interfaces/packaging.py 2009-10-16 15:00:55 +0000 | |||
187 | +++ lib/lp/registry/interfaces/packaging.py 2009-10-19 21:23:14 +0000 | |||
188 | @@ -73,7 +73,10 @@ | |||
189 | 73 | vocabulary='DistroSeries') | 73 | vocabulary='DistroSeries') |
190 | 74 | 74 | ||
191 | 75 | packaging = Choice( | 75 | packaging = Choice( |
193 | 76 | title=_('Packaging'), required=True, vocabulary=PackagingType) | 76 | title=_('Packaging'), required=True, vocabulary=PackagingType, |
194 | 77 | description=_( | ||
195 | 78 | "Is the project the primary content of the source package, " | ||
196 | 79 | "or does the source package include the work of other projects?")) | ||
197 | 77 | 80 | ||
198 | 78 | datecreated = Datetime( | 81 | datecreated = Datetime( |
199 | 79 | title=_('Date Created'), required=True, readonly=True) | 82 | title=_('Date Created'), required=True, readonly=True) |
200 | @@ -92,6 +95,11 @@ | |||
201 | 92 | def deletePackaging(productseries, sourcepackagename, distroseries): | 95 | def deletePackaging(productseries, sourcepackagename, distroseries): |
202 | 93 | """Delete a packaging entry.""" | 96 | """Delete a packaging entry.""" |
203 | 94 | 97 | ||
207 | 95 | def packagingEntryExists(productseries, sourcepackagename, | 98 | def packagingEntryExists(sourcepackagename, distroseries, |
208 | 96 | distroseries): | 99 | productseries=None): |
209 | 97 | """Does this packaging entry already exists?""" | 100 | """Does this packaging entry already exists? |
210 | 101 | |||
211 | 102 | A sourcepackagename is unique to a distroseries. Passing the | ||
212 | 103 | productseries argument verifies that the packaging entry exists and | ||
213 | 104 | that it is for the productseries | ||
214 | 105 | """ | ||
215 | 98 | 106 | ||
216 | === modified file 'lib/lp/registry/model/packaging.py' | |||
217 | --- lib/lp/registry/model/packaging.py 2009-10-16 15:00:55 +0000 | |||
218 | +++ lib/lp/registry/model/packaging.py 2009-10-19 21:23:14 +0000 | |||
219 | @@ -41,7 +41,8 @@ | |||
220 | 41 | datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW) | 41 | datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW) |
221 | 42 | owner = ForeignKey( | 42 | owner = ForeignKey( |
222 | 43 | dbName='owner', foreignKey='Person', | 43 | dbName='owner', foreignKey='Person', |
224 | 44 | storm_validator=validate_public_person, notNull=False, default=DEFAULT) | 44 | storm_validator=validate_public_person, |
225 | 45 | notNull=False, default=DEFAULT) | ||
226 | 45 | 46 | ||
227 | 46 | @property | 47 | @property |
228 | 47 | def sourcepackage(self): | 48 | def sourcepackage(self): |
229 | @@ -56,7 +57,15 @@ | |||
230 | 56 | 57 | ||
231 | 57 | def createPackaging(self, productseries, sourcepackagename, | 58 | def createPackaging(self, productseries, sourcepackagename, |
232 | 58 | distroseries, packaging, owner): | 59 | distroseries, packaging, owner): |
234 | 59 | """See `IPackaging`.""" | 60 | """See `IPackaging`. |
235 | 61 | |||
236 | 62 | Raises an assertion error if there is already packaging for | ||
237 | 63 | the sourcepackagename in the distroseries. | ||
238 | 64 | """ | ||
239 | 65 | if self.packagingEntryExists(sourcepackagename, distroseries): | ||
240 | 66 | raise AssertionError( | ||
241 | 67 | "A packaging entry for %s in %s already exists." % | ||
242 | 68 | (sourcepackagename.name, distroseries.name)) | ||
243 | 60 | Packaging(productseries=productseries, | 69 | Packaging(productseries=productseries, |
244 | 61 | sourcepackagename=sourcepackagename, | 70 | sourcepackagename=sourcepackagename, |
245 | 62 | distroseries=distroseries, | 71 | distroseries=distroseries, |
246 | @@ -77,14 +86,15 @@ | |||
247 | 77 | distroseries.parent.name, distroseries.name)) | 86 | distroseries.parent.name, distroseries.name)) |
248 | 78 | packaging.destroySelf() | 87 | packaging.destroySelf() |
249 | 79 | 88 | ||
252 | 80 | def packagingEntryExists(self, productseries, sourcepackagename, | 89 | def packagingEntryExists(self, sourcepackagename, distroseries, |
253 | 81 | distroseries): | 90 | productseries=None): |
254 | 82 | """See `IPackaging`.""" | 91 | """See `IPackaging`.""" |
257 | 83 | result = Packaging.selectOneBy( | 92 | criteria = dict( |
256 | 84 | productseries=productseries, | ||
258 | 85 | sourcepackagename=sourcepackagename, | 93 | sourcepackagename=sourcepackagename, |
259 | 86 | distroseries=distroseries) | 94 | distroseries=distroseries) |
260 | 95 | if productseries is not None: | ||
261 | 96 | criteria['productseries'] = productseries | ||
262 | 97 | result = Packaging.selectOneBy(**criteria) | ||
263 | 87 | if result is None: | 98 | if result is None: |
264 | 88 | return False | 99 | return False |
265 | 89 | return True | 100 | return True |
266 | 90 | |||
267 | 91 | 101 | ||
268 | === modified file 'lib/lp/registry/tests/test_packaging.py' | |||
269 | --- lib/lp/registry/tests/test_packaging.py 2009-10-16 15:00:55 +0000 | |||
270 | +++ lib/lp/registry/tests/test_packaging.py 2009-10-19 21:23:14 +0000 | |||
271 | @@ -5,26 +5,110 @@ | |||
272 | 5 | 5 | ||
273 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
274 | 7 | 7 | ||
276 | 8 | from unittest import TestCase, TestLoader | 8 | from unittest import TestLoader |
277 | 9 | 9 | ||
278 | 10 | from zope.component import getUtility | 10 | from zope.component import getUtility |
279 | 11 | 11 | ||
280 | 12 | from lp.registry.interfaces.distribution import IDistributionSet | 12 | from lp.registry.interfaces.distribution import IDistributionSet |
282 | 13 | from lp.registry.interfaces.packaging import IPackagingUtil | 13 | from lp.registry.interfaces.packaging import IPackagingUtil, PackagingType |
283 | 14 | from lp.registry.interfaces.product import IProductSet | 14 | from lp.registry.interfaces.product import IProductSet |
284 | 15 | from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet | 15 | from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet |
290 | 16 | from canonical.launchpad.ftests import login | 16 | from lp.testing import login, TestCaseWithFactory |
291 | 17 | from canonical.testing import LaunchpadFunctionalLayer | 17 | |
292 | 18 | 18 | from canonical.testing import DatabaseFunctionalLayer | |
293 | 19 | 19 | ||
294 | 20 | class TestDeletePackaging(TestCase): | 20 | |
295 | 21 | class PackagingUtilMixin: | ||
296 | 22 | """Common items for testing IPackagingUtil.""" | ||
297 | 23 | |||
298 | 24 | layer = DatabaseFunctionalLayer | ||
299 | 25 | |||
300 | 26 | def setUp(self): | ||
301 | 27 | TestCaseWithFactory.setUp(self) | ||
302 | 28 | self.packaging_util = getUtility(IPackagingUtil) | ||
303 | 29 | self.sourcepackagename = self.factory.makeSourcePackageName('sparkle') | ||
304 | 30 | self.distroseries = self.factory.makeDistroRelease(name='dazzle') | ||
305 | 31 | self.productseries = self.factory.makeProductSeries(name='glitter') | ||
306 | 32 | self.owner = self.productseries.product.owner | ||
307 | 33 | |||
308 | 34 | |||
309 | 35 | class TestCreatePackaging(PackagingUtilMixin, TestCaseWithFactory): | ||
310 | 36 | """Test PackagingUtil.packagingEntryExists.""" | ||
311 | 37 | |||
312 | 38 | def test_CreatePackaging_unique(self): | ||
313 | 39 | """Packaging is unique distroseries+sourcepackagename.""" | ||
314 | 40 | self.packaging_util.createPackaging( | ||
315 | 41 | self.productseries, self.sourcepackagename, self.distroseries, | ||
316 | 42 | PackagingType.PRIME, owner=self.owner) | ||
317 | 43 | sourcepackage = self.distroseries.getSourcePackage('sparkle') | ||
318 | 44 | packaging = sourcepackage.direct_packaging | ||
319 | 45 | self.assertEqual(packaging.distroseries, self.distroseries) | ||
320 | 46 | self.assertEqual(packaging.sourcepackagename, self.sourcepackagename) | ||
321 | 47 | self.assertEqual(packaging.productseries, self.productseries) | ||
322 | 48 | |||
323 | 49 | def test_CreatePackaging_assert_unique(self): | ||
324 | 50 | """Assert unique distroseries+sourcepackagename.""" | ||
325 | 51 | self.packaging_util.createPackaging( | ||
326 | 52 | self.productseries, self.sourcepackagename, self.distroseries, | ||
327 | 53 | PackagingType.PRIME, owner=self.owner) | ||
328 | 54 | self.assertRaises( | ||
329 | 55 | AssertionError, self.packaging_util.createPackaging, | ||
330 | 56 | self.productseries, self.sourcepackagename, self.distroseries, | ||
331 | 57 | PackagingType.PRIME, self.owner) | ||
332 | 58 | |||
333 | 59 | |||
334 | 60 | class TestPackagingEntryExists(PackagingUtilMixin, TestCaseWithFactory): | ||
335 | 61 | """Test PackagingUtil.packagingEntryExists.""" | ||
336 | 62 | |||
337 | 63 | def setUpPackaging(self): | ||
338 | 64 | self.packaging_util.createPackaging( | ||
339 | 65 | self.productseries, self.sourcepackagename, self.distroseries, | ||
340 | 66 | PackagingType.PRIME, owner=self.owner) | ||
341 | 67 | |||
342 | 68 | def test_packagingEntryExists_false(self): | ||
343 | 69 | """Verify that non-existent entries are false.""" | ||
344 | 70 | self.assertFalse( | ||
345 | 71 | self.packaging_util.packagingEntryExists( | ||
346 | 72 | sourcepackagename=self.sourcepackagename, | ||
347 | 73 | distroseries=self.distroseries)) | ||
348 | 74 | |||
349 | 75 | def test_packagingEntryExists_unique(self): | ||
350 | 76 | """Packaging entries are unique to distroseries+sourcepackagename.""" | ||
351 | 77 | self.setUpPackaging() | ||
352 | 78 | self.assertTrue( | ||
353 | 79 | self.packaging_util.packagingEntryExists( | ||
354 | 80 | sourcepackagename=self.sourcepackagename, | ||
355 | 81 | distroseries=self.distroseries)) | ||
356 | 82 | other_distroseries = self.factory.makeDistroRelease(name='shimmer') | ||
357 | 83 | self.assertFalse( | ||
358 | 84 | self.packaging_util.packagingEntryExists( | ||
359 | 85 | sourcepackagename=self.sourcepackagename, | ||
360 | 86 | distroseries=other_distroseries)) | ||
361 | 87 | |||
362 | 88 | def test_packagingEntryExists_specific(self): | ||
363 | 89 | """Packaging entries are also specifc to both kinds of series.""" | ||
364 | 90 | self.setUpPackaging() | ||
365 | 91 | self.assertTrue( | ||
366 | 92 | self.packaging_util.packagingEntryExists( | ||
367 | 93 | sourcepackagename=self.sourcepackagename, | ||
368 | 94 | distroseries=self.distroseries, | ||
369 | 95 | productseries=self.productseries)) | ||
370 | 96 | other_productseries = self.factory.makeProductSeries(name='flash') | ||
371 | 97 | self.assertFalse( | ||
372 | 98 | self.packaging_util.packagingEntryExists( | ||
373 | 99 | sourcepackagename=self.sourcepackagename, | ||
374 | 100 | distroseries=self.distroseries, | ||
375 | 101 | productseries=other_productseries)) | ||
376 | 102 | |||
377 | 103 | |||
378 | 104 | class TestDeletePackaging(TestCaseWithFactory): | ||
379 | 21 | """Test PackagingUtil.deletePackaging. | 105 | """Test PackagingUtil.deletePackaging. |
380 | 22 | 106 | ||
381 | 23 | The essential functionality: deleting a Packaging record, is already | 107 | The essential functionality: deleting a Packaging record, is already |
382 | 24 | covered in doctests. | 108 | covered in doctests. |
383 | 25 | """ | 109 | """ |
384 | 26 | 110 | ||
386 | 27 | layer = LaunchpadFunctionalLayer | 111 | layer = DatabaseFunctionalLayer |
387 | 28 | 112 | ||
388 | 29 | def test_deleteNonExistentPackaging(self): | 113 | def test_deleteNonExistentPackaging(self): |
389 | 30 | """Deleting a non-existent Packaging fails. | 114 | """Deleting a non-existent Packaging fails. |
390 | @@ -77,4 +161,3 @@ | |||
391 | 77 | 161 | ||
392 | 78 | def test_suite(): | 162 | def test_suite(): |
393 | 79 | return TestLoader().loadTestsFromName(__name__) | 163 | return TestLoader().loadTestsFromName(__name__) |
394 | 80 |
This is my second branch to ensure valid upstream package links. There
are many oopses relating to the creation and efforts to fix invalid packages.
The root cause is a bad DB constraint and two views that do not do the
required sanity checks: +addpackage and +ubuntupkg
This branch fixes +addpackage to make sane packages.
(+ubuntupkg need major refactoring and is the scope od a single branch.)
lp:~sinzui/launchpad/package-link-validation-1 /bugs.launchpad .net/bugs/ 344376 /bugs.launchpad .net/bugs/ 89392 /bugs.launchpad .net/bugs/ 196774 *(productseries |packaging) ' implementation: flacoste
Diff size: 372
Launchpad bug: https:/
https:/
https:/
Test command: ./bin/test -vv -t 'lp.reg.
Pre-
Target release: 3.1.10
== Fixing upstream packaging links ==
Bug 196774 [It shouldn't be possible to link multiple productseries to a
sourcepackage in a given distroseries]
+addpackage and +ubuntupkg do not verify that the SP is unlinked for the
distroseries.
Bug 344376 [+addpackage oopses if the "Source Package Name" is left blank]
The most common reason users leave the field blank is that they are
trying to remove an invalid packaging link.
Bug 89392 [+addpackage form contains nonsensical "Packaging" field]
The final menu is labeled "Packaging" and contains options "Primary
Product" and "SourcePackage Includes Product". This makes absolutely no
sense to a product author.
== Rules ==
Bug 196774 [It shouldn't be possible to link multiple productseries to a
sourcepackage in a given distroseries]
Verify that the SP for the distroseries is available for linking.
If not, provide a link to the current package so that the user can
investigate it.
Bug 344376 [+addpackage oopses if the "Source Package Name" is left blank]
Use the validate() method and report a form error if the choice is not
sane.
Bug 89392 [+addpackage form contains nonsensical "Packaging" field]
rewrite the form instructions
== QA ==
On staging
* Visit a productseries
* Choose (+) Add packaging
* Verify the packaging field explains primary from source
* Submit the form without a source package name
* Verify the form error message explains that the source package must
be provided
* Submit the form with Ubuntu Karmic, 'gedit', primary
* Verify the form error message explains that the gedit is already
packaged in Ubuntu karmic. Follow the link
* Verify the “gedit” source package in Karmic page displays.
== Lint ==
Linting changed files: registry/ browser/ packaging. py registry/ browser/ tests/productse ries-views. txt registry/ interfaces/ packaging. py registry/ model/packaging .py registry/ tests/test_ packaging. py
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
== Test ==
* lib/lp/ registry/ browser/ tests/productse ries-views. txt registry/ tests/test_ packaging. py
* lib/lp/
== Implementation ==
* lib/lp/ registry/ browser/ packaging. py registry/ interfaces/ packaging. py registry/ model/packaging .py
* lib/lp/
* lib/lp/