# vim: set fileencodings=utf-8
# -*- coding: utf-8; -*-
#
# Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
# license agreements.  See the NOTICE file distributed with this work for
# additional information regarding copyright ownership.  Crate licenses
# this file to you 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.
#
# However, if you have executed another commercial license agreement
# with Crate these terms will supersede the license and you may use the
# software solely pursuant to the terms of the relevant commercial agreement.


from unittest import TestCase
from unittest.mock import PropertyMock, patch

from crate.client._pep440 import Version
from crate.crash.command import CrateShell
from crate.crash.sysinfo import Result as Res, SysInfoCommand

CRATE_VERSION = Version("0.55.2")
assert CrateShell, "Used for patch"


class SysInfoTest(TestCase):

    NODES_FIELDS_FETCHED = (('crate_version', None), ('total_heap_mb', None))
    NODES_INFO = [['0.54.0', 16301], ['0.54.0', 16301]]
    NODES_FIELDS = ['crate_version', 'total_heap_mb']

    CLUSTER_FIELDS_FETCHED = \
        (('number_of_shards', None), ('number_of_records', None))
    CLUSTER_INFO = [[128, 664755863]]
    CLUSTER_FIELDS = ['number_of_shards', 'number_of_records']

    def setUp(self):
        self.patcher = patch(__name__ + '.CrateShell')
        self.cmd = self.patcher.start()
        self.cmd.connection.lowest_server_version = CRATE_VERSION
        self.sys_info = SysInfoCommand(self.cmd)

        # composing return values for multiple calls of cmd.cursor.fetchall method
        # the number of valuses in the list is equal to the number of queries in
        # SysInfoCommand.CLUSTER_INFO and NODES_INFO
        self.fetch_all = [SysInfoTest.CLUSTER_INFO] * len(SysInfoCommand.CLUSTER_INFO)
        self.fetch_all.append(SysInfoTest.NODES_INFO)
        self.desc = [SysInfoTest.CLUSTER_FIELDS_FETCHED] * len(SysInfoCommand.CLUSTER_INFO)
        self.desc.append(SysInfoTest.NODES_FIELDS_FETCHED)

    def tearDown(self):
        self.patcher.stop()

    def test_nodes_info(self):
        self.cmd._exec.return_value = True
        self.cmd.cursor.fetchall.return_value = SysInfoTest.NODES_INFO
        type(self.cmd.cursor).description = \
            PropertyMock(return_value=SysInfoTest.NODES_FIELDS_FETCHED)
        result = []
        succcess = self.sys_info._nodes_info(result)
        expected = Res(SysInfoTest.NODES_INFO, SysInfoTest.NODES_FIELDS)
        self.assertEqual(succcess, True)
        self.assertEqual(expected, result[0])

    def test_sys_info(self):
        self.cmd.cursor.fetchall.side_effect = self.fetch_all
        self.cmd._exec.return_value = True
        type(self.cmd.cursor).description = PropertyMock(side_effect=self.desc)
        succcess, result = self.sys_info._sys_info()
        self.assertEqual(succcess, True)
        expected_nodes = Res(SysInfoTest.NODES_INFO, SysInfoTest.NODES_FIELDS)
        # test only the second part of result
        self.assertEqual(expected_nodes, result[1])

    def test_sys_info_fails(self):
        self.cmd.cursor.fetchall.side_effect = self.fetch_all
        succcess_ = [True] * len(SysInfoCommand.CLUSTER_INFO)
        succcess_.append(False)
        self.cmd._exec.side_effect = succcess_
        type(self.cmd.cursor).description = PropertyMock(side_effect=self.desc)

        succcess, result = self.sys_info._sys_info()
        self.assertEqual(succcess, False)
        Res(SysInfoTest.NODES_INFO, SysInfoTest.NODES_FIELDS)
        # must not contain any partial result from first successful calls
        self.assertEqual([], result)

    def test_cluster_info(self):
        self.cmd._exec.return_value = True
        self.cmd.cursor.fetchall.return_value = SysInfoTest.CLUSTER_INFO
        type(self.cmd.cursor).description = \
            PropertyMock(return_value=SysInfoTest.CLUSTER_FIELDS_FETCHED)
        result = []
        succcess = self.sys_info._nodes_info(result)
        self.assertEqual(succcess, True)
        expected = Res(SysInfoTest.CLUSTER_INFO, SysInfoTest.CLUSTER_FIELDS)
        self.assertEqual(expected, result[0])

    def test_execute_when_nodes_info_fails(self):
        self.cmd._exec.return_value = False
        self.cmd.cursor.fetchall.return_value = SysInfoTest.NODES_INFO
        type(self.cmd.cursor).description = \
            PropertyMock(return_value=SysInfoTest.NODES_FIELDS_FETCHED)
        result = []
        succcess = self.sys_info._nodes_info(result)
        self.assertEqual(succcess, False)
        self.assertEqual([], result)

    def test_exceute_when_nodes_cluster_infos_fails(self):
        self.cmd._exec.return_value = False
        self.cmd.cursor.fetchall.return_value = SysInfoTest.CLUSTER_INFO
        type(self.cmd.cursor).description = \
            PropertyMock(return_value=SysInfoTest.CLUSTER_FIELDS_FETCHED)
        result = []
        succcess = self.sys_info._nodes_info(result)
        self.assertEqual(succcess, False)
        self.assertEqual([], result)
