[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