Merge lp:~abentley/launchpad/push-creates-package into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 13690
Proposed branch: lp:~abentley/launchpad/push-creates-package
Merge into: lp:launchpad
Diff against target: 543 lines (+162/-40)
15 files modified
lib/canonical/launchpad/xmlrpc/configure.zcml (+3/-0)
lib/canonical/launchpad/xmlrpc/faults.py (+11/-0)
lib/lp/code/tests/helpers.py (+14/-0)
lib/lp/code/xmlrpc/codehosting.py (+11/-0)
lib/lp/code/xmlrpc/tests/test_codehosting.py (+25/-5)
lib/lp/codehosting/inmemory.py (+21/-8)
lib/lp/codehosting/tests/test_acceptance.py (+19/-9)
lib/lp/codehosting/vfs/branchfs.py (+2/-5)
lib/lp/codehosting/vfs/tests/test_branchfs.py (+13/-2)
lib/lp/registry/errors.py (+5/-0)
lib/lp/registry/interfaces/person.py (+4/-8)
lib/lp/registry/model/person.py (+1/-1)
lib/lp/registry/model/sourcepackagename.py (+8/-1)
lib/lp/registry/tests/test_person.py (+1/-1)
lib/lp/registry/tests/test_sourcepackagename.py (+24/-0)
To merge this branch: bzr merge lp:~abentley/launchpad/push-creates-package
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+71366@code.launchpad.net

Commit message

Push creates source package names.

Description of the change

= Summary =
Fix bug #386596: pushing to a packaging branch can't create a new package

== Proposed fix ==
Support creating new source package names in createBranch

== Pre-implementation notes ==
None

== Implementation details ==
In the xmlrpc-backed version, catch NoSuchSourcePackageName and handle it by creating the name and doing a recursive call to createBranch.

In the memory-backed version, create the sourcepackagename if not already present.

In both cases, SourcePackageNameSet.new raises InvalidName if valid_name returns False. Accordingly, InvalidName moved from person.py to errors.py.

createBranch translates InvalidName to the new InvalidSourcePackageName fault.

The vfs transport converts InvalidSourcePackageName into PermissionDenied.

== Tests ==
bin/test -t test_createBranch_invalid_package_name -t test_createBranch_missing_sourcepackagename -t test_push_new_branch_of_non_existant_source_package_name -t t test_createBranch_invalid_package_name.

== Demo and Q/A ==

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/canonical/launchpad/xmlrpc/faults.py
  lib/lp/registry/errors.py
  lib/lp/code/xmlrpc/codehosting.py
  lib/lp/code/xmlrpc/tests/test_codehosting.py
  lib/lp/code/tests/helpers.py
  lib/canonical/launchpad/xmlrpc/configure.zcml
  lib/lp/codehosting/vfs/branchfs.py
  lib/lp/registry/model/sourcepackagename.py
  lib/lp/codehosting/inmemory.py
  lib/lp/registry/tests/test_sourcepackagename.py
  lib/lp/codehosting/vfs/tests/test_branchfs.py
  lib/lp/codehosting/tests/test_acceptance.py
  lib/lp/registry/interfaces/person.py
  lib/lp/registry/model/person.py
  lib/lp/registry/tests/test_person.py

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/xmlrpc/configure.zcml'
2--- lib/canonical/launchpad/xmlrpc/configure.zcml 2010-11-08 14:16:17 +0000
3+++ lib/canonical/launchpad/xmlrpc/configure.zcml 2011-08-15 13:18:36 +0000
4@@ -207,4 +207,7 @@
5 <class class="canonical.launchpad.xmlrpc.faults.AccountSuspended">
6 <require like_class="xmlrpclib.Fault" />
7 </class>
8+ <class class="canonical.launchpad.xmlrpc.faults.InvalidSourcePackageName">
9+ <require like_class="xmlrpclib.Fault" />
10+ </class>
11 </configure>
12
13=== modified file 'lib/canonical/launchpad/xmlrpc/faults.py'
14--- lib/canonical/launchpad/xmlrpc/faults.py 2010-12-12 23:09:36 +0000
15+++ lib/canonical/launchpad/xmlrpc/faults.py 2011-08-15 13:18:36 +0000
16@@ -22,6 +22,7 @@
17 'InvalidBranchUniqueName',
18 'InvalidProductIdentifier',
19 'InvalidBranchUrl',
20+ 'InvalidSourcePackageName',
21 'OopsOccurred',
22 'NoBranchWithID',
23 'NoLinkedBranch',
24@@ -476,3 +477,13 @@
25 def __init__(self, server_op, oopsid):
26 LaunchpadFault.__init__(self, server_op=server_op, oopsid=oopsid)
27 self.oopsid = oopsid
28+
29+
30+class InvalidSourcePackageName(LaunchpadFault):
31+
32+ error_code = 390
33+ msg_template = ("%(name)s is not a valid source package name.")
34+
35+ def __init__(self, name):
36+ self.name = name
37+ LaunchpadFault.__init__(self, name=name)
38
39=== modified file 'lib/lp/code/tests/helpers.py'
40--- lib/lp/code/tests/helpers.py 2011-08-03 11:00:11 +0000
41+++ lib/lp/code/tests/helpers.py 2011-08-15 13:18:36 +0000
42@@ -6,6 +6,7 @@
43 __metaclass__ = type
44 __all__ = [
45 'add_revision_to_branch',
46+ 'get_non_existant_source_package_branch_unique_name',
47 'make_erics_fooix_project',
48 'make_linked_package_branch',
49 'make_merge_proposal_without_reviewers',
50@@ -313,3 +314,16 @@
51 for vote in proposal.votes:
52 removeSecurityProxy(vote).destroySelf()
53 return proposal
54+
55+
56+def get_non_existant_source_package_branch_unique_name(owner, factory):
57+ """Return the unique name for a non-existanct source package branch.
58+
59+ Neither the branch nor the source package name will exist.
60+ """
61+ distroseries = factory.makeDistroSeries()
62+ source_package = factory.getUniqueString('source-package')
63+ branch = factory.getUniqueString('branch')
64+ return '~%s/%s/%s/%s/%s' % (
65+ owner, distroseries.distribution.name, distroseries.name,
66+ source_package, branch)
67
68=== modified file 'lib/lp/code/xmlrpc/codehosting.py'
69--- lib/lp/code/xmlrpc/codehosting.py 2011-05-25 20:16:58 +0000
70+++ lib/lp/code/xmlrpc/codehosting.py 2011-08-15 13:18:36 +0000
71@@ -64,6 +64,10 @@
72 LAUNCHPAD_SERVICES,
73 )
74 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
75+from lp.registry.errors import (
76+ InvalidName,
77+ NoSuchSourcePackageName,
78+ )
79 from lp.registry.interfaces.person import (
80 IPersonSet,
81 NoSuchPerson,
82@@ -72,6 +76,7 @@
83 InvalidProductName,
84 NoSuchProduct,
85 )
86+from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
87 from lp.services.scripts.interfaces.scriptactivity import IScriptActivitySet
88 from lp.services.utils import iter_split
89
90@@ -220,6 +225,12 @@
91 except NoSuchProduct, e:
92 return faults.NotFound(
93 "Project '%s' does not exist." % e.name)
94+ except NoSuchSourcePackageName as e:
95+ try:
96+ getUtility(ISourcePackageNameSet).new(e.name)
97+ except InvalidName:
98+ return faults.InvalidSourcePackageName(e.name)
99+ return self.createBranch(login_id, branch_path)
100 except NameLookupFailed, e:
101 return faults.NotFound(str(e))
102 try:
103
104=== modified file 'lib/lp/code/xmlrpc/tests/test_codehosting.py'
105--- lib/lp/code/xmlrpc/tests/test_codehosting.py 2011-08-03 11:00:11 +0000
106+++ lib/lp/code/xmlrpc/tests/test_codehosting.py 2011-08-15 13:18:36 +0000
107@@ -418,19 +418,39 @@
108 message = "No such distribution series: 'ningnangnong'."
109 self.assertEqual(faults.NotFound(message), fault)
110
111+ def test_createBranch_missing_sourcepackagename(self):
112+ # If createBranch is called with the path to a missing source
113+ # package, it will create the source package.
114+ owner = self.factory.makePerson()
115+ distroseries = self.factory.makeDistroSeries()
116+ branch_name = self.factory.getUniqueString()
117+ unique_name = '/~%s/%s/%s/ningnangnong/%s' % (
118+ owner.name, distroseries.distribution.name, distroseries.name,
119+ branch_name)
120+ branch_id = self.codehosting_api.createBranch(
121+ owner.id, escape(unique_name))
122+ login(ANONYMOUS)
123+ branch = self.branch_lookup.get(branch_id)
124+ self.assertEqual(owner, branch.owner)
125+ self.assertEqual(distroseries, branch.distroseries)
126+ self.assertEqual(
127+ 'ningnangnong', branch.sourcepackagename.name)
128+ self.assertEqual(branch_name, branch.name)
129+ self.assertEqual(owner, branch.registrant)
130+ self.assertEqual(BranchType.HOSTED, branch.branch_type)
131+
132 def test_createBranch_invalid_sourcepackagename(self):
133- # If createBranch is called with the path to an invalid source
134- # package, it will return a Fault saying so.
135+ # If createBranch is called with an invalid path, it will fault.
136 owner = self.factory.makePerson()
137 distroseries = self.factory.makeDistroSeries()
138 branch_name = self.factory.getUniqueString()
139- unique_name = '/~%s/%s/%s/ningnangnong/%s' % (
140+ unique_name = '/~%s/%s/%s/ningn%%20angnong/%s' % (
141 owner.name, distroseries.distribution.name, distroseries.name,
142 branch_name)
143 fault = self.codehosting_api.createBranch(
144 owner.id, escape(unique_name))
145- message = "No such source package: 'ningnangnong'."
146- self.assertEqual(faults.NotFound(message), fault)
147+ self.assertEqual(
148+ faults.InvalidSourcePackageName('ningn%20angnong'), fault)
149
150 def test_createBranch_using_branch_alias(self):
151 # Branches can be created using the branch alias and the full unique
152
153=== modified file 'lib/lp/codehosting/inmemory.py'
154--- lib/lp/codehosting/inmemory.py 2011-08-03 11:00:11 +0000
155+++ lib/lp/codehosting/inmemory.py 2011-08-15 13:18:36 +0000
156@@ -25,7 +25,10 @@
157
158 from canonical.database.constants import UTC_NOW
159 from canonical.launchpad.xmlrpc import faults
160-from lp.app.validators import LaunchpadValidationError
161+from lp.app.validators import (
162+ LaunchpadValidationError,
163+ )
164+from lp.app.validators.name import valid_name
165 from lp.code.bzr import (
166 BranchFormat,
167 ControlFormat,
168@@ -51,6 +54,7 @@
169 ProductBranchTarget,
170 )
171 from lp.code.xmlrpc.codehosting import datetime_from_tuple
172+from lp.registry.errors import InvalidName
173 from lp.registry.interfaces.pocket import PackagePublishingPocket
174 from lp.services.utils import iter_split
175 from lp.services.xmlrpc import LaunchpadFault
176@@ -194,6 +198,14 @@
177 self.distroseries._linked_branches[self, pocket] = branch
178
179
180+class SourcePackageNameSet(ObjectSet):
181+
182+ def new(self, name_string):
183+ if not valid_name(name_string):
184+ raise InvalidName(name_string)
185+ return self._add(FakeSourcePackageName(name_string))
186+
187+
188 @adapter(FakeSourcePackage)
189 @implementer(IBranchTarget)
190 def fake_source_package_to_branch_target(fake_package):
191@@ -454,9 +466,7 @@
192 return distroseries
193
194 def makeSourcePackageName(self):
195- sourcepackagename = FakeSourcePackageName(self.getUniqueString())
196- self._sourcepackagename_set._add(sourcepackagename)
197- return sourcepackagename
198+ return self._sourcepackagename_set.new(self.getUniqueString())
199
200 def makeSourcePackage(self, distroseries=None, sourcepackagename=None):
201 if distroseries is None:
202@@ -659,9 +669,12 @@
203 sourcepackagename = self._sourcepackagename_set.getByName(
204 data['sourcepackagename'])
205 if sourcepackagename is None:
206- raise faults.NotFound(
207- "No such source package: '%s'."
208- % (data['sourcepackagename'],))
209+ try:
210+ sourcepackagename = self._sourcepackagename_set.new(
211+ data['sourcepackagename'])
212+ except InvalidName:
213+ raise faults.InvalidSourcePackageName(
214+ data['sourcepackagename'])
215 sourcepackage = self._factory.makeSourcePackage(
216 distroseries, sourcepackagename)
217 else:
218@@ -890,7 +903,7 @@
219 self._product_set = ObjectSet()
220 self._distribution_set = ObjectSet()
221 self._distroseries_set = ObjectSet()
222- self._sourcepackagename_set = ObjectSet()
223+ self._sourcepackagename_set = SourcePackageNameSet()
224 self._factory = FakeObjectFactory(
225 self._branch_set, self._person_set, self._product_set,
226 self._distribution_set, self._distroseries_set,
227
228=== modified file 'lib/lp/codehosting/tests/test_acceptance.py'
229--- lib/lp/codehosting/tests/test_acceptance.py 2011-08-12 14:57:27 +0000
230+++ lib/lp/codehosting/tests/test_acceptance.py 2011-08-15 13:18:36 +0000
231@@ -35,6 +35,9 @@
232 from lp.code.enums import BranchType
233 from lp.code.interfaces.branch import IBranchSet
234 from lp.code.interfaces.branchnamespace import get_branch_namespace
235+from lp.code.tests.helpers import (
236+ get_non_existant_source_package_branch_unique_name,
237+ )
238 from lp.codehosting import (
239 get_bzr_path,
240 get_BZR_PLUGIN_PATH_for_subprocess,
241@@ -56,7 +59,7 @@
242
243
244 class ForkingServerForTests(object):
245- """Map starting/stopping a LPForkingService with setUp() and tearDown()."""
246+ """Map starting/stopping a LPForkingService to setUp() and tearDown()."""
247
248 def __init__(self):
249 self.process = None
250@@ -68,8 +71,8 @@
251 env = os.environ.copy()
252 env['BZR_PLUGIN_PATH'] = BZR_PLUGIN_PATH
253 # TODO: We probably want to use a random disk path for
254- # forking_daemon_socket, but we need to update config so that the
255- # CodeHosting service can find it.
256+ # forking_daemon_socket, but we need to update config so that
257+ # the CodeHosting service can find it.
258 # The main problem is that CodeHostingTac seems to start a tac
259 # server directly from the disk configs, and doesn't use the
260 # in-memory config. So we can't just override the memory
261@@ -83,14 +86,14 @@
262 self.process = process
263 # Wait for it to indicate it is running
264 # The first line should be "Preloading" indicating it is ready
265- preloading_line = process.stderr.readline()
266+ process.stderr.readline()
267 # The next line is the "Listening on socket" line
268- socket_line = process.stderr.readline()
269+ process.stderr.readline()
270 # Now it is ready
271
272 def tearDown(self):
273- # SIGTERM is the graceful exit request, potentially we could wait a bit
274- # and send something stronger?
275+ # SIGTERM is the graceful exit request, potentially we could wait a
276+ # bit and send something stronger?
277 if self.process is not None and self.process.poll() is None:
278 os.kill(self.process.pid, signal.SIGTERM)
279 self.process.wait()
280@@ -102,7 +105,6 @@
281 os.remove(self.socket_path)
282
283
284-
285 class SSHServerLayer(ZopelessAppServerLayer):
286
287 _tac_handler = None
288@@ -611,6 +613,15 @@
289 self.local_branch_path, remote_url,
290 ['Permission denied:', 'Transport operation not possible:'])
291
292+ def test_push_new_branch_of_non_existant_source_package_name(self):
293+ ZopelessAppServerLayer.txn.begin()
294+ unique_name = get_non_existant_source_package_branch_unique_name(
295+ 'testuser', self.factory)
296+ ZopelessAppServerLayer.txn.commit()
297+ remote_url = self.getTransportURL(unique_name)
298+ self.push(self.local_branch_path, remote_url)
299+ self.assertBranchesMatch(self.local_branch_path, remote_url)
300+
301 def test_can_push_loom_branch(self):
302 # We can push and pull a loom branch.
303 self.makeLoomBranchAndTree('loom')
304@@ -696,7 +707,6 @@
305 urllib2.urlopen(web_status_url)
306
307
308-
309 def make_server_tests(base_suite, servers):
310 from lp.codehosting.tests.helpers import (
311 CodeHostingTestProviderAdapter)
312
313=== modified file 'lib/lp/codehosting/vfs/branchfs.py'
314--- lib/lp/codehosting/vfs/branchfs.py 2011-08-06 17:37:47 +0000
315+++ lib/lp/codehosting/vfs/branchfs.py 2011-08-15 13:18:36 +0000
316@@ -46,9 +46,6 @@
317 __metaclass__ = type
318 __all__ = [
319 'AsyncLaunchpadTransport',
320- 'BadUrlLaunchpad',
321- 'BadUrlScheme',
322- 'BadUrlSsh',
323 'branch_id_to_path',
324 'DirectDatabaseLaunchpadServer',
325 'get_lp_server',
326@@ -599,7 +596,8 @@
327 # exist. You may supply --create-prefix to create all leading
328 # parent directories", which is just misleading.
329 fault = trap_fault(
330- fail, faults.NotFound, faults.PermissionDenied)
331+ fail, faults.NotFound, faults.PermissionDenied,
332+ faults.InvalidSourcePackageName)
333 faultString = fault.faultString
334 if isinstance(faultString, unicode):
335 faultString = faultString.encode('utf-8')
336@@ -747,4 +745,3 @@
337 DeferredBlockingProxy(codehosting_client), user_id, branch_transport,
338 seen_new_branch_hook)
339 return lp_server
340-
341
342=== modified file 'lib/lp/codehosting/vfs/tests/test_branchfs.py'
343--- lib/lp/codehosting/vfs/tests/test_branchfs.py 2011-08-12 05:03:42 +0000
344+++ lib/lp/codehosting/vfs/tests/test_branchfs.py 2011-08-15 13:18:36 +0000
345@@ -49,7 +49,6 @@
346 from twisted.internet import defer
347
348 from canonical.launchpad.webapp import errorlog
349-from canonical.launchpad.webapp.errorlog import ErrorReportingUtility
350 from canonical.testing.layers import (
351 ZopelessDatabaseLayer,
352 )
353@@ -798,6 +797,17 @@
354 errors.PermissionDenied, message,
355 transport.mkdir, '~%s/%s/some-name' % (person.name, product.name))
356
357+ def test_createBranch_invalid_package_name(self):
358+ # When createBranch raises faults.InvalidSourcePackageName, the
359+ # transport should translate this to a PermissionDenied exception
360+ transport = self.getTransport()
361+ series = self.factory.makeDistroSeries()
362+ unique_name = '~%s/%s/%s/spaced%%20name/branch' % (
363+ self.requester.name, series.distribution.name, series.name)
364+ return self.assertFiresFailureWithSubstring(
365+ errors.PermissionDenied, "is not a valid source package name",
366+ transport.mkdir, unique_name)
367+
368 def test_rmdir(self):
369 transport = self.getTransport()
370 self.assertFiresFailure(
371@@ -1155,7 +1165,8 @@
372 self.addCleanup(memory_server.stop_server)
373 return memory_server
374
375- def _setUpLaunchpadServer(self, user_id, codehosting_api, backing_transport):
376+ def _setUpLaunchpadServer(self, user_id, codehosting_api,
377+ backing_transport):
378 server = LaunchpadServer(
379 XMLRPCWrapper(codehosting_api), user_id, backing_transport)
380 server.start_server()
381
382=== modified file 'lib/lp/registry/errors.py'
383--- lib/lp/registry/errors.py 2011-07-27 08:13:18 +0000
384+++ lib/lp/registry/errors.py 2011-08-15 13:18:36 +0000
385@@ -8,6 +8,7 @@
386 'CannotTransitionToCountryMirror',
387 'CountryMirrorAlreadySet',
388 'DeleteSubscriptionError',
389+ 'InvalidName',
390 'JoinNotAllowed',
391 'MirrorNotOfficial',
392 'MirrorHasNoHTTPURL',
393@@ -42,6 +43,10 @@
394 """The name given for a person is already in use by other person."""
395
396
397+class InvalidName(Exception):
398+ """The name given for a person is not valid."""
399+
400+
401 class NoSuchDistroSeries(NameLookupFailed):
402 """Raised when we try to find a DistroSeries that doesn't exist."""
403 _message_prefix = "No such distribution series"
404
405=== modified file 'lib/lp/registry/interfaces/person.py'
406--- lib/lp/registry/interfaces/person.py 2011-07-29 06:08:15 +0000
407+++ lib/lp/registry/interfaces/person.py 2011-08-15 13:18:36 +0000
408@@ -27,7 +27,6 @@
409 'ITeamCreation',
410 'ITeamReassignment',
411 'ImmutableVisibilityError',
412- 'InvalidName',
413 'NoSuchPerson',
414 'OPEN_TEAM_POLICY',
415 'PersonCreationRationale',
416@@ -1405,7 +1404,8 @@
417 )
418 @export_factory_operation(Interface, []) # Really IArchive.
419 @operation_for_version("beta")
420- def createPPA(name=None, displayname=None, description=None, private=False):
421+ def createPPA(name=None, displayname=None, description=None,
422+ private=False):
423 """Create a PPA.
424
425 :param name: A string with the name of the new PPA to create. If
426@@ -2255,8 +2255,8 @@
427 addresses associated with.
428
429 When merging teams, from_person must have no IMailingLists
430- associated with it. If it has active members they will be deactivated -
431- and reviewer must be supplied.
432+ associated with it. If it has active members they will be deactivated
433+ - and reviewer must be supplied.
434
435 :param from_person: An IPerson or ITeam that is a duplicate.
436 :param to_person: An IPerson or ITeam that is a master.
437@@ -2447,10 +2447,6 @@
438 """A change in team membership visibility is not allowed."""
439
440
441-class InvalidName(Exception):
442- """The name given for a person is not valid."""
443-
444-
445 class NoSuchPerson(NameLookupFailed):
446 """Raised when we try to look up an IPerson that doesn't exist."""
447
448
449=== modified file 'lib/lp/registry/model/person.py'
450--- lib/lp/registry/model/person.py 2011-08-01 15:28:09 +0000
451+++ lib/lp/registry/model/person.py 2011-08-15 13:18:36 +0000
452@@ -199,6 +199,7 @@
453 HasRequestedReviewsMixin,
454 )
455 from lp.registry.errors import (
456+ InvalidName,
457 JoinNotAllowed,
458 NameAlreadyTaken,
459 PPACreationError,
460@@ -225,7 +226,6 @@
461 )
462 from lp.registry.interfaces.person import (
463 ImmutableVisibilityError,
464- InvalidName,
465 IPerson,
466 IPersonSet,
467 IPersonSettings,
468
469=== modified file 'lib/lp/registry/model/sourcepackagename.py'
470--- lib/lp/registry/model/sourcepackagename.py 2010-11-09 08:38:23 +0000
471+++ lib/lp/registry/model/sourcepackagename.py 2011-08-15 13:18:36 +0000
472@@ -25,7 +25,11 @@
473 )
474 from canonical.launchpad.helpers import ensure_unicode
475 from lp.app.errors import NotFoundError
476-from lp.registry.errors import NoSuchSourcePackageName
477+from lp.app.validators.name import valid_name
478+from lp.registry.errors import (
479+ InvalidName,
480+ NoSuchSourcePackageName,
481+ )
482 from lp.registry.interfaces.sourcepackagename import (
483 ISourcePackageName,
484 ISourcePackageNameSet,
485@@ -90,6 +94,9 @@
486 return SourcePackageName.selectOneBy(name=name)
487
488 def new(self, name):
489+ if not valid_name(name):
490+ raise InvalidName(
491+ "%s is not a valid name for a source package." % name)
492 return SourcePackageName(name=name)
493
494 def getOrCreateByName(self, name):
495
496=== modified file 'lib/lp/registry/tests/test_person.py'
497--- lib/lp/registry/tests/test_person.py 2011-08-03 11:00:11 +0000
498+++ lib/lp/registry/tests/test_person.py 2011-08-15 13:18:36 +0000
499@@ -47,6 +47,7 @@
500 from lp.bugs.model.bug import Bug
501 from lp.bugs.model.bugtask import get_related_bugtasks_search_params
502 from lp.registry.errors import (
503+ InvalidName,
504 NameAlreadyTaken,
505 PrivatePersonLinkageError,
506 )
507@@ -55,7 +56,6 @@
508 from lp.registry.interfaces.nameblacklist import INameBlacklistSet
509 from lp.registry.interfaces.person import (
510 ImmutableVisibilityError,
511- InvalidName,
512 IPersonSet,
513 PersonCreationRationale,
514 PersonVisibility,
515
516=== added file 'lib/lp/registry/tests/test_sourcepackagename.py'
517--- lib/lp/registry/tests/test_sourcepackagename.py 1970-01-01 00:00:00 +0000
518+++ lib/lp/registry/tests/test_sourcepackagename.py 2011-08-15 13:18:36 +0000
519@@ -0,0 +1,24 @@
520+# Copyright 2011 Canonical Ltd. This software is licensed under the
521+# GNU Affero General Public License version 3 (see the file LICENSE).
522+
523+"""Tests for SourcePackageName"""
524+
525+__metaclass__ = type
526+
527+from testtools.testcase import ExpectedException
528+
529+from canonical.testing.layers import DatabaseLayer
530+from lp.registry.errors import InvalidName
531+from lp.registry.model.sourcepackagename import SourcePackageNameSet
532+from lp.testing import TestCase
533+
534+
535+class TestSourcePackageNameSet(TestCase):
536+
537+ layer = DatabaseLayer
538+
539+ def test_invalid_name(self):
540+ with ExpectedException(
541+ InvalidName,
542+ 'invalid%20name is not a valid name for a source package.'):
543+ SourcePackageNameSet().new('invalid%20name')