launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #01875
[Merge] lp:~edwin-grubbs/launchpad/bug-446074-blacklist-form into lp:launchpad/devel
Edwin Grubbs has proposed merging lp:~edwin-grubbs/launchpad/bug-446074-blacklist-form into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
#446074 Need a form to maintain the name blacklist
https://bugs.launchpad.net/bugs/446074
Summary
-------
Added ability for the Registry Experts team to view, add, and edit the
NameBlacklist table.
Tests
-----
./bin/test -vv -t nameblacklist
Demo and Q/A
------------
* Open http://launchpad.dev/+nameblacklist
* Click on "Add blacklist expression"
* It should not be possible to add "(" as an expression.
* Open http://launchpad.dev/+nameblacklist
* Click on a yellow edit icon.
* It should not be possible to add "(" as an expression.
--
https://code.launchpad.net/~edwin-grubbs/launchpad/bug-446074-blacklist-form/+merge/40405
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~edwin-grubbs/launchpad/bug-446074-blacklist-form into lp:launchpad/devel.
=== modified file 'lib/canonical/launchpad/browser/launchpad.py'
--- lib/canonical/launchpad/browser/launchpad.py 2010-11-02 20:10:56 +0000
+++ lib/canonical/launchpad/browser/launchpad.py 2010-11-09 05:26:23 +0000
@@ -89,6 +89,14 @@
INavigationMenu,
)
from canonical.launchpad.webapp.publisher import RedirectionView
+from canonical.launchpad.webapp.url import urlappend
+from canonical.launchpad.webapp.vhosts import allvhosts
+from canonical.lazr import (
+ ExportedFolder,
+ ExportedImageFolder,
+ )
+from canonical.widgets.project import ProjectScopeWidget
+from lp.answers.interfaces.questioncollection import IQuestionSet
# XXX SteveAlexander 2005-09-22: this is imported here because there is no
# general timedelta to duration format adapter available. This should
# be factored out into a generally available adapter for both this
@@ -99,14 +107,6 @@
MenuAPI,
PageTemplateContextsAPI,
)
-from canonical.launchpad.webapp.url import urlappend
-from canonical.launchpad.webapp.vhosts import allvhosts
-from canonical.lazr import (
- ExportedFolder,
- ExportedImageFolder,
- )
-from canonical.widgets.project import ProjectScopeWidget
-from lp.answers.interfaces.questioncollection import IQuestionSet
from lp.app.errors import (
GoneError,
NotFoundError,
@@ -131,6 +131,7 @@
from lp.registry.interfaces.codeofconduct import ICodeOfConductSet
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.karma import IKarmaActionSet
+from lp.registry.interfaces.nameblacklist import INameBlacklistSet
from lp.registry.interfaces.person import IPersonSet
from lp.registry.interfaces.pillar import IPillarNameSet
from lp.registry.interfaces.product import (
@@ -583,6 +584,7 @@
'karmaaction': IKarmaActionSet,
'+imports': ITranslationImportQueue,
'+languages': ILanguageSet,
+ '+nameblacklist': INameBlacklistSet,
'package-sets': IPackagesetSet,
'people': IPersonSet,
'pillars': IPillarNameSet,
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2010-10-21 01:42:14 +0000
+++ lib/canonical/launchpad/security.py 2010-11-09 05:26:23 +0000
@@ -51,6 +51,7 @@
)
from lp.blueprints.interfaces.sprint import ISprint
from lp.blueprints.interfaces.sprintspecification import ISprintSpecification
+from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted
from lp.buildmaster.interfaces.builder import (
IBuilder,
IBuilderSet,
@@ -61,7 +62,6 @@
IBuildFarmJobOld,
)
from lp.buildmaster.interfaces.packagebuild import IPackageBuild
-from lp.bugs.interfaces.bugtarget import IOfficialBugTagTargetRestricted
from lp.code.interfaces.branch import (
IBranch,
user_has_special_branch_access,
@@ -114,6 +114,10 @@
IMilestone,
IProjectGroupMilestone,
)
+from lp.registry.interfaces.nameblacklist import (
+ INameBlacklist,
+ INameBlacklistSet,
+ )
from lp.registry.interfaces.packaging import IPackaging
from lp.registry.interfaces.person import (
IPerson,
@@ -1664,6 +1668,26 @@
return team in user.person.getAdministratedTeams()
+class ViewNameBlacklist(EditByRegistryExpertsOrAdmins):
+ permission = 'launchpad.View'
+ usedfor = INameBlacklist
+
+
+class EditNameBlacklist(EditByRegistryExpertsOrAdmins):
+ permission = 'launchpad.Edit'
+ usedfor = INameBlacklist
+
+
+class ViewNameBlacklistSet(EditByRegistryExpertsOrAdmins):
+ permission = 'launchpad.View'
+ usedfor = INameBlacklistSet
+
+
+class EditNameBlacklistSet(EditByRegistryExpertsOrAdmins):
+ permission = 'launchpad.Edit'
+ usedfor = INameBlacklistSet
+
+
class ViewLanguageSet(AnonymousAuthorization):
"""Anyone can view an ILangaugeSet."""
usedfor = ILanguageSet
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2010-10-11 09:35:29 +0000
+++ lib/lp/registry/browser/configure.zcml 2010-11-09 05:26:23 +0000
@@ -1560,6 +1560,51 @@
class="lp.registry.browser.product.ProductReviewLicenseView"
permission="launchpad.Moderate"
template="../templates/product-review-license.pt"/>
+
+ <browser:page
+ for="lp.registry.interfaces.nameblacklist.INameBlacklist"
+ permission="launchpad.Edit"
+ facet="overview"
+ class="lp.registry.browser.nameblacklist.NameBlacklistEditView"
+ name="+edit"
+ template="../../app/templates/generic-edit.pt"/>
+ <browser:url
+ for="lp.registry.interfaces.nameblacklist.INameBlacklistSet"
+ path_expression="string:+nameblacklist"
+ parent_utility="canonical.launchpad.webapp.interfaces.ILaunchpadRoot"
+ />
+ <browser:url
+ for="lp.registry.interfaces.nameblacklist.INameBlacklist"
+ path_expression="string:${id}"
+ parent_utility="lp.registry.interfaces.nameblacklist.INameBlacklistSet"
+ />
+ <browser:defaultView
+ for="lp.registry.interfaces.nameblacklist.INameBlacklistSet"
+ name="+index"/>
+ <browser:page
+ for="lp.registry.interfaces.nameblacklist.INameBlacklistSet"
+ permission="launchpad.View"
+ facet="overview"
+ class="lp.registry.browser.nameblacklist.NameBlacklistSetView"
+ name="+index"
+ template="../templates/nameblacklists-index.pt"/>
+ <browser:page
+ for="lp.registry.interfaces.nameblacklist.INameBlacklistSet"
+ permission="launchpad.Edit"
+ facet="overview"
+ class="lp.registry.browser.nameblacklist.NameBlacklistAddView"
+ name="+add"
+ template="../../app/templates/generic-edit.pt"/>
+ <browser:navigation
+ module="lp.registry.browser.nameblacklist"
+ classes="NameBlacklistSetNavigation"/>
+ <browser:menus
+ classes="
+ NameBlacklistNavigationMenu
+ NameBlacklistSetNavigationMenu
+ "
+ module="lp.registry.browser.nameblacklist"/>
+
<browser:page
name="+addseries"
for="lp.registry.interfaces.product.IProduct"
=== added file 'lib/lp/registry/browser/nameblacklist.py'
--- lib/lp/registry/browser/nameblacklist.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/nameblacklist.py 2010-11-09 05:26:23 +0000
@@ -0,0 +1,151 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+__all__ = [
+ 'NameBlacklistAddView',
+ 'NameBlacklistEditView',
+ 'NameBlacklistNavigationMenu',
+ 'NameBlacklistSetNavigationMenu',
+ 'NameBlacklistSetView',
+ ]
+
+import re
+
+from zope.app.form.browser import TextWidget
+from zope.component import getUtility
+
+from canonical.launchpad.webapp import action
+from canonical.launchpad.webapp.launchpadform import (
+ custom_widget,
+ LaunchpadFormView,
+ )
+from canonical.launchpad.webapp.menu import (
+ ApplicationMenu,
+ enabled_with_permission,
+ Link,
+ NavigationMenu,
+ )
+from canonical.launchpad.webapp.publisher import (
+ canonical_url,
+ LaunchpadView,
+ Navigation,
+ )
+from lp.registry.browser import RegistryEditFormView
+from lp.registry.interfaces.nameblacklist import (
+ INameBlacklist,
+ INameBlacklistSet,
+ )
+
+
+class NameBlacklistValidationMixin:
+ """Validate regular expression when adding or editing."""
+
+ def validate(self, data):
+ """Validate regular expression."""
+ regexp = data['regexp']
+ try:
+ re.compile(regexp)
+ name_blacklist_set = getUtility(INameBlacklistSet)
+ if (INameBlacklistSet.providedBy(self.context)
+ or self.context.regexp != regexp):
+ # Check if the regular expression already exists if a
+ # new expression is being created or if an existing
+ # regular expression has been modified.
+ if name_blacklist_set.getByRegExp(regexp) is not None:
+ self.setFieldError(
+ 'regexp',
+ 'This regular expression already exists.')
+ except re.error, e:
+ self.setFieldError(
+ 'regexp',
+ 'Invalid regular expression: %s' % e)
+
+
+class NameBlacklistEditView(NameBlacklistValidationMixin,
+ RegistryEditFormView):
+ """View for editing a blacklist expression."""
+
+ schema = INameBlacklist
+ field_names = ['regexp', 'comment']
+
+ @property
+ def cancel_url(self):
+ return canonical_url(getUtility(INameBlacklistSet))
+
+ next_url = cancel_url
+
+
+class NameBlacklistAddView(NameBlacklistValidationMixin, LaunchpadFormView):
+ """View for adding a blacklist expression."""
+
+ schema = INameBlacklist
+ field_names = ['regexp', 'comment']
+ label = "Add a new blacklist expression"
+
+ custom_widget('regexp', TextWidget, displayWidth=60)
+
+ @property
+ def page_title(self):
+ """The page title."""
+ return self.label
+
+ @property
+ def cancel_url(self):
+ """See `LaunchpadFormView`."""
+ return canonical_url(self.context)
+
+ next_url = cancel_url
+
+ @action("Add to blacklist", name='add')
+ def add_action(self, action, data):
+ name_blacklist_set = getUtility(INameBlacklistSet)
+ name_blacklist_set.create(
+ regexp=data['regexp'],
+ comment=data['comment'],
+ )
+ self.request.response.addInfoNotification(
+ 'Regular expression "%s" has been added to the name blacklist.'
+ % data['regexp'])
+
+
+class NameBlacklistSetView(LaunchpadView):
+ """View for /+nameblacklists top level collection."""
+
+ page_title = (
+ 'Blacklist for names of Launchpad pillars, persons, and teams')
+ label = page_title
+
+
+class NameBlacklistSetNavigation(Navigation):
+
+ usedfor = INameBlacklistSet
+
+ def traverse(self, name):
+ return self.context.get(int(name))
+
+
+class NameBlacklistSetNavigationMenu(NavigationMenu):
+ """Action menu for NameBlacklistSet."""
+ usedfor = INameBlacklistSet
+ facet = 'overview'
+ links = [
+ 'add_blacklist_expression',
+ ]
+
+ @enabled_with_permission('launchpad.Edit')
+ def add_blacklist_expression(self):
+ return Link('+add', 'Add blacklist expression', icon='add')
+
+
+class NameBlacklistNavigationMenu(ApplicationMenu):
+ """Action menu for NameBlacklist."""
+ usedfor = INameBlacklist
+ facet = 'overview'
+ links = [
+ 'edit_blacklist_expression',
+ ]
+
+ @enabled_with_permission('launchpad.Edit')
+ def edit_blacklist_expression(self):
+ return Link('+edit', 'Edit blacklist expression', icon='edit')
=== added file 'lib/lp/registry/browser/tests/nameblacklist-views.txt'
--- lib/lp/registry/browser/tests/nameblacklist-views.txt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/tests/nameblacklist-views.txt 2010-11-09 05:26:23 +0000
@@ -0,0 +1,96 @@
+NameBlacklist pages
+===================
+
+ >>> from zope.component import getUtility
+ >>> from lp.testing.sampledata import ADMIN_EMAIL
+ >>> from canonical.launchpad.interfaces.launchpad import (
+ ... ILaunchpadCelebrities)
+ >>> from lp.registry.interfaces.nameblacklist import INameBlacklistSet
+ >>> name_blacklist_set = getUtility(INameBlacklistSet)
+ >>> from canonical.launchpad.testing.pages import (
+ ... extract_text, find_tag_by_id)
+ >>> registry_experts = getUtility(ILaunchpadCelebrities).registry_experts
+ >>> registry_expert = factory.makePerson()
+ >>> login(ADMIN_EMAIL)
+ >>> ignore = registry_experts.addMember(registry_expert, registry_expert)
+
+
+View all
+--------
+
+All the blacklisted regular expressions that filter pillar names and
+person names can be seen on the /+nameblacklist page.
+
+ >>> login_person(registry_expert)
+ >>> view = create_initialized_view(name_blacklist_set, '+index',
+ ... principal=registry_expert)
+ >>> print extract_text(find_tag_by_id(view.render(), 'blacklist'))
+ Regular Expression Comment
+ ^admin Edit blacklist expression
+ blacklist Edit blacklist expression For testing purposes
+
+
+Add expression to blacklist
+---------------------------
+
+An invalid regular expression cannot be added.
+
+ >>> form = {
+ ... 'field.regexp': u'(',
+ ... 'field.comment': u'old-comment',
+ ... 'field.actions.add': 'Add to blacklist',
+ ... }
+ >>> view = create_initialized_view(name_blacklist_set, '+add', form=form)
+ >>> for error in view.errors:
+ ... print error
+ Invalid regular expression: unbalanced parenthesis
+
+A duplicate regular expression cannot be added.
+
+ >>> form['field.regexp'] = u'blacklist'
+ >>> view = create_initialized_view(name_blacklist_set, '+add', form=form)
+ >>> for error in view.errors:
+ ... print error
+ This regular expression already exists.
+
+After adding a regular expression, a notification will be displayed.
+
+ >>> form['field.regexp'] = u'foo'
+ >>> view = create_initialized_view(name_blacklist_set, '+add', form=form)
+ >>> for notification in view.request.response.notifications:
+ ... print notification.message
+ Regular expression "foo" has been added to the name blacklist.
+
+
+Edit expression in blacklist
+----------------------------
+
+When a regular expression is edited, it still must be valid.
+
+ >>> import transaction
+ >>> transaction.commit()
+ >>> foo_exp = name_blacklist_set.getByRegExp(u'foo')
+ >>> form = {
+ ... 'field.regexp': u'(',
+ ... 'field.comment': u'new-comment',
+ ... 'field.actions.change': 'Change',
+ ... }
+ >>> view = create_initialized_view(foo_exp, '+edit', form=form)
+ >>> for error in view.errors:
+ ... print error
+ Invalid regular expression: unbalanced parenthesis
+
+It cannot changed to conflict with another regular expression.
+
+ >>> form['field.regexp'] = u'blacklist'
+ >>> view = create_initialized_view(foo_exp, '+edit', form=form)
+ >>> for error in view.errors:
+ ... print error
+ This regular expression already exists.
+
+Otherwise, the change will be successful.
+
+ >>> form['field.regexp'] = u'bar'
+ >>> view = create_initialized_view(foo_exp, '+edit', form=form)
+ >>> print foo_exp.regexp, foo_exp.comment
+ bar new-comment
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-11-04 03:45:27 +0000
+++ lib/lp/registry/configure.zcml 2010-11-09 05:26:23 +0000
@@ -102,6 +102,29 @@
interface="lp.registry.interfaces.persontransferjob.IMembershipNotificationJob"/>
</class>
+ <!-- INameBlacklist -->
+ <securedutility
+ class="lp.registry.model.nameblacklist.NameBlacklistSet"
+ provides="lp.registry.interfaces.nameblacklist.INameBlacklistSet">
+ <allow
+ interface="lp.registry.interfaces.nameblacklist.INameBlacklistSet"/>
+ </securedutility>
+
+ <class class="lp.registry.model.nameblacklist.NameBlacklistSet">
+ <require
+ permission="launchpad.Edit"
+ interface="lp.registry.interfaces.nameblacklist.INameBlacklistSet"/>
+ </class>
+
+ <class class="lp.registry.model.nameblacklist.NameBlacklist">
+ <require
+ permission="launchpad.View"
+ interface="lp.registry.interfaces.nameblacklist.INameBlacklist"/>
+ <require
+ permission="launchpad.Edit"
+ set_schema="lp.registry.interfaces.nameblacklist.INameBlacklist"/>
+ </class>
+
<!-- Location -->
<class
=== added file 'lib/lp/registry/interfaces/nameblacklist.py'
--- lib/lp/registry/interfaces/nameblacklist.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/interfaces/nameblacklist.py 2010-11-09 05:26:23 +0000
@@ -0,0 +1,44 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""NameBlacklist interfaces."""
+
+__metaclass__ = type
+
+__all__ = [
+ 'INameBlacklist',
+ 'INameBlacklistSet',
+ ]
+
+from zope.interface import Interface
+from zope.schema import (
+ Int,
+ Text,
+ TextLine,
+ )
+
+from canonical.launchpad import _
+
+
+class INameBlacklist(Interface):
+ """The interface for the NameBlacklist table."""
+
+ id = Int(title=_('ID'), required=True, readonly=True)
+ regexp = TextLine(title=_('Regular expression'), required=True)
+ comment = Text(title=_('Comment'), required=False)
+
+
+class INameBlacklistSet(Interface):
+ """The set of INameBlacklist objects."""
+
+ def getAll():
+ """Return all the name blacklist expressions."""
+
+ def create(regexp, comment=None):
+ """Create and return a new NameBlacklist with given arguments."""
+
+ def get(id):
+ """Return the NameBlacklist with the given id or None."""
+
+ def getByRegExp(regexp):
+ """Return the NameBlacklist with the given regexp or None."""
=== added file 'lib/lp/registry/model/nameblacklist.py'
--- lib/lp/registry/model/nameblacklist.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/model/nameblacklist.py 2010-11-09 05:26:23 +0000
@@ -0,0 +1,66 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Classes for managing the NameBlacklist table."""
+
+__metaclass__ = type
+__all__ = [
+ 'NameBlacklist',
+ 'NameBlacklistSet',
+ ]
+
+
+from storm.base import Storm
+from storm.locals import (
+ Int,
+ Unicode,
+ )
+from zope.interface import implements
+
+from canonical.launchpad.interfaces.lpstorm import IStore
+from lp.registry.interfaces.nameblacklist import (
+ INameBlacklist,
+ INameBlacklistSet,
+ )
+
+
+class NameBlacklist(Storm):
+ """Class for the NameBlacklist table."""
+
+ implements(INameBlacklist)
+
+ __storm_table__ = 'NameBlacklist'
+
+ id = Int(primary=True)
+ regexp = Unicode(name='regexp', allow_none=False)
+ comment = Unicode(name='comment', allow_none=True)
+
+
+class NameBlacklistSet:
+ """Class for creating and retrieving NameBlacklist objects."""
+
+ implements(INameBlacklistSet)
+
+ def getAll(self):
+ """See `INameBlacklistSet`."""
+ store = IStore(NameBlacklist)
+ return store.find(NameBlacklist).order_by(NameBlacklist.regexp)
+
+ def create(self, regexp, comment=None):
+ """See `INameBlacklistSet`."""
+ nameblacklist = NameBlacklist()
+ nameblacklist.regexp = regexp
+ nameblacklist.comment = comment
+ store = IStore(NameBlacklist)
+ store.add(nameblacklist)
+ return nameblacklist
+
+ def get(self, id):
+ """See `INameBlacklistSet`."""
+ store = IStore(NameBlacklist)
+ return store.find(NameBlacklist, NameBlacklist.id == id).one()
+
+ def getByRegExp(self, regexp):
+ """See `INameBlacklistSet`."""
+ store = IStore(NameBlacklist)
+ return store.find(NameBlacklist, NameBlacklist.regexp == regexp).one()
=== renamed file 'lib/canonical/launchpad/pagetests/standalone/xx-nameblacklist.txt' => 'lib/lp/registry/stories/object/xx-nameblacklist.txt'
=== added file 'lib/lp/registry/templates/nameblacklists-index.pt'
--- lib/lp/registry/templates/nameblacklists-index.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/templates/nameblacklists-index.pt 2010-11-09 05:26:23 +0000
@@ -0,0 +1,40 @@
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ xml:lang="en"
+ lang="en"
+ dir="ltr"
+ metal:use-macro="view/macro:page/main_side"
+ i18n:domain="launchpad">
+
+ <body>
+
+ <tal:side metal:fill-slot="side">
+ <tal:menu replace="structure view/@@+global-actions" />
+ <tal:menu replace="structure context/@@+related-pages" />
+ </tal:side>
+
+ <div metal:fill-slot="main" class="main-portlet">
+ <table id="blacklist" class="listing sortable">
+ <thead>
+ <th>Regular Expression</th>
+ <th>Comment</th>
+ </thead>
+ <tbody>
+ <tr tal:repeat="item context/getAll">
+ <td>
+ <tt tal:content="item/regexp"/>
+ <tal:link replace="
+ structure
+ item/menu:overview/edit_blacklist_expression/fmt:icon"/>
+ </td>
+ <td tal:content="item/comment"/>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+
+ </body>
+</html>
=== modified file 'lib/lp/registry/tests/test_nameblacklist.py'
--- lib/lp/registry/tests/test_nameblacklist.py 2010-10-04 19:50:45 +0000
+++ lib/lp/registry/tests/test_nameblacklist.py 2010-11-09 05:26:23 +0000
@@ -5,42 +5,54 @@
__metaclass__ = type
-import unittest
-
-from canonical.testing.layers import LaunchpadLayer
-
-
-class TestNameBlacklist(unittest.TestCase):
- layer = LaunchpadLayer
+
+from zope.component import getUtility
+from zope.interface.verify import verifyObject
+
+from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
+from canonical.launchpad.interfaces.lpstorm import IStore
+from canonical.launchpad.webapp.authorization import check_permission
+from canonical.testing.layers import (
+ DatabaseFunctionalLayer,
+ ZopelessDatabaseLayer,
+ )
+from lp.registry.interfaces.nameblacklist import (
+ INameBlacklist,
+ INameBlacklistSet,
+ )
+from lp.testing import (
+ ANONYMOUS,
+ login,
+ login_person,
+ TestCaseWithFactory,
+ )
+from lp.testing.sampledata import ADMIN_EMAIL
+
+
+class TestNameBlacklist(TestCaseWithFactory):
+ layer = ZopelessDatabaseLayer
def setUp(self):
- self.con = self.layer.connect()
- self.cur = self.con.cursor()
-
- # Create a couple of blacklist entries
- self.cur.execute("""
- INSERT INTO NameBlacklist(id, regexp) VALUES (-200, '^foo')
- """)
- self.cur.execute("""
- INSERT INTO NameBlacklist(id, regexp) VALUES (-100, 'foo')
- """)
- self.cur.execute("""
- INSERT INTO NameBlacklist(id, regexp) VALUES (-50, 'v e r b o s e')
- """)
-
- def tearDown(self):
- self.con.close()
+ super(TestNameBlacklist, self).setUp()
+ self.name_blacklist_set = getUtility(INameBlacklistSet)
+ self.caret_foo_exp = self.name_blacklist_set.create(u'^foo')
+ self.foo_exp = self.name_blacklist_set.create(u'foo')
+ self.verbose_exp = self.name_blacklist_set.create(u'v e r b o s e')
+ self.store = IStore(self.foo_exp)
+ self.store.flush()
def name_blacklist_match(self, name):
'''Return the result of the name_blacklist_match stored procedure.'''
- self.cur.execute("SELECT name_blacklist_match(%(name)s)", vars())
- return self.cur.fetchone()[0]
+ result = self.store.execute(
+ "SELECT name_blacklist_match(%s)", (name,))
+ return result.get_one()[0]
def is_blacklisted_name(self, name):
'''Call the is_blacklisted_name stored procedure and return the result
'''
- self.cur.execute("SELECT is_blacklisted_name(%(name)s)", vars())
- blacklisted = self.cur.fetchone()[0]
+ result = self.store.execute(
+ "SELECT is_blacklisted_name(%s)", (name,))
+ blacklisted = result.get_one()[0]
self.failIf(blacklisted is None, 'is_blacklisted_name returned NULL')
return bool(blacklisted)
@@ -52,21 +64,25 @@
# A name that is blacklisted returns the id of the row in the
# NameBlacklist table that matched. Rows are tried in order, and the
# first match is returned.
- self.failUnlessEqual(self.name_blacklist_match("foobar"), -200)
- self.failUnlessEqual(self.name_blacklist_match("barfoo"), -100)
+ self.failUnlessEqual(
+ self.name_blacklist_match("foobar"),
+ self.caret_foo_exp.id)
+ self.failUnlessEqual(
+ self.name_blacklist_match("barfoo"),
+ self.foo_exp.id)
def test_name_blacklist_match_cache(self):
# If the blacklist is changed in the DB, these changes are noticed.
# This test is needed because the stored procedure keeps a cache
# of the compiled regular expressions.
- self.failUnlessEqual(self.name_blacklist_match("foobar"), -200)
- self.cur.execute(
- "UPDATE NameBlacklist SET regexp='nomatch' where id=-200"
- )
- self.failUnlessEqual(self.name_blacklist_match("foobar"), -100)
- self.cur.execute(
- "UPDATE NameBlacklist SET regexp='nomatch2' where id=-100"
- )
+ self.failUnlessEqual(
+ self.name_blacklist_match("foobar"),
+ self.caret_foo_exp.id)
+ self.caret_foo_exp.regexp = u'nomatch'
+ self.failUnlessEqual(
+ self.name_blacklist_match("foobar"),
+ self.foo_exp.id)
+ self.foo_exp.regexp = u'nomatch2'
self.failUnless(self.name_blacklist_match("foobar") is None)
def test_is_blacklisted_name(self):
@@ -74,7 +90,8 @@
# that is friendlier to use in a boolean context.
self.failUnless(self.is_blacklisted_name("bar") is False)
self.failUnless(self.is_blacklisted_name("foo") is True)
- self.cur.execute("UPDATE NameBlacklist SET regexp='bar' || regexp")
+ self.caret_foo_exp.regexp = u'bar'
+ self.foo_exp.regexp = u'bar2'
self.failUnless(self.is_blacklisted_name("foo") is False)
def test_case_insensitive(self):
@@ -85,5 +102,71 @@
self.failUnless(self.is_blacklisted_name("verbose") is True)
-def test_suite():
- return unittest.TestLoader().loadTestsFromName(__name__)
+class TestNameBlacklistSet(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def setUp(self):
+ super(TestNameBlacklistSet, self).setUp()
+ registry_experts = getUtility(ILaunchpadCelebrities).registry_experts
+ registry_expert = self.factory.makePerson()
+ login(ADMIN_EMAIL)
+ registry_experts.addMember(registry_expert, registry_expert)
+ login_person(registry_expert)
+ self.name_blacklist_set = getUtility(INameBlacklistSet)
+
+ def test_create_with_one_arg(self):
+ # Test NameBlacklistSet.create(regexp).
+ name_blacklist = self.name_blacklist_set.create(u'foo')
+ self.assertTrue(verifyObject(INameBlacklist, name_blacklist))
+ self.assertEquals(u'foo', name_blacklist.regexp)
+ self.assertIs(None, name_blacklist.comment)
+
+ def test_create_with_two_args(self):
+ # Test NameBlacklistSet.create(regexp, comment).
+ name_blacklist = self.name_blacklist_set.create(u'foo', u'bar')
+ self.assertTrue(verifyObject(INameBlacklist, name_blacklist))
+ self.assertEquals(u'foo', name_blacklist.regexp)
+ self.assertEquals(u'bar', name_blacklist.comment)
+
+ def test_get(self):
+ # Test NameBlacklistSet.get().
+ name_blacklist = self.name_blacklist_set.create(u'foo', u'bar')
+ store = IStore(name_blacklist)
+ store.flush()
+ retrieved = self.name_blacklist_set.get(name_blacklist.id)
+ self.assertEquals(name_blacklist, retrieved)
+
+ def test_getAll(self):
+ # Test NameBlacklistSet.getAll().
+ result = [
+ (item.regexp, item.comment)
+ for item in self.name_blacklist_set.getAll()]
+ expected = [
+ ('^admin', None),
+ ('blacklist', 'For testing purposes'),
+ ]
+ self.assertEqual(expected, result)
+
+ def test_NameBlacklistSet_permissions(self):
+ # Verify that non-registry-experts do not have permission to
+ # access the NameBlacklistSet.
+ self.assertTrue(
+ check_permission('launchpad.View', self.name_blacklist_set))
+ self.assertTrue(
+ check_permission('launchpad.Edit', self.name_blacklist_set))
+ login(ANONYMOUS)
+ self.assertFalse(
+ check_permission('launchpad.View', self.name_blacklist_set))
+ self.assertFalse(
+ check_permission('launchpad.Edit', self.name_blacklist_set))
+
+ def test_NameBlacklist_permissions(self):
+ # Verify that non-registry-experts do not have permission to
+ # access the NameBlacklist.
+ name_blacklist = self.name_blacklist_set.create(u'foo')
+ self.assertTrue(check_permission('launchpad.View', name_blacklist))
+ self.assertTrue(check_permission('launchpad.Edit', name_blacklist))
+ login(ANONYMOUS)
+ self.assertFalse(check_permission('launchpad.View', name_blacklist))
+ self.assertFalse(check_permission('launchpad.Edit', name_blacklist))