# HG changeset patch # User Gregory Szorc # Date 2018-03-11 19:53:47 # Node ID b2a3308d6a2186a3d7770e1bee6adcb5ada2e44d # Parent a5c478843c82afbcd026455b0c3a8638e9146fe7 tests: add test coverage for parsing WSGI requests A subsequent commit will need to make this code more complicated in order to support alternate base URLs. Let's establish some test coverage before we diverge too far from PEP 3333. As part of this, a minor bug related to a missing SCRIPT_NAME key has been squashed. Differential Revision: https://phab.mercurial-scm.org/D2818 diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py --- a/mercurial/hgweb/request.py +++ b/mercurial/hgweb/request.py @@ -222,7 +222,7 @@ def parserequestfromenv(env, bodyfh): # root. We also exclude its path components from PATH_INFO when resolving # the dispatch path. - apppath = env['SCRIPT_NAME'] + apppath = env.get('SCRIPT_NAME', '') if env.get('REPO_NAME'): if not apppath.endswith('/'): diff --git a/tests/test-wsgirequest.py b/tests/test-wsgirequest.py new file mode 100644 --- /dev/null +++ b/tests/test-wsgirequest.py @@ -0,0 +1,255 @@ +from __future__ import absolute_import, print_function + +import unittest + +from mercurial.hgweb import ( + request as requestmod, +) + +DEFAULT_ENV = { + r'REQUEST_METHOD': r'GET', + r'SERVER_NAME': r'testserver', + r'SERVER_PORT': r'80', + r'SERVER_PROTOCOL': r'http', + r'wsgi.version': (1, 0), + r'wsgi.url_scheme': r'http', + r'wsgi.input': None, + r'wsgi.errors': None, + r'wsgi.multithread': False, + r'wsgi.multiprocess': True, + r'wsgi.run_once': False, +} + +def parse(env, bodyfh=None, extra=None): + env = dict(env) + env.update(extra or {}) + + return requestmod.parserequestfromenv(env, bodyfh) + +class ParseRequestTests(unittest.TestCase): + def testdefault(self): + r = parse(DEFAULT_ENV) + self.assertEqual(r.url, b'http://testserver') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.urlscheme, b'http') + self.assertEqual(r.method, b'GET') + self.assertIsNone(r.remoteuser) + self.assertIsNone(r.remotehost) + self.assertEqual(r.apppath, b'') + self.assertEqual(r.dispatchparts, []) + self.assertEqual(r.dispatchpath, b'') + self.assertFalse(r.havepathinfo) + self.assertIsNone(r.reponame) + self.assertEqual(r.querystring, b'') + self.assertEqual(len(r.qsparams), 0) + self.assertEqual(len(r.headers), 0) + + def testcustomport(self): + r = parse(DEFAULT_ENV, extra={ + r'SERVER_PORT': r'8000', + }) + + self.assertEqual(r.url, b'http://testserver:8000') + self.assertEqual(r.baseurl, b'http://testserver:8000') + self.assertEqual(r.advertisedurl, b'http://testserver:8000') + self.assertEqual(r.advertisedbaseurl, b'http://testserver:8000') + + r = parse(DEFAULT_ENV, extra={ + r'SERVER_PORT': r'4000', + r'wsgi.url_scheme': r'https', + }) + + self.assertEqual(r.url, b'https://testserver:4000') + self.assertEqual(r.baseurl, b'https://testserver:4000') + self.assertEqual(r.advertisedurl, b'https://testserver:4000') + self.assertEqual(r.advertisedbaseurl, b'https://testserver:4000') + + def testhttphost(self): + r = parse(DEFAULT_ENV, extra={ + r'HTTP_HOST': r'altserver', + }) + + self.assertEqual(r.url, b'http://altserver') + self.assertEqual(r.baseurl, b'http://altserver') + self.assertEqual(r.advertisedurl, b'http://testserver') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + + def testscriptname(self): + r = parse(DEFAULT_ENV, extra={ + r'SCRIPT_NAME': r'', + }) + + self.assertEqual(r.url, b'http://testserver') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'') + self.assertEqual(r.dispatchparts, []) + self.assertEqual(r.dispatchpath, b'') + self.assertFalse(r.havepathinfo) + + r = parse(DEFAULT_ENV, extra={ + r'SCRIPT_NAME': r'/script', + }) + + self.assertEqual(r.url, b'http://testserver/script') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/script') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/script') + self.assertEqual(r.dispatchparts, []) + self.assertEqual(r.dispatchpath, b'') + self.assertFalse(r.havepathinfo) + + r = parse(DEFAULT_ENV, extra={ + r'SCRIPT_NAME': r'/multiple words', + }) + + self.assertEqual(r.url, b'http://testserver/multiple%20words') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/multiple%20words') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/multiple words') + self.assertEqual(r.dispatchparts, []) + self.assertEqual(r.dispatchpath, b'') + self.assertFalse(r.havepathinfo) + + def testpathinfo(self): + r = parse(DEFAULT_ENV, extra={ + r'PATH_INFO': r'', + }) + + self.assertEqual(r.url, b'http://testserver') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'') + self.assertEqual(r.dispatchparts, []) + self.assertEqual(r.dispatchpath, b'') + self.assertTrue(r.havepathinfo) + + r = parse(DEFAULT_ENV, extra={ + r'PATH_INFO': r'/pathinfo', + }) + + self.assertEqual(r.url, b'http://testserver/pathinfo') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/pathinfo') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'') + self.assertEqual(r.dispatchparts, [b'pathinfo']) + self.assertEqual(r.dispatchpath, b'pathinfo') + self.assertTrue(r.havepathinfo) + + r = parse(DEFAULT_ENV, extra={ + r'PATH_INFO': r'/one/two/', + }) + + self.assertEqual(r.url, b'http://testserver/one/two/') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/one/two/') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'') + self.assertEqual(r.dispatchparts, [b'one', b'two']) + self.assertEqual(r.dispatchpath, b'one/two') + self.assertTrue(r.havepathinfo) + + def testscriptandpathinfo(self): + r = parse(DEFAULT_ENV, extra={ + r'SCRIPT_NAME': r'/script', + r'PATH_INFO': r'/pathinfo', + }) + + self.assertEqual(r.url, b'http://testserver/script/pathinfo') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/script/pathinfo') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/script') + self.assertEqual(r.dispatchparts, [b'pathinfo']) + self.assertEqual(r.dispatchpath, b'pathinfo') + self.assertTrue(r.havepathinfo) + + r = parse(DEFAULT_ENV, extra={ + r'SCRIPT_NAME': r'/script1/script2', + r'PATH_INFO': r'/path1/path2', + }) + + self.assertEqual(r.url, + b'http://testserver/script1/script2/path1/path2') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, + b'http://testserver/script1/script2/path1/path2') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/script1/script2') + self.assertEqual(r.dispatchparts, [b'path1', b'path2']) + self.assertEqual(r.dispatchpath, b'path1/path2') + self.assertTrue(r.havepathinfo) + + r = parse(DEFAULT_ENV, extra={ + r'HTTP_HOST': r'hostserver', + r'SCRIPT_NAME': r'/script', + r'PATH_INFO': r'/pathinfo', + }) + + self.assertEqual(r.url, b'http://hostserver/script/pathinfo') + self.assertEqual(r.baseurl, b'http://hostserver') + self.assertEqual(r.advertisedurl, b'http://testserver/script/pathinfo') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/script') + self.assertEqual(r.dispatchparts, [b'pathinfo']) + self.assertEqual(r.dispatchpath, b'pathinfo') + self.assertTrue(r.havepathinfo) + + def testreponame(self): + """REPO_NAME path components get stripped from URL.""" + r = parse(DEFAULT_ENV, extra={ + r'REPO_NAME': r'repo', + r'PATH_INFO': r'/path1/path2' + }) + + self.assertEqual(r.url, b'http://testserver/path1/path2') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/path1/path2') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/repo') + self.assertEqual(r.dispatchparts, [b'path1', b'path2']) + self.assertEqual(r.dispatchpath, b'path1/path2') + self.assertTrue(r.havepathinfo) + self.assertEqual(r.reponame, b'repo') + + r = parse(DEFAULT_ENV, extra={ + r'REPO_NAME': r'repo', + r'PATH_INFO': r'/repo/path1/path2', + }) + + self.assertEqual(r.url, b'http://testserver/repo/path1/path2') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, b'http://testserver/repo/path1/path2') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/repo') + self.assertEqual(r.dispatchparts, [b'path1', b'path2']) + self.assertEqual(r.dispatchpath, b'path1/path2') + self.assertTrue(r.havepathinfo) + self.assertEqual(r.reponame, b'repo') + + r = parse(DEFAULT_ENV, extra={ + r'REPO_NAME': r'prefix/repo', + r'PATH_INFO': r'/prefix/repo/path1/path2', + }) + + self.assertEqual(r.url, b'http://testserver/prefix/repo/path1/path2') + self.assertEqual(r.baseurl, b'http://testserver') + self.assertEqual(r.advertisedurl, + b'http://testserver/prefix/repo/path1/path2') + self.assertEqual(r.advertisedbaseurl, b'http://testserver') + self.assertEqual(r.apppath, b'/prefix/repo') + self.assertEqual(r.dispatchparts, [b'path1', b'path2']) + self.assertEqual(r.dispatchpath, b'path1/path2') + self.assertTrue(r.havepathinfo) + self.assertEqual(r.reponame, b'prefix/repo') + +if __name__ == '__main__': + import silenttestrunner + silenttestrunner.main(__name__)