from __future__ import unicode_literals

import logging

import cfgv
import pytest

import pre_commit.constants as C
from pre_commit.clientlib import check_type_tag
from pre_commit.clientlib import CONFIG_HOOK_DICT
from pre_commit.clientlib import CONFIG_REPO_DICT
from pre_commit.clientlib import CONFIG_SCHEMA
from pre_commit.clientlib import DEFAULT_LANGUAGE_VERSION
from pre_commit.clientlib import MANIFEST_SCHEMA
from pre_commit.clientlib import MigrateShaToRev
from pre_commit.clientlib import validate_config_main
from pre_commit.clientlib import validate_manifest_main
from testing.fixtures import sample_local_config


def is_valid_according_to_schema(obj, obj_schema):
    try:
        cfgv.validate(obj, obj_schema)
        return True
    except cfgv.ValidationError:
        return False


@pytest.mark.parametrize('value', ('definitely-not-a-tag', 'fiel'))
def test_check_type_tag_failures(value):
    with pytest.raises(cfgv.ValidationError):
        check_type_tag(value)


@pytest.mark.parametrize(
    ('config_obj', 'expected'), (
        (
            {
                'repos': [{
                    'repo': 'git@github.com:pre-commit/pre-commit-hooks',
                    'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
                    'hooks': [{'id': 'pyflakes', 'files': '\\.py$'}],
                }],
            },
            True,
        ),
        (
            {
                'repos': [{
                    'repo': 'git@github.com:pre-commit/pre-commit-hooks',
                    'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
                    'hooks': [
                        {
                            'id': 'pyflakes',
                            'files': '\\.py$',
                            'args': ['foo', 'bar', 'baz'],
                        },
                    ],
                }],
            },
            True,
        ),
        (
            {
                'repos': [{
                    'repo': 'git@github.com:pre-commit/pre-commit-hooks',
                    'rev': 'cd74dc150c142c3be70b24eaf0b02cae9d235f37',
                    'hooks': [
                        {
                            'id': 'pyflakes',
                            'files': '\\.py$',
                            # Exclude pattern must be a string
                            'exclude': 0,
                            'args': ['foo', 'bar', 'baz'],
                        },
                    ],
                }],
            },
            False,
        ),
    ),
)
def test_config_valid(config_obj, expected):
    ret = is_valid_according_to_schema(config_obj, CONFIG_SCHEMA)
    assert ret is expected


def test_local_hooks_with_rev_fails():
    config_obj = {'repos': [dict(sample_local_config(), rev='foo')]}
    with pytest.raises(cfgv.ValidationError):
        cfgv.validate(config_obj, CONFIG_SCHEMA)


def test_config_with_local_hooks_definition_passes():
    config_obj = {'repos': [sample_local_config()]}
    cfgv.validate(config_obj, CONFIG_SCHEMA)


def test_config_schema_does_not_contain_defaults():
    """Due to the way our merging works, if this schema has any defaults they
    will clobber potentially useful values in the backing manifest. #227
    """
    for item in CONFIG_HOOK_DICT.items:
        assert not isinstance(item, cfgv.Optional)


def test_validate_manifest_main_ok():
    assert not validate_manifest_main(('.pre-commit-hooks.yaml',))


def test_validate_config_main_ok():
    assert not validate_config_main(('.pre-commit-config.yaml',))


def test_validate_config_old_list_format_ok(tmpdir):
    f = tmpdir.join('cfg.yaml')
    f.write('-  {repo: meta, hooks: [{id: identity}]}')
    assert not validate_config_main((f.strpath,))


def test_validate_warn_on_unknown_keys_at_repo_level(tmpdir, caplog):
    f = tmpdir.join('cfg.yaml')
    f.write(
        '-   repo: https://gitlab.com/pycqa/flake8\n'
        '    rev: 3.7.7\n'
        '    hooks:\n'
        '    -   id: flake8\n'
        '    args: [--some-args]\n',
    )
    ret_val = validate_config_main((f.strpath,))
    assert not ret_val
    assert caplog.record_tuples == [
        (
            'pre_commit',
            logging.WARNING,
            'Unexpected config key(s): args',
        ),
    ]


def test_validate_warn_on_unknown_keys_at_top_level(tmpdir, caplog):
    f = tmpdir.join('cfg.yaml')
    f.write(
        'repos:\n'
        '-   repo: https://gitlab.com/pycqa/flake8\n'
        '    rev: 3.7.7\n'
        '    hooks:\n'
        '    -   id: flake8\n'
        'foo:\n'
        '    id: 1.0.0\n',
    )
    ret_val = validate_config_main((f.strpath,))
    assert not ret_val
    assert caplog.record_tuples == [
        (
            'pre_commit',
            logging.WARNING,
            'Unexpected config key(s): foo',
        ),
    ]


@pytest.mark.parametrize('fn', (validate_config_main, validate_manifest_main))
def test_mains_not_ok(tmpdir, fn):
    not_yaml = tmpdir.join('f.notyaml')
    not_yaml.write('{')
    not_schema = tmpdir.join('notconfig.yaml')
    not_schema.write('{}')

    assert fn(('does-not-exist',))
    assert fn((not_yaml.strpath,))
    assert fn((not_schema.strpath,))


@pytest.mark.parametrize(
    ('manifest_obj', 'expected'),
    (
        (
            [{
                'id': 'a',
                'name': 'b',
                'entry': 'c',
                'language': 'python',
                'files': r'\.py$',
            }],
            True,
        ),
        (
            [{
                'id': 'a',
                'name': 'b',
                'entry': 'c',
                'language': 'python',
                'language_version': 'python3.4',
                'files': r'\.py$',
            }],
            True,
        ),
        (
            # A regression in 0.13.5: always_run and files are permissible
            [{
                'id': 'a',
                'name': 'b',
                'entry': 'c',
                'language': 'python',
                'files': '',
                'always_run': True,
            }],
            True,
        ),
    ),
)
def test_valid_manifests(manifest_obj, expected):
    ret = is_valid_according_to_schema(manifest_obj, MANIFEST_SCHEMA)
    assert ret is expected


@pytest.mark.parametrize(
    'dct',
    (
        {'repo': 'local'}, {'repo': 'meta'},
        {'repo': 'wat', 'sha': 'wat'}, {'repo': 'wat', 'rev': 'wat'},
    ),
)
def test_migrate_sha_to_rev_ok(dct):
    MigrateShaToRev().check(dct)


def test_migrate_sha_to_rev_dont_specify_both():
    with pytest.raises(cfgv.ValidationError) as excinfo:
        MigrateShaToRev().check({'repo': 'a', 'sha': 'b', 'rev': 'c'})
    msg, = excinfo.value.args
    assert msg == 'Cannot specify both sha and rev'


@pytest.mark.parametrize(
    'dct',
    (
        {'repo': 'a'},
        {'repo': 'meta', 'sha': 'a'}, {'repo': 'meta', 'rev': 'a'},
    ),
)
def test_migrate_sha_to_rev_conditional_check_failures(dct):
    with pytest.raises(cfgv.ValidationError):
        MigrateShaToRev().check(dct)


def test_migrate_to_sha_apply_default():
    dct = {'repo': 'a', 'sha': 'b'}
    MigrateShaToRev().apply_default(dct)
    assert dct == {'repo': 'a', 'rev': 'b'}


def test_migrate_to_sha_ok():
    dct = {'repo': 'a', 'rev': 'b'}
    MigrateShaToRev().apply_default(dct)
    assert dct == {'repo': 'a', 'rev': 'b'}


@pytest.mark.parametrize(
    'config_repo',
    (
        # i-dont-exist isn't a valid hook
        {'repo': 'meta', 'hooks': [{'id': 'i-dont-exist'}]},
        # invalid to set a language for a meta hook
        {'repo': 'meta', 'hooks': [{'id': 'identity', 'language': 'python'}]},
        # name override must be string
        {'repo': 'meta', 'hooks': [{'id': 'identity', 'name': False}]},
    ),
)
def test_meta_hook_invalid(config_repo):
    with pytest.raises(cfgv.ValidationError):
        cfgv.validate(config_repo, CONFIG_REPO_DICT)


@pytest.mark.parametrize(
    'mapping',
    (
        # invalid language key
        {'pony': '1.0'},
        # not a string for version
        {'python': 3},
    ),
)
def test_default_language_version_invalid(mapping):
    with pytest.raises(cfgv.ValidationError):
        cfgv.validate(mapping, DEFAULT_LANGUAGE_VERSION)


def test_minimum_pre_commit_version_failing():
    with pytest.raises(cfgv.ValidationError) as excinfo:
        cfg = {'repos': [], 'minimum_pre_commit_version': '999'}
        cfgv.validate(cfg, CONFIG_SCHEMA)
    assert str(excinfo.value) == (
        '\n'
        '==> At Config()\n'
        '==> At key: minimum_pre_commit_version\n'
        '=====> pre-commit version 999 is required but version {} is '
        'installed.  Perhaps run `pip install --upgrade pre-commit`.'.format(
            C.VERSION,
        )
    )


def test_minimum_pre_commit_version_passing():
    cfg = {'repos': [], 'minimum_pre_commit_version': '0'}
    cfgv.validate(cfg, CONFIG_SCHEMA)


@pytest.mark.parametrize('schema', (CONFIG_SCHEMA, CONFIG_REPO_DICT))
def test_warn_additional(schema):
    allowed_keys = {item.key for item in schema.items if hasattr(item, 'key')}
    warn_additional, = [
        x for x in schema.items if isinstance(x, cfgv.WarnAdditionalKeys)
    ]
    assert allowed_keys == set(warn_additional.keys)

[((805, 871), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""value"""', "('definitely-not-a-tag', 'fiel')"], {}), "('value', ('definitely-not-a-tag', 'fiel'))\n", (828, 871), False, 'import pytest\n'), ((992, 1697), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (["('config_obj', 'expected')", "(({'repos': [{'repo': 'git@github.com:pre-commit/pre-commit-hooks', 'rev':\n    'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'hooks': [{'id': 'pyflakes',\n    'files': '\\\\.py$'}]}]}, True), ({'repos': [{'repo':\n    'git@github.com:pre-commit/pre-commit-hooks', 'rev':\n    'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'hooks': [{'id': 'pyflakes',\n    'files': '\\\\.py$', 'args': ['foo', 'bar', 'baz']}]}]}, True), ({'repos':\n    [{'repo': 'git@github.com:pre-commit/pre-commit-hooks', 'rev':\n    'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'hooks': [{'id': 'pyflakes',\n    'files': '\\\\.py$', 'exclude': 0, 'args': ['foo', 'bar', 'baz']}]}]}, False)\n    )"], {}), "(('config_obj', 'expected'), (({'repos': [{'repo':\n    'git@github.com:pre-commit/pre-commit-hooks', 'rev':\n    'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'hooks': [{'id': 'pyflakes',\n    'files': '\\\\.py$'}]}]}, True), ({'repos': [{'repo':\n    'git@github.com:pre-commit/pre-commit-hooks', 'rev':\n    'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'hooks': [{'id': 'pyflakes',\n    'files': '\\\\.py$', 'args': ['foo', 'bar', 'baz']}]}]}, True), ({'repos':\n    [{'repo': 'git@github.com:pre-commit/pre-commit-hooks', 'rev':\n    'cd74dc150c142c3be70b24eaf0b02cae9d235f37', 'hooks': [{'id': 'pyflakes',\n    'files': '\\\\.py$', 'exclude': 0, 'args': ['foo', 'bar', 'baz']}]}]}, \n    False)))\n", (1015, 1697), False, 'import pytest\n'), ((4832, 4909), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""fn"""', '(validate_config_main, validate_manifest_main)'], {}), "('fn', (validate_config_main, validate_manifest_main))\n", (4855, 4909), False, 'import pytest\n'), ((5194, 5590), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (["('manifest_obj', 'expected')", "(([{'id': 'a', 'name': 'b', 'entry': 'c', 'language': 'python', 'files':\n    '\\\\.py$'}], True), ([{'id': 'a', 'name': 'b', 'entry': 'c', 'language':\n    'python', 'language_version': 'python3.4', 'files': '\\\\.py$'}], True),\n    ([{'id': 'a', 'name': 'b', 'entry': 'c', 'language': 'python', 'files':\n    '', 'always_run': True}], True))"], {}), "(('manifest_obj', 'expected'), (([{'id': 'a', 'name':\n    'b', 'entry': 'c', 'language': 'python', 'files': '\\\\.py$'}], True), ([\n    {'id': 'a', 'name': 'b', 'entry': 'c', 'language': 'python',\n    'language_version': 'python3.4', 'files': '\\\\.py$'}], True), ([{'id':\n    'a', 'name': 'b', 'entry': 'c', 'language': 'python', 'files': '',\n    'always_run': True}], True)))\n", (5217, 5590), False, 'import pytest\n'), ((6259, 6395), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""dct"""', "({'repo': 'local'}, {'repo': 'meta'}, {'repo': 'wat', 'sha': 'wat'}, {\n    'repo': 'wat', 'rev': 'wat'})"], {}), "('dct', ({'repo': 'local'}, {'repo': 'meta'}, {\n    'repo': 'wat', 'sha': 'wat'}, {'repo': 'wat', 'rev': 'wat'}))\n", (6282, 6395), False, 'import pytest\n'), ((6759, 6870), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""dct"""', "({'repo': 'a'}, {'repo': 'meta', 'sha': 'a'}, {'repo': 'meta', 'rev': 'a'})"], {}), "('dct', ({'repo': 'a'}, {'repo': 'meta', 'sha': 'a'},\n    {'repo': 'meta', 'rev': 'a'}))\n", (6782, 6870), False, 'import pytest\n'), ((7365, 7605), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""config_repo"""', "({'repo': 'meta', 'hooks': [{'id': 'i-dont-exist'}]}, {'repo': 'meta',\n    'hooks': [{'id': 'identity', 'language': 'python'}]}, {'repo': 'meta',\n    'hooks': [{'id': 'identity', 'name': False}]})"], {}), "('config_repo', ({'repo': 'meta', 'hooks': [{'id':\n    'i-dont-exist'}]}, {'repo': 'meta', 'hooks': [{'id': 'identity',\n    'language': 'python'}]}, {'repo': 'meta', 'hooks': [{'id': 'identity',\n    'name': False}]}))\n", (7388, 7605), False, 'import pytest\n'), ((7912, 7980), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""mapping"""', "({'pony': '1.0'}, {'python': 3})"], {}), "('mapping', ({'pony': '1.0'}, {'python': 3}))\n", (7935, 7980), False, 'import pytest\n'), ((8913, 8981), 'pytest.mark.parametrize', 'pytest.mark.parametrize', (['"""schema"""', '(CONFIG_SCHEMA, CONFIG_REPO_DICT)'], {}), "('schema', (CONFIG_SCHEMA, CONFIG_REPO_DICT))\n", (8936, 8981), False, 'import pytest\n'), ((2992, 3032), 'cfgv.validate', 'cfgv.validate', (['config_obj', 'CONFIG_SCHEMA'], {}), '(config_obj, CONFIG_SCHEMA)\n', (3005, 3032), False, 'import cfgv\n'), ((4050, 4084), 'pre_commit.clientlib.validate_config_main', 'validate_config_main', (['(f.strpath,)'], {}), '((f.strpath,))\n', (4070, 4084), False, 'from pre_commit.clientlib import validate_config_main\n'), ((4607, 4641), 'pre_commit.clientlib.validate_config_main', 'validate_config_main', (['(f.strpath,)'], {}), '((f.strpath,))\n', (4627, 4641), False, 'from pre_commit.clientlib import validate_config_main\n'), ((8876, 8909), 'cfgv.validate', 'cfgv.validate', (['cfg', 'CONFIG_SCHEMA'], {}), '(cfg, CONFIG_SCHEMA)\n', (8889, 8909), False, 'import cfgv\n'), ((697, 727), 'cfgv.validate', 'cfgv.validate', (['obj', 'obj_schema'], {}), '(obj, obj_schema)\n', (710, 727), False, 'import cfgv\n'), ((922, 957), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (935, 957), False, 'import pytest\n'), ((967, 988), 'pre_commit.clientlib.check_type_tag', 'check_type_tag', (['value'], {}), '(value)\n', (981, 988), False, 'from pre_commit.clientlib import check_type_tag\n'), ((2794, 2829), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (2807, 2829), False, 'import pytest\n'), ((2839, 2879), 'cfgv.validate', 'cfgv.validate', (['config_obj', 'CONFIG_SCHEMA'], {}), '(config_obj, CONFIG_SCHEMA)\n', (2852, 2879), False, 'import cfgv\n'), ((3392, 3443), 'pre_commit.clientlib.validate_manifest_main', 'validate_manifest_main', (["('.pre-commit-hooks.yaml',)"], {}), "(('.pre-commit-hooks.yaml',))\n", (3414, 3443), False, 'from pre_commit.clientlib import validate_manifest_main\n'), ((3497, 3547), 'pre_commit.clientlib.validate_config_main', 'validate_config_main', (["('.pre-commit-config.yaml',)"], {}), "(('.pre-commit-config.yaml',))\n", (3517, 3547), False, 'from pre_commit.clientlib import validate_config_main\n'), ((3706, 3740), 'pre_commit.clientlib.validate_config_main', 'validate_config_main', (['(f.strpath,)'], {}), '((f.strpath,))\n', (3726, 3740), False, 'from pre_commit.clientlib import validate_config_main\n'), ((6555, 6590), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (6568, 6590), False, 'import pytest\n'), ((6971, 7006), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (6984, 7006), False, 'import pytest\n'), ((7819, 7854), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (7832, 7854), False, 'import pytest\n'), ((7864, 7908), 'cfgv.validate', 'cfgv.validate', (['config_repo', 'CONFIG_REPO_DICT'], {}), '(config_repo, CONFIG_REPO_DICT)\n', (7877, 7908), False, 'import cfgv\n'), ((8142, 8177), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (8155, 8177), False, 'import pytest\n'), ((8187, 8235), 'cfgv.validate', 'cfgv.validate', (['mapping', 'DEFAULT_LANGUAGE_VERSION'], {}), '(mapping, DEFAULT_LANGUAGE_VERSION)\n', (8200, 8235), False, 'import cfgv\n'), ((8294, 8329), 'pytest.raises', 'pytest.raises', (['cfgv.ValidationError'], {}), '(cfgv.ValidationError)\n', (8307, 8329), False, 'import pytest\n'), ((8415, 8448), 'cfgv.validate', 'cfgv.validate', (['cfg', 'CONFIG_SCHEMA'], {}), '(cfg, CONFIG_SCHEMA)\n', (8428, 8448), False, 'import cfgv\n'), ((2964, 2985), 'testing.fixtures.sample_local_config', 'sample_local_config', ([], {}), '()\n', (2983, 2985), False, 'from testing.fixtures import sample_local_config\n'), ((6466, 6483), 'pre_commit.clientlib.MigrateShaToRev', 'MigrateShaToRev', ([], {}), '()\n', (6481, 6483), False, 'from pre_commit.clientlib import MigrateShaToRev\n'), ((7128, 7145), 'pre_commit.clientlib.MigrateShaToRev', 'MigrateShaToRev', ([], {}), '()\n', (7143, 7145), False, 'from pre_commit.clientlib import MigrateShaToRev\n'), ((7281, 7298), 'pre_commit.clientlib.MigrateShaToRev', 'MigrateShaToRev', ([], {}), '()\n', (7296, 7298), False, 'from pre_commit.clientlib import MigrateShaToRev\n'), ((2749, 2770), 'testing.fixtures.sample_local_config', 'sample_local_config', ([], {}), '()\n', (2768, 2770), False, 'from testing.fixtures import sample_local_config\n'), ((6611, 6628), 'pre_commit.clientlib.MigrateShaToRev', 'MigrateShaToRev', ([], {}), '()\n', (6626, 6628), False, 'from pre_commit.clientlib import MigrateShaToRev\n'), ((7016, 7033), 'pre_commit.clientlib.MigrateShaToRev', 'MigrateShaToRev', ([], {}), '()\n', (7031, 7033), False, 'from pre_commit.clientlib import MigrateShaToRev\n')]