[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [bridgedb/develop] Start writing unittests for the bridgedb.HTTPServer module.



commit 9a4bd295a89f2be7f700bfa68d34bcba03954e40
Author: Isis Lovecruft <isis@xxxxxxxxxxxxxx>
Date:   Tue Mar 18 04:12:46 2014 +0000

    Start writing unittests for the bridgedb.HTTPServer module.
    
    The ReCaptchaProtectedResourceTests.test_render_POST_* unittests expose
    the bug in #11231.
---
 lib/bridgedb/test/test_HTTPServer.py |  371 ++++++++++++++++++++++++++++++++++
 1 file changed, 371 insertions(+)

diff --git a/lib/bridgedb/test/test_HTTPServer.py b/lib/bridgedb/test/test_HTTPServer.py
new file mode 100644
index 0000000..da910c6
--- /dev/null
+++ b/lib/bridgedb/test/test_HTTPServer.py
@@ -0,0 +1,371 @@
+# -*- encoding: utf-8 -*-
+#_____________________________________________________________________________
+#
+# This file is part of BridgeDB, a Tor bridge distribution system.
+#
+# :authors: Isis Lovecruft 0xA3ADB67A2CDB8B35 <isis@xxxxxxxxxxxxxx>
+# :copyright: (c) 2014, Isis Lovecruft
+#             (c) 2014, The Tor Project, Inc.
+# :license: see LICENSE for licensing information
+#_____________________________________________________________________________
+
+"""Unittests for :mod:`bridgedb.HTTPServer`."""
+
+import os
+import shutil
+
+import ipaddr
+
+from BeautifulSoup import BeautifulSoup
+
+from twisted.internet import reactor
+from twisted.trial import unittest
+from twisted.web.resource import Resource
+from twisted.web.test import requesthelper
+
+from bridgedb import HTTPServer
+
+import logging
+logging.disable(50)
+
+
+class ReplaceErrorPageTests(unittest.TestCase):
+    """Tests for :func:`bridgedb.HTTPServer.replaceErrorPage`."""
+
+    def test_replaceErrorPage(self):
+        """``replaceErrorPage`` should return the expected html."""
+        exc = Exception("vegan gümmibären")
+        errorPage = HTTPServer.replaceErrorPage(exc)
+        self.assertSubstring("Something went wrong", errorPage)
+        self.assertNotSubstring("vegan gümmibären", errorPage)
+
+
+class CaptchaProtectedResourceTests(unittest.TestCase):
+    """Tests for :mod:`bridgedb.HTTPServer.CaptchaProtectedResource`."""
+
+    def setUp(self):
+        self.dist = None
+        self.sched = None
+        self.pagename = b'somepage.html'
+        self.root = Resource()
+        self.protectedResource = HTTPServer.WebResourceBridges(self.dist,
+                                                               self.sched)
+        self.captchaResource = HTTPServer.CaptchaProtectedResource(
+            useForwardedHeader=True, resource=self.protectedResource)
+        self.root.putChild(self.pagename, self.captchaResource)
+
+    def test_render_GET_noCaptcha(self):
+        """render_GET() should return a page without a CAPTCHA, which has the
+        image alt text.
+        """
+        request = requesthelper.DummyRequest([self.pagename])
+        request.method = b'GET'
+        page = self.captchaResource.render_GET(request)
+        self.assertSubstring(
+            "Your browser is not displaying images properly", page)
+
+    def test_render_GET_missingTemplate(self):
+        """render_GET() with a missing template should raise an error and
+        return the result of replaceErrorPage().
+        """
+        oldLookup = HTTPServer.lookup
+        try:
+            HTTPServer.lookup = None
+            request = requesthelper.DummyRequest([self.pagename])
+            request.method = b'GET'
+            page = self.captchaResource.render_GET(request)
+            errorPage = HTTPServer.replaceErrorPage(Exception('kablam'))
+            self.assertEqual(page, errorPage)
+        finally:
+            HTTPServer.lookup = oldLookup
+
+    def test_getClientIP_XForwardedFor(self):
+        """CaptchaProtectedResource.getClientIP() should return the IP address
+        from the 'X-Forwarded-For' header when ``useForwardedHeader=True``.
+        """
+        requestIP = b'6.6.6.6'
+        request = requesthelper.DummyRequest([self.pagename])
+        request.setHeader(b'X-Forwarded-For', requestIP)
+        request.method = b'GET'
+
+        #child = root.getChild(pagename, request)
+        page = self.captchaResource.render_GET(request)
+        clientIP = self.captchaResource.getClientIP(request)
+        #self.assertEquals(requestIP, clientIP)
+
+    def test_render_POST(self):
+        """render_POST() with a wrong 'captcha_response_field' should return
+        a redirect to the CaptchaProtectedResource page.
+        """
+        pagename = 'captcha.html'
+        self.root.putChild(pagename, self.captchaResource)
+
+        def redirect(request):
+            newRequest = type(request)
+            newRequest.uri = pagename
+            return newRequest
+
+        request = requesthelper.DummyRequest(['captcha.html'])
+        request.method = b'POST'
+        request.redirect = redirect(request)
+
+        page = self.captchaResource.render_POST(request)
+        self.assertEqual(BeautifulSoup(page).find('meta')['http-equiv'],
+                         'refresh')
+
+
+class GimpCaptchaProtectedResourceTests(unittest.TestCase):
+    """Tests for :mod:`bridgedb.HTTPServer.GimpCaptchaProtectedResource`."""
+
+    def setUp(self):
+        """Create a :class:`HTTPServer.WebResourceBridges` and protect it with
+        a :class:`GimpCaptchaProtectedResource`.
+        """
+        # Create our cached CAPTCHA directory:
+        self.captchaDir = 'captchas'
+        if not os.path.isdir(self.captchaDir):
+            os.makedirs(self.captchaDir)
+
+        # Set up our resources to fake a minimal HTTP(S) server:
+        self.pagename = b'captcha.html'
+        self.root = Resource()
+        # (None, None) is the (distributor, scheduleInterval):
+        self.protectedResource = HTTPServer.WebResourceBridges(None, None)
+        self.captchaResource = HTTPServer.GimpCaptchaProtectedResource(
+            secretKey='42',
+            publicKey='23',
+            hmacKey='abcdefghijklmnopqrstuvwxyz012345',
+            captchaDir='captchas',
+            useForwardedHeader=True,
+            resource=self.protectedResource)
+
+        self.root.putChild(self.pagename, self.captchaResource)
+
+        # Set up the basic parts of our faked request:
+        self.request = requesthelper.DummyRequest([self.pagename])
+        self.request.URLPath = lambda: request.uri # Fake the URLPath too
+        self.request.redirect = self.doRedirect(self.request)
+
+    def doRedirect(self, request):
+        """Stub method to add a redirect() to DummyResponse."""
+        newRequest = type(request)
+        newRequest.uri = self.pagename
+        return newRequest
+
+    def tearDown(self):
+        """Delete the cached CAPTCHA directory if it still exists."""
+        if os.path.isdir(self.captchaDir):
+            shutil.rmtree(self.captchaDir)
+
+    def test_extractClientSolution(self):
+        """A (challenge, sollution) pair extracted from a request resulting
+        from a POST should have the same unmodified (challenge, sollution) as
+        the client originally POSTed.
+        """
+        expectedChallenge = '23232323232323232323'
+        expectedResponse = 'awefawefaefawefaewf'
+
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', expectedChallenge)
+        self.request.addArg('captcha_response_field', expectedResponse) 
+        
+        response = self.captchaResource.extractClientSolution(self.request)
+        (challenge, response) = response
+        self.assertEqual(challenge, expectedChallenge)
+        self.assertEqual(response, expectedResponse)
+
+    def test_checkSolution(self):
+        """checkSolution() should return False is the solution is invalid."""
+        expectedChallenge = '23232323232323232323'
+        expectedResponse = 'awefawefaefawefaewf'
+
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', expectedChallenge)
+        self.request.addArg('captcha_response_field', expectedResponse) 
+        
+        valid = self.captchaResource.checkSolution(self.request)
+        self.assertFalse(valid)
+
+    def test_getCaptchaImage(self):
+        """Retrieving a (captcha, challenge) pair with an empty captchaDir
+        should return None for both of the (captcha, challenge) strings.
+        """
+        self.request.method = b'GET'
+        response = self.captchaResource.getCaptchaImage(self.request)
+        (image, challenge) = response
+        # Because we created the directory, there weren't any CAPTCHAs to
+        # retrieve from it:
+        self.assertIs(image, None)
+        self.assertIs(challenge, None)
+
+    def test_getCaptchaImage_noCaptchaDir(self):
+        """Retrieving a (captcha, challenge) with an missing captchaDir should
+        raise a bridgedb.captcha.GimpCaptchaError.
+        """
+        shutil.rmtree(self.captchaDir)
+        self.request.method = b'GET'
+        self.assertRaises(HTTPServer.captcha.GimpCaptchaError,
+                          self.captchaResource.getCaptchaImage, self.request)
+
+    def test_render_GET_missingTemplate(self):
+        """render_GET() with a missing template should raise an error and
+        return the result of replaceErrorPage().
+        """
+        oldLookup = HTTPServer.lookup
+        try:
+            HTTPServer.lookup = None
+            self.request.method = b'GET'
+            page = self.captchaResource.render_GET(self.request)
+            errorPage = HTTPServer.replaceErrorPage(Exception('kablam'))
+            self.assertEqual(page, errorPage)
+        finally:
+            HTTPServer.lookup = oldLookup
+
+    def test_render_POST_blankFields(self):
+        """render_POST() with a blank 'captcha_response_field' should return
+        a redirect to the CaptchaProtectedResource page.
+        """
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', '')
+        self.request.addArg('captcha_response_field', '') 
+
+        page = self.captchaResource.render_POST(self.request)
+        self.assertEqual(BeautifulSoup(page).find('meta')['http-equiv'],
+                         'refresh')
+
+    def test_render_POST_wrongSolution(self):
+        """render_POST() with a wrong 'captcha_response_field' should return
+        a redirect to the CaptchaProtectedResource page.
+        """
+        expectedChallenge = '23232323232323232323'
+        expectedResponse = 'awefawefaefawefaewf'
+
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', expectedChallenge)
+        self.request.addArg('captcha_response_field', expectedResponse) 
+
+        page = self.captchaResource.render_POST(self.request)
+        self.assertEqual(BeautifulSoup(page).find('meta')['http-equiv'],
+                         'refresh')
+
+
+class ReCaptchaProtectedResourceTests(unittest.TestCase):
+    """Tests for :mod:`bridgedb.HTTPServer.ReCaptchaProtectedResource`."""
+
+    def setUp(self):
+        """Create a :class:`HTTPServer.WebResourceBridges` and protect it with
+        a :class:`ReCaptchaProtectedResource`.
+        """
+        # Create our cached CAPTCHA directory:
+        self.captchaDir = 'captchas'
+        if not os.path.isdir(self.captchaDir):
+            os.makedirs(self.captchaDir)
+
+        # Set up our resources to fake a minimal HTTP(S) server:
+        self.pagename = b'captcha.html'
+        self.root = Resource()
+        # (None, None) is the (distributor, scheduleInterval):
+        self.protectedResource = HTTPServer.WebResourceBridges(None, None)
+        self.captchaResource = HTTPServer.ReCaptchaProtectedResource(
+            recaptchaPrivKey='42',
+            recaptchaPubKey='23',
+            remoteip='111.111.111.111',
+            useForwardedHeader=True,
+            resource=self.protectedResource)
+
+        self.root.putChild(self.pagename, self.captchaResource)
+
+        # Set up the basic parts of our faked request:
+        self.request = requesthelper.DummyRequest([self.pagename])
+        self.request.URLPath = lambda: request.uri # Fake the URLPath too
+        self.request.redirect = self.doRedirect(self.request)
+
+    def doRedirect(self, request):
+        """Stub method to add a redirect() to DummyResponse."""
+        newRequest = type(request)
+        newRequest.uri = self.pagename
+        return newRequest
+
+    def tearDown(self):
+        """Cleanup method for removing timed out connections on the reactor.
+
+        This seems to be the solution for the dirty reactor due to
+        ``DelayedCall``s which is mentioned at the beginning of this
+        file. There doesn't seem to be any documentation anywhere which
+        proposes this solution, although this seems to solve the problem.
+        """
+        for delay in reactor.getDelayedCalls():
+            try:
+                delay.cancel()
+            except (AlreadyCalled, AlreadyCancelled):
+                pass
+
+    def test_checkSolution_blankFields(self):
+        """:meth:`HTTPServer.ReCaptchaProtectedResource.checkSolution` should
+        return a redirect if is the solution field is blank.
+        """
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', '')
+        self.request.addArg('captcha_response_field', '') 
+        
+        self.assertIsInstance(
+            self.captchaResource.checkSolution(self.request),
+            HTTPServer.txrecaptcha.RecaptchaResponse)
+
+    def test_getRemoteIP_useRandomIP(self):
+        """Check that removing our remoteip setting produces a random IP."""
+        self.captchaResource.recaptchaRemoteIP = None
+        ip = self.captchaResource.getRemoteIP()
+        realishIP = ipaddr.IPv4Address(ip).compressed
+        self.assertTrue(realishIP)
+        self.assertNotEquals(realishIP, '111.111.111.111')
+
+    def test_getRemoteIP_useConfiguredIP(self):
+        """Check that our remoteip setting is used if configured."""
+        ip = self.captchaResource.getRemoteIP()
+        realishIP = ipaddr.IPv4Address(ip).compressed
+        self.assertTrue(realishIP)
+        self.assertEquals(realishIP, '111.111.111.111')
+
+    def test_render_GET_missingTemplate(self):
+        """render_GET() with a missing template should raise an error and
+        return the result of replaceErrorPage().
+        """
+        oldLookup = HTTPServer.lookup
+        try:
+            HTTPServer.lookup = None
+            self.request.method = b'GET'
+            page = self.captchaResource.render_GET(self.request)
+            errorPage = HTTPServer.replaceErrorPage(Exception('kablam'))
+            self.assertEqual(page, errorPage)
+        finally:
+            HTTPServer.lookup = oldLookup
+
+    def test_render_POST_blankFields(self):
+        """render_POST() with a blank 'captcha_response_field' should return
+        a redirect to the CaptchaProtectedResource page.
+        """
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', '')
+        self.request.addArg('captcha_response_field', '') 
+
+        page = self.captchaResource.render_POST(self.request)
+        print page
+        self.assertEqual(BeautifulSoup(page).find('meta')['http-equiv'],
+                         'refresh')
+
+    def test_render_POST_wrongSolution(self):
+        """render_POST() with a wrong 'captcha_response_field' should return
+        a redirect to the CaptchaProtectedResource page.
+        """
+        expectedChallenge = '23232323232323232323'
+        expectedResponse = 'awefawefaefawefaewf'
+
+        self.request.method = b'POST'
+        self.request.addArg('captcha_challenge_field', expectedChallenge)
+        self.request.addArg('captcha_response_field', expectedResponse) 
+
+        page = self.captchaResource.render_POST(self.request)
+        print page
+        self.assertEqual(BeautifulSoup(page).find('meta')['http-equiv'],
+                         'refresh')



_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits