"""Unit tests for app.validators. """
from wtforms import ValidationError
import flask
from pytest import raises
from app.utils.validators import password_rules, image_file, allowed_file


class DummyField(object):
    """Dummy field object to emulate wtforms field."""
    def __init__(self, data=None, errors=(), raw_data=None):
        self.data = data
        self.errors = list(errors)
        self.raw_data = raw_data

    def gettext(self, string):
        return string

    def ngettext(self, singular, plural, n):
        return singular


class DummyForm(dict):
    """Dummy form object to emulate wtforms form."""
    pass


class DummyFile(object):
    """Dummy file like class to emulate uploaded file handler."""
    def __init__(self, filename):
        self.filename = filename

    def __repr__(self):
        return self.filename


def _run_validator_check(subtests, validator, valid, invalid):
    """Runs tests again validator with valid and invalid inputs.

    Args:
        subtest: Subtests fixture.
        validator: Validator instance to run tests against
        valid: List of valid inputs
        invalid: List of invalid inputs
    """
    field = DummyField()

    for item in valid:
        field.data = item
        with subtests.test(item=item):
            validator(DummyForm(), field)

    for item in invalid:
        field.data = item
        with subtests.test(item=item):
            with raises(ValidationError):
                validator(DummyForm(), field)


def test_allowed_file(subtests, req_context):
    validator = allowed_file()
    extensions = ['exe', 'html']
    valid = ['foo.jpg', 'exe', 'foo.exe.zip', 'foo']
    invalid = ['foo.exe', 'foo.EXE', 'foo.pdf.exe', 'foo.html']

    valid = [DummyFile(x) for x in valid]
    invalid = [DummyFile(x) for x in invalid]
    flask.current_app.config['DISABLED_EXTENSIONS'] = extensions
    with flask.current_app.test_request_context():
        _run_validator_check(subtests, validator, valid, invalid)


def test_allowed_file_multiple(subtests, req_context):
    validator = allowed_file()
    extensions = ['exe', 'html']
    valid = ['foo.jpg', 'exe', 'foo.exe.zip', 'foo']
    invalid = ['foo.exe', 'foo.EXE', 'foo.pdf.exe', 'foo.html']

    valid = [[DummyFile(x) for x in valid], [DummyFile(valid[0])],
             [DummyFile(valid[0]), DummyFile(valid[1])]]
    invalid = [[DummyFile(x) for x in invalid], [DummyFile(invalid[0])],
               [DummyFile(invalid[0]), DummyFile(invalid[1])]]
    flask.current_app.config['DISABLED_EXTENSIONS'] = extensions
    with flask.current_app.test_request_context():
        _run_validator_check(subtests, validator, valid, invalid)


def test_allowed_file_message(req_context):
    validator = allowed_file(message="custom message")

    field = DummyField()
    field.data = DummyFile("blah.foo")

    flask.current_app.config['DISABLED_EXTENSIONS'] = ['foo']
    with flask.current_app.test_request_context():
        with raises(ValidationError) as e:
            validator(DummyForm(), field)
    assert str(e.value) == "custom message"


def test_image_file(subtests, req_context):
    validator = image_file()
    extensions = ['jpg', 'png', 'tiff']
    valid = ['foo.jpg', 'foo.JPG', 'bar.png', 'blah.tiff', 'a.foo.jpg']
    invalid = ['foo', 'jpg', 'foo.pdf', 'foo.jpg.pdf', '', '.jpg', 'o.gif']

    valid = [DummyFile(x) for x in valid]
    invalid = [DummyFile(x) for x in invalid]
    flask.current_app.config['IMAGE_EXTENSIONS'] = extensions
    with flask.current_app.test_request_context():
        _run_validator_check(subtests, validator, valid, invalid)


def test_image_file_multiple(subtests, req_context):
    validator = image_file()
    extensions = ['jpg', 'png', 'tiff']
    valid = ['foo.jpg', 'foo.JPG', 'bar.png', 'blah.tiff', 'a.foo.jpg']
    invalid = ['foo', 'jpg', 'foo.pdf', 'foo.jpg.pdf', '', '.jpg', 'o.gif']

    valid = [[DummyFile(x) for x in valid], [DummyFile(valid[0])],
             [DummyFile(valid[0]), DummyFile(valid[1])]]
    invalid = [[DummyFile(x) for x in invalid], [DummyFile(invalid[0])],
               [DummyFile(invalid[0]), DummyFile(invalid[1])]]
    flask.current_app.config['IMAGE_EXTENSIONS'] = extensions
    with flask.current_app.test_request_context():
        _run_validator_check(subtests, validator, valid, invalid)


def test_image_file_message(req_context):
    validator = image_file(message="custom message")

    field = DummyField()
    field.data = DummyFile("blah")

    flask.current_app.config['IMAGE_EXTENSIONS'] = ['foo']
    with flask.current_app.test_request_context():
        with raises(ValidationError) as e:
            validator(DummyForm(), field)
    assert str(e.value) == "custom message"


def test_password_rules_length(subtests):
    validator = password_rules(length=6, upper=None, lower=None, numeric=None,
                               special=None)
    valid = ["as123.21", "abcdef", "sdadadaswasasa", "1234567", "...,.,..,",
             "AAAAAAA", "AbCdEf"]
    invalid = ["abc", "123", "....", "aBcDe", "a1.V3"]
    _run_validator_check(subtests, validator, valid, invalid)


def test_password_rules_upper(subtests):
    validator = password_rules(length=6, upper=2, lower=None, numeric=None,
                               special=None)
    valid = ["abcDEf", "HellOO", "ABCDEZ", "A.b#3CZ", "ADSDSA"]
    invalid = ["abcdEf", "helloo", "A231sdsd"]
    _run_validator_check(subtests, validator, valid, invalid)


def test_password_rules_lower(subtests):
    validator = password_rules(length=6, upper=None, lower=3, numeric=None,
                               special=None)
    valid = ["abcdefg", "axzBAR", "123abcdsa", "AbCdEfGh", "..as..2ds.."]
    invalid = ["foOBAR", "123ABcdSA", "1a2b.C#"]
    _run_validator_check(subtests, validator, valid, invalid)


def test_password_rules_numeric(subtests):
    validator = password_rules(length=6, upper=None, lower=None, numeric=2,
                               special=None)
    valid = ["1bcd4A.d", "123456", "a?9#.0"]
    invalid = ["2ds.#<", "abcdef", "ABCDEF", "x2U.'Q"]
    _run_validator_check(subtests, validator, valid, invalid)


def test_password_rules_special(subtests):
    validator = password_rules(length=6, upper=None, lower=None, numeric=None,
                               special=3)
    valid = ["ab.?123!", ".#@dS9", "abcdef123><?"]
    invalid = ["abcdef", ".23134", "AbCd123,]"]
    _run_validator_check(subtests, validator, valid, invalid)


def test_password_rules_all(subtests):
    validator = password_rules(length=6, upper=2, lower=1, numeric=1,
                               special=1)
    valid = ["ABc1.2", "abcDEF123#%^", "a2B.C?"]
    invalid = ["helloo", "ABCDEF", "Ab1.?c"]
    _run_validator_check(subtests, validator, valid, invalid)


def test_password_rules_message(subtests):
    validator = password_rules(length=100, message="custom message")

    field = DummyField()
    field.data = "wrong"
    with raises(ValidationError) as e:
        validator(DummyForm(), field)
    assert str(e.value) == "custom message"

[((60, 16, 60, 30), 'app.utils.validators.allowed_file', 'allowed_file', ({}, {}), '()', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((73, 16, 73, 30), 'app.utils.validators.allowed_file', 'allowed_file', ({}, {}), '()', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((88, 16, 88, 54), 'app.utils.validators.allowed_file', 'allowed_file', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((101, 16, 101, 28), 'app.utils.validators.image_file', 'image_file', ({}, {}), '()', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((114, 16, 114, 28), 'app.utils.validators.image_file', 'image_file', ({}, {}), '()', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((129, 16, 129, 52), 'app.utils.validators.image_file', 'image_file', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((142, 16, 143, 44), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((151, 16, 152, 44), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((159, 16, 160, 44), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((167, 16, 168, 44), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((175, 16, 176, 41), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((183, 16, 184, 41), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((191, 16, 191, 68), 'app.utils.validators.password_rules', 'password_rules', (), '', False, 'from app.utils.validators import password_rules, image_file, allowed_file\n'), ((68, 9, 68, 49), 'flask.current_app.test_request_context', 'flask.current_app.test_request_context', ({}, {}), '()', False, 'import flask\n'), ((83, 9, 83, 49), 'flask.current_app.test_request_context', 'flask.current_app.test_request_context', ({}, {}), '()', False, 'import flask\n'), ((94, 9, 94, 49), 'flask.current_app.test_request_context', 'flask.current_app.test_request_context', ({}, {}), '()', False, 'import flask\n'), ((109, 9, 109, 49), 'flask.current_app.test_request_context', 'flask.current_app.test_request_context', ({}, {}), '()', False, 'import flask\n'), ((124, 9, 124, 49), 'flask.current_app.test_request_context', 'flask.current_app.test_request_context', ({}, {}), '()', False, 'import flask\n'), ((135, 9, 135, 49), 'flask.current_app.test_request_context', 'flask.current_app.test_request_context', ({}, {}), '()', False, 'import flask\n'), ((195, 9, 195, 32), 'pytest.raises', 'raises', ({(195, 16, 195, 31): 'ValidationError'}, {}), '(ValidationError)', False, 'from pytest import raises\n'), ((95, 13, 95, 36), 'pytest.raises', 'raises', ({(95, 20, 95, 35): 'ValidationError'}, {}), '(ValidationError)', False, 'from pytest import raises\n'), ((136, 13, 136, 36), 'pytest.raises', 'raises', ({(136, 20, 136, 35): 'ValidationError'}, {}), '(ValidationError)', False, 'from pytest import raises\n'), ((55, 17, 55, 40), 'pytest.raises', 'raises', ({(55, 24, 55, 39): 'ValidationError'}, {}), '(ValidationError)', False, 'from pytest import raises\n')]