Author: chrisz
Date: Mon Jan 17 17:44:30 2011
New Revision: 7209
URL: http://trac.turbogears.org/changeset/7209

Log:
Made visit/identity Tests work with both SA and SO and both CherryPy 3.1 and 
3.2.

Modified:
   branches/1.5/turbogears/identity/base.py
   branches/1.5/turbogears/identity/exceptions.py
   branches/1.5/turbogears/identity/tests/test_identity_sa.py
   branches/1.5/turbogears/identity/tests/test_identity_so.py
   branches/1.5/turbogears/identity/visitor.py
   branches/1.5/turbogears/startup.py
   branches/1.5/turbogears/testutil.py
   branches/1.5/turbogears/visit/__init__.py
   branches/1.5/turbogears/visit/api.py

Modified: branches/1.5/turbogears/identity/base.py
==============================================================================
--- branches/1.5/turbogears/identity/base.py    Mon Jan 17 14:34:21 2011        
(r7208)
+++ branches/1.5/turbogears/identity/base.py    Mon Jan 17 17:44:30 2011        
(r7209)
@@ -85,7 +85,6 @@
 
     try:
         cherrypy.request.user_name = identity.user_name
-
     except AttributeError:
         cherrypy.request.user_name = None
 
@@ -149,7 +148,6 @@
     def identity(self):
         try:
             identity = cherrypy.request.identity
-
         except AttributeError:
             identity = None
 
@@ -184,7 +182,6 @@
     def __getattr__(self, name):
         try:
             provider = cherrypy.request.identityProvider
-
         except AttributeError:
             try:
                 provider = create_default_provider()

Modified: branches/1.5/turbogears/identity/exceptions.py
==============================================================================
--- branches/1.5/turbogears/identity/exceptions.py      Mon Jan 17 14:34:21 
2011        (r7208)
+++ branches/1.5/turbogears/identity/exceptions.py      Mon Jan 17 17:44:30 
2011        (r7209)
@@ -50,19 +50,22 @@
     presence of an HTTP request.
 
     """
+
     def __str__(self):
-        return "An attempt was made to use a facility of the TurboGears " \
-            "Identity Management framework that relies on an HTTP request " \
-            "outside of a request."
+        return self.args and self.args[0] or (
+            "An attempt was made to use a facility of the TurboGears "
+            "Identity Management framework that relies on an HTTP request "
+            "outside of a request.")
 
 
 class IdentityManagementNotEnabledException(IdentityException):
     """User forgot to enable Identity management."""
 
     def __str__(self):
-        return "An attempt was made to use a facility of the TurboGears " \
-            "Identity Management framework, but identity management hasn't " \
-            "been enabled in the config file [via identity.on]."
+        return self.args and self.args[0] or (
+            "An attempt was made to use a facility of the TurboGears "
+            "Identity Management framework, but identity management hasn't "
+            "been enabled in the config file (via identity.on).")
 
 
 class IdentityConfigurationException(IdentityException):
@@ -72,11 +75,10 @@
     correctly. Mostly, when failure_url is not specified.
 
     """
-    args = ()
 
     def __str__(self):
-        return (self.args and self.args[0] or
-            'Unknown Identity configuration error')
+        return self.args and self.args[0] or (
+            "Unknown Identity configuration error.")
 
 
 class IdentityFailure(cherrypy.InternalRedirect, IdentityException):

Modified: branches/1.5/turbogears/identity/tests/test_identity_sa.py
==============================================================================
--- branches/1.5/turbogears/identity/tests/test_identity_sa.py  Mon Jan 17 
14:34:21 2011        (r7208)
+++ branches/1.5/turbogears/identity/tests/test_identity_sa.py  Mon Jan 17 
17:44:30 2011        (r7209)
@@ -7,7 +7,7 @@
 import formencode
 import cherrypy
 
-from turbogears import testutil, database, identity, config, expose
+from turbogears import testutil, identity, config, expose
 from turbogears.controllers import Controller, RootController
 from turbogears.database import session
 from turbogears.identity import (SecureObject, SecureResource,
@@ -274,7 +274,7 @@
 
     @expose()
     def is_anonymous(self):
-        assert cherrypy.serving.request.identity.user_name == None
+        assert cherrypy.serving.request.identity.user_name is None
         assert cherrypy.serving.request.identity.anonymous
         assert identity.current.anonymous
         return 'is_anonymous'
@@ -293,26 +293,35 @@
 
     def setUp(self):
         # identity requires visit and a failure_url
-        test_config = {'global': {
-            'visit.on': True, 'identity.on': True,
+        test_config = {
+            'visit.on': True,
             'visit.manager': 'sqlalchemy',
-            'identity.failure_url': '/identity_failed',
+            'identity.on': True,
             'identity.provider': 'sqlalchemy',
-            'identity.saprovider.encryption_algorithm': None,
+            'identity.failure_url': '/identity_failed',
+            'identity.source': 'form,http_auth,visit',
             'identity.http_basic_auth': False,
             'identity.http_auth_realm': None,
+            'identity.http_basic_auth': False,
+            'identity.form.user_name': 'user_name',
+            'identity.form.password': 'password',
+            'identity.form.submit': 'login',
+            'identity.saprovider.encryption_algorithm': None,
+            'identity.custom_encryption': None,
             'tg.strict_parameters': True,
-            'tg.allow_json': False}}
+            'tg.allow_json': False}
         if not self.config:
             original_config = dict()
-            for key in test_config['global']:
+            for key in test_config:
                 original_config[key] = config.get(key)
             self.config = original_config
         config.configure_loggers(test_config)
-        config.update(test_config['global'])
+        config.update(test_config)
+        testutil.stop_server(tg_only=False)
         super(TestIdentity, self).setUp()
         self.init_model()
 
+
     def init_model(self):
         if not session.query(TG_User).first():
             user = TG_User()
@@ -511,14 +520,18 @@
             'user_name=samIam&password=secret&login=Login')
         assert 'in_peon_group' in response, response
         user = TG_User.by_user_name(u'samIam')
+        assert user.user_name == 'samIam'
 
     def test_require_group_viewable(self):
         """Test that the current user and group properties are set 
correctly."""
         response = self.app.get('/in_peon_group?'
             'user_name=samIam&password=secret&login=Login')
+        assert 'in_peon_group' in response, response
         user = TG_User.by_user_name(u'samIam')
+        assert user.user_name == 'samIam'
         group_ids = set((TG_Group.by_group_name(u'peon').group_id,
             TG_Group.by_group_name(u'other').group_id))
+        assert group_ids == set((2, 3))
 
     def test_user_not_in_right_group(self):
         """Test that a user is denied access if they aren't in the right group.
@@ -529,8 +542,7 @@
 
     def test_require_permission(self):
         """Test that an anonymous user is denied access to a permission
-        restricted url.
-        """
+        restricted url."""
         response = self.app.get('/has_chopper_permission', status=403)
         assert 'identity_failed_answer' in response, response
 
@@ -623,6 +635,7 @@
         user.email_address = u'[email protected]'
         user.password = 'geheim'
         session.add(user)
+        assert user.user_name == u'säm'
         response = self.app.get('/logged_in_only?'
             'user_name=säm&password=geheim&login=Login')
         assert 'logged_in_only' in response, response
@@ -733,16 +746,17 @@
 class TestIdentityVisitPlugin(unittest.TestCase):
 
     def setUp(self):
-        test_config = {'global': {
+        test_config = {
             'visit.on': True,
+            'visit.manager': 'sqlalchemy',
             'identity.on': True,
             'identity.provider': 'sqlalchemy',
-            'identity.source': 'form, http_auth, visit, '}}
+            'identity.source': 'form, http_auth, visit, '}
         original_config = dict()
-        for key in test_config['global']:
+        for key in test_config:
             original_config[key] = config.get(key)
         self._original_config = original_config
-        config.update(test_config['global'])
+        config.update(test_config)
 
     def tearDown(self):
         config.update(self._original_config)

Modified: branches/1.5/turbogears/identity/tests/test_identity_so.py
==============================================================================
--- branches/1.5/turbogears/identity/tests/test_identity_so.py  Mon Jan 17 
14:34:21 2011        (r7208)
+++ branches/1.5/turbogears/identity/tests/test_identity_so.py  Mon Jan 17 
17:44:30 2011        (r7209)
@@ -276,7 +276,7 @@
 
     @expose()
     def is_anonymous(self):
-        assert cherrypy.serving.request.identity.user_name == None
+        assert cherrypy.serving.request.identity.user_name is None
         assert cherrypy.serving.request.identity.anonymous
         assert identity.current.anonymous
         return 'is_anonymous'
@@ -295,23 +295,29 @@
 
     def setUp(self):
         # identity requires visit and a failure_url
-        test_config = {'global': {
-            'visit.on': True, 'identity.on': True,
+        test_config = {
+            'visit.on': True,
             'visit.manager': 'sqlobject',
-            'identity.failure_url': '/identity_failed',
+            'identity.on': True,
             'identity.provider': 'sqlobject',
-            'identity.soprovider.encryption_algorithm': None,
+            'identity.failure_url': '/identity_failed',
+            'identity.source': 'form,http_auth,visit',
             'identity.http_basic_auth': False,
             'identity.http_auth_realm': None,
+            'identity.form.user_name': 'user_name',
+            'identity.form.password': 'password',
+            'identity.form.submit': 'login',
+            'identity.soprovider.encryption_algorithm': None,
+            'identity.custom_encryption': None,
             'tg.strict_parameters': True,
-            'tg.allow_json': False}}
+            'tg.allow_json': False}
         if not self.config:
             original_config = dict()
-            for key in test_config['global']:
+            for key in test_config:
                 original_config[key] = config.get(key)
             self.config = original_config
         config.configure_loggers(test_config)
-        config.update(test_config['global'])
+        config.update(test_config)
         super(TestIdentity, self).setUp()
         self.init_model()
 
@@ -540,14 +546,18 @@
             'user_name=samIam&password=secret&login=Login')
         assert 'in_peon_group' in response, response
         user = TG_User.by_user_name('samIam')
+        assert user.user_name == 'samIam'
 
     def test_require_group_viewable(self):
         """Test that the current user and group properties are set 
correctly."""
         response = self.app.get('/in_peon_group?'
             'user_name=samIam&password=secret&login=Login')
+        assert 'in_peon_group' in response, response
         user = TG_User.by_user_name('samIam')
+        assert user.user_name == 'samIam'
         group_ids = set((TG_Group.by_group_name('peon').id,
             TG_Group.by_group_name('other').id))
+        assert group_ids == set((2, 3))
 
     def test_user_not_in_right_group(self):
         """Test that a user is denied access if they aren't in the right group.
@@ -558,8 +568,7 @@
 
     def test_require_permission(self):
         """Test that an anonymous user is denied access to a permission
-        restricted url.
-        """
+        restricted url."""
         response = self.app.get('/has_chopper_permission', status=403)
         assert 'identity_failed_answer' in response, response
 
@@ -648,6 +657,7 @@
         """Test that we can have non-ascii user names."""
         user = TG_User(user_name=u'säm', display_name=u'Sämüel Käsfuß',
             email_address='[email protected]', password='geheim')
+        assert user.user_name == u'säm'
         response = self.app.get('/logged_in_only?'
             'user_name=säm&password=geheim&login=Login')
         assert 'logged_in_only' in response, response
@@ -759,16 +769,17 @@
 class TestIdentityVisitPlugin(unittest.TestCase):
 
     def setUp(self):
-        test_config = {'global': {
+        test_config = {
             'visit.on': True,
+            'visit.manager': 'sqlobject',
             'identity.on': True,
             'identity.provider': 'sqlobject',
-            'identity.source': 'form, http_auth, visit, '}}
+            'identity.source': 'form, http_auth, visit, '}
         original_config = dict()
-        for key in test_config['global']:
+        for key in test_config:
             original_config[key] = config.get(key)
         self._original_config = original_config
-        config.update(test_config['global'])
+        config.update(test_config)
 
     def tearDown(self):
         config.update(self._original_config)

Modified: branches/1.5/turbogears/identity/visitor.py
==============================================================================
--- branches/1.5/turbogears/identity/visitor.py Mon Jan 17 14:34:21 2011        
(r7208)
+++ branches/1.5/turbogears/identity/visitor.py Mon Jan 17 17:44:30 2011        
(r7209)
@@ -24,14 +24,20 @@
 log = logging.getLogger("turbogears.identity")
 
 
+# Global Visit plugin
+_plugin = None
+
+
 # Interface for the TurboGears extension
 
 def start_extension():
-    """Register the IdentityVisitPluguin with the visit plug-in framework.
+    """Register the IdentityVisitPlugin with the visit plugin framework.
 
     Also sets up the configured Identity provider.
 
     """
+    global _plugin
+
     # Bail out if the application hasn't enabled this extension
     if not config.get('identity.on', False):
         return
@@ -39,24 +45,37 @@
     # Identity requires that Visit tracking be enabled
     if not config.get('visit.on', False):
         raise IdentityConfigurationException(
-            "Visit tracking must be enabled (visit.on)")
+            "Visit tracking must be enabled (via visit.on).")
+
+    # Bail out if Visit tracking plugin is already registered
+    if _plugin:
+        log.info("Identity already started")
+        return
 
     log.info("Identity starting")
     # Temporary until tg-admin can call create_extension_model
     create_extension_model()
-    # Register the plugin for the Visit Tracking framework
-    visit.enable_visit_plugin(IdentityVisitPlugin())
+    # Create and register the plugin for the Visit Tracking framework
+    _plugin = IdentityVisitPlugin()
+    visit.enable_visit_plugin(_plugin)
 
 
 def shutdown_extension():
-    """Stops the IdentityVisitPlugin.
+    """Stops the IdentityVisitPlugin."""
+    global _plugin
 
-    This is practically a no-op currently.
-
-    """
     # Bail out if the application hasn't enabled this extension
     if not config.get('identity.on', False):
         return
+
+    # Bail out if the Visit tracking plugin is already unregistered
+    if not _plugin:
+        log.info("Identity already shut down")
+        return
+
+    # Unregister the plugin for the Visit Tracking framework
+    visit.disable_visit_plugin(_plugin)
+    _plugin = None
     log.info("Identity has been shut down.")
 
 
@@ -67,7 +86,7 @@
 
 
 class IdentityVisitPlugin(object):
-    """Visit plug-in tying the Identity framework to the visit management."""
+    """Visit plugin tying the Identity framework to the visit management."""
 
     def __init__(self):
         log.info("Identity visit plugin initialized")
@@ -85,7 +104,10 @@
         # Sources for identity information and the order in which they should 
be
         # checked. These terms are mapped to methods by prepending
         # "identity_from_".
-        sources = get('identity.source', 'form,http_auth,visit') or ''
+        sources = get('identity.source', 'form,http_auth,visit')
+        if not sources:
+            raise IdentityConfigurationException(
+                "You must set some identity source (via identity.source).")
         sources = [s.strip() for s in sources.split(',')]
         self.identity_sources = list()
         for s in sources:
@@ -93,8 +115,8 @@
                 try:
                     source_method = getattr(self, 'identity_from_' + s)
                 except AttributeError:
-                    raise IdentityConfigurationException(
-                        "Invalid identity source: %s" % s)
+                    raise IdentityConfigurationException("Invalid "
+                        "identity source: %s (check identity.source)" % s)
                 self.identity_sources.append(source_method)
 
     def identity_from_request(self, visit_key):
@@ -202,7 +224,7 @@
 
     def record_request(self, visit):
         """Authenticate request and try to associate the visit with an 
identity."""
-        # This method is called by the visit plug-in mechanism on each request 
with a visit key.
+        # This method is called by the visit plugin mechanism on each request 
with a visit key.
         # default to keeping the identity filter off
         if not config.get('identity.on', True):
             log.debug("Identity is not enabled. Setting current identity to 
None")

Modified: branches/1.5/turbogears/startup.py
==============================================================================
--- branches/1.5/turbogears/startup.py  Mon Jan 17 14:34:21 2011        (r7208)
+++ branches/1.5/turbogears/startup.py  Mon Jan 17 17:44:30 2011        (r7209)
@@ -26,6 +26,7 @@
 
 from turbogears import config, database, scheduler, view
 from turbogears.visit.api import VisitTool
+from turbogears.identity.exceptions import IdentityConfigurationException
 from turbogears.identity.base import verify_identity_status
 from turbogears.database import hub_registry
 from turbogears.dispatchers import VirtualPathDispatcher
@@ -160,18 +161,18 @@
     log.info("Adding CherryPy tools, hooks and dispatchers...")
     config_static()
     config_root()
-    if config.get('visit.on', False):
-        # The VisitTool needs to happen after cherrypy.tools.decode
-        # so that request params are properly decoded before it runs,
-        # but it must run before the NestedVariablesHook to work properly
-        cherrypy.tools.visit = cherrypy.Tool(
-            'before_handler', VisitTool(), priority=55)
     hooks = cherrypy.request.hooks
     hooks.attach('before_finalize', verify_identity_status)
     hooks.attach('on_end_resource', database.EndTransactions)
     # The NestedVariablesHook needs to happen after cherrypy.tools.decode
     # so that request params are properly decoded before it runs
-    hooks.attach('before_handler', NestedVariablesHook, priority=60)
+    hooks.attach('before_handler', NestedVariablesHook, priority=70)
+    if config.get('visit.on', False):
+        # The VisitTool needs to happen after cherrypy.tools.decode
+        # so that request params are properly decoded before it runs,
+        # but it must run before the NestedVariablesHook to work properly
+        cherrypy.tools.visit = cherrypy.Tool(
+            'before_handler', VisitTool(), priority=60)
 
     # Register server with Bonjour framework
     bonjoursetting = config.get('tg.bonjour', None)
@@ -206,6 +207,8 @@
             except Exception, e:
                 log.exception("Error starting TurboGears extension %s: %s",
                     entrypoint, e)
+                if isinstance(e, IdentityConfigurationException):
+                    raise # don't swallow internal configuration error
 
     # Call registered startup functions
     if call_on_startup:

Modified: branches/1.5/turbogears/testutil.py
==============================================================================
--- branches/1.5/turbogears/testutil.py Mon Jan 17 14:34:21 2011        (r7208)
+++ branches/1.5/turbogears/testutil.py Mon Jan 17 17:44:30 2011        (r7209)
@@ -58,6 +58,8 @@
 def mount(controller, path='/'):
     """Mount a controller at a path.  Returns a WSGI application."""
     startup.config_static()
+    if path == '/':
+        startup.config_root()
     config.update({'environment': 'test_suite', 'log.screen': False})
     cherrypy.tree.mount(controller, path, config=config.app)
     return make_wsgiapp()

Modified: branches/1.5/turbogears/visit/__init__.py
==============================================================================
--- branches/1.5/turbogears/visit/__init__.py   Mon Jan 17 14:34:21 2011        
(r7208)
+++ branches/1.5/turbogears/visit/__init__.py   Mon Jan 17 17:44:30 2011        
(r7209)
@@ -8,11 +8,14 @@
     'create_extension_model',
     'current',
     'enable_visit_plugin',
+    'disable_visit_plugin',
     'set_current',
     'start_extension',
     'shutdown_extension',
 ]
 
-from turbogears.visit.api import (BaseVisitManager, Visit, VisitTool,
-    create_extension_model, current, enable_visit_plugin, set_current,
+from turbogears.visit.api import (
+    BaseVisitManager, Visit, VisitTool,
+    create_extension_model, current, set_current,
+    enable_visit_plugin, disable_visit_plugin,
     start_extension, shutdown_extension)

Modified: branches/1.5/turbogears/visit/api.py
==============================================================================
--- branches/1.5/turbogears/visit/api.py        Mon Jan 17 14:34:21 2011        
(r7208)
+++ branches/1.5/turbogears/visit/api.py        Mon Jan 17 17:44:30 2011        
(r7209)
@@ -8,6 +8,7 @@
     'create_extension_model',
     'current',
     'enable_visit_plugin',
+    'disable_visit_plugin',
     'set_current',
     'start_extension',
     'shutdown_extension',
@@ -120,6 +121,11 @@
     _plugins.append(plugin)
 
 
+def disable_visit_plugin(plugin):
+    """Unregister a visit tracking plugin."""
+    _plugins[:] = [p for p in _plugins if p is not plugin]
+
+
 class Visit(object):
     """Basic container for visit related data."""
 

Reply via email to