launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18578
[Merge] lp:~cjwatson/launchpad/git-namespace-tests into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/git-namespace-tests into lp:launchpad.
Commit message:
Add GitNamespace tests, similar to those for BranchNamespace.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-namespace-tests/+merge/259266
When I wrote GitNamespace, we didn't yet have enough infrastructure to allow writing unit tests for it. Now we do, so we should fill in this gap in our test coverage.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-namespace-tests into lp:launchpad.
=== modified file 'lib/lp/code/model/tests/test_gitnamespace.py'
--- lib/lp/code/model/tests/test_gitnamespace.py 2015-05-14 13:57:51 +0000
+++ lib/lp/code/model/tests/test_gitnamespace.py 2015-05-15 16:11:39 +0000
@@ -4,18 +4,679 @@
"""Tests for `IGitNamespace` implementations."""
from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
+from lp.app.enums import (
+ FREE_INFORMATION_TYPES,
+ InformationType,
+ NON_EMBARGOED_INFORMATION_TYPES,
+ PUBLIC_INFORMATION_TYPES,
+ )
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.app.interfaces.services import IService
+from lp.app.validators import LaunchpadValidationError
from lp.code.errors import (
GitDefaultConflict,
+ GitRepositoryCreatorNotMemberOfOwnerTeam,
+ GitRepositoryCreatorNotOwner,
GitRepositoryExists,
)
+from lp.code.interfaces.gitnamespace import (
+ get_git_namespace,
+ IGitNamespace,
+ IGitNamespacePolicy,
+ )
from lp.code.interfaces.gitrepository import IGitRepositorySet
-from lp.code.model.gitnamespace import ProjectGitNamespace
-from lp.testing import TestCaseWithFactory
+from lp.code.model.gitnamespace import (
+ PackageGitNamespace,
+ PersonalGitNamespace,
+ ProjectGitNamespace,
+ )
+from lp.registry.enums import (
+ BranchSharingPolicy,
+ PersonVisibility,
+ SharingPermission,
+ )
+from lp.testing import (
+ person_logged_in,
+ TestCaseWithFactory,
+ )
from lp.testing.layers import DatabaseFunctionalLayer
+class NamespaceMixin:
+ """Tests common to all namespace implementations.
+
+ You might even call these 'interface tests'.
+ """
+
+ def test_provides_interface(self):
+ # All Git namespaces provide IGitNamespace.
+ self.assertProvides(self.getNamespace(), IGitNamespace)
+
+ def test_createRepository_right_namespace(self):
+ # createRepository creates a repository in that namespace.
+ namespace = self.getNamespace()
+ repository_name = self.factory.getUniqueUnicode()
+ registrant = removeSecurityProxy(namespace).owner
+ repository = namespace.createRepository(registrant, repository_name)
+ self.assertEqual(
+ "%s/+git/%s" % (namespace.name, repository_name),
+ repository.unique_name)
+ self.assertEqual(InformationType.PUBLIC, repository.information_type)
+
+ def test_createRepository_passes_through(self):
+ # createRepository takes all the arguments that the `GitRepository`
+ # constructor takes, except for the ones that define the namespace.
+ namespace = self.getNamespace()
+ repository_name = self.factory.getUniqueUnicode()
+ registrant = removeSecurityProxy(namespace).owner
+ reviewer = self.factory.makePerson()
+ description = self.factory.getUniqueUnicode()
+ repository = namespace.createRepository(
+ registrant, repository_name, reviewer=reviewer,
+ description=description)
+ self.assertEqual(repository_name, repository.name)
+ self.assertEqual(registrant, repository.registrant)
+ self.assertEqual(reviewer, repository.reviewer)
+ self.assertEqual(description, repository.description)
+
+ def test_createRepository_subscribes_owner(self):
+ owner = self.factory.makeTeam()
+ namespace = self.getNamespace(owner)
+ repository_name = self.factory.getUniqueUnicode()
+ registrant = owner.teamowner
+ repository = namespace.createRepository(registrant, repository_name)
+ self.assertEqual([owner], list(repository.subscribers))
+
+ def test_getRepositories_no_repositories(self):
+ # getRepositories on an IGitNamespace returns a result set of
+ # repositories in that namespace. If there are no repositories, the
+ # result set is empty.
+ namespace = self.getNamespace()
+ self.assertEqual([], list(namespace.getRepositories()))
+
+ def test_getRepositories_some_repositories(self):
+ # getRepositories on an IGitNamespace returns a result set of
+ # repositories in that namespace.
+ namespace = self.getNamespace()
+ repository_name = self.factory.getUniqueUnicode()
+ repository = namespace.createRepository(
+ removeSecurityProxy(namespace).owner, repository_name)
+ self.assertEqual([repository], list(namespace.getRepositories()))
+
+ def test_getByName_default(self):
+ # getByName returns the given default if there is no repository in
+ # the namespace with that name.
+ namespace = self.getNamespace()
+ default = object()
+ match = namespace.getByName(self.factory.getUniqueUnicode(), default)
+ self.assertIs(default, match)
+
+ def test_getByName_default_is_none(self):
+ # The default 'default' return value is None.
+ namespace = self.getNamespace()
+ match = namespace.getByName(self.factory.getUniqueUnicode())
+ self.assertIsNone(match)
+
+ def test_getByName_matches(self):
+ namespace = self.getNamespace()
+ repository_name = self.factory.getUniqueUnicode()
+ repository = namespace.createRepository(
+ removeSecurityProxy(namespace).owner, repository_name)
+ match = namespace.getByName(repository_name)
+ self.assertEqual(repository, match)
+
+ def test_isNameUsed_not(self):
+ namespace = self.getNamespace()
+ name = self.factory.getUniqueUnicode()
+ self.assertFalse(namespace.isNameUsed(name))
+
+ def test_isNameUsed_yes(self):
+ namespace = self.getNamespace()
+ repository_name = self.factory.getUniqueUnicode()
+ namespace.createRepository(
+ removeSecurityProxy(namespace).owner, repository_name)
+ self.assertTrue(namespace.isNameUsed(repository_name))
+
+ def test_findUnusedName_unused(self):
+ # findUnusedName returns the given name if that name is not used.
+ namespace = self.getNamespace()
+ name = self.factory.getUniqueUnicode()
+ unused_name = namespace.findUnusedName(name)
+ self.assertEqual(name, unused_name)
+
+ def test_findUnusedName_used(self):
+ # findUnusedName returns the given name with a numeric suffix if
+ # it's already used.
+ namespace = self.getNamespace()
+ name = self.factory.getUniqueUnicode()
+ namespace.createRepository(removeSecurityProxy(namespace).owner, name)
+ unused_name = namespace.findUnusedName(name)
+ self.assertEqual("%s-1" % name, unused_name)
+
+ def test_findUnusedName_used_twice(self):
+ # findUnusedName returns the given name with a numeric suffix if
+ # it's already used.
+ namespace = self.getNamespace()
+ name = self.factory.getUniqueUnicode()
+ namespace.createRepository(removeSecurityProxy(namespace).owner, name)
+ namespace.createRepository(
+ removeSecurityProxy(namespace).owner, name + "-1")
+ unused_name = namespace.findUnusedName(name)
+ self.assertEqual("%s-2" % name, unused_name)
+
+ def test_validateMove(self):
+ # If the mover is allowed to move the repository into the namespace,
+ # if there are absolutely no problems at all, then validateMove
+ # raises nothing and returns None.
+ namespace = self.getNamespace()
+ namespace_owner = removeSecurityProxy(namespace).owner
+ repository = self.factory.makeGitRepository()
+ # Doesn't raise an exception.
+ namespace.validateMove(repository, namespace_owner)
+
+ def test_validateMove_repository_with_name_exists(self):
+ # If a repository with the same name as the given repository already
+ # exists in the namespace, validateMove raises a GitRepositoryExists
+ # error.
+ namespace = self.getNamespace()
+ namespace_owner = removeSecurityProxy(namespace).owner
+ name = self.factory.getUniqueUnicode()
+ namespace.createRepository(removeSecurityProxy(namespace).owner, name)
+ repository = self.factory.makeGitRepository(name=name)
+ self.assertRaises(
+ GitRepositoryExists,
+ namespace.validateMove, repository, namespace_owner)
+
+ def test_validateMove_forbidden_owner(self):
+ # If the mover isn't allowed to create repositories in the
+ # namespace, then they aren't allowed to move repositories in there
+ # either, so validateMove wil raise a GitRepositoryCreatorNotOwner
+ # error.
+ namespace = self.getNamespace()
+ repository = self.factory.makeGitRepository()
+ mover = self.factory.makePerson()
+ self.assertRaises(
+ GitRepositoryCreatorNotOwner,
+ namespace.validateMove, repository, mover)
+
+ def test_validateMove_not_team_member(self):
+ # If the mover isn't allowed to create repositories in the namespace
+ # because they aren't a member of the team that owns the namespace,
+ # validateMove raises a GitRepositoryCreatorNotMemberOfOwnerTeam
+ # error.
+ team = self.factory.makeTeam()
+ namespace = self.getNamespace(person=team)
+ repository = self.factory.makeGitRepository()
+ mover = self.factory.makePerson()
+ self.assertRaises(
+ GitRepositoryCreatorNotMemberOfOwnerTeam,
+ namespace.validateMove, repository, mover)
+
+ def test_validateMove_with_other_name(self):
+ # If you pass a name to validateMove, that'll check to see whether
+ # the repository could be safely moved given a rename.
+ namespace = self.getNamespace()
+ namespace_owner = removeSecurityProxy(namespace).owner
+ name = self.factory.getUniqueUnicode()
+ namespace.createRepository(removeSecurityProxy(namespace).owner, name)
+ repository = self.factory.makeGitRepository()
+ self.assertRaises(
+ GitRepositoryExists,
+ namespace.validateMove, repository, namespace_owner, name=name)
+
+
+class TestPersonalGitNamespace(TestCaseWithFactory, NamespaceMixin):
+ """Tests for `PersonalGitNamespace`."""
+
+ layer = DatabaseFunctionalLayer
+
+ def getNamespace(self, person=None):
+ if person is None:
+ person = self.factory.makePerson()
+ return get_git_namespace(person, person)
+
+ def test_name(self):
+ # A personal namespace has repositories with names starting with
+ # ~foo.
+ person = self.factory.makePerson()
+ namespace = PersonalGitNamespace(person)
+ self.assertEqual("~%s" % person.name, namespace.name)
+
+ def test_owner(self):
+ # The person passed to a personal namespace is the owner.
+ person = self.factory.makePerson()
+ namespace = PersonalGitNamespace(person)
+ self.assertEqual(person, removeSecurityProxy(namespace).owner)
+
+ def test_target(self):
+ # The target of a personal namespace is the owner of that namespace.
+ person = self.factory.makePerson()
+ namespace = PersonalGitNamespace(person)
+ self.assertEqual(person, namespace.target)
+
+
+class TestProjectGitNamespace(TestCaseWithFactory, NamespaceMixin):
+ """Tests for `ProjectGitNamespace`."""
+
+ layer = DatabaseFunctionalLayer
+
+ def getNamespace(self, person=None):
+ if person is None:
+ person = self.factory.makePerson()
+ return get_git_namespace(self.factory.makeProduct(), person)
+
+ def test_name(self):
+ # A project namespace has repositories with names starting with
+ # ~foo/bar.
+ person = self.factory.makePerson()
+ project = self.factory.makeProduct()
+ namespace = ProjectGitNamespace(person, project)
+ self.assertEqual(
+ "~%s/%s" % (person.name, project.name), namespace.name)
+
+ def test_owner(self):
+ # The person passed to a project namespace is the owner.
+ person = self.factory.makePerson()
+ project = self.factory.makeProduct()
+ namespace = ProjectGitNamespace(person, project)
+ self.assertEqual(person, removeSecurityProxy(namespace).owner)
+
+ def test_target(self):
+ # The target for a project namespace is the project.
+ person = self.factory.makePerson()
+ project = self.factory.makeProduct()
+ namespace = ProjectGitNamespace(person, project)
+ self.assertEqual(project, namespace.target)
+
+
+class TestProjectGitNamespacePrivacyWithInformationType(TestCaseWithFactory):
+ """Tests for the privacy aspects of `ProjectGitNamespace`.
+
+ This tests the behaviour for a project using the new
+ branch_sharing_policy rules.
+ """
+
+ layer = DatabaseFunctionalLayer
+
+ def makeProjectGitNamespace(self, sharing_policy, person=None):
+ if person is None:
+ person = self.factory.makePerson()
+ project = self.factory.makeProduct()
+ self.factory.makeCommercialSubscription(product=project)
+ with person_logged_in(project.owner):
+ project.setBranchSharingPolicy(sharing_policy)
+ namespace = ProjectGitNamespace(person, project)
+ return namespace
+
+ def test_public_anyone(self):
+ namespace = self.makeProjectGitNamespace(BranchSharingPolicy.PUBLIC)
+ self.assertContentEqual(
+ FREE_INFORMATION_TYPES, namespace.getAllowedInformationTypes())
+ self.assertEqual(
+ InformationType.PUBLIC, namespace.getDefaultInformationType())
+
+ def test_forbidden_anyone(self):
+ namespace = self.makeProjectGitNamespace(BranchSharingPolicy.FORBIDDEN)
+ self.assertEqual([], namespace.getAllowedInformationTypes())
+ self.assertIsNone(namespace.getDefaultInformationType())
+
+ def test_public_or_proprietary_anyone(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PUBLIC_OR_PROPRIETARY)
+ self.assertContentEqual(
+ NON_EMBARGOED_INFORMATION_TYPES,
+ namespace.getAllowedInformationTypes())
+ self.assertEqual(
+ InformationType.PUBLIC, namespace.getDefaultInformationType())
+
+ def test_proprietary_or_public_anyone(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PROPRIETARY_OR_PUBLIC)
+ self.assertEqual([], namespace.getAllowedInformationTypes())
+ self.assertIsNone(namespace.getDefaultInformationType())
+
+ def test_proprietary_or_public_owner_grantee(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PROPRIETARY_OR_PUBLIC)
+ with person_logged_in(namespace.target.owner):
+ getUtility(IService, "sharing").sharePillarInformation(
+ namespace.target, namespace.owner, namespace.target.owner,
+ {InformationType.PROPRIETARY: SharingPermission.ALL})
+ self.assertContentEqual(
+ NON_EMBARGOED_INFORMATION_TYPES,
+ namespace.getAllowedInformationTypes())
+ self.assertEqual(
+ InformationType.PROPRIETARY,
+ namespace.getDefaultInformationType())
+
+ def test_proprietary_or_public_caller_grantee(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PROPRIETARY_OR_PUBLIC)
+ grantee = self.factory.makePerson()
+ with person_logged_in(namespace.target.owner):
+ getUtility(IService, "sharing").sharePillarInformation(
+ namespace.target, grantee, namespace.target.owner,
+ {InformationType.PROPRIETARY: SharingPermission.ALL})
+ self.assertContentEqual(
+ NON_EMBARGOED_INFORMATION_TYPES,
+ namespace.getAllowedInformationTypes(grantee))
+ self.assertEqual(
+ InformationType.PROPRIETARY,
+ namespace.getDefaultInformationType(grantee))
+
+ def test_proprietary_anyone(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PROPRIETARY)
+ self.assertEqual([], namespace.getAllowedInformationTypes())
+ self.assertIsNone(namespace.getDefaultInformationType())
+
+ def test_proprietary_repository_owner_grantee(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PROPRIETARY)
+ with person_logged_in(namespace.target.owner):
+ getUtility(IService, "sharing").sharePillarInformation(
+ namespace.target, namespace.owner, namespace.target.owner,
+ {InformationType.PROPRIETARY: SharingPermission.ALL})
+ self.assertContentEqual(
+ [InformationType.PROPRIETARY],
+ namespace.getAllowedInformationTypes())
+ self.assertEqual(
+ InformationType.PROPRIETARY,
+ namespace.getDefaultInformationType())
+
+ def test_proprietary_caller_grantee(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.PROPRIETARY)
+ grantee = self.factory.makePerson()
+ with person_logged_in(namespace.target.owner):
+ getUtility(IService, "sharing").sharePillarInformation(
+ namespace.target, grantee, namespace.target.owner,
+ {InformationType.PROPRIETARY: SharingPermission.ALL})
+ self.assertContentEqual(
+ [InformationType.PROPRIETARY],
+ namespace.getAllowedInformationTypes(grantee))
+ self.assertEqual(
+ InformationType.PROPRIETARY,
+ namespace.getDefaultInformationType(grantee))
+
+ def test_embargoed_or_proprietary_anyone(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY)
+ self.assertEqual([], namespace.getAllowedInformationTypes())
+ self.assertIsNone(namespace.getDefaultInformationType())
+
+ def test_embargoed_or_proprietary_owner_grantee(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY)
+ with person_logged_in(namespace.target.owner):
+ getUtility(IService, "sharing").sharePillarInformation(
+ namespace.target, namespace.owner, namespace.target.owner,
+ {InformationType.PROPRIETARY: SharingPermission.ALL})
+ self.assertContentEqual(
+ [InformationType.PROPRIETARY, InformationType.EMBARGOED],
+ namespace.getAllowedInformationTypes())
+ self.assertEqual(
+ InformationType.EMBARGOED,
+ namespace.getDefaultInformationType())
+
+ def test_embargoed_or_proprietary_caller_grantee(self):
+ namespace = self.makeProjectGitNamespace(
+ BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY)
+ grantee = self.factory.makePerson()
+ with person_logged_in(namespace.target.owner):
+ getUtility(IService, "sharing").sharePillarInformation(
+ namespace.target, grantee, namespace.target.owner,
+ {InformationType.PROPRIETARY: SharingPermission.ALL})
+ self.assertContentEqual(
+ [InformationType.PROPRIETARY, InformationType.EMBARGOED],
+ namespace.getAllowedInformationTypes(grantee))
+ self.assertEqual(
+ InformationType.EMBARGOED,
+ namespace.getDefaultInformationType(grantee))
+
+
+class TestPackageGitNamespace(TestCaseWithFactory, NamespaceMixin):
+ """Tests for `PackageGitNamespace`."""
+
+ layer = DatabaseFunctionalLayer
+
+ def getNamespace(self, person=None):
+ if person is None:
+ person = self.factory.makePerson()
+ return get_git_namespace(
+ self.factory.makeDistributionSourcePackage(), person)
+
+ def test_name(self):
+ # A package namespace has repositories that start with
+ # ~foo/distribution/+source/packagename.
+ person = self.factory.makePerson()
+ dsp = self.factory.makeDistributionSourcePackage()
+ namespace = PackageGitNamespace(person, dsp)
+ self.assertEqual(
+ "~%s/%s/+source/%s" % (
+ person.name, dsp.distribution.name,
+ dsp.sourcepackagename.name),
+ namespace.name)
+
+ def test_owner(self):
+ # The person passed to a package namespace is the owner.
+ person = self.factory.makePerson()
+ dsp = self.factory.makeDistributionSourcePackage()
+ namespace = PackageGitNamespace(person, dsp)
+ self.assertEqual(person, removeSecurityProxy(namespace).owner)
+
+ def test_target(self):
+ # The target for a package namespace is the distribution source
+ # package.
+ person = self.factory.makePerson()
+ dsp = self.factory.makeDistributionSourcePackage()
+ namespace = PackageGitNamespace(person, dsp)
+ self.assertEqual(dsp, namespace.target)
+
+
+class TestNamespaceSet(TestCaseWithFactory):
+ """Tests for `get_namespace`."""
+
+ layer = DatabaseFunctionalLayer
+
+ def test_get_personal(self):
+ person = self.factory.makePerson()
+ namespace = get_git_namespace(person, person)
+ self.assertIsInstance(namespace, PersonalGitNamespace)
+
+ def test_get_project(self):
+ person = self.factory.makePerson()
+ project = self.factory.makeProduct()
+ namespace = get_git_namespace(project, person)
+ self.assertIsInstance(namespace, ProjectGitNamespace)
+
+ def test_get_package(self):
+ person = self.factory.makePerson()
+ dsp = self.factory.makeDistributionSourcePackage()
+ namespace = get_git_namespace(dsp, person)
+ self.assertIsInstance(namespace, PackageGitNamespace)
+
+
+class TestPersonalGitNamespaceAllowedInformationTypes(TestCaseWithFactory):
+ """Tests for PersonalGitNamespace.getAllowedInformationTypes."""
+
+ layer = DatabaseFunctionalLayer
+
+ def test_anyone(self):
+ # Personal repositories are not private for individuals.
+ person = self.factory.makePerson()
+ namespace = PersonalGitNamespace(person)
+ self.assertContentEqual(
+ FREE_INFORMATION_TYPES, namespace.getAllowedInformationTypes())
+
+ def test_public_team(self):
+ # Personal repositories for public teams cannot be private.
+ team = self.factory.makeTeam()
+ namespace = PersonalGitNamespace(team)
+ self.assertContentEqual(
+ FREE_INFORMATION_TYPES, namespace.getAllowedInformationTypes())
+
+ def test_private_team(self):
+ # Personal repositories can be private or public for private teams.
+ team = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
+ namespace = PersonalGitNamespace(team)
+ self.assertContentEqual(
+ NON_EMBARGOED_INFORMATION_TYPES,
+ namespace.getAllowedInformationTypes())
+
+
+class TestPackageGitNamespaceAllowedInformationTypes(TestCaseWithFactory):
+ """Tests for PackageGitNamespace.getAllowedInformationTypes."""
+
+ layer = DatabaseFunctionalLayer
+
+ def test_anyone(self):
+ # Package repositories are always public.
+ dsp = self.factory.makeDistributionSourcePackage()
+ person = self.factory.makePerson()
+ namespace = PackageGitNamespace(person, dsp)
+ self.assertContentEqual(
+ PUBLIC_INFORMATION_TYPES, namespace.getAllowedInformationTypes())
+
+
+class BaseValidateNewRepositoryMixin:
+
+ layer = DatabaseFunctionalLayer
+
+ def _getNamespace(self, owner):
+ # Return a namespace appropriate for the owner specified.
+ raise NotImplementedError(self._getNamespace)
+
+ def test_registrant_not_owner(self):
+ # If the namespace owner is an individual, and the registrant is not
+ # the owner, GitRepositoryCreatorNotOwner is raised.
+ namespace = self._getNamespace(self.factory.makePerson())
+ self.assertRaises(
+ GitRepositoryCreatorNotOwner,
+ namespace.validateRegistrant, self.factory.makePerson())
+
+ def test_registrant_not_in_owner_team(self):
+ # If the namespace owner is a team, and the registrant is not in the
+ # team, GitRepositoryCreatorNotMemberOfOwnerTeam is raised.
+ namespace = self._getNamespace(self.factory.makeTeam())
+ self.assertRaises(
+ GitRepositoryCreatorNotMemberOfOwnerTeam,
+ namespace.validateRegistrant, self.factory.makePerson())
+
+ def test_existing_repository(self):
+ # If a repository exists with the same name, then
+ # GitRepositoryExists is raised.
+ namespace = self._getNamespace(self.factory.makePerson())
+ repository = namespace.createRepository(
+ namespace.owner, self.factory.getUniqueUnicode())
+ self.assertRaises(
+ GitRepositoryExists,
+ namespace.validateRepositoryName, repository.name)
+
+ def test_invalid_name(self):
+ # If the repository name is not valid, a LaunchpadValidationError is
+ # raised.
+ namespace = self._getNamespace(self.factory.makePerson())
+ self.assertRaises(
+ LaunchpadValidationError,
+ namespace.validateRepositoryName, u"+foo")
+
+ def test_permitted_first_character(self):
+ # The first character of a repository name must be a letter or a
+ # number.
+ namespace = self._getNamespace(self.factory.makePerson())
+ for c in [unichr(i) for i in range(128)]:
+ if c.isalnum():
+ namespace.validateRepositoryName(c)
+ else:
+ self.assertRaises(
+ LaunchpadValidationError,
+ namespace.validateRepositoryName, c)
+
+ def test_permitted_subsequent_character(self):
+ # After the first character, letters, numbers and certain
+ # punctuation is permitted.
+ namespace = self._getNamespace(self.factory.makePerson())
+ for c in [unichr(i) for i in range(128)]:
+ if c.isalnum() or c in "+-_@.":
+ namespace.validateRepositoryName("a" + c)
+ else:
+ self.assertRaises(
+ LaunchpadValidationError,
+ namespace.validateRepositoryName, "a" + c)
+
+
+class TestPersonalGitNamespaceValidateNewRepository(
+ TestCaseWithFactory, BaseValidateNewRepositoryMixin):
+
+ def _getNamespace(self, owner):
+ return PersonalGitNamespace(owner)
+
+
+class TestPackageGitNamespaceValidateNewRepository(
+ TestCaseWithFactory, BaseValidateNewRepositoryMixin):
+
+ def _getNamespace(self, owner):
+ dsp = self.factory.makeDistributionSourcePackage()
+ return PackageGitNamespace(owner, dsp)
+
+
+class TestProjectGitNamespaceValidateNewRepository(
+ TestCaseWithFactory, BaseValidateNewRepositoryMixin):
+
+ def _getNamespace(self, owner):
+ project = self.factory.makeProduct()
+ return ProjectGitNamespace(owner, project)
+
+
+class TestPersonalGitRepositories(TestCaseWithFactory):
+ """Personal repositories have no branch visibility policy."""
+
+ layer = DatabaseFunctionalLayer
+
+ def assertPublic(self, creator, owner):
+ """Assert that the policy check would result in a public repository.
+
+ :param creator: The user creating the repository.
+ :param owner: The person or team that will be the owner of the
+ repository.
+ """
+ namespace = get_git_namespace(owner, owner)
+ self.assertNotIn(
+ InformationType.PROPRIETARY,
+ namespace.getAllowedInformationTypes())
+
+ def assertPolicyCheckRaises(self, error, creator, owner):
+ """Assert that the policy check raises an exception.
+
+ :param error: The exception class that should be raised.
+ :param creator: The user creating the repository.
+ :param owner: The person or team that will be the owner of the
+ repository.
+ """
+ policy = IGitNamespacePolicy(get_git_namespace(owner, owner))
+ self.assertRaises(error, policy.validateRegistrant, registrant=creator)
+
+ def test_personal_repositories_public(self):
+ # Personal repositories created by anyone are public.
+ person = self.factory.makePerson()
+ self.assertPublic(person, person)
+
+ def test_team_personal_repositories(self):
+ # Team-owned personal repositories are allowed, and are public.
+ person = self.factory.makePerson()
+ team = self.factory.makeTeam(members=[person])
+ self.assertPublic(person, team)
+
+ def test_no_create_personal_repository_for_other_user(self):
+ # One user can't create personal repositories owned by another.
+ self.assertPolicyCheckRaises(
+ GitRepositoryCreatorNotOwner, self.factory.makePerson(),
+ self.factory.makePerson())
+
+
class TestGitNamespaceMoveRepository(TestCaseWithFactory):
"""Test the IGitNamespace.moveRepository method."""
Follow ups