Merge lp:~james-w/launchpad/archive-collection into lp:launchpad

Proposed by James Westby
Status: Work in progress
Proposed branch: lp:~james-w/launchpad/archive-collection
Merge into: lp:launchpad
Diff against target: 611 lines (+421/-70)
7 files modified
lib/lp/services/database/collection.py (+3/-3)
lib/lp/soyuz/configure.zcml (+13/-0)
lib/lp/soyuz/interfaces/archivecollection.py (+70/-0)
lib/lp/soyuz/model/archive.py (+17/-64)
lib/lp/soyuz/model/archivecollection.py (+86/-0)
lib/lp/soyuz/tests/test_archivecollection.py (+220/-0)
lib/lp/testing/factory.py (+12/-3)
To merge this branch: bzr merge lp:~james-w/launchpad/archive-collection
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+31499@code.launchpad.net

Description of the change

Hi,

Here's a branch to add an ArchiveCollection based
on the new generic Collection.

It's fairly standalone right now, though I ported a
few easy queries in ArchiveSet methods over to it.
Hopefully once it is available it will see more use.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :
Download full text (5.8 KiB)

On Mon, Aug 2, 2010 at 1:41 AM, James Westby <email address hidden> wrote:
> James Westby has proposed merging lp:~james-w/launchpad/archive-collection into lp:launchpad/devel.
>
> Requested reviews:
>  Launchpad code reviewers (launchpad-reviewers)
>
>
> Hi,
>
> Here's a branch to add an ArchiveCollection based
> on the new generic Collection.
>
> It's fairly standalone right now, though I ported a
> few easy queries in ArchiveSet methods over to it.
> Hopefully once it is available it will see more use.
>
>

Thanks for doing this. It's important to have new code actually be used.

> === modified file 'lib/lp/services/database/collection.py'
> --- lib/lp/services/database/collection.py      2010-07-16 14:10:18 +0000
> +++ lib/lp/services/database/collection.py      2010-08-02 00:41:06 +0000
> @@ -68,9 +68,6 @@
>             base_tables = list(base.tables)
>
>         self.store = kwargs.get('store')
> -        if self.store is None:
> -            self.store = getUtility(IStoreSelector).get(
> -                MAIN_STORE, DEFAULT_FLAVOR)
>
>         self.tables = (
>             starting_tables + base_tables +
> @@ -118,6 +115,9 @@
>         If no values are requested, this selects the type of object that
>         the Collection is a collection of.
>         """
> +        if self.store is None:
> +            self.store = getUtility(IStoreSelector).get(
> +                MAIN_STORE, DEFAULT_FLAVOR)
>         if len(self.tables) == 0:
>             source = self.store
>         else:
>

What's the reason for this change? Where are the corresponding tests?

> === added file 'lib/lp/soyuz/interfaces/archivecollection.py'
> --- lib/lp/soyuz/interfaces/archivecollection.py        1970-01-01 00:00:00 +0000
> +++ lib/lp/soyuz/interfaces/archivecollection.py        2010-08-02 00:41:06 +0000
> @@ -0,0 +1,70 @@
> +# Copyright 2010 Canonical Ltd.  This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +"""A collection of archives.
> +
...
> +
> +    def withPurposes(purposes):
> +        """Restrict the archives to one of the given purposes."""

In IBranchCollection, we have a *args method for something similar. Do
you think that would be nicer here?

> === added file 'lib/lp/soyuz/model/archivecollection.py'
> --- lib/lp/soyuz/model/archivecollection.py     1970-01-01 00:00:00 +0000
> +++ lib/lp/soyuz/model/archivecollection.py     2010-08-02 00:41:06 +0000
> @@ -0,0 +1,86 @@
> +# Copyright 2010 Canonical Ltd.  This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +__metaclass__ = type
> +
...
> +
> +    def visibleByUser(self, user):
> +        public_archive = And(Archive.private == False,
> +                             Archive._enabled == True)

Maybe it's my crappy email client, but the indentation on this looks messed up.

> +        if user is None:
> +            # Anonymous user; filter to include only public archives in
> +            # the results.
> +            return self.refine(public_archive)
> +        celebs = getUtility(ILaunchpadCelebrities)
> +        if user.inTeam(celebs.admin):
> +            # Admi...

Read more...

Revision history for this message
James Westby (james-w) wrote :

On Mon, 02 Aug 2010 15:58:40 -0000, Jonathan Lange <email address hidden> wrote:
> What's the reason for this change? Where are the corresponding tests?

Sorry, I forgot to mention it in the cover letter.

When you register a Collection as a utility it is instantiated by the
zcml.

However, as the store is queried in the constructor this can fail if
there is not a utility registered for the store yet.

How this manifested was that as soon as I added the utility I could no
longer run any tests as there was an exception during zcml processing.

I'm not sure how I would test this directly? A test that created a
Collection and tested that .store was None?

It does make me realise that I should probably add a test that the
utility returns something sensible?

> In IBranchCollection, we have a *args method for something similar. Do
> you think that would be nicer here?

The implementation is *purposes, so the interface needs correcting.

> Maybe it's my crappy email client, but the indentation on this looks messed up.

It's non-standard as it was copy-paste code, I will fix.

> > +        # FIXME: Include private PPA's if user is an uploader
> > +        return self.refine(
> > +            Or(public_archive, Archive.ownerID.is_in(user_teams_subselect)))
> >
>
> Are you going to fix this in this branch? Won't this be buggy if
> someone uses it?

This is copy-paste code, so we apparently have survived this far without
needing it.

I can look at fixing it though.

> These tests are good, thanks.
>
> One thing I've done with similar tests is have a setUp() that removes
> all of the data in question (e.g. removing all branches). That way you
> can do actual equality testing rather than testing to if if objects
> are members in tests.
>
> You don't have to though.

That's a good idea, I'll look for your code to learn from it.

Thanks,

James

Unmerged revisions

11270. By James Westby

Port a few more easy queries to ArchiveCollection.

11269. By James Westby

Move the first method over to the new code.

11268. By James Westby

Add the Interfaces and register a utility for all archives.

Registering a utility means that the zcml processor tries to instantiate
our class, which fails given that the IStoreSelector utility isn't
registered yet. Therefore we lazily get the store in the base Collection
if it isn't in the kwargs in the constructor.

11267. By James Westby

Implement visibleByUser.

11266. By James Westby

Basic ArchiveCollection implementation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/services/database/collection.py'
--- lib/lp/services/database/collection.py 2010-07-16 14:10:18 +0000
+++ lib/lp/services/database/collection.py 2010-08-02 00:41:06 +0000
@@ -68,9 +68,6 @@
68 base_tables = list(base.tables)68 base_tables = list(base.tables)
6969
70 self.store = kwargs.get('store')70 self.store = kwargs.get('store')
71 if self.store is None:
72 self.store = getUtility(IStoreSelector).get(
73 MAIN_STORE, DEFAULT_FLAVOR)
7471
75 self.tables = (72 self.tables = (
76 starting_tables + base_tables +73 starting_tables + base_tables +
@@ -118,6 +115,9 @@
118 If no values are requested, this selects the type of object that115 If no values are requested, this selects the type of object that
119 the Collection is a collection of.116 the Collection is a collection of.
120 """117 """
118 if self.store is None:
119 self.store = getUtility(IStoreSelector).get(
120 MAIN_STORE, DEFAULT_FLAVOR)
121 if len(self.tables) == 0:121 if len(self.tables) == 0:
122 source = self.store122 source = self.store
123 else:123 else:
124124
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2010-06-30 17:35:36 +0000
+++ lib/lp/soyuz/configure.zcml 2010-08-02 00:41:06 +0000
@@ -67,6 +67,19 @@
67 interface="lp.soyuz.interfaces.archivearch.IArchiveArchSet"/>67 interface="lp.soyuz.interfaces.archivearch.IArchiveArchSet"/>
68 </securedutility>68 </securedutility>
6969
70 <!-- ArchiveCollection -->
71 <securedutility
72 class="lp.soyuz.model.archivecollection.ArchiveCollection"
73 provides="lp.soyuz.interfaces.archivecollection.IAllArchives">
74 <allow
75 interface="lp.soyuz.interfaces.archivecollection.IAllArchives" />
76 </securedutility>
77
78 <class class="lp.soyuz.model.archivecollection.ArchiveCollection">
79 <allow
80 interface="lp.soyuz.interfaces.archivecollection.IAllArchives" />
81 </class>
82
70 <!-- DistroSeriesPackageCache -->83 <!-- DistroSeriesPackageCache -->
71 <class84 <class
72 class="canonical.launchpad.database.DistroSeriesPackageCache">85 class="canonical.launchpad.database.DistroSeriesPackageCache">
7386
=== added file 'lib/lp/soyuz/interfaces/archivecollection.py'
--- lib/lp/soyuz/interfaces/archivecollection.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/interfaces/archivecollection.py 2010-08-02 00:41:06 +0000
@@ -0,0 +1,70 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""A collection of archives.
5
6See `IArchiveCollection` for more details.
7"""
8
9__metaclass__ = type
10__all__ = [
11 'IAllArchives',
12 'IArchiveCollection',
13 ]
14
15from zope.interface import Interface
16
17
18class IArchiveCollection(Interface):
19 """A collection of archives.
20
21 An `IArchiveCollection` is an immutable collection of archives.
22
23 You can use the methods it provides to get a new collection with
24 a filtered set of archives.
25
26 Finally you can call `select` to get a ResultSet from the collection.
27 """
28
29 def select(*args):
30 """Return a ResultSet for this collection, with values set to args."""
31
32 def withPurpose(purpose):
33 """Restrict the archives to only the given purpose."""
34
35 def withPurposes(purposes):
36 """Restrict the archives to one of the given purposes."""
37
38 def withStatus(status):
39 """Restrict the archives to only the given status."""
40
41 def withDistribution(distribution):
42 """Restrict the archives to only the given distribution."""
43
44 def ownedBy(owner):
45 """Restrict the archives to only those owned by the given person."""
46
47 def withName(name):
48 """Restrict the archives to only those with the given name."""
49
50 def isEnabled(enabled=True):
51 """Restrict the archives to only those enabled as passed."""
52
53 def isPrivate(private=True):
54 """Restrict the archives to only those with the given privacy."""
55
56 def isRequireVirtualized(require_virtualized=True):
57 """Restrict the archives to those requiring virtualization."""
58
59 def isCommercial(commercial=True):
60 """Restrict the archives to commercial as given."""
61
62 def visibleByUser(user):
63 """Restrict the archives to those visible by the given user.
64
65 :param user: a Person, or None for public archives.
66 """
67
68
69class IAllArchives(IArchiveCollection):
70 """An `IArchiveCollection` representing all archives in Launchpad."""
071
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2010-07-27 12:53:24 +0000
+++ lib/lp/soyuz/model/archive.py 2010-08-02 00:41:06 +0000
@@ -40,6 +40,7 @@
40from lp.soyuz.adapters.packagelocation import PackageLocation40from lp.soyuz.adapters.packagelocation import PackageLocation
41from canonical.launchpad.components.tokens import (41from canonical.launchpad.components.tokens import (
42 create_unique_token_for_table)42 create_unique_token_for_table)
43from lp.soyuz.interfaces.archivecollection import IAllArchives
43from lp.soyuz.model.archivedependency import ArchiveDependency44from lp.soyuz.model.archivedependency import ArchiveDependency
44from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken45from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken
45from lp.soyuz.model.archivesubscriber import ArchiveSubscriber46from lp.soyuz.model.archivesubscriber import ArchiveSubscriber
@@ -1810,13 +1811,10 @@
18101811
1811 def getPPAOwnedByPerson(self, person, name=None):1812 def getPPAOwnedByPerson(self, person, name=None):
1812 """See `IArchiveSet`."""1813 """See `IArchiveSet`."""
1813 store = Store.of(person)1814 ppas = getUtility(IAllArchives).withPurpose(ArchivePurpose.PPA)
1814 clause = [
1815 Archive.purpose == ArchivePurpose.PPA,
1816 Archive.owner == person]
1817 if name is not None:1815 if name is not None:
1818 clause.append(Archive.name == name)1816 ppas = ppas.withName(name)
1819 result = store.find(Archive, *clause).order_by(Archive.id).first()1817 result = ppas.ownedBy(person).select().order_by(Archive.id).first()
1820 if name is not None and result is None:1818 if name is not None and result is None:
1821 raise NoSuchPPA(name)1819 raise NoSuchPPA(name)
1822 return result1820 return result
@@ -1954,79 +1952,34 @@
19541952
1955 def getPrivatePPAs(self):1953 def getPrivatePPAs(self):
1956 """See `IArchiveSet`."""1954 """See `IArchiveSet`."""
1957 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)1955 ppas = getUtility(IAllArchives).withPurpose(ArchivePurpose.PPA)
1958 return store.find(1956 return ppas.isPrivate().select()
1959 Archive,
1960 Archive.private == True,
1961 Archive.purpose == ArchivePurpose.PPA)
19621957
1963 def getCommercialPPAs(self):1958 def getCommercialPPAs(self):
1964 """See `IArchiveSet`."""1959 """See `IArchiveSet`."""
1965 store = IStore(Archive)1960 ppas = getUtility(IAllArchives).withPurpose(ArchivePurpose.PPA)
1966 return store.find(1961 return ppas.isCommercial().select()
1967 Archive,
1968 Archive.commercial == True,
1969 Archive.purpose == ArchivePurpose.PPA)
19701962
1971 def getArchivesForDistribution(self, distribution, name=None,1963 def getArchivesForDistribution(self, distribution, name=None,
1972 purposes=None, user=None,1964 purposes=None, user=None,
1973 exclude_disabled=True):1965 exclude_disabled=True):
1974 """See `IArchiveSet`."""1966 """See `IArchiveSet`."""
1975 extra_exprs = []1967 archives = getUtility(IAllArchives).withDistribution(distribution)
19761968
1977 # If a single purpose is passed in, convert it into a tuple,
1978 # otherwise assume a list was passed in.
1979 if purposes in ArchivePurpose:1969 if purposes in ArchivePurpose:
1980 purposes = (purposes,)1970 archives = archives.withPurpose(purposes)
19811971 elif purposes:
1982 if purposes:1972 archives = archives.withPurposes(*purposes)
1983 extra_exprs.append(Archive.purpose.is_in(purposes))
19841973
1985 if name is not None:1974 if name is not None:
1986 extra_exprs.append(Archive.name == name)1975 archives = archives.withName(name)
19871976
1988 public_archive = And(Archive.private == False,1977 archives = archives.visibleByUser(user)
1989 Archive._enabled == True)
1990
1991 if user is not None:
1992 admins = getUtility(ILaunchpadCelebrities).admin
1993 if not user.inTeam(admins):
1994 # Enforce privacy-awareness for logged-in, non-admin users,
1995 # so that they can only see the private archives that they're
1996 # allowed to see.
1997
1998 # Create a subselect to capture all the teams that are
1999 # owners of archives AND the user is a member of:
2000 user_teams_subselect = Select(
2001 TeamParticipation.teamID,
2002 where=And(
2003 TeamParticipation.personID == user.id,
2004 TeamParticipation.teamID == Archive.ownerID))
2005
2006 # Append the extra expression to capture either public
2007 # archives, or archives owned by the user, or archives
2008 # owned by a team of which the user is a member:
2009 # Note: 'Archive.ownerID == user.id'
2010 # is unnecessary below because there is a TeamParticipation
2011 # entry showing that each person is a member of the "team"
2012 # that consists of themselves.
2013
2014 # FIXME: Include private PPA's if user is an uploader
2015 extra_exprs.append(
2016 Or(public_archive,
2017 Archive.ownerID.is_in(user_teams_subselect)))
2018 else:
2019 # Anonymous user; filter to include only public archives in
2020 # the results.
2021 extra_exprs.append(public_archive)
20221978
2023 if exclude_disabled:1979 if exclude_disabled:
2024 extra_exprs.append(Archive._enabled == True)1980 archives = archives.isEnabled()
20251981
2026 query = Store.of(distribution).find(1982 query = archives.select()
2027 Archive,
2028 Archive.distribution == distribution,
2029 *extra_exprs)
20301983
2031 return query.order_by(Archive.name)1984 return query.order_by(Archive.name)
20321985
20331986
=== added file 'lib/lp/soyuz/model/archivecollection.py'
--- lib/lp/soyuz/model/archivecollection.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/model/archivecollection.py 2010-08-02 00:41:06 +0000
@@ -0,0 +1,86 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6from storm.expr import And, Or, Select
7from zope.component import getUtility
8from zope.interface import implements
9
10from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
11from lp.registry.model.teammembership import TeamParticipation
12from lp.services.database.collection import Collection
13from lp.soyuz.interfaces.archivecollection import IArchiveCollection
14from lp.soyuz.model.archive import Archive
15
16
17class ArchiveCollection(Collection):
18
19 implements(IArchiveCollection)
20
21 starting_table = Archive
22
23 def withPurpose(self, purpose):
24 return self.refine(Archive.purpose == purpose)
25
26 def withPurposes(self, *purposes):
27 return self.refine(Archive.purpose.is_in(purposes))
28
29 def withStatus(self, status):
30 return self.refine(Archive.status == status)
31
32 def withDistribution(self, distribution):
33 return self.refine(Archive.distribution == distribution)
34
35 def ownedBy(self, owner):
36 return self.refine(Archive.owner == owner)
37
38 def withName(self, name):
39 return self.refine(Archive.name == name)
40
41 def isEnabled(self, enabled=True):
42 return self.refine(Archive._enabled == enabled)
43
44 def isPrivate(self, private=True):
45 return self.refine(Archive.private == private)
46
47 def isRequireVirtualized(self, require_virtualized=True):
48 return self.refine(Archive.require_virtualized == require_virtualized)
49
50 def isCommercial(self, commercial=True):
51 return self.refine(Archive.commercial == commercial)
52
53 def visibleByUser(self, user):
54 public_archive = And(Archive.private == False,
55 Archive._enabled == True)
56 if user is None:
57 # Anonymous user; filter to include only public archives in
58 # the results.
59 return self.refine(public_archive)
60 celebs = getUtility(ILaunchpadCelebrities)
61 if user.inTeam(celebs.admin):
62 # Admins can see everything
63 return self
64 # Enforce privacy-awareness for non-admin users,
65 # so that they can only see the private archives that they're
66 # allowed to see.
67
68 # Create a subselect to capture all the teams that are
69 # owners of archives AND the user is a member of:
70 user_teams_subselect = Select(
71 TeamParticipation.teamID,
72 where=And(
73 TeamParticipation.personID == user.id,
74 TeamParticipation.teamID == Archive.ownerID))
75
76 # Append the extra expression to capture either public
77 # archives, or archives owned by the user, or archives
78 # owned by a team of which the user is a member:
79 # Note: 'Archive.ownerID == user.id'
80 # is unnecessary below because there is a TeamParticipation
81 # entry showing that each person is a member of the "team"
82 # that consists of themselves.
83
84 # FIXME: Include private PPA's if user is an uploader
85 return self.refine(
86 Or(public_archive, Archive.ownerID.is_in(user_teams_subselect)))
087
=== added file 'lib/lp/soyuz/tests/test_archivecollection.py'
--- lib/lp/soyuz/tests/test_archivecollection.py 1970-01-01 00:00:00 +0000
+++ lib/lp/soyuz/tests/test_archivecollection.py 2010-08-02 00:41:06 +0000
@@ -0,0 +1,220 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for `ArchiveCollection`."""
5
6__metaclass__ = type
7
8from zope.component import getUtility
9from zope.security.proxy import removeSecurityProxy
10
11from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
12from canonical.testing import DatabaseFunctionalLayer
13from lp.soyuz.interfaces.archive import ArchivePurpose, ArchiveStatus
14from lp.soyuz.model.archivecollection import ArchiveCollection
15from lp.testing import TestCaseWithFactory
16
17
18class TestGetPublicationsInArchive(TestCaseWithFactory):
19
20 layer = DatabaseFunctionalLayer
21
22 def test_baseline(self):
23 archive = self.factory.makeArchive()
24 collection = ArchiveCollection()
25 self.assertIn(archive, collection.select())
26
27 def test_purpose_found(self):
28 archive = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
29 collection = ArchiveCollection()
30 ppas = collection.withPurpose(ArchivePurpose.PPA)
31 self.assertIn(archive, ppas.select())
32
33 def test_purpose_not_found(self):
34 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
35 collection = ArchiveCollection()
36 ppas = collection.withPurpose(ArchivePurpose.PPA)
37 self.assertNotIn(archive, ppas.select())
38
39 def test_purposes(self):
40 archive = self.factory.makeArchive(purpose=ArchivePurpose.PRIMARY)
41 ppa = self.factory.makeArchive(purpose=ArchivePurpose.PPA)
42 copy_archive = self.factory.makeArchive(purpose=ArchivePurpose.COPY)
43 archives = ArchiveCollection().withPurposes(
44 ArchivePurpose.PPA, ArchivePurpose.COPY)
45 result = archives.select()
46 self.assertIn(ppa, result)
47 self.assertIn(copy_archive, result)
48 self.assertNotIn(archive, result)
49
50 def test_distribution_found(self):
51 distribution = self.factory.makeDistribution()
52 archive = self.factory.makeArchive(distribution=distribution)
53 archives = ArchiveCollection().withDistribution(distribution)
54 self.assertIn(archive, archives.select())
55
56 def test_distribution_notfound(self):
57 distribution = self.factory.makeDistribution()
58 archive = self.factory.makeArchive(distribution=distribution)
59 other_distribution = self.factory.makeDistribution()
60 archives = ArchiveCollection().withDistribution(other_distribution)
61 self.assertNotIn(archive, archives.select())
62
63 def test_owner_found(self):
64 owner = self.factory.makePerson()
65 archive = self.factory.makeArchive(owner=owner)
66 archives = ArchiveCollection().ownedBy(owner)
67 self.assertIn(archive, archives.select())
68
69 def test_owner_not_found(self):
70 owner = self.factory.makePerson()
71 archive = self.factory.makeArchive(owner=owner)
72 other_person = self.factory.makePerson()
73 archives = ArchiveCollection().ownedBy(other_person)
74 self.assertNotIn(archive, archives.select())
75
76 def test_name_found(self):
77 name = "foo"
78 archive = self.factory.makeArchive(name=name)
79 archives = ArchiveCollection().withName(name)
80 self.assertIn(archive, archives.select())
81
82 def test_name_not_found(self):
83 name = "foo"
84 archive = self.factory.makeArchive(name=name)
85 archives = ArchiveCollection().withName("bar")
86 self.assertNotIn(archive, archives.select())
87
88 def test_enabled_found(self):
89 archive = self.factory.makeArchive(enabled=True)
90 archives = ArchiveCollection().isEnabled()
91 self.assertIn(archive, archives.select())
92
93 def test_enabled_not_found(self):
94 archive = self.factory.makeArchive(enabled=True)
95 archives = ArchiveCollection().isEnabled(False)
96 self.assertNotIn(archive, archives.select())
97
98 def test_disabled_found(self):
99 archive = self.factory.makeArchive(enabled=False)
100 archives = ArchiveCollection().isEnabled(False)
101 self.assertIn(archive, archives.select())
102
103 def test_disabled_not_found(self):
104 archive = self.factory.makeArchive(enabled=False)
105 archives = ArchiveCollection().isEnabled()
106 self.assertNotIn(archive, archives.select())
107
108 def test_private_found(self):
109 archive = self.factory.makeArchive(private=True)
110 archives = ArchiveCollection().isPrivate()
111 self.assertIn(archive, archives.select())
112
113 def test_private_not_found(self):
114 archive = self.factory.makeArchive(private=True)
115 archives = ArchiveCollection().isPrivate(False)
116 self.assertNotIn(archive, archives.select())
117
118 def test_non_private_found(self):
119 archive = self.factory.makeArchive(private=False)
120 archives = ArchiveCollection().isPrivate(False)
121 self.assertIn(archive, archives.select())
122
123 def test_non_private_not_found(self):
124 archive = self.factory.makeArchive(private=False)
125 archives = ArchiveCollection().isPrivate()
126 self.assertNotIn(archive, archives.select())
127
128 def test_require_virtualized_found(self):
129 archive = self.factory.makeArchive(virtualized=True)
130 archives = ArchiveCollection().isRequireVirtualized()
131 self.assertIn(archive, archives.select())
132
133 def test_require_virtualized_not_found(self):
134 archive = self.factory.makeArchive(virtualized=True)
135 archives = ArchiveCollection().isRequireVirtualized(False)
136 self.assertNotIn(archive, archives.select())
137
138 def test_not_require_virtualized_found(self):
139 archive = self.factory.makeArchive(virtualized=False)
140 archives = ArchiveCollection().isRequireVirtualized(False)
141 self.assertIn(archive, archives.select())
142
143 def test_not_require_virtualized_not_found(self):
144 archive = self.factory.makeArchive(virtualized=False)
145 archives = ArchiveCollection().isRequireVirtualized()
146 self.assertNotIn(archive, archives.select())
147
148 def test_status_found(self):
149 archive = self.factory.makeArchive(status=ArchiveStatus.ACTIVE)
150 archives = ArchiveCollection().withStatus(ArchiveStatus.ACTIVE)
151 self.assertIn(archive, archives.select())
152
153 def test_status_not_found(self):
154 archive = self.factory.makeArchive(status=ArchiveStatus.ACTIVE)
155 archives = ArchiveCollection().withStatus(ArchiveStatus.DELETING)
156 self.assertNotIn(archive, archives.select())
157
158 def test_commercial_found(self):
159 archive = self.factory.makeArchive(commercial=True)
160 archives = ArchiveCollection().isCommercial()
161 self.assertIn(archive, archives.select())
162
163 def test_commercial_not_found(self):
164 archive = self.factory.makeArchive(commercial=True)
165 archives = ArchiveCollection().isCommercial(False)
166 self.assertNotIn(archive, archives.select())
167
168 def test_not_commercial_not_found(self):
169 archive = self.factory.makeArchive(commercial=False)
170 archives = ArchiveCollection().isCommercial()
171 self.assertNotIn(archive, archives.select())
172
173 def test_not_commercial_found(self):
174 archive = self.factory.makeArchive(commercial=False)
175 archives = ArchiveCollection().isCommercial(False)
176 self.assertIn(archive, archives.select())
177
178 def test_admins_can_see_all(self):
179 archive = self.factory.makeArchive(private=True)
180 all_archives = ArchiveCollection()
181 admin = self.factory.makePerson()
182 admin_team = removeSecurityProxy(
183 getUtility(ILaunchpadCelebrities).admin)
184 admin_team.addMember(admin, admin_team.teamowner)
185 archives = ArchiveCollection().visibleByUser(admin)
186 self.assertEqual(
187 sorted(all_archives.select()), sorted(archives.select()))
188
189 def test_plain_user_cant_see_private(self):
190 archive = self.factory.makeArchive(private=True)
191 person = self.factory.makePerson()
192 archives = ArchiveCollection().visibleByUser(person)
193 self.assertNotIn(archive, archives.select())
194
195 def test_plain_user_cant_see_disabled(self):
196 archive = self.factory.makeArchive(enabled=False, private=False)
197 person = self.factory.makePerson()
198 archives = ArchiveCollection().visibleByUser(person)
199 self.assertNotIn(archive, archives.select())
200
201 def test_plain_user_can_see_public_enabled(self):
202 archive = self.factory.makeArchive(enabled=True, private=False)
203 person = self.factory.makePerson()
204 archives = ArchiveCollection().visibleByUser(person)
205 self.assertIn(archive, archives.select())
206
207 def test_user_can_see_private_disabled_archives_they_own(self):
208 person = self.factory.makePerson()
209 archive = self.factory.makeArchive(
210 enabled=False, private=True, owner=person)
211 archives = ArchiveCollection().visibleByUser(person)
212 self.assertIn(archive, archives.select())
213
214 def test_user_can_see_private_disabled_archives_for_their_teams(self):
215 person = self.factory.makePerson()
216 team = self.factory.makeTeam(members=[person])
217 archive = self.factory.makeArchive(
218 enabled=False, private=True, owner=team)
219 archives = ArchiveCollection().visibleByUser(person)
220 self.assertIn(archive, archives.select())
0221
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-08-01 04:34:26 +0000
+++ lib/lp/testing/factory.py 2010-08-02 00:41:06 +0000
@@ -144,7 +144,7 @@
144144
145from lp.soyuz.adapters.packagelocation import PackageLocation145from lp.soyuz.adapters.packagelocation import PackageLocation
146from lp.soyuz.interfaces.archive import (146from lp.soyuz.interfaces.archive import (
147 default_name_by_purpose, IArchiveSet, ArchivePurpose)147 default_name_by_purpose, IArchiveSet, ArchivePurpose, ArchiveStatus)
148from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet148from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
149from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat149from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
150from lp.soyuz.interfaces.component import IComponentSet150from lp.soyuz.interfaces.component import IComponentSet
@@ -1731,7 +1731,8 @@
17311731
1732 def makeArchive(self, distribution=None, owner=None, name=None,1732 def makeArchive(self, distribution=None, owner=None, name=None,
1733 purpose=None, enabled=True, private=False,1733 purpose=None, enabled=True, private=False,
1734 virtualized=True, description=None, displayname=None):1734 virtualized=True, description=None, displayname=None,
1735 commercial=False, status=None):
1735 """Create and return a new arbitrary archive.1736 """Create and return a new arbitrary archive.
17361737
1737 :param distribution: Supply IDistribution, defaults to a new one1738 :param distribution: Supply IDistribution, defaults to a new one
@@ -1744,6 +1745,8 @@
1744 :param private: Whether the archive is created private.1745 :param private: Whether the archive is created private.
1745 :param virtualized: Whether the archive is virtualized.1746 :param virtualized: Whether the archive is virtualized.
1746 :param description: A description of the archive.1747 :param description: A description of the archive.
1748 :param commercial: Whether the archive is commercial.
1749 :param status: The ArchiveStatus of the archive.
1747 """1750 """
1748 if purpose is None:1751 if purpose is None:
1749 purpose = ArchivePurpose.PPA1752 purpose = ArchivePurpose.PPA
@@ -1772,11 +1775,17 @@
1772 enabled=enabled, require_virtualized=virtualized,1775 enabled=enabled, require_virtualized=virtualized,
1773 description=description)1776 description=description)
17741777
1775 if private:1778 if private or commercial:
1776 naked_archive = removeSecurityProxy(archive)1779 naked_archive = removeSecurityProxy(archive)
1777 naked_archive.private = True1780 naked_archive.private = True
1778 naked_archive.buildd_secret = "sekrit"1781 naked_archive.buildd_secret = "sekrit"
17791782
1783 if commercial:
1784 removeSecurityProxy(archive).commercial = True
1785
1786 if status is not None:
1787 removeSecurityProxy(archive).status = status
1788
1780 return archive1789 return archive
17811790
1782 def makeBuilder(self, processor=None, url=None, name=None, title=None,1791 def makeBuilder(self, processor=None, url=None, name=None, title=None,