# -*- 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_checking_validity_of_component():
    """Verify that we validate components we're validating."""
    with pytest.raises(ValueError):
        validators.Validator().check_validity_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."""
    validators.Validator().validate(uri)

    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'),
    rfc3986.uri_reference(
        'ssh://ssh@git.openstack.org:22/sigmavirus24?foo=bar#fragment'
    ),
    rfc3986.uri_reference(
        'ssh://git.openstack.org:22/sigmavirus24?foo=bar#fragment'
    ),
    rfc3986.uri_reference('ssh://git.openstack.org:22/?foo=bar#fragment'),
    rfc3986.uri_reference('ssh://git.openstack.org:22/sigmavirus24#fragment'),
    rfc3986.uri_reference('ssh://git.openstack.org:22/#fragment'),
    rfc3986.uri_reference('ssh://git.openstack.org:22/'),
    rfc3986.uri_reference('ssh://ssh@git.openstack.org:22/?foo=bar#fragment'),
    rfc3986.uri_reference(
        'ssh://ssh@git.openstack.org:22/sigmavirus24#fragment'
    ),
    rfc3986.uri_reference('ssh://ssh@git.openstack.org:22/#fragment'),
    rfc3986.uri_reference('ssh://ssh@git.openstack.org:22/'),
])
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',
    ).check_validity_of(
        'scheme', 'userinfo', 'host', 'port', 'path', 'query', 'fragment',
    ).validate(uri)


def test_invalid_uri_generates_error(invalid_uri):
    """Verify we catch invalid URIs."""
    uri = rfc3986.uri_reference(invalid_uri)
    with pytest.raises(exceptions.InvalidComponentsError):
        validators.Validator().check_validity_of('host').validate(uri)


def test_invalid_uri_with_invalid_path(invalid_uri):
    """Verify we catch multiple invalid components."""
    uri = rfc3986.uri_reference(invalid_uri)
    uri = uri.copy_with(path='#foobar')
    with pytest.raises(exceptions.InvalidComponentsError):
        validators.Validator().check_validity_of(
            'host', 'path',
        ).validate(uri)


def test_validating_rfc_4007_ipv6_zone_ids():
    """Verify that RFC 4007 IPv6 Zone IDs are invalid
    host/authority but after normalization are valid
    """
    uri = rfc3986.uri_reference("http://[::1%eth0]")
    with pytest.raises(exceptions.InvalidComponentsError):
        validators.Validator().check_validity_of(
            'host'
        ).validate(uri)

    uri = uri.normalize()
    assert uri.host == '[::1%25eth0]'

    validators.Validator().check_validity_of(
        'host'
    ).validate(uri)
