From f0d12ea82e494c13ce504b73a13c6967ab8fb3e1 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 19 Dec 2009 23:05:23 +0000 Subject: - Add four new testing-related APIs to the ``repoze.bfg.configuration.Configurator`` class: ``testing_securitypolicy``, ``testing_models``, ``testing_add_subscriber``, and ``testing_add_template``. These were added in order to provide more direct access to the functionality of the ``repoze.bfg.testing`` APIs named ``registerDummySecurityPolicy``, ``registerModels``, ``registerEventListener``, and ``registerTemplateRenderer`` when a configurator is used. The ``testing`` APIs named are nominally deprecated (although they will likely remain around "forever", as they are in heavy use in the wild). - Doc-deprecated most helper functions in the ``repoze.bfg.testing`` module. These helper functions likely won't be removed any time soon, nor will they generate a warning any time soon, due to their heavy use in the wild, but equivalent behavior exists in methods of a Configurator. --- repoze/bfg/configuration.py | 83 ++++++++++++++++++++++++++++++++++ repoze/bfg/testing.py | 83 +++++++++++++++++----------------- repoze/bfg/tests/test_configuration.py | 78 ++++++++++++++++++++++++++++++++ repoze/bfg/tests/test_testing.py | 46 +++++++++++++------ 4 files changed, 235 insertions(+), 55 deletions(-) (limited to 'repoze') diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py index 8d88c5384..ccfeb7347 100644 --- a/repoze/bfg/configuration.py +++ b/repoze/bfg/configuration.py @@ -29,6 +29,8 @@ from repoze.bfg.interfaces import IRouteRequest from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.interfaces import ISecuredView from repoze.bfg.interfaces import ISettings +from repoze.bfg.interfaces import ITemplateRenderer +from repoze.bfg.interfaces import ITraverser from repoze.bfg.interfaces import IView from repoze.bfg import chameleon_text @@ -51,6 +53,7 @@ from repoze.bfg.settings import Settings from repoze.bfg.static import StaticRootFactory from repoze.bfg.threadlocal import get_current_registry from repoze.bfg.threadlocal import manager +from repoze.bfg.traversal import traversal_path from repoze.bfg.traversal import DefaultRootFactory from repoze.bfg.traversal import find_interface from repoze.bfg.urldispatch import RoutesMapper @@ -1183,6 +1186,86 @@ class Configurator(object): view_for=StaticRootFactory, factory=StaticRootFactory(spec), _info=_info) + # testing API + + def testing_securitypolicy(self, userid=None, groupids=(), + permissive=True): + """Unit/integration testing helper: registers a a pair of + dummy :mod:`repoze.bfg` security policies: an + :term:`authorization policy` and an :term:`authentication + policy`) using the userid ``userid`` and the group ids + ``groupids``. If ``permissive`` is true, a 'permissive' + authorization policy is registered; this policy allows all + access. If ``permissive`` is false, a nonpermissive + authorization policy is registered; this policy denies all + access. This function is most useful when testing code that + uses the ``repoze.bfg.security`` APIs named + ``has_permission``, ``authenticated_userid``, + ``effective_principals`` and + ``principals_allowed_by_permission``. + """ + from repoze.bfg.testing import DummySecurityPolicy + policy = DummySecurityPolicy(userid, groupids, permissive) + self.registry.registerUtility(policy, IAuthorizationPolicy) + self.registry.registerUtility(policy, IAuthenticationPolicy) + + def testing_models(self, models): + """Unit/integration testing helper: registers a dictionary of + models that can be resolved via + ``repoze.bfg.traversal.find_model``. This is most useful for + testing code that wants to call the + ``repoze.bfg.traversal.find_model`` API. The ``find_model`` + API is called with a path as one of its arguments. If the + dictionary you register when calling this method contains that + path as a string key (e.g. ``/foo/bar`` or ``foo/bar``), the + corresponding value will be returned to ``find_model`` (and + thus to your code) when ``find_model`` is called with an + equivalent path string or tuple.""" + class DummyTraverserFactory: + def __init__(self, context): + self.context = context + + def __call__(self, request): + path = request['PATH_INFO'] + ob = models[path] + traversed = traversal_path(path) + return {'context':ob, 'view_name':'','subpath':(), + 'traversed':traversed, 'virtual_root':ob, + 'virtual_root_path':(), 'root':ob} + self.registry.registerAdapter(DummyTraverserFactory, (Interface,), + ITraverser) + return models + + def testing_add_subscriber(self, event_iface=Interface): + """Unit/integration testing helper: Registers a + :term:`subscriber` listening for events of the type + ``event_iface`` and returns a list which is appended to by the + subscriber. When an event is dispatched that matches + ``event_iface``, that event will be appended to the list. You + can then compare the values in the list to expected event + notifications. This method is useful when testing code that + wants to call ``registry.notify``, + ``zope.component.event.dispatch`` or + ``zope.component.event.objectEventNotify``. + """ + L = [] + def subscriber(*event): + L.extend(event) + self.add_subscriber(subscriber, event_iface) + return L + + def testing_add_template(self, path, renderer=None): + """ Register a template tenderer at ``path`` (usually a relative + filename ala ``templates/foo.pt``) and return the renderer object. + If the ``renderer`` argument is None, a 'dummy' renderer will be + used. This function is useful when testing code that calls the + ``render_template_to_response`` or any other ``render_template*`` + API of any of the built-in templating systems.""" + from repoze.bfg.testing import DummyTemplateRenderer + if renderer is None: + renderer = DummyTemplateRenderer() + self.registry.registerUtility(renderer, ITemplateRenderer, path) + return renderer def _make_predicates(xhr=None, request_method=None, path_info=None, request_param=None, header=None, accept=None, diff --git a/repoze/bfg/testing.py b/repoze/bfg/testing.py index 5d0a756e7..cd43c297e 100644 --- a/repoze/bfg/testing.py +++ b/repoze/bfg/testing.py @@ -10,14 +10,10 @@ from zope.interface import implements from zope.interface import Interface from zope.interface import alsoProvides -from repoze.bfg.interfaces import IAuthenticationPolicy -from repoze.bfg.interfaces import IAuthorizationPolicy from repoze.bfg.interfaces import IRequest from repoze.bfg.interfaces import IRoutesMapper from repoze.bfg.interfaces import ISecuredView from repoze.bfg.interfaces import ISettings -from repoze.bfg.interfaces import ITemplateRenderer -from repoze.bfg.interfaces import ITraverser from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IViewPermission @@ -32,7 +28,6 @@ from repoze.bfg.security import has_permission from repoze.bfg.settings import Settings from repoze.bfg.threadlocal import get_current_registry from repoze.bfg.threadlocal import manager -from repoze.bfg.traversal import traversal_path from repoze.bfg.urldispatch import RoutesMapper from repoze.bfg.zcml import zcml_configure # API @@ -52,11 +47,15 @@ def registerDummySecurityPolicy(userid=None, groupids=(), permissive=True): ``repoze.bfg.security`` APIs named ``has_permission``, ``authenticated_userid``, ``effective_principals``, ``principals_allowed_by_permission``, and so on. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the ``testing_securitypolicy`` method of a + :term:`Configurator` in your unit and integration tests. """ - policy = DummySecurityPolicy(userid, groupids, permissive) - reg = get_current_registry() - reg.registerUtility(policy, IAuthorizationPolicy) - reg.registerUtility(policy, IAuthenticationPolicy) + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_securitypolicy(userid=userid, groupids=groupids, + permissive=permissive) def registerModels(models): """ Registers a dictionary of models that can be resolved via @@ -68,21 +67,15 @@ def registerModels(models): string key (e.g. ``/foo/bar`` or ``foo/bar``), the corresponding value will be returned to ``find_model`` (and thus to your code) when ``find_model`` is called with an equivalent path string or - tuple.""" - class DummyTraverserFactory: - def __init__(self, context): - self.context = context - - def __call__(self, request): - path = request['PATH_INFO'] - ob = models[path] - traversed = traversal_path(path) - return {'context':ob, 'view_name':'','subpath':(), - 'traversed':traversed, 'virtual_root':ob, - 'virtual_root_path':(), 'root':ob} - - registerTraverser(DummyTraverserFactory) - return models + tuple. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the ``testing_models`` method of a + :term:`Configurator` in your unit and integration tests. + """ + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_models(models) def registerEventListener(event_iface=Interface): """ Registers an :term:`event` listener (aka :term:`subscriber`) @@ -94,12 +87,14 @@ def registerEventListener(event_iface=Interface): testing code that wants to call ``registry.notify``, ``zope.component.event.dispatch`` or ``zope.component.event.objectEventNotify``. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the ``testing_add_subscriber`` method of a + :term:`Configurator` in your unit and integration tests. """ - L = [] - def subscriber(*event): - L.extend(event) - registerSubscriber(subscriber, event_iface) - return L + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_add_subscriber(event_iface) def registerTemplateRenderer(path, renderer=None): """ Register a template tenderer at ``path`` (usually a relative @@ -107,10 +102,16 @@ def registerTemplateRenderer(path, renderer=None): If the ``renderer`` argument is None, a 'dummy' renderer will be used. This function is useful when testing code that calls the ``render_template_to_response`` or any other ``render_template*`` - API of any of the built-in templating systems.""" - if renderer is None: - renderer = DummyTemplateRenderer() - return registerUtility(renderer, ITemplateRenderer, path) + API of any of the built-in templating systems. + + .. warning:: This API is deprecated as of :mod:`repoze.bfg` 1.2. + Instead use the ``testing_add_template`` method of a + :term:`Configurator` in your unit and integration tests. + + """ + registry = get_current_registry() + config = Configurator(registry=registry) + return config.testing_add_template(path, renderer) # registerDummyRenderer is a deprecated alias that should never be removed # (far too much usage in the wild) @@ -249,14 +250,9 @@ def registerSubscriber(subscriber, iface=Interface): Instead use the ``add_subscriber`` method of a :term:`Configurator` in your unit and integration tests. """ - reg = get_current_registry() - if not isinstance(iface, (tuple, list)): - iface = (iface,) - reg.registerHandler(subscriber, iface) - return subscriber - -def registerTraverser(traverser, for_=Interface): - return registerAdapter(traverser, for_, ITraverser) + registry = get_current_registry() + config = Configurator(registry) + return config.add_subscriber(subscriber, iface=iface) def registerRoute(path, name, factory=None): """ Register a new :term:`route` using a path @@ -296,6 +292,11 @@ def registerRoutesMapper(root_factory=None): ``repoze.bfg.testing.registerRoute``. .. note:: This API was added in :mod:`repoze.bfg` version 1.1. + + .. warning:: This API is not useful in :mod:`repoze.bfg` 1.2 or better + because a route mapper is no longer required to be + present for successful operation of the system without + any routes present. """ mapper = RoutesMapper() reg = get_current_registry() diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py index 9c48cc010..a303f550f 100644 --- a/repoze/bfg/tests/test_configuration.py +++ b/repoze/bfg/tests/test_configuration.py @@ -1864,6 +1864,81 @@ class ConfiguratorTests(unittest.TestCase): result = config.registry.queryUtility(IDummy, name='foo') self.assertEqual(result, 'OK') + def test_testing_securitypolicy(self): + from repoze.bfg.testing import DummySecurityPolicy + config = self._makeOne() + config.testing_securitypolicy('user', ('group1', 'group2'), + permissive=False) + from repoze.bfg.interfaces import IAuthenticationPolicy + from repoze.bfg.interfaces import IAuthorizationPolicy + ut = config.registry.getUtility(IAuthenticationPolicy) + self.failUnless(isinstance(ut, DummySecurityPolicy)) + ut = config.registry.getUtility(IAuthorizationPolicy) + self.assertEqual(ut.userid, 'user') + self.assertEqual(ut.groupids, ('group1', 'group2')) + self.assertEqual(ut.permissive, False) + + def test_testing_models(self): + from repoze.bfg.traversal import find_model + from repoze.bfg.interfaces import ITraverser + ob1 = object() + ob2 = object() + models = {'/ob1':ob1, '/ob2':ob2} + config = self._makeOne() + config.testing_models(models) + adapter = config.registry.getAdapter(None, ITraverser) + result = adapter({'PATH_INFO':'/ob1'}) + self.assertEqual(result['context'], ob1) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob1',)) + self.assertEqual(result['virtual_root'], ob1) + self.assertEqual(result['virtual_root_path'], ()) + result = adapter({'PATH_INFO':'/ob2'}) + self.assertEqual(result['context'], ob2) + self.assertEqual(result['view_name'], '') + self.assertEqual(result['subpath'], ()) + self.assertEqual(result['traversed'], (u'ob2',)) + self.assertEqual(result['virtual_root'], ob2) + self.assertEqual(result['virtual_root_path'], ()) + self.assertRaises(KeyError, adapter, {'PATH_INFO':'/ob3'}) + try: + config.begin() + self.assertEqual(find_model(None, '/ob1'), ob1) + finally: + config.end() + + def test_testing_add_subscriber_single(self): + config = self._makeOne() + L = config.testing_add_subscriber(IDummy) + event = DummyEvent() + config.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + config.registry.notify(object()) + self.assertEqual(len(L), 1) + + def test_testing_add_subscriber_multiple(self): + config = self._makeOne() + L = config.testing_add_subscriber((Interface, IDummy)) + event = DummyEvent() + event.object = 'foo' + # the below is the equivalent of z.c.event.objectEventNotify(event) + config.registry.subscribers((event.object, event), None) + self.assertEqual(len(L), 2) + self.assertEqual(L[0], 'foo') + self.assertEqual(L[1], event) + + def test_testing_add_subscriber_defaults(self): + config = self._makeOne() + L = config.testing_add_subscriber() + event = object() + config.registry.notify(event) + self.assertEqual(L[-1], event) + event2 = object() + config.registry.notify(event2) + self.assertEqual(L[-1], event2) + class Test__map_view(unittest.TestCase): def setUp(self): from repoze.bfg.registry import Registry @@ -2872,3 +2947,6 @@ class DummyFactory(object): def __call__(self): """ """ +class DummyEvent: + implements(IDummy) + diff --git a/repoze/bfg/tests/test_testing.py b/repoze/bfg/tests/test_testing.py index 72ecd96b0..4762eda19 100644 --- a/repoze/bfg/tests/test_testing.py +++ b/repoze/bfg/tests/test_testing.py @@ -221,28 +221,32 @@ class Test_registerView(TestBase): class Test_registerAdapter(TestBase): def test_registerAdapter(self): - from zope.interface import implements from zope.interface import Interface class provides(Interface): pass class Provider: - implements(provides) - def __init__(self, context, request): - self.context = context - self.request = request + pass class for_(Interface): pass - class For_: - implements(for_) - for1 = For_() - for2 = For_() from repoze.bfg import testing testing.registerAdapter(Provider, (for_, for_), provides, name='foo') - adapter = self.registry.getMultiAdapter( - (for1, for2), provides, name='foo') - self.failUnless(isinstance(adapter, Provider)) - self.assertEqual(adapter.context, for1) - self.assertEqual(adapter.request, for2) + adapter = self.registry.adapters.lookup( + (for_, for_), provides, name='foo') + self.assertEqual(adapter, Provider) + + def test_registerAdapter_notlist(self): + from zope.interface import Interface + class provides(Interface): + pass + class Provider: + pass + class for_(Interface): + pass + from repoze.bfg import testing + testing.registerAdapter(Provider, for_, provides, name='foo') + adapter = self.registry.adapters.lookup( + (for_,), provides, name='foo') + self.assertEqual(adapter, Provider) class Test_registerUtility(TestBase): def test_registerUtility(self): @@ -259,6 +263,20 @@ class Test_registerUtility(TestBase): testing.registerUtility(utility, iface, name='mudge') self.assertEqual(self.registry.getUtility(iface, name='mudge')(), 'foo') +class Test_registerSubscriber(TestBase): + def test_it(self): + from repoze.bfg import testing + L = [] + def subscriber(event): + L.append(event) + testing.registerSubscriber(subscriber, iface=IDummy) + event = DummyEvent() + self.registry.notify(event) + self.assertEqual(len(L), 1) + self.assertEqual(L[0], event) + self.registry.notify(object()) + self.assertEqual(len(L), 1) + class Test_registerRoute(TestBase): def test_registerRoute(self): from repoze.bfg.url import route_url -- cgit v1.2.3