1. Olemis Lang
  2. trac-rpc-dev

Commits

osimons  committed 9ce0ac8

[svn r6064] XmlRpcPlugin: Implemented a test subsystem, and the start of functional unittests - #5382.

Test subsystem depends on a patch for Trac 0.11-stable (not yet committed - see tracticket8388), and Trac needs to be running from source (either installing Trac using `python setup.py develop`, or adding Trac source to `PYTHONPATH` when running tests - it reuses Trac functional test infrastructure to create a test project and start a standalone server that answer requests for the duration of the test run.

The test server is available with basic authentication and 3 users (anonymous|user|admin), and any aspect of the plugin should be testable using this infrastructure. Currently it contains just a couple of complicated tests for workflow and security policy plugins, but a long list of plain method->result tests also needs to be added. Patches welcome :-)

Running tests for plugin: `python setup.py test`

  • Participants
  • Parent commits 9b3744c
  • Branches xmlrpcplugin

Comments (0)

Files changed (3)

File trunk/setup.py

View file
     url='http://trac-hacks.org/wiki/XmlRpcPlugin',
     description='XML-RPC interface to Trac',
     zip_safe=True,
+    test_suite = 'tracrpc.tests.suite',
     packages=find_packages(exclude=['*.tests']),
     package_data={
         'tracrpc': ['templates/*.html']

File trunk/tracrpc/tests/__init__.py

View file
+import unittest
+import os
+import time
+import urllib2
+
+try:
+    from trac.tests.functional.svntestenv import SvnFunctionalTestEnvironment
+
+    if not hasattr(SvnFunctionalTestEnvironment, 'init'):
+        raise Exception("\nTrac version is out of date. " \
+            "Tests require minimum Trac 0.11.x rXXXX to run.")
+
+    class RpcTestEnvironment(SvnFunctionalTestEnvironment):
+
+        def __del__(self):
+            print "\nStopping web server...\n"
+            self.stop()
+            if hasattr(SvnFunctionalTestEnvironment, '__del__'):
+                SvnFunctionalTestEnvironment.__del__(self)
+
+        def init(self):
+            self.trac_src = os.path.realpath(os.path.join( 
+                    __import__('trac', []).__file__, '..' , '..'))
+            print "\nFound Trac source: %s" % self.trac_src
+            SvnFunctionalTestEnvironment.init(self)
+
+        def post_create(self, env):
+            print "Enabling RPC plugin and permissions..."
+            env.config.set('components', 'tracrpc.*', 'enabled')
+            env.config.save()
+            self._tracadmin('permission', 'add', 'anonymous', 'XML_RPC')
+            print "Created test environment: %s" % self.dirname
+            parts = urllib2.urlparse.urlsplit(self.url)
+            self.url_anon = '%s://%s:%s/xmlrpc' % (parts[0], parts[1],
+                                self.port)
+            self.url_user = '%s://user:user@%s:%s/login/xmlrpc' % \
+                                (parts[0], parts[1], self.port)
+            self.url_admin = '%s://admin:admin@%s:%s/login/xmlrpc' % \
+                                (parts[0], parts[1], self.port)
+            print "Starting web server: %s:%s\n" % (self.url, self.port)
+            self.restart()
+            SvnFunctionalTestEnvironment.post_create(self, env)
+
+        def restart(self):
+            SvnFunctionalTestEnvironment.restart(self)
+            # Add a delay to make sure server comes up...
+            time.sleep(1)
+
+    rpc_testenv = RpcTestEnvironment(os.path.realpath(os.path.join(
+                os.path.realpath(__file__), '..', '..', '..', 'rpctestenv')),
+                '8765', 'http://127.0.0.1')
+
+    def suite():
+        suite = unittest.TestSuite()
+        import tracrpc.tests.ticket
+        suite.addTest(tracrpc.tests.ticket.suite())
+        return suite
+
+except Exception, e:
+    print e
+    print "Trac test infrastructure not available."
+    print "Install Trac as 'python setup.py develop' (run Trac from source).\n"
+    suite = unittest.TestSuite() # return empty suite

File trunk/tracrpc/tests/ticket.py

View file
+import unittest
+
+import xmlrpclib
+import os
+import shutil
+
+from tracrpc.tests import rpc_testenv
+
+class RpcTicketTestCase(unittest.TestCase):
+    
+    def setUp(self):
+        self.anon = xmlrpclib.ServerProxy(rpc_testenv.url_anon)
+        self.user = xmlrpclib.ServerProxy(rpc_testenv.url_user)
+        self.admin = xmlrpclib.ServerProxy(rpc_testenv.url_admin)
+
+    def test_getAvailableActions_DeleteTicket(self):
+        # http://trac-hacks.org/ticket/5387
+        tid = self.admin.ticket.create('abc', 'def', {})
+        self.assertEquals(False,
+                'delete' in self.admin.ticket.getAvailableActions(tid))
+        env = rpc_testenv.get_trac_environment()
+        shutil.copy(os.path.join(
+            rpc_testenv.trac_src, 'sample-plugins', 'workflow', 'DeleteTicket.py'),
+            os.path.join(rpc_testenv.tracdir, 'plugins'))
+        env.config.set('ticket', 'workflow',
+                'ConfigurableTicketWorkflow,DeleteTicketActionController')
+        env.config.save()
+        self.assertEquals(True,
+                'delete' in self.admin.ticket.getAvailableActions(tid))
+        self.assertEquals(False,
+                'delete' in self.user.ticket.getAvailableActions(tid))
+        env.config.set('ticket', 'workflow',
+                'ConfigurableTicketWorkflow')
+        env.config.save()
+        rpc_testenv.restart()
+        self.assertEquals(False,
+                'delete' in self.admin.ticket.getAvailableActions(tid))
+
+    def test_FineGrainedSecurity(self):
+        self.assertEquals(1, self.admin.ticket.create('abc', '123', {}))
+        self.assertEquals(2, self.admin.ticket.create('def', '456', {}))
+        # First some non-restricted tests for comparison:
+        self.assertRaises(xmlrpclib.Fault, self.anon.ticket.create, 'abc', 'def')
+        self.assertEquals([1,2], self.user.ticket.query())
+        self.assertTrue(self.user.ticket.get(2))
+        self.assertTrue(self.user.ticket.update(1, "ok"))
+        self.assertTrue(self.user.ticket.update(2, "ok"))
+        # Enable security policy and test
+        from trac.core import Component, implements
+        from trac.perm import IPermissionPolicy
+        policy = os.path.join(rpc_testenv.tracdir, 'plugins', 'TicketPolicy.py')
+        open(policy, 'w').write(
+        "from trac.core import *\n"
+        "from trac.perm import IPermissionPolicy\n"
+        "class TicketPolicy(Component):\n"
+        "    implements(IPermissionPolicy)\n"
+        "    def check_permission(self, action, username, resource, perm):\n"
+        "        if username == 'user' and resource and resource.id == 2:\n"
+        "            return False\n"
+        "        if username == 'anonymous' and action == 'TICKET_CREATE':\n"
+        "            return True\n")
+        env = rpc_testenv.get_trac_environment()
+        _old_conf = env.config.get('trac', 'permission_policies')
+        env.config.set('trac', 'permission_policies', 'TicketPolicy,'+_old_conf)
+        env.config.save()
+        rpc_testenv.restart()
+        self.assertEquals([1], self.user.ticket.query())
+        self.assertTrue(self.user.ticket.get(1))
+        self.assertRaises(xmlrpclib.Fault, self.user.ticket.get, 2)
+        self.assertTrue(self.user.ticket.update(1, "ok"))
+        self.assertRaises(xmlrpclib.Fault, self.user.ticket.update, 2, "not ok")
+        self.assertEquals(3, self.anon.ticket.create('efg', '789', {}))
+        # Clean, reset and simple verification
+        env.config.set('trac', 'permission_policies', _old_conf)
+        env.config.save()
+        os.unlink(policy)
+        rpc_testenv.restart()
+        self.assertEquals([1,2,3], self.user.ticket.query())
+        self.assertEquals(0, self.admin.ticket.delete(1))
+        self.assertEquals(0, self.admin.ticket.delete(2))
+        self.assertEquals(0, self.admin.ticket.delete(3))
+        
+def suite():
+    return unittest.makeSuite(RpcTicketTestCase)
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')