#!/usr/bin/env python3
# Copyright 2020 Canonical Ltd.
#
# 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.
#
# For those usages not covered by the Apache License, Version 2.0 please
# contact: legal@canonical.com
#
# To get in touch with the maintainers, please contact:
# osm-charmers@lists.launchpad.net
##

from typing import NoReturn
import unittest
from ops.model import BlockedStatus

from ops.testing import Harness

from charm import LcmCharm


class TestCharm(unittest.TestCase):
    """LCM Charm unit tests."""

    def setUp(self) -> NoReturn:
        """Test setup"""
        self.harness = Harness(LcmCharm)
        self.harness.set_leader(is_leader=True)
        self.harness.begin()

    def test_on_start_without_relations(self) -> NoReturn:
        """Test installation without any relation."""
        self.harness.charm.on.start.emit()

        # Verifying status
        self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)

        # Verifying status message
        self.assertGreater(len(self.harness.charm.unit.status.message), 0)
        self.assertTrue(
            self.harness.charm.unit.status.message.startswith("Waiting for ")
        )
        self.assertIn("kafka", self.harness.charm.unit.status.message)
        self.assertIn("mongodb", self.harness.charm.unit.status.message)
        self.assertIn("ro", self.harness.charm.unit.status.message)
        self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations"))

    def test_on_start_with_relations(self) -> NoReturn:
        """Test deployment without keystone."""
        expected_result = {
            "version": 3,
            "containers": [
                {
                    "name": "lcm",
                    "imageDetails": self.harness.charm.image.fetch(),
                    "imagePullPolicy": "Always",
                    "ports": [
                        {
                            "name": "lcm",
                            "containerPort": 9999,
                            "protocol": "TCP",
                        }
                    ],
                    "envConfig": {
                        "ALLOW_ANONYMOUS_LOGIN": "yes",
                        "OSMLCM_GLOBAL_LOGLEVEL": "INFO",
                        "OSMLCM_RO_HOST": "ro",
                        "OSMLCM_RO_PORT": 9090,
                        "OSMLCM_RO_TENANT": "osm",
                        "OSMLCM_MESSAGE_DRIVER": "kafka",
                        "OSMLCM_MESSAGE_HOST": "kafka",
                        "OSMLCM_MESSAGE_PORT": 9092,
                        "OSMLCM_DATABASE_DRIVER": "mongo",
                        "OSMLCM_DATABASE_URI": "mongodb://mongo:27017",
                        "OSMLCM_DATABASE_COMMONKEY": "osm",
                        "OSMLCM_STORAGE_DRIVER": "mongo",
                        "OSMLCM_STORAGE_PATH": "/app/storage",
                        "OSMLCM_STORAGE_COLLECTION": "files",
                        "OSMLCM_STORAGE_URI": "mongodb://mongo:27017",
                        "OSMLCM_VCA_HOST": "admin",
                        "OSMLCM_VCA_PORT": 17070,
                        "OSMLCM_VCA_USER": "admin",
                        "OSMLCM_VCA_PUBKEY": "secret",
                        "OSMLCM_VCA_SECRET": "secret",
                        "OSMLCM_VCA_CACERT": "",
                        "OSMLCM_VCA_CLOUD": "localhost",
                        "OSMLCM_VCA_K8S_CLOUD": "k8scloud",
                    },
                }
            ],
            "kubernetesResources": {"ingressResources": []},
        }

        self.harness.charm.on.start.emit()

        # Check if kafka datastore is initialized
        self.assertIsNone(self.harness.charm.state.message_host)
        self.assertIsNone(self.harness.charm.state.message_port)

        # Check if mongodb datastore is initialized
        self.assertIsNone(self.harness.charm.state.database_uri)

        # Check if RO datastore is initialized
        self.assertIsNone(self.harness.charm.state.ro_host)
        self.assertIsNone(self.harness.charm.state.ro_port)

        # Initializing the kafka relation
        kafka_relation_id = self.harness.add_relation("kafka", "kafka")
        self.harness.add_relation_unit(kafka_relation_id, "kafka/0")
        self.harness.update_relation_data(
            kafka_relation_id, "kafka/0", {"host": "kafka", "port": 9092}
        )

        # Initializing the mongo relation
        mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb")
        self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0")
        self.harness.update_relation_data(
            mongodb_relation_id,
            "mongodb/0",
            {"connection_string": "mongodb://mongo:27017"},
        )

        # Initializing the RO relation
        ro_relation_id = self.harness.add_relation("ro", "ro")
        self.harness.add_relation_unit(ro_relation_id, "ro/0")
        self.harness.update_relation_data(
            ro_relation_id, "ro/0", {"host": "ro", "port": 9090}
        )

        # Checking if kafka data is stored
        self.assertEqual(self.harness.charm.state.message_host, "kafka")
        self.assertEqual(self.harness.charm.state.message_port, 9092)

        # Checking if mongodb data is stored
        self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017")

        # Checking if RO data is stored
        self.assertEqual(self.harness.charm.state.ro_host, "ro")
        self.assertEqual(self.harness.charm.state.ro_port, 9090)

        # Verifying status
        self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus)

        pod_spec, _ = self.harness.get_pod_spec()

        self.assertDictEqual(expected_result, pod_spec)

    def test_on_kafka_relation_unit_changed(self) -> NoReturn:
        """Test to see if kafka relation is updated."""
        self.harness.charm.on.start.emit()

        self.assertIsNone(self.harness.charm.state.message_host)
        self.assertIsNone(self.harness.charm.state.message_port)

        relation_id = self.harness.add_relation("kafka", "kafka")
        self.harness.add_relation_unit(relation_id, "kafka/0")
        self.harness.update_relation_data(
            relation_id, "kafka/0", {"host": "kafka", "port": 9092}
        )

        self.assertEqual(self.harness.charm.state.message_host, "kafka")
        self.assertEqual(self.harness.charm.state.message_port, 9092)

        # Verifying status
        self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)

        # Verifying status message
        self.assertGreater(len(self.harness.charm.unit.status.message), 0)
        self.assertTrue(
            self.harness.charm.unit.status.message.startswith("Waiting for ")
        )
        self.assertNotIn("kafka", self.harness.charm.unit.status.message)
        self.assertIn("mongodb", self.harness.charm.unit.status.message)
        self.assertIn("ro", self.harness.charm.unit.status.message)
        self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations"))

    def test_on_mongodb_unit_relation_changed(self) -> NoReturn:
        """Test to see if mongodb relation is updated."""
        self.harness.charm.on.start.emit()

        self.assertIsNone(self.harness.charm.state.database_uri)

        relation_id = self.harness.add_relation("mongodb", "mongodb")
        self.harness.add_relation_unit(relation_id, "mongodb/0")
        self.harness.update_relation_data(
            relation_id, "mongodb/0", {"connection_string": "mongodb://mongo:27017"}
        )

        self.assertEqual(self.harness.charm.state.database_uri, "mongodb://mongo:27017")

        # Verifying status
        self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)

        # Verifying status message
        self.assertGreater(len(self.harness.charm.unit.status.message), 0)
        self.assertTrue(
            self.harness.charm.unit.status.message.startswith("Waiting for ")
        )
        self.assertIn("kafka", self.harness.charm.unit.status.message)
        self.assertNotIn("mongodb", self.harness.charm.unit.status.message)
        self.assertIn("ro", self.harness.charm.unit.status.message)
        self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations"))

    def test_on_ro_unit_relation_changed(self) -> NoReturn:
        """Test to see if RO relation is updated."""
        self.harness.charm.on.start.emit()

        self.assertIsNone(self.harness.charm.state.ro_host)
        self.assertIsNone(self.harness.charm.state.ro_port)

        relation_id = self.harness.add_relation("ro", "ro")
        self.harness.add_relation_unit(relation_id, "ro/0")
        self.harness.update_relation_data(
            relation_id, "ro/0", {"host": "ro", "port": 9090}
        )

        self.assertEqual(self.harness.charm.state.ro_host, "ro")
        self.assertEqual(self.harness.charm.state.ro_port, 9090)

        # Verifying status
        self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)

        # Verifying status message
        self.assertGreater(len(self.harness.charm.unit.status.message), 0)
        self.assertTrue(
            self.harness.charm.unit.status.message.startswith("Waiting for ")
        )
        self.assertIn("kafka", self.harness.charm.unit.status.message)
        self.assertIn("mongodb", self.harness.charm.unit.status.message)
        self.assertNotIn("ro", self.harness.charm.unit.status.message)
        self.assertTrue(self.harness.charm.unit.status.message.endswith(" relations"))


if __name__ == "__main__":
    unittest.main()
