"""Tests for the validators module."""
import pytest

import rfc3986
from rfc3986 import exceptions
from rfc3986 import validators


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)
