import fixtures
from oslotest import mockpatch
import six
from stevedore import extension

from heat.common import exception
from heat.common import template_format
from heat.engine import function
from heat.engine import template
from heat.tests import common


class TemplatePluginFixture(fixtures.Fixture):
    def __init__(self, templates={}):
        super(TemplatePluginFixture, self).__init__()
        self.templates = [extension.Extension(k, None, v, None)
                          for (k, v) in templates.items()]

    def _get_template_extension_manager(self):
        return extension.ExtensionManager.make_test_instance(self.templates)

    def setUp(self):
        super(TemplatePluginFixture, self).setUp()

        def clear_template_classes():
            template._template_classes = None

        clear_template_classes()
        self.useFixture(mockpatch.PatchObject(
            template,
            '_get_template_extension_manager',
            new=self._get_template_extension_manager))
        self.addCleanup(clear_template_classes)


class TestTemplatePluginManager(common.HeatTestCase):
    def test_template_NEW_good(self):
        class NewTemplate(template.Template):
            SECTIONS = (VERSION, MAPPINGS) = ('NEWTemplateFormatVersion',
                                              '__undefined__')
            RESOURCES = 'thingies'

            def param_schemata(self):
                pass

            def parameters(self, stack_identifier, user_params):
                pass

            def resource_definitions(self, stack):
                pass

            def add_resource(self, definition, name=None):
                pass

            def __getitem__(self, section):
                return {}

            def functions(self):
                return {}

        class NewTemplatePrint(function.Function):
            def result(self):
                return 'always this'

        self.useFixture(TemplatePluginFixture(
            {'NEWTemplateFormatVersion.2345-01-01': NewTemplate}))

        t = {'NEWTemplateFormatVersion': '2345-01-01'}
        tmpl = template.Template(t)
        err = tmpl.validate()
        self.assertIsNone(err)


class TestTemplateVersion(common.HeatTestCase):

    versions = (('heat_template_version', '2013-05-23'),
                ('HeatTemplateFormatVersion', '2012-12-12'),
                ('AWSTemplateFormatVersion', '2010-09-09'))

    def test_hot_version(self):
        tmpl = {
            'heat_template_version': '2013-05-23',
            'foo': 'bar',
            'parameters': {}
        }
        self.assertEqual(('heat_template_version', '2013-05-23'),
                         template.get_version(tmpl, self.versions))

    def test_cfn_version(self):
        tmpl = {
            'AWSTemplateFormatVersion': '2010-09-09',
            'foo': 'bar',
            'Parameters': {}
        }
        self.assertEqual(('AWSTemplateFormatVersion', '2010-09-09'),
                         template.get_version(tmpl, self.versions))

    def test_heat_cfn_version(self):
        tmpl = {
            'HeatTemplateFormatVersion': '2012-12-12',
            'foo': 'bar',
            'Parameters': {}
        }
        self.assertEqual(('HeatTemplateFormatVersion', '2012-12-12'),
                         template.get_version(tmpl, self.versions))

    def test_missing_version(self):
        tmpl = {
            'foo': 'bar',
            'Parameters': {}
        }
        ex = self.assertRaises(exception.InvalidTemplateVersion,
                               template.get_version, tmpl, self.versions)
        self.assertEqual('The template version is invalid: Template version '
                         'was not provided', six.text_type(ex))

    def test_ambiguous_version(self):
        tmpl = {
            'AWSTemplateFormatVersion': '2010-09-09',
            'HeatTemplateFormatVersion': '2012-12-12',
            'foo': 'bar',
            'Parameters': {}
        }
        self.assertRaises(exception.InvalidTemplateVersion,
                          template.get_version, tmpl, self.versions)


class TestTemplateValidate(common.HeatTestCase):

    def test_template_validate_cfn_good(self):
        t = {
            'AWSTemplateFormatVersion': '2010-09-09',
            'Description': 'foo',
            'Parameters': {},
            'Mappings': {},
            'Resources': {
                'server': {
                    'Type': 'OS::Nova::Server'
                }
            },
            'Outputs': {},
        }

        tmpl = template.Template(t)
        err = tmpl.validate()
        self.assertIsNone(err)

        # test with alternate version key
        t = {
            'HeatTemplateFormatVersion': '2012-12-12',
            'Description': 'foo',
            'Parameters': {},
            'Mappings': {},
            'Resources': {
                'server': {
                    'Type': 'OS::Nova::Server'
                }
            },
            'Outputs': {},
        }

        tmpl = template.Template(t)
        err = tmpl.validate()
        self.assertIsNone(err)

    def test_template_validate_cfn_bad_section(self):
        t = {
            'AWSTemplateFormatVersion': '2010-09-09',
            'Description': 'foo',
            'Parameteers': {},
            'Mappings': {},
            'Resources': {
                'server': {
                    'Type': 'OS::Nova::Server'
                }
            },
            'Outputs': {},
        }

        tmpl = template.Template(t)
        err = self.assertRaises(exception.InvalidTemplateSection,
                                tmpl.validate)
        self.assertIn('Parameteers', six.text_type(err))

    def test_template_validate_cfn_empty(self):
        t = template_format.parse('''
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Resources:
Outputs:
''')
        tmpl = template.Template(t)
        err = tmpl.validate()
        self.assertIsNone(err)

    def test_template_validate_hot_good(self):
        t = {
            'heat_template_version': '2013-05-23',
            'description': 'foo',
            'parameters': {},
            'resources': {
                'server': {
                    'type': 'OS::Nova::Server'
                }
            },
            'outputs': {},
        }

        tmpl = template.Template(t)
        err = tmpl.validate()
        self.assertIsNone(err)

    def test_template_validate_hot_bad_section(self):
        t = {
            'heat_template_version': '2013-05-23',
            'description': 'foo',
            'parameteers': {},
            'resources': {
                'server': {
                    'type': 'OS::Nova::Server'
                }
            },
            'outputs': {},
        }

        tmpl = template.Template(t)
        err = self.assertRaises(exception.InvalidTemplateSection,
                                tmpl.validate)
        self.assertIn('parameteers', six.text_type(err))
