# Path: wwwhisper_auth/url_utils.py
# def collapse_slashes(path):
#     """Replaces repeated path separators ('/') with a single one."""
#     return re.sub('//+', '/', path)
# 
# Path: wwwhisper_auth/url_utils.py
# def contains_fragment(path):
#     """True if path contains fragment id ('#' part)."""
#     return path.count('#') != 0
# 
# Path: wwwhisper_auth/url_utils.py
# def contains_params(path):
#     """True if path contains params (';' part)."""
#     return path.count(';') != 0
# 
# Path: wwwhisper_auth/url_utils.py
# def contains_query(path):
#     """True if path contains query string ('?' part)."""
#     return path.count('?') != 0
# 
# Path: wwwhisper_auth/url_utils.py
# def decode(path):
#     """Decodes URL encoded characters in path."""
#     return urllib.unquote_plus(path)
# 
# Path: wwwhisper_auth/url_utils.py
# def is_canonical(path):
#     """True if path is absolute and normalized.
# 
#     Canonical path is unique, i.e. two different such paths are never
#     equivalent.
#     """
#     # Posix recognizes '//' as a normalized path, but it is not
#     # canonical (it is the same as '/').
#     if path == '' or not posixpath.isabs(path) or path.startswith('//'):
#         return False
#     # Normpath removes trailing '/'.
#     normalized_path =  posixpath.normpath(path)
#     if (normalized_path != path and normalized_path + '/' != path):
#         return False
#     return True
# 
# Path: wwwhisper_auth/url_utils.py
# def validate_redirection_target(path):
#     """Checks if login can safely redirect to a path.
# 
#     For example, if instead of a path a full url is used (with a
#     domain part), the redirect could leak a login token in the Referer
#     header (not an obvious problem since token is intended to be one
#     time, but better to prevent it).
# 
#     These are conservative checks. Discarding a valid path is not a
#     distaster, because login then redirects to '/' and is still
#     successful.
#     """
#     if len(path) > REDIRECTION_PATH_LIMIT:
#         return False
#     parsed_url = urlparse.urlparse(path)
#     for attr in ['scheme', 'netloc', 'username', 'password']:
#         val = getattr(parsed_url, attr, None)
#         if  val is not None and val != '':
#             return False
#     # Filter new lines for additional protection from Location header
#     # split into two headers (should be also prevented by Django).
#     return (is_canonical(parsed_url.path) and
#             not '\n' in path)
# 
# Path: wwwhisper_auth/url_utils.py
# def validate_site_url(url):
#     parsed_url = urlparse.urlparse(url)
#     if parsed_url.scheme == '':
#         return (False, 'missing scheme (http:// or https://)')
#     if parsed_url.scheme not in ('http', 'https'):
#         return (False, 'incorrect scheme (should be http:// or https://)')
#     if parsed_url.netloc == '':
#         return (False, 'missing domain')
# 
#     for attr in ['path', 'username', 'query', 'params', 'fragment', 'password']:
#         val = getattr(parsed_url, attr, None)
#         if val is not None and val != '':
#             return (False, 'contains ' + attr)
#     return (True, None)
# 
# Path: wwwhisper_auth/url_utils.py
# def remove_default_port(url):
#     parts = url.split(':')
#     if len(parts) != 3:
#         return url
#     scheme, rest, port = parts
#     if ((scheme == 'https' and port == '443') or
#         (scheme == 'http' and port == '80')):
#         return "%s:%s" % (scheme, rest)
#     return url
# 
# Path: wwwhisper_auth/url_utils.py
# def strip_query(path):
#     """Strips query from a path."""
#     query_start = path.find('?')
#     if query_start != -1:
#         return path[:query_start]
#     return path


from django.test import TestCase
from wwwhisper_auth.url_utils import collapse_slashes
from wwwhisper_auth.url_utils import contains_fragment
from wwwhisper_auth.url_utils import contains_params
from wwwhisper_auth.url_utils import contains_query
from wwwhisper_auth.url_utils import decode
from wwwhisper_auth.url_utils import is_canonical
from wwwhisper_auth.url_utils import validate_redirection_target
from wwwhisper_auth.url_utils import validate_site_url
from wwwhisper_auth.url_utils import remove_default_port
from wwwhisper_auth.url_utils import strip_query
    def test_collapse_slashes(self):
        self.assertEqual(collapse_slashes('/'), '/')
        self.assertEqual(collapse_slashes('/foo/'), '/foo/')
        self.assertEqual(collapse_slashes('/foo'), '/foo')

        self.assertEqual(collapse_slashes('//'), '/')
        self.assertEqual(collapse_slashes('///'), '/')
        self.assertEqual(collapse_slashes('///foo//////bar//'), '/foo/bar/')
        self.assertEqual(collapse_slashes('///foo// ///bar//'), '/foo/ /bar/')

    def test_decode(self):
        self.assertEqual('/foo bar#', decode('/foo%20bar%23'))
        self.assertEqual('/FoO', decode('%2F%46%6f%4F'))
        self.assertEqual('/', decode('/'))
        self.assertEqual('/foo', decode('/foo'))
        self.assertEqual('/foo/', decode('/foo/'))

    def test_strip_query(self):
        self.assertEqual('/foo/', strip_query('/foo/?bar=abc'))
        self.assertEqual('/foo', strip_query('/foo?'))
        self.assertEqual('/foo', strip_query('/foo?bar=abc?baz=xyz'))

    def test_contains_fragment(self):
        self.assertTrue(contains_fragment('/foo#123'))
        self.assertTrue(contains_fragment('/foo#'))
        # Encoded '#' should not be treated as fragment separator.
        self.assertFalse(contains_fragment('/foo%23'))

    def test_contains_query(self):
        self.assertTrue(contains_query('/foo?'))

# Path: wwwhisper_auth/url_utils.py
# def collapse_slashes(path):
#     """Replaces repeated path separators ('/') with a single one."""
#     return re.sub('//+', '/', path)
# 
# Path: wwwhisper_auth/url_utils.py
# def contains_fragment(path):
#     """True if path contains fragment id ('#' part)."""
#     return path.count('#') != 0
# 
# Path: wwwhisper_auth/url_utils.py
# def contains_params(path):
#     """True if path contains params (';' part)."""
#     return path.count(';') != 0
# 
# Path: wwwhisper_auth/url_utils.py
# def contains_query(path):
#     """True if path contains query string ('?' part)."""
#     return path.count('?') != 0
# 
# Path: wwwhisper_auth/url_utils.py
# def decode(path):
#     """Decodes URL encoded characters in path."""
#     return urllib.unquote_plus(path)
# 
# Path: wwwhisper_auth/url_utils.py
# def is_canonical(path):
#     """True if path is absolute and normalized.
# 
#     Canonical path is unique, i.e. two different such paths are never
#     equivalent.
#     """
#     # Posix recognizes '//' as a normalized path, but it is not
#     # canonical (it is the same as '/').
#     if path == '' or not posixpath.isabs(path) or path.startswith('//'):
#         return False
#     # Normpath removes trailing '/'.
#     normalized_path =  posixpath.normpath(path)
#     if (normalized_path != path and normalized_path + '/' != path):
#         return False
#     return True
# 
# Path: wwwhisper_auth/url_utils.py
# def validate_redirection_target(path):
#     """Checks if login can safely redirect to a path.
# 
#     For example, if instead of a path a full url is used (with a
#     domain part), the redirect could leak a login token in the Referer
#     header (not an obvious problem since token is intended to be one
#     time, but better to prevent it).
# 
#     These are conservative checks. Discarding a valid path is not a
#     distaster, because login then redirects to '/' and is still
#     successful.
#     """
#     if len(path) > REDIRECTION_PATH_LIMIT:
#         return False
#     parsed_url = urlparse.urlparse(path)
#     for attr in ['scheme', 'netloc', 'username', 'password']:
#         val = getattr(parsed_url, attr, None)
#         if  val is not None and val != '':
#             return False
#     # Filter new lines for additional protection from Location header
#     # split into two headers (should be also prevented by Django).
#     return (is_canonical(parsed_url.path) and
#             not '\n' in path)
# 
# Path: wwwhisper_auth/url_utils.py
# def validate_site_url(url):
#     parsed_url = urlparse.urlparse(url)
#     if parsed_url.scheme == '':
#         return (False, 'missing scheme (http:// or https://)')
#     if parsed_url.scheme not in ('http', 'https'):
#         return (False, 'incorrect scheme (should be http:// or https://)')
#     if parsed_url.netloc == '':
#         return (False, 'missing domain')
# 
#     for attr in ['path', 'username', 'query', 'params', 'fragment', 'password']:
#         val = getattr(parsed_url, attr, None)
#         if val is not None and val != '':
#             return (False, 'contains ' + attr)
#     return (True, None)
# 
# Path: wwwhisper_auth/url_utils.py
# def remove_default_port(url):
#     parts = url.split(':')
#     if len(parts) != 3:
#         return url
#     scheme, rest, port = parts
#     if ((scheme == 'https' and port == '443') or
#         (scheme == 'http' and port == '80')):
#         return "%s:%s" % (scheme, rest)
#     return url
# 
# Path: wwwhisper_auth/url_utils.py
# def strip_query(path):
#     """Strips query from a path."""
#     query_start = path.find('?')
#     if query_start != -1:
#         return path[:query_start]
#     return path


# Path: wwwhisper_auth/tests/tests_url_utils.py
from django.test import TestCase
from wwwhisper_auth.url_utils import collapse_slashes
from wwwhisper_auth.url_utils import contains_fragment
from wwwhisper_auth.url_utils import contains_params
from wwwhisper_auth.url_utils import contains_query
from wwwhisper_auth.url_utils import decode
from wwwhisper_auth.url_utils import is_canonical
from wwwhisper_auth.url_utils import validate_redirection_target
from wwwhisper_auth.url_utils import validate_site_url
from wwwhisper_auth.url_utils import remove_default_port
from wwwhisper_auth.url_utils import strip_query

    def test_collapse_slashes(self):
        self.assertEqual(collapse_slashes('/'), '/')
        self.assertEqual(collapse_slashes('/foo/'), '/foo/')
        self.assertEqual(collapse_slashes('/foo'), '/foo')

        self.assertEqual(collapse_slashes('//'), '/')
        self.assertEqual(collapse_slashes('///'), '/')
        self.assertEqual(collapse_slashes('///foo//////bar//'), '/foo/bar/')
        self.assertEqual(collapse_slashes('///foo// ///bar//'), '/foo/ /bar/')

    def test_decode(self):
        self.assertEqual('/foo bar#', decode('/foo%20bar%23'))
        self.assertEqual('/FoO', decode('%2F%46%6f%4F'))
        self.assertEqual('/', decode('/'))
        self.assertEqual('/foo', decode('/foo'))
        self.assertEqual('/foo/', decode('/foo/'))

    def test_strip_query(self):
        self.assertEqual('/foo/', strip_query('/foo/?bar=abc'))
        self.assertEqual('/foo', strip_query('/foo?'))
        self.assertEqual('/foo', strip_query('/foo?bar=abc?baz=xyz'))

    def test_contains_fragment(self):
        self.assertTrue(contains_fragment('/foo#123'))
        self.assertTrue(contains_fragment('/foo#'))
        # Encoded '#' should not be treated as fragment separator.
        self.assertFalse(contains_fragment('/foo%23'))

    def test_contains_query(self):
        self.assertTrue(contains_query('/foo?'))

        self.assertFalse(contains_query('/foo'))