# -*- coding: utf-8 -*-

#    Copyright 2014 Mirantis, Inc.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.
import jsonschema
import mock

from fuel_plugin_builder import errors
from fuel_plugin_builder.tests.base import BaseTestCase
from fuel_plugin_builder.validators import LegacyBaseValidator


class LegacyBaseValidatorTestCase(BaseTestCase):

    def setUp(self):
        class NewValidator(LegacyBaseValidator):

            @property
            def basic_version(self):
                return None

            def validate(self):
                pass

        self.plugin_path = '/tmp/plugin_path'
        self.validator = NewValidator(self.plugin_path)
        self.data = {'data': 'data1'}
        self.schema = self.make_schema(['data'], {'data': {'type': 'string'}})
        self.format_checker = jsonschema.FormatChecker

    @classmethod
    def make_schema(cls, required, properties):
        return {
            '$schema': 'http://json-schema.org/draft-04/schema#',
            'type': 'object',
            'required': required,
            'properties': properties}

    @mock.patch('fuel_plugin_builder.validators.base.jsonschema')
    def test_validate_schema(self, schema_mock):
        self.validator.validate_schema(
            self.data,
            self.schema,
            'file_path')
        schema_mock.validate.assert_called_once_with(
            self.data,
            self.schema, format_checker=self.format_checker)

    def test_validate_schema_raises_error(self):
        schema = self.make_schema(['key'], {'key': {'type': 'string'}})
        data = {}

        with self.assertRaisesRegexp(
                errors.ValidationError,
                "File 'file_path', 'key' is a required property"):
            self.validator.validate_schema(data, schema, 'file_path')

    def test_validate_schema_raises_error_path_in_message(self):
        schema = self.make_schema(
            ['key'],
            {'key': {'type': 'array', 'items': {'type': 'string'}}})
        data = {'key': ['str', 'str', 0]}

        expected_error = ("File 'file_path', 0 is not of type "
                          "'string', value path 'key -> 2'")
        with self.assertRaisesRegexp(
                errors.ValidationError,
                expected_error):
            self.validator.validate_schema(data, schema, 'file_path')

    def test_validate_schema_raises_error_custom_value_path(self):
        schema = self.make_schema(['key'], {'key': {'type': 'string'}})
        data = {}

        with self.assertRaisesRegexp(
                errors.ValidationError,
                "File 'file_path', 'key' is a required property, "
                "value path '0 -> path2'"):
            self.validator.validate_schema(
                data, schema, 'file_path', value_path=[0, 'path2'])

    @mock.patch(
        'fuel_plugin_builder.validators.base'
        '.LegacyBaseValidator.validate_schema')
    def test_validate_file_by_schema_failed(self, utils_mock):
        utils_mock.parse_yaml.return_value = self.data
        with self.assertRaisesRegexp(
                errors.FileDoesNotExist,
                "File '/tmp/plugin_path' does not exist"):
            self.validator.validate_file_by_schema(
                self.schema, self.plugin_path)

    @mock.patch('fuel_plugin_builder.validators.base.utils')
    @mock.patch(
        'fuel_plugin_builder.validators.base'
        '.LegacyBaseValidator.validate_schema')
    def test_validate_file_by_schema(self, validate_mock, utils_mock):
        utils_mock.parse_yaml.return_value = self.data
        self.validator.validate_file_by_schema(self.schema, self.plugin_path)
        utils_mock.parse_yaml.assert_called_once_with(self.plugin_path)
        validate_mock(self.data, self.schema, self.plugin_path)

    @mock.patch('fuel_plugin_builder.validators.base.utils')
    @mock.patch(
        'fuel_plugin_builder.validators.base'
        '.LegacyBaseValidator.validate_schema')
    def test_validate_file_by_schema_empty_file_passes(
            self, validate_mock, utils_mock):
        utils_mock.parse_yaml.return_value = None
        self.validator.validate_file_by_schema(
            self.schema,
            self.plugin_path,
            allow_empty=True)
        utils_mock.parse_yaml.assert_called_once_with(self.plugin_path)

    @mock.patch('fuel_plugin_builder.validators.base.utils')
    @mock.patch(
        'fuel_plugin_builder.validators.base'
        '.LegacyBaseValidator.validate_schema')
    def test_validate_file_by_schema_empty_file_fails(
            self, validate_mock, utils_mock):
        utils_mock.parse_yaml.return_value = None
        with self.assertRaises(errors.FileIsEmpty):
            self.validator.validate_file_by_schema(
                self.schema,
                self.plugin_path,
                allow_empty=False)

    def test_validate_schema_with_subschemas(self):
        schema_object = {
            'key': {
                'type': 'array',
                'items': {
                    'anyOf': [
                        {
                            'type': 'string'
                        },
                        {
                            'type': 'object',
                            'required': ['inner_key'],
                            'properties': {
                                'inner_key_1': {'type': 'string'},
                                'inner_key_2': {'type': 'string'},
                            }
                        },
                        {
                            'type': 'object',
                            'minProperties': 1,
                            'maxProperties': 1
                        }
                    ]
                }
            }
        }

        schema = self.make_schema(['key'], schema_object)

        with self.assertRaisesRegexp(
                errors.ValidationError,
                "File 'file_path', True is not of type 'string', "
                "value path '0 -> path1 -> key -> 0'"):
            data = {'key': [True]}
            self.validator.validate_schema(
                data, schema, 'file_path', value_path=[0, 'path1'])

        with self.assertRaisesRegexp(
                errors.ValidationError,
                "File 'file_path', True is not of type 'string', "
                "value path '0 -> path1 -> key -> 0 -> inner_key_1'"):
            data = {'key': [{'inner_key_1': True, 'inner_key_2': 'str'}]}
            self.validator.validate_schema(
                data, schema, 'file_path', value_path=[0, 'path1'])
