This (in my opinion) is a far more explicit API method as reading it
allowed sounds like English, e.g.,
Validator().require_presence_of('host', 'port', 'path')
Or "Validator require(s) presence of host, port, and path".
This also adds a bunch more tests around validation and tacks some of
the validation error information onto the exception object for easier
introspection by users.
197 lines
7.1 KiB
Python
197 lines
7.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Tests for the validators module."""
|
|
import rfc3986
|
|
from rfc3986 import exceptions
|
|
from rfc3986 import validators
|
|
|
|
import pytest
|
|
|
|
|
|
def test_defaults():
|
|
"""Verify the default Validator settings."""
|
|
validator = validators.Validator()
|
|
|
|
assert validator.required_components == {
|
|
c: False for c in validator.COMPONENT_NAMES
|
|
}
|
|
assert validator.allow_password is True
|
|
assert validator.allowed_schemes == set()
|
|
assert validator.allowed_hosts == set()
|
|
assert validator.allowed_ports == set()
|
|
|
|
|
|
def test_allowing_schemes():
|
|
"""Verify the ability to select schemes to be allowed."""
|
|
validator = validators.Validator().allow_schemes('http', 'https')
|
|
|
|
assert 'http' in validator.allowed_schemes
|
|
assert 'https' in validator.allowed_schemes
|
|
|
|
|
|
def test_allowing_hosts():
|
|
"""Verify the ability to select hosts to be allowed."""
|
|
validator = validators.Validator().allow_hosts(
|
|
'pypi.python.org', 'pypi.org',
|
|
)
|
|
|
|
assert 'pypi.python.org' in validator.allowed_hosts
|
|
assert 'pypi.org' in validator.allowed_hosts
|
|
|
|
|
|
def test_allowing_ports():
|
|
"""Verify the ability select ports to be allowed."""
|
|
validator = validators.Validator().allow_ports('80', '100')
|
|
|
|
assert '80' in validator.allowed_ports
|
|
assert '100' in validator.allowed_ports
|
|
|
|
|
|
def test_requiring_invalid_component():
|
|
"""Verify that we validate required component names."""
|
|
with pytest.raises(ValueError):
|
|
validators.Validator().require_presence_of('frob')
|
|
|
|
|
|
def test_use_of_password():
|
|
"""Verify the behaviour of {forbid,allow}_use_of_password."""
|
|
validator = validators.Validator()
|
|
assert validator.allow_password is True
|
|
|
|
validator.forbid_use_of_password()
|
|
assert validator.allow_password is False
|
|
|
|
validator.allow_use_of_password()
|
|
assert validator.allow_password is True
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('https://user:password@github.com'),
|
|
rfc3986.uri_reference('https://user:password@github.com/path'),
|
|
rfc3986.uri_reference('https://user:password@github.com/path?query'),
|
|
rfc3986.uri_reference('https://user:password@github.com/path?query#frag'),
|
|
rfc3986.uri_reference('//user:password@github.com'),
|
|
])
|
|
def test_forbidden_passwords(uri):
|
|
"""Verify that passwords are disallowed."""
|
|
validator = validators.Validator().forbid_use_of_password()
|
|
with pytest.raises(exceptions.PasswordForbidden):
|
|
validator.validate(uri)
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('https://user@github.com'),
|
|
rfc3986.uri_reference('https://user@github.com/path'),
|
|
rfc3986.uri_reference('https://user@github.com/path?query'),
|
|
rfc3986.uri_reference('https://user@github.com/path?query#frag'),
|
|
rfc3986.uri_reference('//user@github.com'),
|
|
rfc3986.uri_reference('//github.com'),
|
|
rfc3986.uri_reference('https://github.com'),
|
|
])
|
|
def test_passwordless_uris_pass_validation(uri):
|
|
"""Verify password-less URLs validate properly."""
|
|
validator = validators.Validator().forbid_use_of_password()
|
|
validator.validate(uri)
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('https://'),
|
|
rfc3986.uri_reference('/path/to/resource'),
|
|
])
|
|
def test_missing_host_component(uri):
|
|
"""Verify that missing host components cause errors."""
|
|
validator = validators.Validator().require_presence_of('host')
|
|
with pytest.raises(exceptions.MissingComponentError):
|
|
validator.validate(uri)
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('https://'),
|
|
rfc3986.uri_reference('//google.com'),
|
|
rfc3986.uri_reference('//google.com?query=value'),
|
|
rfc3986.uri_reference('//google.com#fragment'),
|
|
rfc3986.uri_reference('https://google.com'),
|
|
rfc3986.uri_reference('https://google.com#fragment'),
|
|
rfc3986.uri_reference('https://google.com?query=value'),
|
|
])
|
|
def test_missing_path_component(uri):
|
|
"""Verify that missing path components cause errors."""
|
|
validator = validators.Validator().require_presence_of('path')
|
|
with pytest.raises(exceptions.MissingComponentError):
|
|
validator.validate(uri)
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('//google.com'),
|
|
rfc3986.uri_reference('//google.com?query=value'),
|
|
rfc3986.uri_reference('//google.com#fragment'),
|
|
])
|
|
def test_multiple_missing_components(uri):
|
|
"""Verify that multiple missing components are caught."""
|
|
validator = validators.Validator().require_presence_of('scheme', 'path')
|
|
with pytest.raises(exceptions.MissingComponentError) as captured_exc:
|
|
validator.validate(uri)
|
|
exception = captured_exc.value
|
|
assert 2 == len(exception.args[-1])
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('smtp://'),
|
|
rfc3986.uri_reference('telnet://'),
|
|
])
|
|
def test_ensure_uri_has_a_scheme(uri):
|
|
"""Verify validation with allowed schemes."""
|
|
validator = validators.Validator().allow_schemes('https', 'http')
|
|
with pytest.raises(exceptions.UnpermittedComponentError):
|
|
validator.validate(uri)
|
|
|
|
|
|
@pytest.mark.parametrize('uri, failed_component', [
|
|
(rfc3986.uri_reference('git://github.com'), 'scheme'),
|
|
(rfc3986.uri_reference('http://github.com'), 'scheme'),
|
|
(rfc3986.uri_reference('ssh://gitlab.com'), 'host'),
|
|
(rfc3986.uri_reference('https://gitlab.com'), 'host'),
|
|
])
|
|
def test_allowed_hosts_and_schemes(uri, failed_component):
|
|
"""Verify each of these fails."""
|
|
validator = validators.Validator().allow_schemes(
|
|
'https', 'ssh',
|
|
).allow_hosts(
|
|
'github.com', 'git.openstack.org',
|
|
)
|
|
with pytest.raises(exceptions.UnpermittedComponentError) as caught_exc:
|
|
validator.validate(uri)
|
|
|
|
exc = caught_exc.value
|
|
assert exc.component_name == failed_component
|
|
|
|
|
|
@pytest.mark.parametrize('uri', [
|
|
rfc3986.uri_reference('https://github.com/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://github.com/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://ssh@github.com:22/sigmavirus24'),
|
|
rfc3986.uri_reference('https://github.com:443/sigmavirus24'),
|
|
rfc3986.uri_reference('https://gitlab.com/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://gitlab.com/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://ssh@gitlab.com:22/sigmavirus24'),
|
|
rfc3986.uri_reference('https://gitlab.com:443/sigmavirus24'),
|
|
rfc3986.uri_reference('https://bitbucket.org/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://bitbucket.org/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://ssh@bitbucket.org:22/sigmavirus24'),
|
|
rfc3986.uri_reference('https://bitbucket.org:443/sigmavirus24'),
|
|
rfc3986.uri_reference('https://git.openstack.org/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://git.openstack.org/sigmavirus24'),
|
|
rfc3986.uri_reference('ssh://ssh@git.openstack.org:22/sigmavirus24'),
|
|
rfc3986.uri_reference('https://git.openstack.org:443/sigmavirus24'),
|
|
])
|
|
def test_successful_complex_validation(uri):
|
|
"""Verify we do not raise ValidationErrors for good URIs."""
|
|
validators.Validator().allow_schemes(
|
|
'https', 'ssh',
|
|
).allow_hosts(
|
|
'github.com', 'bitbucket.org', 'gitlab.com', 'git.openstack.org',
|
|
).allow_ports(
|
|
'22', '443',
|
|
).require_presence_of(
|
|
'scheme', 'host', 'path',
|
|
).validate(uri)
|