from collections import OrderedDict
import tempfile
import shutil
import uuid
import six
import git
from datetime import timedelta
from django.utils.timezone import now
from yaml import dump
from ._base import BaseTestCase, os
from ..tasks import ScheduledTask
from ..unittests.ansible import inventory_data, valid_inventory


test_ansible_cfg = '''
[defaults]
library = lib:lib2
'''

test_module_content = '''
#!/usr/bin/python
# -*- coding: utf-8 -*-

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['stableinterface'],
                    'supported_by': 'core'}

DOCUMENTATION = """
---
module: test_module
short_description: Test module
description:
  - Test module for check functionality.
version_added: "0.0.2"
options:
  name:
    description:
      - Test description.
author: "Sergey Klyuykov"
notes:
   - Test module
"""
'''

test_playbook_content = '''
---
- hosts: all
  gather_facts: False
  vars:
    ansible_connection: local
  tasks:
    - name: Some local task
      ping:
'''

test_yaml_templates = {
    'test module': {
        "notes": "Module test template.",
        "kind": "Module",
        "data": {
            "group": "all",
            "vars": {},
            "args": "",
            "module": "ping",
            "inventory": 'localhost,'
        },
        "options": {
            "uptime": {
                "args": "uptime",
                "module": "shell"
            },
        }
    },
    'test playbook': {
        "notes": "Playbook test template.",
        "kind": "Task",
        "data": {
            "vars": {
                "become": True
            },
            "playbook": "main.yml",
            "inventory": 'localhost,'
        },
        "options": {
            "update": {
                "playbook": "other.yml"
            }
        }
    }
}

test_yaml_view = {
    'fields': {
        'string': {
            'title': 'Field string',
            'default': 0,
            'format': 'string',
            'help': 'Some help text'
        },
        'integer': {
            'title': 'Field integer',
            'default': 0,
            'format': 'integer',
            'help': 'Some help text'
        },
        'float': {
            'title': 'Field float',
            'default': 0,
            'format': 'float',
            'help': 'Some help text'
        },
        'boolean': {
            'title': 'Field boolean',
            'default': 0,
            'format': 'boolean',
            'help': 'Some help text'
        },
        'enum_string': {
            'title': 'Field enum_string',
            'default': 0,
            'format': 'string',
            'help': 'Some help text',
            'enum': list(range(10))
        },
        'unknown': {
            'title': 'Field unknown',
            'default': 0,
            'format': 'invalid_or_unknown',
            'help': 'Some help text'
        },
    },
    'playbooks': {
        'main.yml': {
            'title': 'Execute title',
            'help': 'Some help text'
        }
    }
}


class Object(object):
    pass


class BaseExecutionsTestCase(BaseTestCase):
    def setUp(self):
        super(BaseExecutionsTestCase, self).setUp()
        self.path = self._settings('PROJECTS_DIR', '/tmp/unknown')

    @property
    def template_module(self):
        return dict(
            kind="Module",
            name='Test module template',
            data=dict(
                module="ping",
                group="all",
                inventory='localhost',
                args="",
                vars=dict(
                    forks=8,
                    connection='local'
                ),
            ),
            options=dict(
                one=dict(module='shell', args='uname', verbose=3),
                two=dict(vars=dict(forks=1))
            )
        )

    @property
    def template_playbook(self):
        return dict(
            kind="Task",
            name='Test playbook template',
            data=dict(
                playbook="test-0.yml",
                inventory='localhost',
                vars=dict(
                    forks=8,
                    connection='local',
                    verbose=4,
                ),
            ),
            options=dict(
                tree=dict(vars=dict(limit='localhost')),
                four=dict(vars={'forks': 1, 'private_key': './key.pem'})
            )
        )

    def get_periodic_task_data(self, pk, template_id):
        periodic_template = dict(
            kind='TEMPLATE', template=template_id, schedule="10", type="INTERVAL",
        )
        return [
            self.get_mod_bulk('project', pk, periodic_template, 'periodic_task'),
        ]

    def get_templates_data(self, bulk_data, pk, inventory='localhost,'):
        template_module = self.template_module
        template_module['data']['inventory'] = inventory
        template_playbook = self.template_playbook
        template_playbook['data']['inventory'] = inventory
        count_bulk = len(bulk_data)
        template_module_index = count_bulk
        return (
            bulk_data + [
                self.get_mod_bulk('project', pk, template_module, 'template'),
                self.get_mod_bulk('project', pk, template_playbook, 'template'),
            ] +
            self.get_periodic_task_data(
                pk, '<{}[data][id]>'.format(template_module_index)
            )
        )

    def get_complex_data(self, with_subs=False):
        hostlocl_v = dict(ansible_user='centos', ansible_ssh_private_key_file='PATH')
        groups1_v = dict(ansible_user='ubuntu', ansible_ssh_pass='mypass')
        complex_inventory_v = dict(
            ansible_ssh_private_key_file="PATH", custom_var1='hello_world'
        )
        bulk_data = [
            # Create hosts
            self.get_complex_bulk('host', name='127.0.1.1'),
            self.get_complex_bulk('host', name='127.0.1.[3:4]', type="RANGE"),
            self.get_complex_bulk('host', name='127.0.1.[5:6]', type="RANGE"),
            self.get_complex_bulk('host', name='hostlocl'),
            # Create groups
            self.get_complex_bulk('group', name='hosts1'),
            self.get_complex_bulk('group', name='hosts2'),
            self.get_complex_bulk('group', name='groups1', children=True),
            self.get_complex_bulk('group', name='groups2', children=True),
            self.get_complex_bulk('group', name='groups3', children=True),
            # Create inventory
            self.get_complex_bulk('inventory', name='complex_inventory'),
        ]
        # Create manual project
        bulk_data += [
            self.get_complex_bulk('project', name="complex", repository='MANUAL')
        ]
        # Set vars
        bulk_data += [
            self.get_mod_bulk('host', "<3[data][id]>", dict(key=k, value=v))
            for k, v in hostlocl_v.items()
        ]
        bulk_data += [
            self.get_mod_bulk('group', "<6[data][id]>", dict(key=k, value=v))
            for k, v in groups1_v.items()
        ]
        bulk_data += [
            self.get_mod_bulk('inventory', "<9[data][id]>", dict(key=k, value=v))
            for k, v in complex_inventory_v.items()
        ]
        # Add children
        bulk_data += [
            # to hosts1
            self.get_mod_bulk(
                'group', "<4[data][id]>", dict(id="<0[data][id]>"), 'host',
            ),
            self.get_mod_bulk(
                'group', "<4[data][id]>", dict(id="<3[data][id]>"), 'host',
            ),
            # to hosts2
            self.get_mod_bulk(
                'group', "<5[data][id]>", dict(id="<1[data][id]>"), 'host',
            ),
            self.get_mod_bulk(
                'group', "<5[data][id]>", dict(id="<2[data][id]>"), 'host',
            ),
            # to groups1
            self.get_mod_bulk(
                'group', "<6[data][id]>", dict(id="<7[data][id]>"), 'group',
            ),
            self.get_mod_bulk(
                'group', "<6[data][id]>", dict(id="<8[data][id]>"), 'group',
            ),
            # to groups2
            self.get_mod_bulk(
                'group', "<7[data][id]>", dict(id="<8[data][id]>"), 'group',
            ),
            # to groups3
            self.get_mod_bulk(
                'group', "<8[data][id]>", dict(id="<4[data][id]>"), 'group',
            ),
            self.get_mod_bulk(
                'group', "<8[data][id]>", dict(id="<5[data][id]>"), 'group',
            ),
            # to inventory
            self.get_mod_bulk(
                'inventory', "<9[data][id]>", dict(id="<6[data][id]>"), 'group',
            ),
            self.get_mod_bulk(
                'inventory', "<9[data][id]>", dict(id="<0[data][id]>"), 'host',
            ),
            self.get_mod_bulk(
                'inventory', "<9[data][id]>", dict(id="<1[data][id]>"), 'host',
            ),
            self.get_mod_bulk(
                'inventory', "<9[data][id]>", dict(id="<3[data][id]>"), 'host',
            ),
            # to project
            self.get_mod_bulk(
                'project', "<10[data][id]>", dict(id="<9[data][id]>"), 'inventory'
            ),
        ]
        bulk_data = (
            self.get_templates_data(bulk_data, "<10[data][id]>", "<9[data][id]>")
            if with_subs else bulk_data
        )
        # Execute actions
        _exec = dict(
            connection="local", inventory="<9[data][id]>",
            module="ping", group="127.0.1.1", args="", forks=1, verbose=4
        )
        bulk_data += [
            self.get_mod_bulk(
                'project', "<10[data][id]>", {}, 'sync',
            ),
            self.get_mod_bulk(
                'project', "<10[data][id]>", _exec, 'execute_module',
            ),
            self.get_bulk(
                'history', {}, 'get',
                pk="<{}[data][history_id]>".format(len(bulk_data) + 1)
            ),
            self.get_mod_bulk(
                'history', "<{}[data][history_id]>".format(len(bulk_data) + 1), {},
                'raw', 'get', filters='color=yes'
            ),
        ]
        return bulk_data

    def get_access_deps(self):
        return self.get_complex_data(with_subs=True)

    def generate_subs(self, bulk_results=None):
        # Create project and dependences
        if bulk_results is None:
            bulk_results = self.make_bulk(self.get_access_deps())
        objects = OrderedDict(
            host=[], group=[], inventory=[],
            project=[], template=[], periodic_task=[], history=[],
            team=[]
        )
        hard_subitems = ['template', 'periodic_task']
        for result in bulk_results:
            if result['type'] == 'add':
                self.assertEqual(result['status'], 201)
                objects[result['item']].append(result['data'])
            if result['type'] == 'mod' and result['subitem'] in hard_subitems:
                self.assertEqual(result['status'], 201)
                objects[result['subitem']].append(result['data'])
            if result['type'] == 'get' and result['item'] == 'history':
                self.assertEqual(result['status'], 200)
                history = result['data']
                self.assertEqual(history['revision'], "NO VCS")
                self.assertEqual(history['mode'], 'ping')
                self.assertEqual(history['kind'], 'MODULE')
                self.assertEqual(history['inventory'], objects['inventory'][0]['id'])
                self.assertEqual(
                    history['status'], "OK",
                    self.get_result('get', self.get_url('history', history['id'], 'raw'))
                )
                etalon = self._get_string_from_file('exemplary_complex_inventory')
                etalon = etalon.replace('PATH', '[~~ENCRYPTED~~]')
                etalon = etalon.replace('mypass', '[~~ENCRYPTED~~]')
                self.assertEqual(
                    list(map(str.strip, str(history['raw_inventory']).split("\n"))),
                    list(map(str.strip, etalon.split("\n")))
                )
                objects[result['item']].append(history)
        return objects

    def get_project_dir(self, id, **kwargs):
        return '{}/{}'.format(self.path, id)

    def sync_project(self, id, **kwargs):
        return self.make_bulk([
            self.get_mod_bulk('project', id, {}, 'sync'),
            self.get_bulk('project', {}, 'get', pk=id)
        ])[1]['data']

    def create_project_test(self, name=None, repo_type="MANUAL", **kwargs):
        name = name or str(uuid.uuid1())
        status_after_create = kwargs.pop('status_new', 'NEW')
        status_after_sync = kwargs.pop('status', 'OK')
        status_after_rm_sync = kwargs.pop('status_rm', status_after_sync)
        result = self.mass_create_bulk('project', [
            dict(
                name=name, repository=kwargs.pop('repository', repo_type),
                variables=dict(repo_type=repo_type, **kwargs)
            )
        ])
        project_data = result[0]['data']
        self.assertEqual(project_data['status'], status_after_create)
        self.assertEqual(project_data['name'], name)
        project_data = self.sync_project(project_data['id'])
        self.assertEqual(project_data['status'], status_after_sync)
        self.assertEqual(project_data['name'], name)
        self.remove_project_dir(project_data['id'])
        project_data = self.sync_project(project_data['id'])
        self.assertEqual(project_data['status'], status_after_rm_sync)
        self.assertEqual(project_data['name'], name)
        return project_data

    def remove_project_dir(self, id, **kwargs):
        shutil.rmtree(self.get_project_dir(id, **kwargs))

    def remove_project(self, id, **kwargs):
        self.get_result('delete', self.get_url('project', id))
        self.assertFalse(os.path.exists(self.get_project_dir(id, **kwargs)))

    def _check_copy_project(self, id, **kwargs):
        obj = self.get_model_filter('Project', pk=id).get()
        bulk_data = [
            self.get_mod_bulk('project', obj.id, {'name': 'copied'}, 'copy'),
            self.get_mod_bulk('project', '<0[data][id]>', {}, 'variables', method='GET'),
            self.get_bulk('project', {}, 'del', pk='<0[data][id]>'),
        ]
        results = self.make_bulk(bulk_data)
        self.assertEqual(results[0]['data']['status'], 'NEW')
        self.assertEqual(results[1]['data']['count'], len(obj.vars))
        for value in results[1]['data']['results']:
            self.assertIn(
                value['value'], [obj.vars[value['key']], '[~~ENCRYPTED~~]'], value
            )

    def project_workflow(self, repo_type, **kwargs):
        execute = kwargs.pop('execute', False)
        project_data = self.create_project_test(str(uuid.uuid1()), repo_type, **kwargs)
        self.remove_project_dir(**project_data)
        self.remove_project(**project_data)
        project_data = self.create_project_test(str(uuid.uuid1()), repo_type, **kwargs)
        try:
            self._check_copy_project(**project_data)
            if not execute:
                return
            kwargs = getattr(self, 'wip_{}'.format(repo_type.lower()), str)(project_data)
            kwargs = kwargs if not isinstance(kwargs, six.string_types) else dict()
            self.change_owner(project_data)
            self.playbook_tests(project_data, **kwargs)
            self.module_tests(project_data)
        finally:
            self.remove_project(**project_data)

    def get_file_path(self, name, path):
        return "{}/{}".format(path, name)

    def generate_playbook(self, path, name='test', count=1, data=test_playbook_content):
        '''
        Generate playbooks in project path

        :param path: path, where playbook will appear
        :type path: str,unicode
        :param name: filename pattern or list of names for playbooks
        :type name: str,unicode,list,tuple
        :param count: count files for pattern
        :type count: int
        :param data: playbook data
        :type data: str,bytes,unicode
        :return:
        '''
        files = []
        if isinstance(name, (list, tuple)):
            _files = name[:count or len(name)]
        else:
            _files = ['{}-{}.yml'.format(name, i) for i in range(count or 1)]
        for filename in _files:
            file_path = self.get_file_path(filename, path)
            with open(file_path, 'w') as playbook:
                playbook.write(data)
            files.append(filename)
        return files

    def project_bulk_sync_and_playbooks(self, id, **kwargs):
        return [
            self.get_mod_bulk('project', id, {}, 'sync', 'post'),
            self.get_mod_bulk('project', id, {}, 'playbook', 'get'),
        ]

    def change_owner(self, project_data):
        new_owner = self._create_user(False)
        pk = project_data['id']
        bulk_data = [
            self.get_mod_bulk('project', pk, dict(user_id=new_owner.id), 'set_owner'),
            self.get_bulk('project', {}, 'get', pk=pk),
            self.get_mod_bulk('project', pk, dict(user_id=self.user.id), 'set_owner'),
            self.get_bulk('project', {}, 'get', pk=pk),
        ]
        results = self.make_bulk(bulk_data)
        self.assertEqual(results[0]['status'], 201)
        self.assertEqual(results[2]['status'], 201)
        self.assertEqual(results[1]['data']['owner']['id'], new_owner.id)
        self.assertEqual(results[3]['data']['owner']['id'], self.user.id)

    def project_execute(self, project_data, exec_data=None, type='playbook'):
        exec_data = exec_data or dict(
            playbook='main.yml', inventory='inventory.ini',
            save_result=False, verbose=4
        )
        exec_data['check'] = True
        exec_data['syntax-check'] = True
        return self.make_bulk([
            self.get_mod_bulk(
                'project', project_data['id'], exec_data, 'execute_{}'.format(type)
            ),
            self.get_mod_bulk(
                'project', project_data['id'], {}, 'history/<0[data][history_id]>', 'get'
            ),
        ], 'put')

    def playbook_tests(self, prj, playbook_count=1, execute=None, inventory="localhost"):
        _exec = dict(
            connection="local", limit="docker",
            playbook="<1[data][results][0][playbook]>", inventory=inventory
        )
        bulk_data = self.project_bulk_sync_and_playbooks(prj['id'])
        bulk_data += [
            self.get_mod_bulk('project', prj['id'], _exec, 'execute_playbook'),
        ] if execute else []
        results = self.make_bulk(bulk_data, 'put')
        self.assertEqual(results[0]['status'], 200)
        self.assertEqual(results[1]['status'], 200)
        self.assertEqual(results[1]['data']['count'], playbook_count)
        if not execute:
            return
        self.assertEqual(results[2]['status'], 201)

    def module_tests(self, prj):
        bulk_data = [
            self.get_mod_bulk(
                'project', prj['id'], {}, 'module', 'get', filters='limit=20'
            ),
            self.get_mod_bulk(
                'project', prj['id'], {}, 'module', 'get', filters='path=redis'
            ),
            self.get_mod_bulk(
                'project', prj['id'], {}, 'module/<1[data][results][0][id]>', 'get'
            ),
        ]
        results = self.make_bulk(bulk_data, 'put')
        for result in results:
            self.assertEqual(result['status'], 200)
        self.assertTrue(results[0]['data']['count'] > 1000)
        self.assertEqual(results[1]['data']['count'], 1)
        self.assertEqual(results[1]['data']['results'][0]['name'], 'redis')
        self.assertEqual(results[2]['data']['data']['module'], 'redis')

    def get_complex_bulk(self, item, op='add', **kwargs):
        return self.get_bulk(item, kwargs, op)


class ProjectTestCase(BaseExecutionsTestCase):

    def tearDown(self):
        super(ProjectTestCase, self).tearDown()
        repo_dir = getattr(self, 'repo_dir', None)
        if repo_dir:
            shutil.rmtree(repo_dir)

    def wip_manual(self, project_data):
        files = self.generate_playbook(self.get_project_dir(**project_data))
        path = self.get_project_dir(**project_data)
        self.get_model_filter('Project', pk=project_data['id']).get().set_status('NEW')
        self.sync_project(**project_data)
        # Create test ssh-key
        with open(self.get_file_path('key.pem', path), 'w') as key:
            key.write('BEGIN RSA PRIVATE KEY')
        self.make_test_templates(project_data)
        self.make_test_periodic_task(project_data)
        self.make_test_readme(project_data)
        return dict(playbook_count=len(files), execute=True)

    def wip_git(self, project_data):
        # Check brunch and revision
        self.assertEqual(project_data['revision'], self.revisions[-1])
        self.assertEqual(project_data['branch'], 'master')
        # Update branch
        new_branch_var = dict(key='repo_branch', value='invalid_branch')
        self.make_bulk([
            self.get_mod_bulk('project', project_data['id'], new_branch_var)
        ])
        project_data = self.sync_project(project_data['id'])
        self.assertEqual(project_data['status'], 'ERROR')
        self.assertEqual(project_data['branch'], 'waiting...')

        new_branch_var = dict(key='repo_branch', value='master')
        self.make_bulk([
            self.get_mod_bulk('project', project_data['id'], new_branch_var)
        ])
        project_data = self.sync_project(project_data['id'])
        self.assertEqual(project_data['status'], 'OK')
        self.assertEqual(project_data['branch'], 'master')

        new_branch_var = dict(key='repo_branch', value='tags/new_tag')
        self.make_bulk([
            self.get_mod_bulk('project', project_data['id'], new_branch_var)
        ])
        project_data = self.sync_project(project_data['id'])
        # Check updated brunch and revision
        self.assertEqual(project_data['branch'], 'tags/new_tag')
        self.assertEqual(project_data['revision'], self.revisions[1])
        # Return old branch
        new_branch_var['value'] = 'new_branch'
        self.make_bulk([
            self.get_mod_bulk('project', project_data['id'], new_branch_var)
        ])
        project_data = self.sync_project(project_data['id'])
        new_branch_var['value'] = 'master'
        results = self.make_bulk([
            self.get_mod_bulk('project', project_data['id'], new_branch_var),
            self.get_mod_bulk(
                'project', project_data['id'], {},
                method='get', filters='key=repo_sync_on_run'
            ),
            self.get_mod_bulk('project', project_data['id'], {}, 'template', 'get'),
        ])
        # Check synced templates
        self.assertTrue(results[1]['data']['results'][0]['value'])
        self.assertEqual(results[2]['data']['count'], 2)
        for template in results[2]['data']['results']:
            origin_template_data = test_yaml_templates[template['name']]
            for option in origin_template_data['options'].keys():
                self.assertIn(option, template['options_list'])
        # Check extra execute-view data in project
        extra_view_data = project_data['execute_view_data']
        for field_name in test_yaml_view['fields']:
            self.assertIn(field_name, extra_view_data['fields'].keys())
            field = extra_view_data['fields'][field_name]
            for required_field in ['title', 'default', 'format', 'help']:
                self.assertIn(required_field, field.keys())
            self.assertEqual(field_name.split('_')[-1], field['format'], field)
            default_type = (six.string_types, six.text_type)
            if field['format'] == 'boolean':
                default_type = bool
            elif field['format'] == 'integer':
                default_type = int
            elif field['format'] == 'float':
                default_type = float
            self.assertTrue(isinstance(field['default'], default_type), field)
            if field_name == 'enum_string':
                self.assertIn('enum', field.keys())
                self.assertTrue(isinstance(field['enum'], (list, tuple)))
                for value in field['enum']:
                    self.assertTrue(isinstance(value, default_type))
        for playbook_name in test_yaml_view['playbooks']:
            self.assertIn(playbook_name, extra_view_data['playbooks'].keys())
            playbook = extra_view_data['playbooks'][playbook_name]
            for required_field in ['title', 'help']:
                self.assertIn(required_field, playbook.keys())
        # Check ansible.cfg
        proj = self.get_model_filter('Project', pk=project_data['id']).get()
        proj_config = getattr(proj, 'config', None)
        self.assertTrue(proj_config is not None)
        # Check modules
        proj_modules = proj.module.filter(path__startswith='polemarch.project.')
        self.assertEqual(proj_modules.count(), 1)
        proj_module = proj_modules.first()
        self.assertEqual(proj_module.name, 'test_module')
        self.assertEqual(proj_module.data['short_description'], 'Test module')
        return dict(playbook_count=len(self.revisions), execute=True)

    def make_test_templates(self, project_data):
        pk = project_data['id']
        template_module = self.template_module
        template_playbook = self.template_playbook
        template_playbook['options']['tree']['vars']['private-key'] = 'PATH'
        template_playbook['data']['vars']['private-key'] = './key.pem'
        m_opts = dict(option='one')
        p_opts = dict(option='four')
        bulk_data = [
            self.get_mod_bulk('project', pk, template_module, 'template'),
            self.get_mod_bulk('project', pk, template_playbook, 'template'),
            self.get_mod_bulk('project', pk, {}, 'template/<0[data][id]>/execute'),
            self.get_mod_bulk('project', pk, {}, 'template/<1[data][id]>/execute'),
            self.get_mod_bulk('project', pk, m_opts, 'template/<0[data][id]>/execute'),
            self.get_mod_bulk('project', pk, p_opts, 'template/<1[data][id]>/execute'),
            self.get_mod_bulk('project', pk, {}, 'template/<1[data][id]>', 'get'),
        ]
        results = self.make_bulk(bulk_data)
        for result in results:
            if result['status'] == 200:
                self.assertEqual(
                    result['data']['options']['tree']['vars']['private-key'],
                    '[~~ENCRYPTED~~]'
                )
                continue
            self.assertEqual(result['status'], 201)

        tmplt_mod = results[0]['data']
        tmplt_play = results[1]['data']
        # Check update keys
        check_update = self.make_bulk([
            self.get_mod_bulk(
                'project', pk, dict(data=tmplt_play['data']),
                'template/{}'.format(tmplt_play['id']), 'patch'
            ),
        ])[0]
        self.assertEqual(check_update['status'], 200)
        self.assertEqual(
            check_update['data']['data']['vars']['private-key'], '[~~ENCRYPTED~~]'
        )
        results = self.get_result(
            'get', self.get_url('project', project_data['id'], 'history') + '?limit=4'
        )
        self.assertEqual(results['results'][-1]['status'], 'OK')
        self.assertEqual(results['results'][-1]['kind'], 'MODULE')
        self.assertEqual(results['results'][-1]['initiator_type'], 'template')
        self.assertEqual(results['results'][-1]['mode'], 'ping')
        self.assertEqual(results['results'][-2]['status'], 'OK')
        self.assertEqual(results['results'][-2]['kind'], 'PLAYBOOK')
        self.assertEqual(results['results'][-2]['initiator_type'], 'template')
        self.assertEqual(results['results'][-2]['mode'], 'test-0.yml')
        self.assertEqual(results['results'][-3]['status'], 'OK')
        self.assertEqual(results['results'][-3]['kind'], 'MODULE')
        self.assertEqual(results['results'][-3]['initiator_type'], 'template')
        self.assertEqual(results['results'][-3]['options']['template_option'], 'one')
        self.assertEqual(results['results'][-4]['status'], 'OK')
        self.assertEqual(results['results'][-4]['kind'], 'PLAYBOOK')
        self.assertEqual(results['results'][-4]['initiator_type'], 'template')
        self.assertEqual(results['results'][-4]['options']['template_option'], 'four')

        # Templates in periodic tasks
        data = dict(
            mode="test-1.yml", schedule="10", type="INTERVAL",
            project=project_data['id'],
            inventory='localhost,', name="one"
        )
        new_data = dict(kind='TEMPLATE', template=tmplt_mod['id'], template_opt='one')
        bulk_data = [
            self.get_mod_bulk('project', pk, data, 'periodic_task'),
            self.get_mod_bulk(
                'project', pk, new_data, 'periodic_task/<0[data][id]>', 'patch'
            ),
        ]
        results = self.make_bulk(bulk_data)
        self.assertEqual(results[0]['status'], 201)
        self.assertEqual(results[1]['status'], 200)
        self.assertEqual(results[1]['data']['kind'], 'TEMPLATE')
        self.assertEqual(results[1]['data']['inventory'], '')
        self.assertEqual(results[1]['data']['mode'], '')
        # Create new periodic_tasks
        ptask_data = [
            dict(
                kind='TEMPLATE', template=tmplt_mod['id'], template_opt='one',
                schedule="10", type="INTERVAL"
            ),
            dict(
                kind='TEMPLATE', template=tmplt_play['id'], template_opt='four',
                schedule="10", type="INTERVAL"
            ),
        ]
        bulk_data = [
            self.get_mod_bulk('project', pk, dt, 'periodic_task')
            for dt in ptask_data
        ]
        results = self.make_bulk(bulk_data)
        for result in results:
            self.assertEqual(result['status'], 201)
            ScheduledTask.delay(result['data']['id'])
        results = self.get_result(
            'get', self.get_url('project', project_data['id'], 'history') + '?limit=2'
        )
        self.assertEqual(results['results'][-1]['status'], 'OK')
        self.assertEqual(results['results'][-1]['kind'], 'MODULE')
        self.assertEqual(results['results'][-1]['initiator_type'], 'scheduler')
        self.assertEqual(results['results'][-1]['mode'], 'shell')
        self.assertEqual(results['results'][-2]['status'], 'OK')
        self.assertEqual(results['results'][-2]['kind'], 'PLAYBOOK')
        self.assertEqual(results['results'][-2]['initiator_type'], 'scheduler')
        self.assertEqual(results['results'][-2]['mode'], 'test-0.yml')

        # Try to send cencel message
        result = self.get_result(
            'post', self.get_url(
                'project', project_data['id'],
                'history/{}/cancel'.format(results['results'][-2]['id'])
            ),
            code=200
        )
        self.assertEqual(
            result['detail'], "Task canceled: {}".format(results['results'][-2]['id'])
        )
        # Check Templates without inventory
        invalid_type_template = dict(**template_playbook)
        invalid_type_template['kind'] = 'UnknownKind'
        invalid_options_template = dict(**template_playbook)
        invalid_options_template['options'] = 'options'
        invalid_override_template = dict(**template_playbook)
        invalid_override_template['options'] = dict(test=dict(inventory='some_ovveride'))
        invalid_template = dict(**template_playbook)
        invalid_template['data'] = dict(**template_playbook['data'])
        del invalid_template['data']['inventory']
        bulk_data = [
            self.get_mod_bulk('project', pk, invalid_template, 'template'),
            self.get_mod_bulk('project', pk, invalid_type_template, 'template'),
            self.get_mod_bulk('project', pk, invalid_options_template, 'template'),
            self.get_mod_bulk('project', pk, invalid_override_template, 'template'),
        ]
        results = self.make_bulk(bulk_data, 'put')
        self.assertEqual(results[0]['status'], 400)
        self.assertEqual(
            results[0]['data']['detail']['inventory'], ["Inventory have to set."]
        )
        self.assertEqual(results[1]['status'], 400)
        self.assertEqual(results[2]['status'], 400)
        self.assertEqual(results[3]['status'], 400)
        self.assertEqual(
            results[3]['data']['detail']['inventory'],
            ["Disallowed to override inventory."]
        )

    def make_test_periodic_task(self, project_data):
        # Check periodic tasks
        # Check correct values
        ptasks_data = [
            dict(
                mode="test-1.yml", schedule="10", type="INTERVAL",
                project=project_data['id'],
                inventory='localhost', name="one"
            ),
            dict(
                mode="test-1.yml",
                schedule="* */2 1-15 * sun,fri",
                type="CRONTAB", project=project_data['id'],
                inventory='localhost', name="two"
            ),
            dict(
                mode="test-1.yml", schedule="", type="CRONTAB",
                project=project_data['id'],
                inventory='localhost', name="thre"
            ),
            dict(
                mode="test-1.yml", schedule="30 */4", type="CRONTAB",
                project=project_data['id'],
                inventory='localhost', name="four"
            ),
            dict(
                mode="ping", schedule="10", type="INTERVAL",
                project=project_data['id'],
                kind="MODULE", name="one", inventory='localhost'
            )
        ]
        bulk_data = [
            self.get_mod_bulk('project', project_data['id'], data, 'periodic_task')
            for data in ptasks_data
        ]
        bulk_data += [
            self.get_mod_bulk(
                'project', project_data['id'], dict(key='connection', value='local'),
                'periodic_task/<0[data][id]>/variables'
            ),
            self.get_mod_bulk(
                'project', project_data['id'], dict(key='forks', value='5'),
                'periodic_task/<0[data][id]>/variables'
            ),
        ]
        results = self.make_bulk(bulk_data)
        for result in results:
            self.assertEqual(result['status'], 201)
        # Check incorrect values
        incorrect_ptasks_data = [
            dict(
                mode="test-1.yml", schedule="30 */4 foo", type="CRONTAB",
                project=project_data['id'], inventory='localhost'
            ),
            dict(
                mode="test-1.yml", schedule="30 */4", type="crontab",
                project=project_data['id'], inventory='localhost',
                name="four"
            ),
        ]
        bulk_data = [
            self.get_mod_bulk('project', project_data['id'], data, 'periodic_task')
            for data in incorrect_ptasks_data
        ]
        bulk_data += [
            self.get_mod_bulk(
                'project', project_data['id'], dict(key='incorrect_var', value='blabla'),
                'periodic_task/{}/variables'.format(results[0]['data']['id'])
            ),
            self.get_mod_bulk(
                'project', project_data['id'], dict(key='forks', value='3423kldf'),
                'periodic_task/{}/variables'.format(results[4]['data']['id'])
            ),
        ]
        results = self.make_bulk(bulk_data, 'put')
        self.assertEqual(results[0]['status'], 400)
        self.assertIn(
            "Invalid weekday literal", results[0]['data']['detail']['schedule'][0]
        )
        self.assertEqual(results[1]['status'], 400)
        self.assertEqual(results[2]['status'], 400)
        self.assertIn("Incorrect argument", results[2]['data']["detail"]['playbook'][0])
        self.assertIn('incorrect_var', results[2]['data']["detail"]['argument'][0])
        self.assertEqual(results[3]['status'], 400)
        self.assertIn("Incorrect argument", results[3]['data']["detail"]['module'][0])
        self.assertIn('forks', results[3]['data']["detail"]['argument'][0])

        # Try to execute now
        data = dict(
            mode="test-0.yml", schedule="10", type="INTERVAL", name="one",
            project=project_data['id'], inventory='localhost,'
        )
        results = self.make_bulk([
            self.get_mod_bulk('project', project_data['id'], data, 'periodic_task'),
            self.get_mod_bulk(
                'project', project_data['id'], data,
                'periodic_task/<0[data][id]>/execute',
                'post'
            ),
            self.get_mod_bulk(
                'project', project_data['id'], {}, 'history/<1[data][history_id]>', 'get'
            ),
            self.get_mod_bulk(
                'project', project_data['id'], dict(save_result=False),
                'periodic_task/<0[data][id]>', 'patch'
            ),
            self.get_mod_bulk(
                'project', project_data['id'], data,
                'periodic_task/<0[data][id]>/execute',
                'post'
            ),
            self.get_mod_bulk(
                'project', project_data['id'], dict(save_result=True),
                'periodic_task/<0[data][id]>', 'patch'
            ),
        ], 'put')
        self.assertEqual(results[0]['status'], 201)
        self.assertEqual(results[1]['status'], 201)
        self.assertEqual(
            results[1]['data']['detail'], "Started at inventory localhost,."
        )
        self.assertEqual(results[2]['status'], 200)
        self.assertEqual(results[2]['data']['status'], "OK")
        self.assertEqual(results[3]['status'], 200)
        self.assertEqual(results[4]['status'], 201)
        self.assertEqual(results[4]['data']['history_id'], None)
        self.assertEqual(results[5]['status'], 200)
        # Just exec
        ScheduledTask.delay(results[0]['data']['id'])
        # Except on execution
        with self.patch('polemarch.main.utils.CmdExecutor.execute') as _exec:
            def _exec_error(*args, **kwargs):
                raise Exception("It is not error. Just test execution.")

            _exec.side_effect = _exec_error
            ScheduledTask.delay(results[0]['data']['id'])
            self.assertEquals(_exec.call_count, 1)
            _exec.reset_mock()

        # No task
        with self.patch('polemarch.main.utils.CmdExecutor.execute') as _exec:
            ScheduledTask.delay(999)
            self.assertEquals(_exec.call_count, 0)

        results = self.get_result(
            'get', self.get_url('project', project_data['id'], 'history') + '?limit=2'
        )
        self.assertEqual(results['results'][-1]['status'], 'OK')
        self.assertEqual(results['results'][-2]['status'], 'ERROR')

    def make_test_readme(self, project_data):
        project = self.get_model_filter("Project", pk=project_data['id']).get()

        def get_bulk_readme():
            bulk_data = [
                self.get_mod_bulk('project', project_data['id'], {}, 'sync', 'post'),
                self.get_bulk('project', {}, 'get', pk=project_data['id']),
            ]
            results = self.make_bulk(bulk_data)
            self.assertEqual(results[0]['status'], 200)
            self.assertEqual(results[1]['status'], 200)
            return results[1]['data']

        with open(project.path+"/readme.md", "w") as f:
            f.write("# test README.md \n **bold** \n *italic* \n")

        self.assertEqual(
            get_bulk_readme()['readme_content'],
            '<h1 id="test-readmemd">test README.md</h1>\n\n<p><strong>bold</strong>' +
            ' \n <em>italic</em> </p>\n'
        )
        with open(project.path+"/readme.rst", "w") as f:
            f.write("test README.rst \n **bold** \n *italic* \n")

        self.assertEqual(
            get_bulk_readme()['readme_content'],
            '<div class="document">\n<dl class="docutils">\n<dt>' +
            'test README.rst</dt>\n<dd><strong>bold</strong>\n' +
            '<em>italic</em></dd>\n</dl>\n</div>\n'
        )

    def test_project_manual(self):
        self.project_workflow('MANUAL', execute=True)

    def test_project_tar(self):
        with self.patch('polemarch.main.repo._base._ArchiveRepo._download') as download:
            download.side_effect = [self.tests_path + '/test_repo.tar.gz'] * 10
            self.project_workflow(
                'TAR', repository='http://localhost:8000/test_repo.tar.gz', execute=True
            )
            download.reset_mock()

            # try error
            def over_download(*args, **kwargs):
                raise Exception("It is not error. Just test")

            result = self.mass_create_bulk('project', [
                dict(
                    name=str(uuid.uuid1()),
                    repository='http://localhost:8000/test_repo.tar.gz',
                    variables=dict(repo_type="TAR", repo_sync_on_run=True)
                )
            ])
            project_data = result[0]['data']
            self.project_execute(project_data)

            with self.patch('tarfile.open') as extract:
                extract.side_effect = over_download
                _ex_module = dict(
                    module='ping', group='all',
                    inventory='192.168.254.255', become_method='sudo'
                )
                _ex_playbook = dict(
                    playbook='unknown.yml', inventory='192.168.254.255',
                    private_key='BEGIN RSA PRIVATE KEY'
                )
                unsync = dict(key='repo_sync_on_run', value=False)
                pk = project_data['id']
                results = self.make_bulk([
                    self.get_bulk('project', {}, 'get', pk=pk),
                    self.get_mod_bulk('project', pk, {}, 'sync'),
                    self.get_bulk('project', {}, 'get', pk=project_data['id']),
                    self.get_mod_bulk('project', pk, _ex_module, 'execute_module'),
                    self.get_mod_bulk('project', pk, unsync, 'variables'),
                    self.get_mod_bulk('project', pk, _ex_playbook, 'execute_playbook'),
                    self.get_mod_bulk(
                        'project', pk, {}, 'history', method='get', filters='limit=2'
                    ),
                ], 'put')
                project_data = results[2]['data']
                self.assertEqual(project_data['status'], 'ERROR')
                self.assertEqual(results[-1]['data']['results'][-1]['status'], 'ERROR')
                self.assertEqual(results[-1]['data']['results'][-2]['status'], 'ERROR')

    def test_project_git(self):
        # Prepare .polemarch.yaml
        pm_yaml = dict()
        # sync_on_run
        pm_yaml['sync_on_run'] = True
        # templates
        pm_yaml['templates'] = test_yaml_templates
        pm_yaml['templates_rewrite'] = False
        # fast task widget
        pm_yaml['view'] = test_yaml_view
        # Prepare repo
        self.repo_dir = tempfile.mkdtemp()
        self.generate_playbook(self.repo_dir, ['main.yml'])
        self.generate_playbook(self.repo_dir, ['.polemarch.yaml'], data=dump(pm_yaml))
        self.generate_playbook(self.repo_dir, ['ansible.cfg'], data=test_ansible_cfg)
        lib_dir = self.repo_dir + '/lib'
        if not os.path.exists(lib_dir):
            os.makedirs(lib_dir)
            self.generate_playbook(lib_dir, ['test_module.py'], data=test_module_content)
        repo = git.Repo.init(self.repo_dir)
        repo.index.add([
            "main.yml", ".polemarch.yaml", "ansible.cfg", lib_dir + '/test_module.py'
        ])
        repo.index.commit("no message")
        first_revision = repo.head.object.hexsha
        repo.create_head('new_branch')
        pm_yaml['sync_on_run'] = False
        self.generate_playbook(self.repo_dir, ['.polemarch.yaml'], data=dump(pm_yaml))
        self.generate_playbook(self.repo_dir, ['other.yml'])
        repo.index.add(["other.yml", ".polemarch.yaml"])
        repo.index.commit("no message 2")
        second_revision = repo.head.object.hexsha
        repo.create_tag('new_tag')

        # Test project
        self.revisions = [first_revision, second_revision]
        self.project_workflow(
            'GIT', repository=self.repo_dir, repo_password='', execute=True
        )
        self.project_workflow(
            'GIT', repository=self.repo_dir, repo_branch='new_branch', repo_key='key'
        )

        # Test invalid repo
        result = self.mass_create_bulk('project', [
            dict(
                name=str(uuid.uuid1()), repository='/tmp/not_git_path',
                variables=dict(repo_type="GIT")
            )
        ])
        project_data = result[0]['data']
        results = self.make_bulk([
            self.get_bulk('project', {}, 'get', pk=project_data['id']),
            self.get_mod_bulk('project', '<0[data][id]>', {}, 'sync'),
            self.get_bulk('project', {}, 'get', pk=project_data['id']),
        ], 'put')
        project_data = results[2]['data']
        self.assertEqual(project_data['status'], 'ERROR')
        self.assertEqual(project_data['revision'], 'ERROR')
        self.assertEqual(project_data['branch'], 'waiting...')

    def test_complex(self):
        bulk_data = self.get_complex_data()
        # additionaly test hooks
        self.hook_model.objects.all().delete()
        hook_urls = ['localhost:64000', 'localhost:64001']
        recipients = ' | '.join(hook_urls)
        data = [
            dict(type='HTTP', recipients=recipients, when='on_execution'),
            dict(type='HTTP', recipients=recipients, when='after_execution'),
        ]
        self.generate_hooks(hook_urls)
        self.mass_create_bulk('hook', data)
        response = Object()
        response.status_code = 200
        response.reason = None
        response.text = "OK"
        ##
        with self.patch('requests.post') as mock:
            iterations = 2 * len(hook_urls)
            mock.side_effect = [response] * iterations
            # results = self.make_bulk(bulk_data, 'put')
            subs = self.generate_subs()
            self.assertEqual(mock.call_count, iterations)
            self.hook_model.objects.all().delete()
        # Check clear output
        bulk_data = [
            self.get_mod_bulk(
                'history', subs['history'][0]['id'], {}, 'raw', 'get',
            ),
            self.get_mod_bulk(
                'history', subs['history'][0]['id'], {}, 'clear', 'delete',
            ),
            self.get_mod_bulk(
                'project', subs['history'][0]['project'], {},
                'history/{}/raw'.format(subs['history'][0]['id']), 'get',
            ),
        ]
        new_results = self.make_bulk(bulk_data)
        self.assertEqual(new_results[0]['status'], 200)
        self.assertEqual(new_results[1]['status'], 204)
        self.assertEqual(new_results[2]['status'], 200)
        self.assertEqual(new_results[2]['data']['detail'], "Output trancated.\n")
        # Check all_hosts
        self.mass_create_bulk('host', [
            dict(name='complex{}'.format(i)) for i in range(3)
        ])
        bulk_data = [
            self.get_mod_bulk(
                'inventory', subs['inventory'][0]['id'], {}, 'all_hosts', 'get',
            ),
            self.get_mod_bulk(
                'inventory', subs['inventory'][0]['id'], {}, 'all_hosts', 'post',
            ),
            self.get_mod_bulk(
                'inventory', subs['inventory'][0]['id'], {}, 'all_groups', 'get',
            ),
            self.get_mod_bulk(
                'inventory', subs['inventory'][0]['id'], {}, 'all_groups', 'post',
            ),
        ]
        new_results = self.make_bulk(bulk_data, 'put')
        self.assertEqual(new_results[0]['status'], 200)
        self.assertEqual(new_results[0]['data']['count'], 4)
        self.assertEqual(new_results[1]['status'], 405)
        self.assertEqual(new_results[2]['status'], 200)
        self.assertEqual(new_results[2]['data']['count'], 5)
        self.assertEqual(new_results[3]['status'], 405)

        # Check history filters
        df = "%Y-%m-%dT%H:%M:%S.%fZ"
        now_time = (now() + timedelta(hours=1)).strftime(df)
        bulk_data = [
            self.get_bulk('history', {}, 'get', filters='newer={}'.format(now_time)),
            self.get_bulk('history', {}, 'get', filters='older={}'.format(now_time)),
        ]
        results = self.make_bulk(bulk_data)
        self.assertEqual(results[0]['status'], 200)
        self.assertEqual(results[0]['data']['count'], 0)
        self.assertEqual(results[1]['status'], 200)
        self.assertEqual(results[1]['data']['count'], len(subs['history']))

    def test_project_required_inventory(self):
        # Test removing inventory linked to periodic task or template
        bulk_data = [
            self.get_complex_bulk('inventory', name='project_required_inventory'),
            self.get_complex_bulk('project', name='project_req', repository='MANUAL'),
            # to project
            self.get_mod_bulk(
                'project', "<1[data][id]>", dict(id="<0[data][id]>"), 'inventory'
            ),
        ]
        bulk_data = self.get_templates_data(bulk_data, "<1[data][id]>", "<0[data][id]>")
        subs = self.generate_subs(self.make_bulk(bulk_data))
        subs['project'][0] = self.sync_project(subs['project'][0]['id'])
        prj_id = subs['project'][0]['id']
        inv_id = subs['inventory'][0]['id']
        ptask_id = subs['periodic_task'][0]['id']
        tmplt_ids = [t['id'] for t in subs['template']]
        test_bulk_data = [
            self.get_mod_bulk(
                'project', prj_id, {}, 'inventory/{}'.format(inv_id), 'delete'
            ),
            self.get_mod_bulk(
                'project', prj_id, {}, 'periodic_task/{}'.format(ptask_id), 'delete'
            ),
            self.get_mod_bulk(
                'project', prj_id, {}, 'inventory/{}'.format(inv_id), 'delete'
            ),
            self.get_mod_bulk(
                'project', prj_id, {}, 'template/{}'.format(tmplt_ids[0]), 'delete'
            ),
            self.get_mod_bulk(
                'project', prj_id, {}, 'inventory/{}'.format(inv_id), 'delete'
            ),
            self.get_mod_bulk(
                'project', prj_id, {}, 'template/{}'.format(tmplt_ids[1]), 'delete'
            ),
            self.get_bulk('inventory', {}, 'del', pk=inv_id),
            self.get_mod_bulk(
                'project', prj_id, {}, 'inventory/{}'.format(inv_id), 'delete'
            ),
            self.get_bulk('inventory', {}, 'del', pk=inv_id),
        ]
        results = self.make_bulk(test_bulk_data, 'put')
        for result in results[:-2]:
            if 'inventory' not in result.get('subitem', result['item']):
                self.assertEqual(result['status'], 204, result)
            else:
                self.assertEqual(result['status'], 400, result)
        self.assertEqual(results[-2]['status'], 204)
        self.assertEqual(results[-1]['status'], 204)
        self.assertTrue(not self.get_model_filter('Inventory', pk=inv_id).exists())

    def test_history_facts(self):
        history_kwargs = dict(project=None, mode="setup",
                              kind="MODULE",
                              raw_inventory="inventory",
                              raw_stdout="text",
                              inventory=None,
                              status="OK",
                              start_time=now() - timedelta(hours=15),
                              stop_time=now() - timedelta(hours=14))
        history = self.get_model_class('History').objects.create(**history_kwargs)
        stdout = self._get_string_from_file("facts_stdout")
        history.raw_stdout = stdout
        history.save()
        url = self.get_url('history', history.id, 'facts')
        parsed = self.get_result("get", url)
        self.assertCount(parsed, 6)
        self.assertEquals(parsed['172.16.1.31']['status'], 'SUCCESS')
        self.assertEquals(parsed['test.vst.lan']['status'], 'SUCCESS')
        self.assertEquals(parsed['172.16.1.29']['status'], 'SUCCESS')
        self.assertEquals(parsed['172.16.1.32']['status'], 'FAILED!')
        self.assertEquals(parsed['172.16.1.30']['status'], 'UNREACHABLE!')
        self.assertEquals(parsed['172.16.1.31']['ansible_facts']
                          ['ansible_memfree_mb'], 736)
        self.assertCount(
            parsed['test.vst.lan']['ansible_facts']["ansible_devices"], 2
        )
        self.assertIn('No route to host',
                      parsed['172.16.1.30']['msg'])
        for status in ['RUN', 'DELAY']:
            history.status = status
            history.save()
            self.get_result("get", url, code=424)
        history.status = "OK"
        history.kind = "PLAYBOOK"
        history.save()
        self.get_result("get", url, code=404)

    def test_import_inventory(self):
        bulk_data = [
            dict(data_type=['project'], method='post', data=dict(name='testProj')),
            self.get_mod_bulk(
                'project', '<0[data][id]>',
                {'name': 'test-inventory', 'raw_data': inventory_data},
                ['inventory', 'import_inventory'],
            ),
            self.get_mod_bulk(
                'inventory',
                '<1[data][inventory_id]>',
                {},
                'all_hosts',
                method='get'
            ),
            self.get_mod_bulk(
                'inventory',
                '<1[data][inventory_id]>',
                {},
                'all_groups',
                method='get'
            ),
            self.get_mod_bulk(
                'inventory',
                '<1[data][inventory_id]>',
                {},
                'variables',
                method='get'
            ),
            self.get_mod_bulk(
                'project',
                '<0[data][id]>',
                {},
                ['inventory', '<1[data][inventory_id]>'],
                method='get'
            ),
        ]
        results = self.make_bulk(bulk_data, 'put')

        self.assertEqual(results[0]['status'], 201)
        self.assertEqual(results[1]['status'], 201)
        self.assertIn('inventory_id', results[1]['data'])

        self.assertEqual(
            results[2]['data']['count'],
            len(valid_inventory['hosts'])
        )
        self.assertEqual(
            results[3]['data']['count'],
            len(valid_inventory['groups'])
        )
        self.assertEqual(
            results[4]['data']['count'],
            len(valid_inventory['vars'])
        )

        self.assertEqual(results[5]['status'], 200)
        self.assertEqual(results[5]['data']['name'], 'test-inventory')

        for host in results[2]['data']['results']:
            self.assertIn(host['name'], valid_inventory['hosts'].keys())
        for group in results[3]['data']['results']:
            self.assertIn(group['name'], valid_inventory['groups'].keys())
        for variable in results[4]['data']['results']:
            self.assertIn(variable['key'], valid_inventory['vars'].keys())
