示例#1
0
class TestMongoApiWithMongoOnline(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        # Same as in setUp(), to avoid running all tests if Mongo is offline

        dummy_logger = logging.getLogger('dummy')
        dummy_logger.disabled = True
        db = env.DB_NAME
        host = env.DB_IP
        port = env.DB_PORT
        user = ''
        password = ''
        mongo = MongoApi(logger=dummy_logger,
                         db_name=db,
                         host=host,
                         port=port,
                         username=user,
                         password=password)

        # Ping Mongo
        try:
            mongo.ping_unsafe()
        except PyMongoError:
            raise Exception("Mongo is not online.")

    def setUp(self) -> None:
        self.dummy_logger = logging.getLogger('dummy')
        self.dummy_logger.disabled = True
        self.db = env.DB_NAME
        self.host = env.DB_IP
        self.port = env.DB_PORT
        self.user = ''
        self.password = ''
        self.mongo = MongoApi(logger=self.dummy_logger,
                              db_name=self.db,
                              host=self.host,
                              port=self.port,
                              username=self.user,
                              password=self.password)

        try:
            self.mongo.ping_unsafe()
        except PyMongoError:
            self.fail("Mongo is not online.")

        # Clear test database
        self.mongo.drop_db()

        self.col1 = 'collection1'
        self.col2 = 'collection2'

        self.val1 = {'a': 'b', 'c': 'd'}
        self.val2 = {'e': 'f', 'g': 'h'}
        self.val3 = {'i': 'j'}
        self.val4 = {'k': 'l', 'm': {'n': ['o', 'p', True, False, 1, 2.1]}}

        self.id_1 = '1'
        self.id_2 = '2'

        self.key_m_1 = 'key_m_1'
        self.key_m_2 = 'key_m_2'

        self.val_m_1 = 'm1'
        self.val_m_2 = 'm2'

        self.test_1 = 'test_1'
        self.test_2 = 'test_2'

        self.time_used = datetime(2021, 1, 28).timestamp()
        self.query1 = {'doc_type': self.test_1, 'd': self.time_used}
        self.query2 = {'doc_type': self.test_2, 'd': self.time_used}

        self.doc_1 = {
            '$push': {
                self.id_1: {
                    self.key_m_1: self.val_m_1
                }
            },
            '$inc': {
                'n_entries': 1
            }
        }

        self.doc_2 = {
            '$push': {
                self.id_2: {
                    self.key_m_2: self.val_m_2
                }
            },
            '$inc': {
                'n_entries': 1
            }
        }

        self.doc_3 = {
            '$push': {
                self.id_1: {
                    self.key_m_2: self.val_m_2
                }
            },
            '$inc': {
                'n_entries': 1
            }
        }

        self.time = timedelta(seconds=3)
        self.time_with_error_margin = timedelta(seconds=4)

        self.default_str = 'DEFAULT'
        self.default_int = 789
        self.default_bool = False

    def tearDown(self) -> None:
        self.dummy_logger = None
        self.mongo.drop_db()
        self.mongo = None

    def test_insert_one_inserts_value_into_the_specified_collection(self):
        # Check that col1 is empty
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 0)

        # Insert val1 into col1
        self.mongo.insert_one(self.col1, self.val1)

        # Check that value was added to col1
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 1)
        self.assertEqual(dict(get_result[0]), self.val1)

    def test_insert_one_supports_more_complex_documents(self):
        # Check that col1 is empty
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 0)

        # Insert val4 into col1
        self.mongo.insert_one(self.col1, self.val4)

        # Check that value was added to col1
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 1)
        self.assertEqual(dict(get_result[0]), self.val4)

    def test_insert_many_inserts_all_values_into_the_specified_collection(
            self):
        # Check that col1 is empty
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 0)

        # Insert val1, val2, and val3 into col1
        self.mongo.insert_many(self.col1, [self.val1, self.val2, self.val3])

        # Check that the values was added to col1
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 3)
        self.assertEqual(dict(get_result[0]), self.val1)
        self.assertEqual(dict(get_result[1]), self.val2)
        self.assertEqual(dict(get_result[2]), self.val3)

    def test_update_one_inserts_value_into_the_specified_collection(self):
        # Check that col1 is empty
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 0)

        self.mongo.update_one(self.col1, self.query1, self.doc_1)

        # Check that value was added to col1
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 1)
        self.assertEqual(get_result[0]['1'][0][self.key_m_1], self.val_m_1)
        self.assertEqual(get_result[0]['n_entries'], 1)
        self.assertEqual(get_result[0]['doc_type'], self.test_1)
        self.assertEqual(get_result[0]['d'], self.time_used)

    def test_update_one_multiple_times_inserts_values_into_the_specified_collection(
            self):
        # Check that col1 is empty
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 0)

        self.mongo.update_one(self.col1, self.query1, self.doc_1)
        self.mongo.update_one(self.col1, self.query1, self.doc_3)

        # Check that value was added to col1
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 1)
        self.assertEqual(get_result[0]['1'][0][self.key_m_1], self.val_m_1)
        self.assertEqual(get_result[0]['1'][1][self.key_m_2], self.val_m_2)
        self.assertEqual(get_result[0]['n_entries'], 2)
        self.assertEqual(get_result[0]['doc_type'], self.test_1)
        self.assertEqual(get_result[0]['d'], self.time_used)

    def test_update_one_multiple_documents_inserts_values_into_the_specified_collection(
            self):
        # Check that col1 is empty
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 0)

        self.mongo.update_one(self.col1, self.query1, self.doc_1)
        self.mongo.update_one(self.col1, self.query2, self.doc_2)

        # Check that value was added to col1
        get_result = list(self.mongo._db[self.col1].find({}))
        self.assertEqual(len(get_result), 2)
        self.assertEqual(get_result[0]['1'][0][self.key_m_1], self.val_m_1)
        self.assertEqual(get_result[0]['n_entries'], 1)
        self.assertEqual(get_result[0]['doc_type'], self.test_1)
        self.assertEqual(get_result[0]['d'], self.time_used)

        self.assertEqual(get_result[1]['2'][0][self.key_m_2], self.val_m_2)
        self.assertEqual(get_result[1]['n_entries'], 1)
        self.assertEqual(get_result[1]['doc_type'], self.test_2)
        self.assertEqual(get_result[1]['d'], self.time_used)

    def test_get_all_returns_inserted_values_in_order_of_insert(self):
        # Check that col1 is empty
        get_result = self.mongo.get_all(self.col1)
        self.assertEqual(len(get_result), 0)

        # Insert val1, val2, and val3 into col1
        self.mongo._db[self.col1].insert_many(
            [self.val1, self.val2, self.val3])

        # Check that the values was added to col1
        get_result = self.mongo.get_all(self.col1)
        self.assertEqual(len(get_result), 3)
        self.assertEqual(dict(get_result[0]), self.val1)
        self.assertEqual(dict(get_result[1]), self.val2)
        self.assertEqual(dict(get_result[2]), self.val3)

    def test_drop_collection_deletes_the_specified_collection(self):
        # Check that col1 and col2 are empty
        get_result1 = list(self.mongo._db[self.col1].find({}))
        get_result2 = list(self.mongo._db[self.col2].find({}))
        self.assertEqual(len(get_result1), 0)
        self.assertEqual(len(get_result2), 0)

        # Insert val1, val2, and val3 into col1 and val4 into col2
        self.mongo._db[self.col1].insert_many(
            [self.val1, self.val2, self.val3])
        self.mongo._db[self.col2].insert_one(self.val4)

        # Check that col1 and col2 are not empty
        get_result1 = list(self.mongo._db[self.col1].find({}))
        get_result2 = list(self.mongo._db[self.col2].find({}))
        self.assertEqual(len(get_result1), 3)
        self.assertEqual(len(get_result2), 1)

        # Delete col1
        self.mongo.drop_collection(self.col1)

        # Check that col1 is back to being empty but col2 is not empty
        get_result1 = list(self.mongo._db[self.col1].find({}))
        get_result2 = list(self.mongo._db[self.col2].find({}))
        self.assertEqual(len(get_result1), 0)
        self.assertEqual(len(get_result2), 1)

    def test_drop_db_deletes_all_collections(self):
        # Check that col1 and col2 are empty
        get_result1 = list(self.mongo._db[self.col1].find({}))
        get_result2 = list(self.mongo._db[self.col2].find({}))
        self.assertEqual(len(get_result1), 0)
        self.assertEqual(len(get_result2), 0)

        # Insert val1, val2, and val3 into col1 and val4 into col2
        self.mongo._db[self.col1].insert_many(
            [self.val1, self.val2, self.val3])
        self.mongo._db[self.col2].insert_one(self.val4)

        # Check that col1 and col2 are not empty
        get_result1 = list(self.mongo._db[self.col1].find({}))
        get_result2 = list(self.mongo._db[self.col2].find({}))
        self.assertEqual(len(get_result1), 3)
        self.assertEqual(len(get_result2), 1)

        # Drop db
        self.mongo.drop_db()

        # Check that col1 and col2 are back to being empty
        get_result1 = list(self.mongo._db[self.col1].find({}))
        get_result2 = list(self.mongo._db[self.col2].find({}))
        self.assertEqual(len(get_result1), 0)
        self.assertEqual(len(get_result2), 0)

    def test_ping_returns_true(self):
        self.assertTrue(self.mongo.ping_unsafe())

    def test_ping_auth_throws_value_error_for_empty_password(self):
        self.assertRaises(ValueError, self.mongo.ping_auth, self.user, '')

    def test_ping_auth_throws_operation_failure_for_wrong_password(self):
        self.assertRaises(OperationFailure, self.mongo.ping_auth, self.user,
                          'incorrect_password')
示例#2
0
class TestMongoApiWithMongoOffline(unittest.TestCase):
    def setUp(self) -> None:
        self.dummy_logger = logging.getLogger('dummy')
        self.dummy_logger.disabled = True
        self.db = env.DB_NAME
        self.host = 'dummyhost'
        self.port = env.DB_PORT
        self.user = ''
        self.password = ''
        self.mongo = MongoApi(self.dummy_logger,
                              self.db,
                              self.host,
                              self.port,
                              timeout_ms=1)
        # timeout_ms is set to 1ms to speed up tests. It cannot be 0

        self.col1 = 'collection1'
        self.val1 = {'a': 'b', 'c': 'd'}
        self.val2 = {'e': 'f', 'g': 'h'}
        self.val3 = {'i': 'j'}

        self.id_1 = '1'
        self.key_m_1 = 'key_m_1'
        self.val_m_1 = 'm1'
        self.test_1 = 'test_1'
        self.time_used = datetime(2021, 1, 28).timestamp()
        self.query1 = {'doc_type': self.test_1, 'd': self.time_used}

        self.doc_1 = {
            '$push': {
                self.id_1: {
                    self.key_m_1: self.val_m_1
                }
            },
            '$inc': {
                'n_entries': 1
            }
        }

    def tearDown(self) -> None:
        self.dummy_logger = None
        self.mongo.drop_db()
        self.mongo = None

    def test_insert_one_returns_none_first_time_round(self):
        default_return = self.mongo.insert_one(self.col1, self.val1)
        self.assertIsNone(default_return)

    def test_insert_many_returns_none_first_time_round(self):
        default_return = self.mongo.insert_many(
            self.col1, [self.val1, self.val2, self.val3])
        self.assertIsNone(default_return)

    def test_update_one_returns_none_first_time_round(self):
        default_return = self.mongo.update_one(self.col1, self.query1,
                                               self.doc_1)
        self.assertIsNone(default_return)

    def test_get_all_returns_none_first_time_round(self):
        default_return = self.mongo.get_all(self.col1)
        self.assertIsNone(default_return)

    def test_drop_collection_returns_none_first_time_round(self):
        default_return = self.mongo.drop_collection(self.col1)
        self.assertIsNone(default_return)

    def test_drop_db_returns_none_first_time_round(self):
        default_return = self.mongo.drop_db()
        self.assertIsNone(default_return)

    def test_ping_unsafe_throws_exception_first_time_round(self):
        self.assertRaises(ServerSelectionTimeoutError, self.mongo.ping_unsafe)

    def test_ping_auth_throws_exception_first_time_round(self):
        self.assertRaises(ServerSelectionTimeoutError, self.mongo.ping_auth,
                          self.user, self.password)

    def test_insert_one_returns_none_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertIsNone(self.mongo.insert_one(self.col1, self.val1))

    def test_update_one_returns_none_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertIsNone(
            self.mongo.update_one(self.col1, self.query1, self.doc_1))

    def test_insert_many_returns_none_if_mongo_already_down(self):
        self.mongo._set_as_down()
        documents = [self.val1, self.val2, self.val3]
        self.assertIsNone(self.mongo.insert_many(self.col1, documents))

    def test_get_all_returns_none_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertIsNone(self.mongo.get_all(self.col1))

    def test_drop_collection_returns_none_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertIsNone(self.mongo.drop_collection(self.col1))

    def test_drop_db_returns_none_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertIsNone(self.mongo.drop_db())

    def test_ping_unsafe_throws_exception_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertRaises(ServerSelectionTimeoutError, self.mongo.ping_unsafe)

    def test_ping_auth_throws_exception_if_mongo_already_down(self):
        self.mongo._set_as_down()
        self.assertRaises(ServerSelectionTimeoutError, self.mongo.ping_auth,
                          self.user, self.password)
示例#3
0
class TestAlertStore(unittest.TestCase):
    def setUp(self) -> None:
        self.dummy_logger = logging.getLogger('Dummy')
        self.dummy_logger.disabled = True
        self.connection_check_time_interval = timedelta(seconds=0)
        self.rabbit_ip = env.RABBIT_IP
        self.rabbitmq = RabbitMQApi(
            self.dummy_logger,
            self.rabbit_ip,
            connection_check_time_interval=self.connection_check_time_interval)

        self.test_rabbit_manager = RabbitMQApi(
            self.dummy_logger,
            self.rabbit_ip,
            connection_check_time_interval=self.connection_check_time_interval)

        self.mongo_ip = env.DB_IP
        self.mongo_db = env.DB_NAME
        self.mongo_port = env.DB_PORT

        self.mongo = MongoApi(logger=self.dummy_logger.getChild(
            MongoApi.__name__),
                              db_name=self.mongo_db,
                              host=self.mongo_ip,
                              port=self.mongo_port)

        self.redis_db = env.REDIS_DB
        self.redis_host = env.REDIS_IP
        self.redis_port = env.REDIS_PORT
        self.redis_namespace = env.UNIQUE_ALERTER_IDENTIFIER
        self.redis = RedisApi(self.dummy_logger, self.redis_db,
                              self.redis_host, self.redis_port, '',
                              self.redis_namespace,
                              self.connection_check_time_interval)

        self.test_store_name = 'store name'
        self.test_store = AlertStore(self.test_store_name, self.dummy_logger,
                                     self.rabbitmq)

        self.routing_key = 'heartbeat.worker'
        self.test_queue_name = 'test queue'

        connect_to_rabbit(self.rabbitmq)
        self.rabbitmq.exchange_declare(HEALTH_CHECK_EXCHANGE, 'topic', False,
                                       True, False, False)
        self.rabbitmq.exchange_declare(STORE_EXCHANGE, 'direct', False, True,
                                       False, False)
        self.rabbitmq.queue_declare(ALERT_STORE_INPUT_QUEUE, False, True,
                                    False, False)
        self.rabbitmq.queue_bind(ALERT_STORE_INPUT_QUEUE, STORE_EXCHANGE,
                                 ALERT_STORE_INPUT_ROUTING_KEY)

        connect_to_rabbit(self.test_rabbit_manager)
        self.test_rabbit_manager.queue_declare(self.test_queue_name, False,
                                               True, False, False)
        self.test_rabbit_manager.queue_bind(self.test_queue_name,
                                            HEALTH_CHECK_EXCHANGE,
                                            self.routing_key)

        self.test_data_str = 'test data'
        self.test_exception = PANICException('test_exception', 1)

        self.parent_id = 'test_parent_id'

        self.alert_id = 'test_alert_id'
        self.origin_id = 'test_origin_id'
        self.alert_name = 'test_alert'
        self.metric = 'system_is_down'
        self.severity = 'warning'
        self.message = 'alert message'
        self.value = 'alert_code_1'

        self.alert_id_2 = 'test_alert_id_2'
        self.origin_id_2 = 'test_origin_id_2'
        self.alert_name_2 = 'test_alert_2'
        self.severity_2 = 'critical'
        self.message_2 = 'alert message 2'
        self.value_2 = 'alert_code_2'

        self.alert_id_3 = 'test_alert_id_3'
        self.origin_id_3 = 'test_origin_id_3'
        self.alert_name_3 = 'test_alert_3'
        self.severity_3 = 'info'
        self.message_3 = 'alert message 3'
        self.value_3 = 'alert_code_3'

        self.last_monitored = datetime(2012, 1, 1).timestamp()
        self.none = None

        self.alert_data_1 = {
            'parent_id': self.parent_id,
            'origin_id': self.origin_id,
            'alert_code': {
                'name': self.alert_name,
                'value': self.value,
            },
            'severity': self.severity,
            'metric': self.metric,
            'message': self.message,
            'timestamp': self.last_monitored,
        }
        self.alert_data_2 = {
            'parent_id': self.parent_id,
            'origin_id': self.origin_id_2,
            'alert_code': {
                'name': self.alert_name_2,
                'value': self.value_2,
            },
            'severity': self.severity_2,
            'metric': self.metric,
            'message': self.message_2,
            'timestamp': self.last_monitored,
        }
        self.alert_data_3 = {
            'parent_id': self.parent_id,
            'origin_id': self.origin_id_3,
            'alert_code': {
                'name': self.alert_name_3,
                'value': self.value_3,
            },
            'severity': self.severity_3,
            'metric': self.metric,
            'message': self.message_3,
            'timestamp': self.last_monitored,
        }
        self.alert_data_key_error = {"result": {"data": {}, "data2": {}}}
        self.alert_data_unexpected = {"unexpected": {}}

    def tearDown(self) -> None:
        connect_to_rabbit(self.rabbitmq)
        delete_queue_if_exists(self.rabbitmq, ALERT_STORE_INPUT_QUEUE)
        delete_exchange_if_exists(self.rabbitmq, STORE_EXCHANGE)
        delete_exchange_if_exists(self.rabbitmq, HEALTH_CHECK_EXCHANGE)
        disconnect_from_rabbit(self.rabbitmq)

        connect_to_rabbit(self.test_rabbit_manager)
        delete_queue_if_exists(self.test_rabbit_manager, self.test_queue_name)
        disconnect_from_rabbit(self.test_rabbit_manager)

        self.dummy_logger = None
        self.connection_check_time_interval = None
        self.rabbitmq = None
        self.test_rabbit_manager = None
        self.redis.delete_all_unsafe()
        self.redis = None
        self.mongo.drop_collection(self.parent_id)
        self.mongo = None
        self.test_store = None

    def test__str__returns_name_correctly(self) -> None:
        self.assertEqual(self.test_store_name, str(self.test_store))

    def test_name_property_returns_name_correctly(self) -> None:
        self.assertEqual(self.test_store_name, self.test_store.name)

    def test_mongo_ip_property_returns_mongo_ip_correctly(self) -> None:
        self.assertEqual(self.mongo_ip, self.test_store.mongo_ip)

    def test_mongo_db_property_returns_mongo_db_correctly(self) -> None:
        self.assertEqual(self.mongo_db, self.test_store.mongo_db)

    def test_mongo_port_property_returns_mongo_port_correctly(self) -> None:
        self.assertEqual(self.mongo_port, self.test_store.mongo_port)

    def test_mongo_property_returns_mongo(self) -> None:
        self.assertEqual(type(self.mongo), type(self.test_store.mongo))

    def test_redis_property_returns_redis_correctly(self) -> None:
        self.assertEqual(type(self.redis), type(self.test_store.redis))

    def test_initialise_rabbitmq_initialises_everything_as_expected(
            self) -> None:
        try:
            # To make sure that the exchanges have not already been declared
            self.rabbitmq.connect()
            self.rabbitmq.queue_delete(ALERT_STORE_INPUT_QUEUE)
            self.test_rabbit_manager.queue_delete(self.test_queue_name)
            self.rabbitmq.exchange_delete(HEALTH_CHECK_EXCHANGE)
            self.rabbitmq.exchange_delete(STORE_EXCHANGE)
            self.rabbitmq.disconnect()

            self.test_store._initialise_rabbitmq()

            # Perform checks that the connection has been opened, marked as open
            # and that the delivery confirmation variable is set.
            self.assertTrue(self.test_store.rabbitmq.is_connected)
            self.assertTrue(self.test_store.rabbitmq.connection.is_open)
            self.assertTrue(
                self.test_store.rabbitmq.channel._delivery_confirmation)

            # Check whether the producing exchanges have been created by
            # using passive=True. If this check fails an exception is raised
            # automatically.
            self.test_store.rabbitmq.exchange_declare(STORE_EXCHANGE,
                                                      passive=True)
            self.test_store.rabbitmq.exchange_declare(HEALTH_CHECK_EXCHANGE,
                                                      passive=True)

            # Check whether the exchange has been creating by sending messages
            # to it. If this fails an exception is raised, hence the test fails.
            self.test_store.rabbitmq.basic_publish_confirm(
                exchange=HEALTH_CHECK_EXCHANGE,
                routing_key=self.routing_key,
                body=self.test_data_str,
                is_body_dict=False,
                properties=pika.BasicProperties(delivery_mode=2),
                mandatory=False)

            # Check whether the exchange has been creating by sending messages
            # to it. If this fails an exception is raised, hence the test fails.
            self.test_store.rabbitmq.basic_publish_confirm(
                exchange=STORE_EXCHANGE,
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY,
                body=self.test_data_str,
                is_body_dict=False,
                properties=pika.BasicProperties(delivery_mode=2),
                mandatory=False)

            # Re-declare queue to get the number of messages
            res = self.test_store.rabbitmq.queue_declare(
                ALERT_STORE_INPUT_QUEUE, False, True, False, False)

            self.assertEqual(1, res.method.message_count)
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @parameterized.expand([
        ("KeyError", "self.alert_data_key_error "),
    ])
    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    @mock.patch("src.data_store.stores.store.Store._send_heartbeat",
                autospec=True)
    def test_process_data_with_bad_data_does_raises_exceptions(
            self, mock_error, mock_bad_data, mock_send_hb, mock_ack) -> None:
        mock_ack.return_value = None
        try:
            self.test_store._initialise_rabbitmq()

            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(
                blocking_channel, method_chains, properties,
                json.dumps(self.alert_data_unexpected).encode())
            self.assertRaises(eval(mock_error),
                              self.test_store._process_mongo_store,
                              eval(mock_bad_data))
            mock_ack.assert_called_once()
            mock_send_hb.assert_not_called()
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @freeze_time("2012-01-01")
    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    @mock.patch("src.data_store.stores.alert.AlertStore._process_redis_store",
                autospec=True)
    @mock.patch("src.data_store.stores.alert.AlertStore._process_mongo_store",
                autospec=True)
    def test_process_data_sends_heartbeat_correctly(self,
                                                    mock_process_mongo_store,
                                                    mock_process_redis_store,
                                                    mock_basic_ack) -> None:

        mock_basic_ack.return_value = None
        try:
            self.test_rabbit_manager.connect()
            self.test_store._initialise_rabbitmq()

            self.test_rabbit_manager.queue_delete(self.test_queue_name)
            res = self.test_rabbit_manager.queue_declare(
                queue=self.test_queue_name,
                durable=True,
                exclusive=False,
                auto_delete=False,
                passive=False)
            self.assertEqual(0, res.method.message_count)

            self.test_rabbit_manager.queue_bind(queue=self.test_queue_name,
                                                exchange=HEALTH_CHECK_EXCHANGE,
                                                routing_key=self.routing_key)

            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(
                blocking_channel, method_chains, properties,
                json.dumps(self.alert_data_1).encode())

            res = self.test_rabbit_manager.queue_declare(
                queue=self.test_queue_name,
                durable=True,
                exclusive=False,
                auto_delete=False,
                passive=True)
            self.assertEqual(1, res.method.message_count)

            heartbeat_test = {
                'component_name': self.test_store_name,
                'is_alive': True,
                'timestamp': datetime(2012, 1, 1).timestamp()
            }

            _, _, body = self.test_rabbit_manager.basic_get(
                self.test_queue_name)
            self.assertEqual(heartbeat_test, json.loads(body))
            mock_process_mongo_store.assert_called_once()
            mock_process_redis_store.assert_called_once()
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    def test_process_data_doesnt_send_heartbeat_on_processing_error(
            self, mock_basic_ack) -> None:

        mock_basic_ack.return_value = None
        try:
            self.test_rabbit_manager.connect()
            self.test_store._initialise_rabbitmq()

            self.test_rabbit_manager.queue_delete(self.test_queue_name)
            res = self.test_rabbit_manager.queue_declare(
                queue=self.test_queue_name,
                durable=True,
                exclusive=False,
                auto_delete=False,
                passive=False)
            self.assertEqual(0, res.method.message_count)

            self.test_rabbit_manager.queue_bind(queue=self.test_queue_name,
                                                exchange=HEALTH_CHECK_EXCHANGE,
                                                routing_key=self.routing_key)

            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(
                blocking_channel, method_chains, properties,
                json.dumps(self.alert_data_unexpected).encode())

            res = self.test_rabbit_manager.queue_declare(
                queue=self.test_queue_name,
                durable=True,
                exclusive=False,
                auto_delete=False,
                passive=True)
            self.assertEqual(0, res.method.message_count)
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @mock.patch.object(MongoApi, "update_one")
    def test_process_mongo_store_calls_update_one(self,
                                                  mock_update_one) -> None:
        self.test_store._process_mongo_store(self.alert_data_1)
        mock_update_one.assert_called_once()

    @mock.patch.object(RedisApi, "hset")
    def test_process_redis_store_calls_hset(self, mock_hset) -> None:
        self.test_store._process_redis_store(self.alert_data_1)
        mock_hset.assert_called_once()

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    @freeze_time("2012-01-01")
    @mock.patch.object(MongoApi, "update_one")
    def test_process_mongo_store_calls_mongo_correctly(
            self, mock_system_data, mock_update_one) -> None:
        data = eval(mock_system_data)
        self.test_store._process_mongo_store(data)

        call_1 = call(data['parent_id'], {
            'doc_type': 'alert',
            'n_alerts': {
                '$lt': 1000
            }
        }, {
            '$push': {
                'alerts': {
                    'origin': data['origin_id'],
                    'alert_name': data['alert_code']['name'],
                    'severity': data['severity'],
                    'metric': data['metric'],
                    'message': data['message'],
                    'timestamp': str(data['timestamp']),
                }
            },
            '$min': {
                'first': data['timestamp']
            },
            '$max': {
                'last': data['timestamp']
            },
            '$inc': {
                'n_alerts': 1
            },
        })
        mock_update_one.assert_has_calls([call_1])

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    @freeze_time("2012-01-01")
    @mock.patch.object(RedisApi, "hset")
    def test_process_redis_store_calls_redis_correctly(self, mock_system_data,
                                                       mock_hset) -> None:
        data = eval(mock_system_data)
        self.test_store._process_redis_store(data)

        metric_data = {
            'severity': data['severity'],
            'message': data['message']
        }
        key = data['origin_id']

        call_1 = call(Keys.get_hash_parent(data['parent_id']),
                      eval('Keys.get_alert_{}(key)'.format(data['metric'])),
                      json.dumps(metric_data))
        mock_hset.assert_has_calls([call_1])

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    @freeze_time("2012-01-01")
    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    @mock.patch("src.data_store.stores.alert.AlertStore._process_redis_store",
                autospec=True)
    @mock.patch("src.data_store.stores.store.Store._send_heartbeat",
                autospec=True)
    @mock.patch.object(MongoApi, "update_one")
    def test_process_data_calls_mongo_correctly(self, mock_system_data,
                                                mock_update_one, mock_send_hb,
                                                mock_process_redis_store,
                                                mock_ack) -> None:

        mock_ack.return_value = None
        try:
            self.test_store._initialise_rabbitmq()

            data = eval(mock_system_data)
            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(blocking_channel, method_chains,
                                          properties,
                                          json.dumps(data).encode())

            mock_ack.assert_called_once()
            mock_send_hb.assert_called_once()

            call_1 = call(data['parent_id'], {
                'doc_type': 'alert',
                'n_alerts': {
                    '$lt': 1000
                }
            }, {
                '$push': {
                    'alerts': {
                        'origin': data['origin_id'],
                        'alert_name': data['alert_code']['name'],
                        'severity': data['severity'],
                        'metric': data['metric'],
                        'message': data['message'],
                        'timestamp': str(data['timestamp']),
                    }
                },
                '$min': {
                    'first': data['timestamp']
                },
                '$max': {
                    'last': data['timestamp']
                },
                '$inc': {
                    'n_alerts': 1
                },
            })
            mock_update_one.assert_has_calls([call_1])
            mock_process_redis_store.assert_called_once()
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    @freeze_time("2012-01-01")
    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    @mock.patch("src.data_store.stores.alert.AlertStore._process_mongo_store",
                autospec=True)
    @mock.patch("src.data_store.stores.store.Store._send_heartbeat",
                autospec=True)
    @mock.patch.object(RedisApi, "hset")
    def test_process_data_calls_redis_correctly(self, mock_system_data,
                                                mock_hset, mock_send_hb,
                                                mock_process_mongo_store,
                                                mock_ack) -> None:

        mock_ack.return_value = None
        try:
            self.test_store._initialise_rabbitmq()

            data = eval(mock_system_data)
            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(blocking_channel, method_chains,
                                          properties,
                                          json.dumps(data).encode())

            mock_ack.assert_called_once()
            mock_send_hb.assert_called_once()

            metric_data = {
                'severity': data['severity'],
                'message': data['message']
            }
            key = data['origin_id']

            call_1 = call(
                Keys.get_hash_parent(data['parent_id']),
                eval('Keys.get_alert_{}(key)'.format(data['metric'])),
                json.dumps(metric_data))
            mock_hset.assert_has_calls([call_1])
            mock_process_mongo_store.assert_called_once()
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    def test_process_mongo_store_mongo_stores_correctly(
            self, mock_system_data) -> None:

        data = eval(mock_system_data)
        self.test_store._process_mongo_store(data)

        documents = self.mongo.get_all(data['parent_id'])
        document = documents[0]
        expected = [
            'alert', 1,
            str(data['origin_id']),
            str(data['alert_code']['name']),
            str(data['severity']),
            str(data['metric']),
            str(data['message']),
            str(data['timestamp'])
        ]
        actual = [
            document['doc_type'], document['n_alerts'],
            document['alerts'][0]['origin'],
            document['alerts'][0]['alert_name'],
            document['alerts'][0]['severity'], document['alerts'][0]['metric'],
            document['alerts'][0]['message'],
            document['alerts'][0]['timestamp']
        ]

        self.assertListEqual(expected, actual)

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    def test_process_redis_store_redis_stores_correctly(
            self, mock_system_data) -> None:

        data = eval(mock_system_data)
        self.test_store._process_redis_store(data)

        key = data['origin_id']

        stored_data = self.redis.hget(
            Keys.get_hash_parent(data['parent_id']),
            eval('Keys.get_alert_{}(key)'.format(data['metric'])))

        expected_data = {
            'severity': data['severity'],
            'message': data['message']
        }

        self.assertEqual(expected_data, json.loads(stored_data))

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    @mock.patch("src.data_store.stores.alert.AlertStore._process_redis_store",
                autospec=True)
    @mock.patch("src.data_store.stores.store.Store._send_heartbeat",
                autospec=True)
    def test_process_data_results_stores_in_mongo_correctly(
            self, mock_system_data, mock_send_hb, mock_process_redis_store,
            mock_ack) -> None:

        mock_ack.return_value = None
        try:
            self.test_store._initialise_rabbitmq()

            data = eval(mock_system_data)
            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(blocking_channel, method_chains,
                                          properties,
                                          json.dumps(data).encode())

            mock_process_redis_store.assert_called_once()
            mock_ack.assert_called_once()
            mock_send_hb.assert_called_once()

            documents = self.mongo.get_all(data['parent_id'])
            document = documents[0]
            expected = [
                'alert', 1,
                str(data['origin_id']),
                str(data['alert_code']['name']),
                str(data['severity']),
                str(data['message']),
                str(data['timestamp'])
            ]
            actual = [
                document['doc_type'], document['n_alerts'],
                document['alerts'][0]['origin'],
                document['alerts'][0]['alert_name'],
                document['alerts'][0]['severity'],
                document['alerts'][0]['message'],
                document['alerts'][0]['timestamp']
            ]

            self.assertListEqual(expected, actual)
        except Exception as e:
            self.fail("Test failed: {}".format(e))

    @parameterized.expand([
        ("self.alert_data_1", ),
        ("self.alert_data_2", ),
        ("self.alert_data_3", ),
    ])
    @mock.patch("src.data_store.stores.store.RabbitMQApi.basic_ack",
                autospec=True)
    @mock.patch("src.data_store.stores.alert.AlertStore._process_mongo_store",
                autospec=True)
    @mock.patch("src.data_store.stores.store.Store._send_heartbeat",
                autospec=True)
    def test_process_data_results_stores_in_redis_correctly(
            self, mock_system_data, mock_send_hb, mock_process_mongo_store,
            mock_ack) -> None:

        mock_ack.return_value = None
        try:
            self.test_store._initialise_rabbitmq()

            data = eval(mock_system_data)
            blocking_channel = self.test_store.rabbitmq.channel
            method_chains = pika.spec.Basic.Deliver(
                routing_key=ALERT_STORE_INPUT_ROUTING_KEY)

            properties = pika.spec.BasicProperties()
            self.test_store._process_data(blocking_channel, method_chains,
                                          properties,
                                          json.dumps(data).encode())

            mock_process_mongo_store.assert_called_once()
            mock_ack.assert_called_once()
            mock_send_hb.assert_called_once()

            key = data['origin_id']

            stored_data = self.redis.hget(
                Keys.get_hash_parent(data['parent_id']),
                eval('Keys.get_alert_{}(key)'.format(data['metric'])))

            expected_data = {
                'severity': data['severity'],
                'message': data['message']
            }

            self.assertEqual(expected_data, json.loads(stored_data))
        except Exception as e:
            self.fail("Test failed: {}".format(e))