Exemple #1
0
class TestPeriodicWithoutRedis(unittest.TestCase):
    def setUp(self) -> None:
        self.alerter_name = 'testalerter'
        self.logger = logging.getLogger('dummy')
        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel], TestInternalConf)

        self.par = PeriodicAliveReminder(timedelta(), self.channel_set, None)

    def test_periodic_alive_reminder_sends_info_alert_if_redis_disabled(self):
        self.counter_channel.reset()  # ignore previous alerts
        self.par.send_alive_alert()
        self.assertEqual(self.counter_channel.warning_count, 0)
        self.assertEqual(self.counter_channel.critical_count, 0)
        self.assertEqual(self.counter_channel.info_count, 1)
        self.assertEqual(self.counter_channel.error_count, 0)
Exemple #2
0
class TestPeriodicWithRedis(unittest.TestCase):
    def setUp(self) -> None:
        self.alerter_name = 'testalerter'
        self.logger = logging.getLogger('dummy')
        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel])

        self.db = TestInternalConf.redis_test_database
        self.host = TestUserConf.redis_host
        self.port = TestUserConf.redis_port
        self.password = TestUserConf.redis_password
        self.redis = RedisApi(self.logger, self.db, self.host, self.port,
                              self.password)
        self.redis.delete_all_unsafe()

        try:
            self.redis.ping_unsafe()
        except RedisConnectionError:
            self.fail('Redis is not online.')

        self.mute_key = TestInternalConf.redis_periodic_alive_reminder_mute_key

        self.par = PeriodicAliveReminder(timedelta(), self.channel_set,
                                         self.mute_key, self.redis)

    def test_periodic_alive_reminder_sends_info_alert_if_no_mute_key(self):
        self.counter_channel.reset()  # ignore previous alerts
        self.par.send_alive_alert()
        self.assertEqual(self.counter_channel.warning_count, 0)
        self.assertEqual(self.counter_channel.critical_count, 0)
        self.assertEqual(self.counter_channel.info_count, 1)
        self.assertEqual(self.counter_channel.error_count, 0)

    def test_periodic_alive_reminder_sends_no_alert_if_mute_key_present(self):
        hours = timedelta(hours=float(1))
        until = str(datetime.now() + hours)
        self.redis.set_for(self.mute_key, until, hours)
        self.counter_channel.reset()  # ignore previous alerts
        self.par.send_alive_alert()
        self.redis.remove(self.mute_key)
        self.assertEqual(self.counter_channel.warning_count, 0)
        self.assertEqual(self.counter_channel.critical_count, 0)
        self.assertEqual(self.counter_channel.info_count, 0)
        self.assertEqual(self.counter_channel.error_count, 0)
Exemple #3
0
class TestPolkadotApi(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.ws_url = 'the_ws'
        cls.api_endpoint = 'the_api'
        cls.block_hash = 'the_block_hash'
        cls.acc_addr = 'the_account_address'
        cls.validator_id = "the_validator_id"
        cls.block_no = 1
        cls.referendum_index = 2
        cls.session_index = 3
        cls.auth_index = 4

    def setUp(self) -> None:
        self.logger = logging.getLogger('dummy')
        self.wrapper = PolkadotApiWrapper(self.logger, self.api_endpoint)

        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel], TestInternalConf)

        self.params = {'websocket': self.ws_url}

    def test_api_endpoint_returns_api_endpoint(self):
        self.assertEqual(self.api_endpoint, self.wrapper.api_endpoint)

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_block_hash(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/chain/getBlockHash'
        self.params['block_number'] = self.block_no
        api_call = 'chain/getBlockHash'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_block_hash(self.ws_url,
                                                    self.block_no))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_finalized_head(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/chain/getFinalizedHead'
        api_call = 'chain/getFinalizedHead'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_finalized_head(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_header(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/chain/getHeader'
        self.params['hash'] = self.block_hash
        api_call = 'chain/getHeader'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_header(self.ws_url, self.block_hash))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_system_chain(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/system/chain'
        api_call = 'system/chain'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_system_chain(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_system_health(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/system/health'
        api_call = 'system/health'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_system_health(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_council_members(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/council/members'
        api_call = 'council/members'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_council_members(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_council_proposal_count(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/council/proposalCount'
        api_call = 'council/proposalCount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_council_proposal_count(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_public_proposal_count(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/democracy/publicPropCount'
        api_call = 'democracy/publicPropCount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_public_proposal_count(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_referendum_count(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/democracy/referendumCount'
        api_call = 'democracy/referendumCount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_referendum_count(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_referendum_info_of(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/democracy/referendumInfoOf'
        api_call = 'democracy/referendumInfoOf'
        self.params['referendum_index'] = self.referendum_index
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_referendum_info_of(self.ws_url,
                                                self.referendum_index))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_authored_block(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/imOnline/authoredBlocks'
        self.params['validator_id'] = self.validator_id
        self.params['session_index'] = self.session_index
        api_call = 'imOnline/authoredBlocks'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_authored_blocks(self.ws_url, self.session_index,
                                             self.validator_id))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_received_heartbeats(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/imOnline/receivedHeartbeats'
        self.params['session_index'] = self.session_index
        self.params['auth_index'] = self.auth_index
        api_call = 'imOnline/receivedHeartbeats'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_received_heartbeats(self.ws_url,
                                                 self.session_index,
                                                 self.auth_index))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_current_index(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/session/currentIndex'
        api_call = 'session/currentIndex'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_current_index(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_disabled_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/session/disabledValidators'
        api_call = 'session/disabledValidators'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_disabled_validators(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_session_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/session/validators'
        api_call = 'session/validators'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_session_validators(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_derive_staking_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/derive/staking/validators'
        api_call = 'staking/validators'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_derive_staking_validators(
            self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_eras_stakers(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/staking/erasStakers'
        self.params['account_id'] = self.acc_addr
        api_call = 'staking/erasStakers'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_eras_stakers(self.ws_url, self.acc_addr))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_events(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/system/events'
        self.params['block_hash'] = self.block_hash
        api_call = 'system/events'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_events(self.ws_url, self.block_hash))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_slash_amount(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/custom/getSlashAmount'
        self.params['block_hash'] = self.block_hash
        self.params['account_address'] = self.acc_addr
        api_call = 'custom/getSlashAmount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_slash_amount(self.ws_url, self.block_hash,
                                          self.acc_addr))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_web_sockets_connected_to_an_api(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/getConnectionsList'
        api_call = ''
        mock.api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_web_sockets_connected_to_an_api())

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_ping_api(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/pingApi'
        self.params = {}
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.ping_api())

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_ping_node(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/pingNode'
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.ping_node(self.ws_url))

    def test_set_api_as_down_produces_alert_if_api_not_down(self):
        # By default, API not down

        # First time round, error alert is produced
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", self.channel_set)
        self.assertEqual(1, self.counter_channel.error_count)

        # Second time round, API is already down, so no alerts
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", self.channel_set)
        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_api_as_up_produces_alert_if_api_is_down(self):
        # By default, API not down

        # First time round, no alert is produced
        self.counter_channel.reset()
        self.wrapper.set_api_as_up("", self.channel_set)
        self.assertTrue(self.counter_channel.no_alerts())

        # In between, we set the api as down
        self.wrapper.set_api_as_down("", self.channel_set)

        # API is now down, so info alert is produced
        self.counter_channel.reset()
        self.wrapper.set_api_as_up("", self.channel_set)
        self.assertEqual(1, self.counter_channel.info_count)
Exemple #4
0
class TestPolkadotApi(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.ws_url = 'the_ws'
        cls.api_endpoint = 'the_api'
        cls.block_hash = 'the_block_hash'
        cls.acc_addr = 'the_account_address'
        cls.validator_id = "the_validator_id"
        cls.block_no = 1
        cls.referendum_index = 2
        cls.session_index = 3
        cls.auth_index = 4

    def setUp(self) -> None:
        self.logger = logging.getLogger('dummy')
        self.wrapper = PolkadotApiWrapper(self.logger, self.api_endpoint)

        self.max_time = 15
        self.max_time_less = self.max_time - 10
        self.max_time_more = self.max_time + 2

        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel], TestInternalConf)

        self.params = {'websocket': self.ws_url}

    def test_api_endpoint_returns_api_endpoint(self):
        self.assertEqual(self.api_endpoint, self.wrapper.api_endpoint)

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_block_hash(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/chain/getBlockHash'
        self.params['block_number'] = self.block_no
        api_call = 'chain/getBlockHash'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_block_hash(self.ws_url, self.block_no))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_finalized_head(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/chain/getFinalizedHead'
        api_call = 'chain/getFinalizedHead'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_finalized_head(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_header(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/chain/getHeader'
        self.params['hash'] = self.block_hash
        api_call = 'chain/getHeader'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_header(self.ws_url, self.block_hash))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_system_chain(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/system/chain'
        api_call = 'system/chain'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_system_chain(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_system_health(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/rpc/system/health'
        api_call = 'system/health'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_system_health(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_council_members(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/council/members'
        api_call = 'council/members'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_council_members(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_council_proposal_count(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/council/proposalCount'
        api_call = 'council/proposalCount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_council_proposal_count(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_public_proposal_count(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/democracy/publicPropCount'
        api_call = 'democracy/publicPropCount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_public_proposal_count(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_referendum_count(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/democracy/referendumCount'
        api_call = 'democracy/referendumCount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_referendum_count(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_referendum_info_of(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/democracy/referendumInfoOf'
        api_call = 'democracy/referendumInfoOf'
        self.params['referendum_index'] = self.referendum_index
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_referendum_info_of(
            self.ws_url, self.referendum_index))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_authored_block(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/imOnline/authoredBlocks'
        self.params['validator_id'] = self.validator_id
        self.params['session_index'] = self.session_index
        api_call = 'imOnline/authoredBlocks'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_authored_blocks(
            self.ws_url, self.session_index, self.validator_id))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_received_heartbeats(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/imOnline/receivedHeartbeats'
        self.params['session_index'] = self.session_index
        self.params['auth_index'] = self.auth_index
        api_call = 'imOnline/receivedHeartbeats'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_received_heartbeats(
            self.ws_url, self.session_index, self.auth_index))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_current_index(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/session/currentIndex'
        api_call = 'session/currentIndex'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_current_index(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_disabled_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/session/disabledValidators'
        api_call = 'session/disabledValidators'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_disabled_validators(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_session_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/session/validators'
        api_call = 'session/validators'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_session_validators(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_derive_staking_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/derive/staking/validators'
        api_call = 'staking/validators'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_derive_staking_validators(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_eras_stakers(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/staking/erasStakers'
        self.params['account_id'] = self.acc_addr
        api_call = 'staking/erasStakers'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_eras_stakers(self.ws_url, self.acc_addr))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_active_era(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/staking/activeEra'
        api_call = 'staking/activeEra'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_active_era(self.ws_url))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_events(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/query/system/events'
        self.params['block_hash'] = self.block_hash
        api_call = 'system/events'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_events(self.ws_url, self.block_hash))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_slash_amount(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/custom/getSlashAmount'
        self.params['block_hash'] = self.block_hash
        self.params['account_address'] = self.acc_addr
        api_call = 'custom/getSlashAmount'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_slash_amount(
            self.ws_url, self.block_hash, self.acc_addr))

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_get_web_sockets_connected_to_an_api(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/getConnectionsList'
        api_call = ''
        mock.api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_web_sockets_connected_to_an_api())

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_ping_api(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/pingApi'
        self.params = {}
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.ping_api())

    @patch(GET_POLKADOT_JSON_FUNCTION)
    def test_ping_node(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/pingNode'
        api_call = 'pingNode'
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.ping_node(self.ws_url))

    def test_api_not_down_by_default(self) -> None:
        self.assertFalse(self.wrapper.is_api_down)

    def test_set_api_as_down_produces_warning_alert_if_api_not_down(
            self) -> None:
        # Test for validator monitors
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(1, self.counter_channel.warning_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsDownAlert)

        # Set API up again, and clear state
        self.wrapper.set_api_as_up("", self.channel_set)
        self.counter_channel.reset()

        # Test for non-validator monitors
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertEqual(1, self.counter_channel.warning_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsDownAlert)

    def test_set_api_as_down_produces_no_warning_alert_if_api_down(
            self) -> None:
        # Test for validator monitors
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(0, self.counter_channel.warning_count)

        # Set API up again, and clear state
        self.wrapper.set_api_as_up("", self.channel_set)
        self.counter_channel.reset()

        # Test for non-validator monitors
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertEqual(0, self.counter_channel.warning_count)

    def test_set_api_as_down_sets_api_down(self) -> None:
        # Test for validator monitors - API previously not down
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

        # Test for validator monitors - API previously down
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

        # Set API up and reset state
        self.counter_channel.reset()
        self.wrapper.set_api_as_up("", self.channel_set)

        # Test for non-validator monitors - API previously not down
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

        # Test for non-validator monitors - API previously down
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

    def test_set_api_as_down_raises_critical_alert_for_val_monitors_if_conditions_are_met(
            self) -> None:
        # Declare API as down and start timer.
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.counter_channel.reset()

        # Enough time passed - no critical alert sent yet
        sleep(self.max_time_more)
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(1, self.counter_channel.critical_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsDownAlert)

        # Enough time passed - critical alert sent
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(0, self.counter_channel.critical_count)

        # To reset state
        self.wrapper.set_api_as_up("", self.channel_set)
        self.counter_channel.reset()

        # Not enough time passed - no critical alert sent yet
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(0, self.counter_channel.critical_count)
        self.counter_channel.reset()

        # Not enough time passed - critical alert sent
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_down_raises_no_critical_alert_for_non_val_monitors(
            self) -> None:

        # Not enough time passed
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertEqual(0, self.counter_channel.critical_count)
        self.counter_channel.reset()

        # Enough time passed
        sleep(self.max_time_more)
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_up_produces_info_alert_if_api_is_down(self):
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_up("", self.channel_set)
        self.assertEqual(1, self.counter_channel.info_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsUpAgainAlert)

    def test_set_api_as_up_produces_no_alert_if_api_is_up(self):
        self.wrapper.set_api_as_up("", self.channel_set)
        self.assertTrue(self.counter_channel.no_alerts())
Exemple #5
0
class TestChannelSet(unittest.TestCase):
    def setUp(self) -> None:
        self.alerter_name = 'testalerter'
        self.logger = logging.getLogger('dummy')
        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel],
                                      TestInternalConfSomeAlertsDisabled)

        self.dummy_alert = Alert(AlertCode.TestAlert, 'dummy')
        self.severities_map_bkp = \
            TestInternalConfSomeAlertsDisabled.severities_enabled_map.copy()
        self.alerts_map_bkp = \
            TestInternalConfSomeAlertsDisabled.alerts_enabled_map.copy()

    @staticmethod
    def enable_severity(severity: SeverityCode):
        TestInternalConfSomeAlertsDisabled.severities_enabled_map[
            severity.name] = True

    @staticmethod
    def disable_severity(severity: SeverityCode):
        TestInternalConfSomeAlertsDisabled.severities_enabled_map[
            severity.name] = False

    @staticmethod
    def enable_alert(alert: AlertCode):
        TestInternalConfSomeAlertsDisabled.alerts_enabled_map[
            alert.name] = True

    @staticmethod
    def disable_alert(alert: AlertCode):
        TestInternalConfSomeAlertsDisabled.alerts_enabled_map[
            alert.name] = False

    def tearDown(self) -> None:
        self.counter_channel.reset()  # ignore previous alerts
        TestInternalConfSomeAlertsDisabled.severities_enabled_map = \
            self.severities_map_bkp
        TestInternalConfSomeAlertsDisabled.alerts_enabled_map = \
            self.alerts_map_bkp

    def test_info_severity_disabled_from_config(self):
        # As set in test_internal_config_alerts, info is disabled by default
        self.channel_set.alert_info(self.dummy_alert)
        self.assertEqual(self.counter_channel.info_count, 0)

    def test_warning_severity_disabled_from_config(self):
        # As set in test_internal_config_alerts, warning is disabled by default
        self.channel_set.alert_warning(self.dummy_alert)
        self.assertEqual(self.counter_channel.warning_count, 0)

    def test_critical_severity_enabled_from_config(self):
        # As set in test_internal_config_alerts, critical is enabled by default
        self.channel_set.alert_critical(self.dummy_alert)
        self.assertEqual(self.counter_channel.critical_count, 1)

    def test_error_severity_enabled_from_config(self):
        # As set in test_internal_config_alerts, error is enabled by default
        self.channel_set.alert_error(self.dummy_alert)
        self.assertEqual(self.counter_channel.error_count, 1)

    def test_info_severity_does_not_work_if_disabled(self):
        self.disable_severity(SeverityCode.INFO)
        self.channel_set.alert_info(self.dummy_alert)
        self.assertEqual(self.counter_channel.info_count, 0)

    def test_warning_severity_does_not_work_if_disabled(self):
        self.disable_severity(SeverityCode.WARNING)
        self.channel_set.alert_warning(self.dummy_alert)
        self.assertEqual(self.counter_channel.warning_count, 0)

    def test_critical_severity_does_not_work_if_disabled(self):
        self.disable_severity(SeverityCode.CRITICAL)
        self.channel_set.alert_critical(self.dummy_alert)
        self.assertEqual(self.counter_channel.critical_count, 0)

    def test_error_severity_does_not_work_if_disabled(self):
        self.disable_severity(SeverityCode.ERROR)
        self.channel_set.alert_error(self.dummy_alert)
        self.assertEqual(self.counter_channel.error_count, 0)

    def test_info_severity_works_if_enabled(self):
        self.enable_severity(SeverityCode.INFO)
        self.channel_set.alert_info(self.dummy_alert)
        self.assertEqual(self.counter_channel.info_count, 1)

    def test_warning_severity_works_if_enabled(self):
        self.enable_severity(SeverityCode.WARNING)
        self.channel_set.alert_warning(self.dummy_alert)
        self.assertEqual(self.counter_channel.warning_count, 1)

    def test_critical_severity_works_if_enabled(self):
        self.enable_severity(SeverityCode.CRITICAL)
        self.channel_set.alert_critical(self.dummy_alert)
        self.assertEqual(self.counter_channel.critical_count, 1)

    def test_error_severity_works_if_enabled(self):
        self.enable_severity(SeverityCode.ERROR)
        self.channel_set.alert_error(self.dummy_alert)
        self.assertEqual(self.counter_channel.error_count, 1)

    def test_alert_works_if_severity_and_alert_enabled_in_config(self):
        # As set in test_internal_config_alerts,
        # - info, warning are disabled by default
        # - critical, error are enabled by default
        # - test alert code is enabled by default
        self.channel_set.alert_info(self.dummy_alert)
        self.channel_set.alert_warning(self.dummy_alert)
        self.channel_set.alert_critical(self.dummy_alert)
        self.channel_set.alert_error(self.dummy_alert)

        self.assertEqual(self.counter_channel.info_count, 0)
        self.assertEqual(self.counter_channel.warning_count, 0)
        self.assertEqual(self.counter_channel.critical_count, 1)
        self.assertEqual(self.counter_channel.error_count, 1)

    def test_alert_does_not_work_on_any_severity_if_disabled(self):
        self.disable_alert(self.dummy_alert.alert_code)

        self.channel_set.alert_info(self.dummy_alert)
        self.channel_set.alert_warning(self.dummy_alert)
        self.channel_set.alert_critical(self.dummy_alert)
        self.channel_set.alert_error(self.dummy_alert)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_alert_works_on_any_severity_if_enabled(self):
        self.enable_severity(SeverityCode.INFO)
        self.enable_severity(SeverityCode.WARNING)
        self.enable_severity(SeverityCode.CRITICAL)
        self.enable_severity(SeverityCode.ERROR)
        self.enable_alert(self.dummy_alert.alert_code)

        self.channel_set.alert_info(self.dummy_alert)
        self.channel_set.alert_warning(self.dummy_alert)
        self.channel_set.alert_critical(self.dummy_alert)
        self.channel_set.alert_error(self.dummy_alert)

        self.assertEqual(self.counter_channel.info_count, 1)
        self.assertEqual(self.counter_channel.warning_count, 1)
        self.assertEqual(self.counter_channel.critical_count, 1)
        self.assertEqual(self.counter_channel.error_count, 1)
class TestOasisApi(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        cls.ws_url = 'the_ws'
        cls.api_endpoint = 'http://localhost:8688'
        cls.acc_addr = 'the_account_address'
        cls.validator_id = "the_validator_id"
        cls.block_no = 1

    def setUp(self) -> None:
        self.logger = logging.getLogger('dummy')
        self.node_name = 'dummy_node'
        self.wrapper = OasisApiWrapper(self.logger)

        self.max_time = 15
        self.max_time_less = self.max_time - 10
        self.max_time_more = self.max_time + 2

        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel], TestInternalConf)

        self.params = {'name': self.node_name}

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_block_header(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/consensus/blockheader'
        api_call = ''
        self.params = {'name': self.node_name}
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_block_header(self.api_endpoint, self.node_name))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_is_syncing(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/nodecontroller/synced'
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_is_syncing(self.api_endpoint, self.node_name))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_node(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/registry/node'
        api_call = ''
        self.params['nodeID'] = self.validator_id
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_node(self.api_endpoint, self.node_name,
                                  self.validator_id))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_prometheus_gauge(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/prometheus/gauge'
        api_call = ''
        self.params['gauge'] = "peers"
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_prometheus_gauge(self.api_endpoint,
                                              self.node_name, "peers"))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_consensus_genesis(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/consensus/genesis'
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_consensus_genesis(self.api_endpoint,
                                               self.node_name))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_consensus_block(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/consensus/block'
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_consensus_block(self.api_endpoint,
                                             self.node_name))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_block_header_at_height(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/consensus/blockheader'
        api_call = ''
        self.params['height'] = self.block_no
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_block_header_height(self.api_endpoint,
                                                 self.node_name,
                                                 self.block_no))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_session_validators(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/scheduler/validators'
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(
            self.wrapper.get_session_validators(self.api_endpoint,
                                                self.node_name))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_web_sockets_connected_to_an_api(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/getconnectionslist'
        api_call = ''
        mock.api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_web_sockets_connected_to_an_api( \
            self.api_endpoint))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_ping_api(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/ping'
        self.params = {}
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.ping_api(self.api_endpoint))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_tendermint_address(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/consensus/pubkeyaddress'
        self.params = {'consensus_public_key': self.acc_addr}
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_tendermint_address(self.api_endpoint, \
           self.acc_addr))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_registry_node(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/registry/node'
        self.params = {'name': self.node_name, 'nodeID': self.acc_addr}
        api_call = ''
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)

        self.assertTrue(self.wrapper.get_registry_node(self.api_endpoint, \
            self.node_name, self.acc_addr))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_ping_node(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/pingnode'
        api_call = ''
        self.params = {'name': self.node_name}
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)
        self.assertTrue(
            self.wrapper.ping_node(self.api_endpoint, self.node_name))

    @patch(GET_OASIS_JSON_FUNCTION)
    def test_get_staking_account(self, mock):
        # Set up mock
        endpoint = self.api_endpoint + '/api/staking/account'
        api_call = ''
        self.params = {'name': self.node_name, 'address': self.acc_addr}
        mock.side_effect = api_mock_generator(endpoint, self.params, api_call)
        self.assertTrue(
            self.wrapper.get_staking_account(self.api_endpoint, self.node_name,
                                             self.acc_addr))

    def test_set_api_as_down_produces_warning_alert_if_api_not_down_for_val_monitors(
            self) -> None:
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(1, self.counter_channel.warning_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsDownAlert)

    def test_set_api_as_down_produces_no_error_alert_if_api_down_for_val_monitors(
            self) -> None:
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertEqual(0, self.counter_channel.error_count)

    def test_set_api_as_down_produces_warning_alert_if_api_not_down_for_non_val_monitors(
            self) -> None:
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertEqual(1, self.counter_channel.warning_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsDownAlert)

    def test_set_api_as_down_produces_no_error_alert_if_api_down_for_non_val_monitors(
            self) -> None:
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertEqual(0, self.counter_channel.error_count)

    def test_set_api_as_down_sets_api_down_if_api_not_down_for_val_monitors(
            self) -> None:
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

    def test_api_not_down_by_default(self) -> None:
        self.assertFalse(self.wrapper.is_api_down)

    def test_set_api_as_down_sets_api_down_if_api_down_for_non_val_monitors(
            self) -> None:
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

    def test_set_api_as_down_sets_api_down_if_api_down_for_val_monitors(
            self) -> None:
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

    def test_set_api_as_down_sets_api_down_if_api_not_down_for_non_val_monitors(
            self) -> None:
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.assertTrue(self.wrapper.is_api_down)

    def test_set_api_as_down_raises_warning_alert_for_val_monitors_if_enough_time_passed_for_first_time(
            self) -> None:
        # To perform did task
        self.wrapper.set_api_as_up("", self.channel_set)
        self.counter_channel.reset()
        sleep(self.max_time_more)
        self.wrapper.set_api_as_down("", True, self.channel_set)

        self.assertEqual(1, self.counter_channel.warning_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsDownAlert)

    def test_set_api_as_down_raises_no_critical_alert_for_val_monitors_if_enough_time_passed_for_second_time(
            self) -> None:
        # To perform did task
        self.wrapper.set_api_as_up("", self.channel_set)
        sleep(self.max_time_more)
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)

        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_down_raises_no_critical_alert_for_val_monitors_if_not_enough_time_passed_for_second_time(
            self) -> None:
        # To perform did task
        self.wrapper.set_api_as_up("", self.channel_set)
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_down("", True, self.channel_set)

        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_down_raises_no_critical_alert_for_non_val_monitors_if_enough_time_passed_for_first_time(
            self) -> None:
        # To perform did task
        self.wrapper.set_api_as_up("", self.channel_set)
        sleep(self.max_time_more)
        self.wrapper.set_api_as_down("", False, self.channel_set)

        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_down_raises_no_critical_alert_for_non_val_monitors_if_enough_time_passed_for_second_time(
            self) -> None:
        # To perform did task
        self.wrapper.set_api_as_up("", self.channel_set)
        sleep(self.max_time_more)
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.wrapper.set_api_as_down("", False, self.channel_set)

        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_down_raises_no_critical_alert_for_non_val_monitors_if_not_enough_time_passed_for_second_time(
            self) -> None:
        # To perform did task
        self.wrapper.set_api_as_up("", self.channel_set)
        self.wrapper.set_api_as_down("", False, self.channel_set)
        self.wrapper.set_api_as_down("", False, self.channel_set)

        self.assertEqual(0, self.counter_channel.critical_count)

    def test_set_api_as_up_produces_info_alert_if_api_is_down(self):
        self.wrapper.set_api_as_down("", True, self.channel_set)
        self.counter_channel.reset()
        self.wrapper.set_api_as_up("", self.channel_set)
        self.assertEqual(1, self.counter_channel.info_count)
        self.assertIsInstance(self.counter_channel.latest_alert,
                              ApiIsUpAgainAlert)

    def test_set_api_as_up_produces_no_alert_if_api_is_up(self):
        self.wrapper.set_api_as_up("", self.channel_set)
        self.assertTrue(self.counter_channel.no_alerts())
Exemple #7
0
class TestNodeWithoutRedis(unittest.TestCase):
    def setUp(self) -> None:
        self.node_name = 'testnode'
        self.logger = logging.getLogger('dummy')

        self.downtime_initial_alert_delay = \
            TestInternalConf.downtime_initial_alert_delay
        self.downtime_initial_alert_delay_with_error_margin = \
            self.downtime_initial_alert_delay + timedelta(seconds=0.5)

        self.downtime_reminder_alert_interval = \
            TestInternalConf.downtime_reminder_interval_seconds
        self.downtime_reminder_alert_interval_with_error_margin = \
            self.downtime_reminder_alert_interval + timedelta(seconds=0.5)

        self.max_missed_blocks_time_interval = \
            TestInternalConf.max_missed_blocks_time_interval
        self.max_missed_blocks_time_interval_with_error_margin = \
            self.max_missed_blocks_time_interval + timedelta(seconds=0.5)

        self.max_missed_blocks_in_time_interval = \
            TestInternalConf.max_missed_blocks_in_time_interval

        self.full_node = Node(name=self.node_name,
                              rpc_url=None,
                              node_type=NodeType.NON_VALIDATOR_FULL_NODE,
                              pubkey=None,
                              network='',
                              redis=None,
                              internal_conf=TestInternalConf)

        self.validator = Node(name=self.node_name,
                              rpc_url=None,
                              node_type=NodeType.VALIDATOR_FULL_NODE,
                              pubkey=None,
                              network='',
                              redis=None,
                              internal_conf=TestInternalConf)

        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel])
        self.dummy_exception = DummyException()

        self.dummy_block_height = -1
        self.dummy_block_time = datetime.min + timedelta(days=123)
        self.dummy_block_time_after_time_interval = \
            self.dummy_block_time + \
            self.max_missed_blocks_time_interval_with_error_margin
        self.dummy_missing_validators = -1
        self.dummy_voting_power = 1000
        self.dummy_no_of_peers = 1000

        self.peers_validator_danger_boundary = \
            TestInternalConf.validator_peer_danger_boundary
        self.peers_less_than_validator_danger_boundary = \
            self.peers_validator_danger_boundary - 2
        self.peers_more_than_validator_danger_boundary = \
            self.peers_validator_danger_boundary + 2

        self.peers_validator_safe_boundary = \
            TestInternalConf.validator_peer_safe_boundary
        self.peers_less_than_validator_safe_boundary = \
            self.peers_validator_safe_boundary - 2
        self.peers_more_than_validator_safe_boundary = \
            self.peers_validator_safe_boundary + 2

        self.peers_full_node_danger_boundary = \
            TestInternalConf.full_node_peer_danger_boundary
        self.peers_less_than_full_node_danger_boundary = \
            self.peers_full_node_danger_boundary - 2
        self.peers_more_than_full_node_danger_boundary = \
            self.peers_full_node_danger_boundary + 2

    def test_is_validator_true_if_is_validator(self):
        self.assertTrue(self.validator.is_validator)

    def test_is_validator_false_if_not_validator(self):
        self.assertFalse(self.full_node.is_validator)

    def test_is_down_false_by_default(self):
        self.assertFalse(self.validator.is_down)

    def test_is_missing_blocks_false_by_default(self):
        self.assertFalse(self.validator.is_missing_blocks)

    def test_consecutive_blocks_missed_is_0_by_default(self):
        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 0)

    def test_voting_power_is_none_by_default(self):
        self.assertIsNone(self.validator.voting_power)

    def test_catching_up_is_false_by_default(self):
        self.assertFalse(self.validator.catching_up)

    def test_no_of_peers_is_none_by_default(self):
        self.assertIsNone(self.validator.no_of_peers)

    def test_status_returns_three_values(self):
        self.validator._voting_power = 123
        self.validator._catching_up = True
        self.validator._no_of_peers = 999

        self.assertEqual(
            self.validator.status(), 'voting_power=123, '
            'catching_up=True, '
            'number_of_peers=999')

    def test_set_as_down_validator(self):
        NODE = self.validator
        self.assertFalse(NODE.is_down)

        # First one sets validator to down but sends no alerts
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # Second one sends info alert and keeps validator down
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.info_count, 1)
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # Next is the initial downtime alert, but this is
        # not sent if not enough time has passed for it
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # ...if enough time passed for an initial alert, a major alert is sent
        sleep(self.downtime_initial_alert_delay_with_error_margin.seconds)
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.major_count, 1)
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # Next is the downtime reminder alert, but this is
        # not sent if not enough time has passed for it
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # ...if enough time passed for a reminder, a major alert is sent again
        sleep(self.downtime_reminder_alert_interval_with_error_margin.seconds)
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.major_count, 1)
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

    def test_set_as_down_full_node(self):
        NODE = self.full_node
        self.assertFalse(NODE.is_down)

        # First one sets full node to down but sends no alerts
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # Second one sends info alert and keeps full node down
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.info_count, 1)
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # Next is the initial downtime alert, but this is
        # not sent if not enough time has passed for it
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # ...if enough time passed for an initial alert, a minor alert is sent
        sleep(self.downtime_initial_alert_delay_with_error_margin.seconds)
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.minor_count, 1)
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # Next is the downtime reminder alert, but this is
        # not sent if not enough time has passed for it
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

        # ...if enough time passed for a reminder, a minor alert is sent again
        sleep(self.downtime_reminder_alert_interval_with_error_margin.seconds)
        NODE.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.minor_count, 1)
        self.assertTrue(NODE.is_down)

        self.counter_channel.reset()

    def test_set_as_up_sends_no_alerts_if_not_down(self):
        self.validator.set_as_up(self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())
        self.assertFalse(self.validator.is_down)

    def test_set_as_up_sends_no_alerts_if_initial_downtime_alert_not_sent(
            self):
        self.validator._went_down_at = datetime.min  # non-None, i.e. down
        self.validator._initial_downtime_alert_sent = False

        # Confirm that nodes down
        self.assertTrue(self.validator.is_down)

        self.counter_channel.reset()  # ignore previous alerts

        self.validator.set_as_up(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())

        self.assertFalse(self.validator.is_down)

    def test_set_as_up_sends_info_alerts_if_initial_downtime_alert_sent(self):
        self.validator._went_down_at = datetime.min  # non-None, i.e. down
        self.validator._initial_downtime_alert_sent = True

        # Confirm that nodes down
        self.assertTrue(self.validator.is_down)

        self.counter_channel.reset()  # ignore previous alerts

        self.validator.set_as_up(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.info_count, 1)

        self.assertFalse(self.validator.is_down)

    def test_set_as_up_resets_initial_downtime_alert_timing(self):
        self.validator.set_as_down(self.channel_set, self.logger)
        self.validator.set_as_down(self.channel_set, self.logger)
        sleep(self.downtime_initial_alert_delay_with_error_margin.seconds)
        self.validator.set_as_up(self.channel_set, self.logger)

        self.counter_channel.reset()  # ignore previous alerts

        self.validator.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(self.validator.is_down)

        # Without the set_as_up, the set_as_down produces an alert. We can try
        # this out by first setting the validator back to up
        self.validator.set_as_up(self.channel_set, self.logger)

        self.validator.set_as_down(self.channel_set, self.logger)
        self.validator.set_as_down(self.channel_set, self.logger)
        sleep(self.downtime_initial_alert_delay_with_error_margin.seconds)
        # self.validator.set_as_up(self.channel_set, self.logger) # !!

        self.counter_channel.reset()  # ignore previous alerts

        self.validator.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.major_count, 1)
        self.assertTrue(self.validator.is_down)

    def test_set_as_up_resets_downtime_reminder_alert_timing(self):
        self.validator.set_as_down(self.channel_set, self.logger)
        self.validator.set_as_down(self.channel_set, self.logger)
        sleep(self.downtime_initial_alert_delay_with_error_margin.seconds)
        self.validator.set_as_down(self.channel_set, self.logger)
        sleep(self.downtime_reminder_alert_interval_with_error_margin.seconds)
        self.validator.set_as_up(self.channel_set, self.logger)

        self.counter_channel.reset()  # ignore previous alerts

        self.validator.set_as_down(self.channel_set, self.logger)
        self.assertTrue(self.counter_channel.no_alerts())
        self.assertTrue(self.validator.is_down)

        # Without the set_as_up, the set_as_down produces an alert. We can try
        # this out by first setting the validator back to up
        self.validator.set_as_up(self.channel_set, self.logger)

        self.validator.set_as_down(self.channel_set, self.logger)
        self.validator.set_as_down(self.channel_set, self.logger)
        sleep(self.downtime_initial_alert_delay_with_error_margin.seconds)
        self.validator.set_as_down(self.channel_set, self.logger)
        sleep(self.downtime_reminder_alert_interval_with_error_margin.seconds)
        # self.validator.set_as_up(self.channel_set, self.logger) # !!

        self.counter_channel.reset()  # ignore previous alerts

        self.validator.set_as_down(self.channel_set, self.logger)
        self.assertEqual(self.counter_channel.major_count, 1)
        self.assertTrue(self.validator.is_down)

    def test_first_missed_block_increases_missed_blocks_count_but_no_alerts(
            self):
        if TestInternalConf.missed_blocks_danger_boundary != 5:
            self.fail('Expected missed blocks danger boundary to be 5.')

        self.validator.add_missed_block(self.dummy_block_height,
                                        self.dummy_block_time,
                                        self.dummy_missing_validators,
                                        self.channel_set, self.logger)

        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 1)
        self.assertTrue(self.counter_channel.no_alerts())

    def test_four_missed_blocks_increases_missed_blocks_count_and_alerts(self):
        if TestInternalConf.missed_blocks_danger_boundary != 5:
            self.fail('Expected missed blocks danger boundary to be 5.')

        for i in range(4):
            self.validator.add_missed_block(self.dummy_block_height,
                                            self.dummy_block_time,
                                            self.dummy_missing_validators,
                                            self.channel_set, self.logger)

        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 4)
        self.assertEqual(self.counter_channel.info_count, 3)
        # 1 raises no alerts, 2,3,4 raise an info alert

    def test_five_missed_blocks_increases_missed_blocks_count_and_alerts(self):
        if TestInternalConf.missed_blocks_danger_boundary != 5:
            self.fail('Expected missed blocks danger boundary to be 5.')

        for i in range(5):
            self.validator.add_missed_block(self.dummy_block_height,
                                            self.dummy_block_time,
                                            self.dummy_missing_validators,
                                            self.channel_set, self.logger)

        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 5)
        self.assertEqual(self.counter_channel.info_count, 3)
        self.assertEqual(self.counter_channel.minor_count, 1)
        # 1 raises no alerts, 2,3,4 raise an info alert, 5 raises a minor alert

    def test_ten_missed_blocks_increases_missed_blocks_count_and_alerts(self):
        if TestInternalConf.missed_blocks_danger_boundary != 5:
            self.fail('Expected missed blocks danger boundary to be 5.')

        for i in range(10):
            self.validator.add_missed_block(self.dummy_block_height,
                                            self.dummy_block_time,
                                            self.dummy_missing_validators,
                                            self.channel_set, self.logger)

        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 10)
        self.assertEqual(self.counter_channel.info_count, 3)
        self.assertEqual(self.counter_channel.minor_count, 1)
        self.assertEqual(self.counter_channel.major_count, 1)
        # 1 raises no alerts, 2,3,4 raise an info alert,
        # 5 raises a minor alert, 10 raises a major alert

    def test_ten_non_consecutive_missed_blocks_within_time_interval_triggers_major_alert(
            self):
        if TestInternalConf.missed_blocks_danger_boundary != 5:
            self.fail('Expected missed blocks danger boundary to be 5.')

        # Miss 9 non-consecutive blocks
        for i in range(9):
            self.validator.add_missed_block(self.dummy_block_height,
                                            self.dummy_block_time,
                                            self.dummy_missing_validators,
                                            self.channel_set, self.logger)
            self.validator.clear_missed_blocks(self.channel_set, self.logger)

        self.counter_channel.reset()  # ignore previous alerts

        # Miss 10th block within time interval
        self.validator.add_missed_block(self.dummy_block_height,
                                        self.dummy_block_time,
                                        self.dummy_missing_validators,
                                        self.channel_set, self.logger)

        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 1)
        self.assertEqual(self.counter_channel.major_count, 1)

    def test_ten_non_consecutive_missed_blocks_outside_time_interval_does_nothing(
            self):
        if TestInternalConf.missed_blocks_danger_boundary != 5:
            self.fail('Expected missed blocks danger boundary to be 5.')

        # Miss 9 non-consecutive blocks
        for i in range(9):
            self.validator.add_missed_block(self.dummy_block_height,
                                            self.dummy_block_time,
                                            self.dummy_missing_validators,
                                            self.channel_set, self.logger)
            self.validator.clear_missed_blocks(self.channel_set, self.logger)

        self.counter_channel.reset()  # ignore previous alerts

        # Miss 10th block outside of time interval
        self.validator.add_missed_block(
            self.dummy_block_height, self.dummy_block_time_after_time_interval,
            self.dummy_missing_validators, self.channel_set, self.logger)

        self.assertEqual(self.validator.consecutive_blocks_missed_so_far, 1)
        self.assertTrue(self.counter_channel.no_alerts())

    def test_clear_missed_blocks_raises_no_alert_if_was_not_missing_blocks(
            self):
        self.validator.clear_missed_blocks(self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_clear_missed_blocks_raises_info_alert_if_no_longer_missing_blocks_for_one_missed_block(
            self):
        # Miss one block
        self.validator.add_missed_block(self.dummy_block_height,
                                        self.dummy_block_time,
                                        self.dummy_missing_validators,
                                        self.channel_set, self.logger)

        self.counter_channel.reset()  # ignore previous alerts
        self.validator.clear_missed_blocks(self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_clear_missed_blocks_raises_info_alert_if_no_longer_missing_blocks_for_two_missed_blocks(
            self):
        # Miss two blocks
        self.validator.add_missed_block(self.dummy_block_height,
                                        self.dummy_block_time,
                                        self.dummy_missing_validators,
                                        self.channel_set, self.logger)
        self.validator.add_missed_block(self.dummy_block_height,
                                        self.dummy_block_time,
                                        self.dummy_missing_validators,
                                        self.channel_set, self.logger)

        self.counter_channel.reset()  # ignore previous alerts
        self.validator.clear_missed_blocks(self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_voting_power_raises_no_alerts_first_time_round(self):
        self.validator.set_voting_power(0, self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_voting_power_raises_no_alerts_if_voting_power_the_same(self):
        self.validator.set_voting_power(self.dummy_voting_power,
                                        self.channel_set, self.logger)
        self.validator.set_voting_power(self.dummy_voting_power,
                                        self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_voting_power_raises_info_alert_if_voting_power_increases_from_non_0(
            self):
        increased_voting_power = self.dummy_voting_power + 1

        self.validator.set_voting_power(self.dummy_voting_power,
                                        self.channel_set, self.logger)
        self.validator.set_voting_power(increased_voting_power,
                                        self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_voting_power_raises_info_alert_if_voting_power_increases_from_0(
            self):
        # This is just to cover the unique message when power increases from 0

        self.validator.set_voting_power(0, self.channel_set, self.logger)
        self.validator.set_voting_power(self.dummy_voting_power,
                                        self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_voting_power_raises_info_alert_if_voting_power_decreases_to_non_0(
            self):
        decreased_voting_power = self.dummy_voting_power - 1

        self.validator.set_voting_power(self.dummy_voting_power,
                                        self.channel_set, self.logger)
        self.validator.set_voting_power(decreased_voting_power,
                                        self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_voting_power_raises_major_alert_if_voting_power_decreases_to_0(
            self):
        self.validator.set_voting_power(self.dummy_voting_power,
                                        self.channel_set, self.logger)
        self.validator.set_voting_power(0, self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.major_count, 1)

    def test_set_catching_up_raises_minor_alert_first_time_round_if_true(self):
        self.validator.set_catching_up(True, self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.minor_count, 1)

    def test_set_catching_up_raises_no_alerts_first_time_round_if_false(self):
        self.validator.set_catching_up(False, self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_catching_up_raises_no_alerts_if_from_true_to_true(self):
        self.validator.set_catching_up(True, self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_catching_up(True, self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_catching_up_raises_no_alerts_if_from_false_to_false(self):
        self.validator.set_catching_up(False, self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_catching_up(False, self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_catching_up_raises_minor_alert_if_from_false_to_true(self):
        self.validator.set_catching_up(False, self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_catching_up(True, self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.minor_count, 1)

    def test_set_catching_up_raises_info_alert_if_from_true_to_false(self):
        self.validator.set_catching_up(True, self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_catching_up(False, self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_no_of_peers_raises_no_alerts_first_time_round_for_validator(
            self):
        self.validator.set_no_of_peers(self.dummy_no_of_peers,
                                       self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_no_of_peers_raises_no_alerts_first_time_round_for_full_node(
            self):
        self.full_node.set_no_of_peers(self.dummy_no_of_peers,
                                       self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_no_of_peers_raises_no_alerts_if_increase_for_validator_if_outside_safe_range(
            self):
        increased_no_of_peers = self.dummy_no_of_peers + 1

        self.validator.set_no_of_peers(self.dummy_no_of_peers,
                                       self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(increased_no_of_peers, self.channel_set,
                                       self.logger)

        self.assertEqual(self.counter_channel.minor_count, 0)
        self.assertEqual(self.counter_channel.major_count, 0)
        self.assertEqual(self.counter_channel.info_count, 0)
        self.assertEqual(self.counter_channel.error_count, 0)

    def test_set_no_of_peers_raises_info_alert_if_increase_for_full_node_if_inside_danger(
            self):
        self.full_node.set_no_of_peers(
            self.peers_less_than_full_node_danger_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.full_node.set_no_of_peers(
            self.peers_less_than_full_node_danger_boundary + 1,
            self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_no_of_peers_raises_no_alerts_if_increase_for_full_node_if_outside_danger(
            self):
        self.full_node.set_no_of_peers(
            self.peers_more_than_full_node_danger_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.full_node.set_no_of_peers(
            self.peers_more_than_full_node_danger_boundary + 1,
            self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_no_of_peers_raises_info_alert_if_increase_for_full_node_if_inside_to_outside_danger(
            self):
        self.full_node.set_no_of_peers(
            self.peers_less_than_full_node_danger_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.full_node.set_no_of_peers(
            self.peers_more_than_full_node_danger_boundary, self.channel_set,
            self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_no_of_peers_raises_info_alert_if_increase_for_validator_if_inside_danger(
            self):
        self.validator.set_no_of_peers(
            self.peers_less_than_validator_danger_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(
            self.peers_less_than_validator_danger_boundary + 1,
            self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_no_of_peers_raises_info_alert_if_increase_for_validator_if_outside_danger_inside_safe(
            self):
        self.validator.set_no_of_peers(self.peers_validator_danger_boundary,
                                       self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(
            self.peers_validator_danger_boundary + 1, self.channel_set,
            self.logger)

        self.assertEqual(self.counter_channel.info_count, 1)

    def test_set_no_of_peers_raises_info_alert_if_decrease_for_validator_if_outside_danger_inside_safe(
            self):
        self.validator.set_no_of_peers(self.peers_validator_safe_boundary,
                                       self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(self.peers_validator_safe_boundary - 1,
                                       self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.minor_count, 1)

    def test_set_no_of_peers_raises_minor_alert_if_decrease_for_full_node_if_inside_danger(
            self):
        self.full_node.set_no_of_peers(
            self.peers_more_than_full_node_danger_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.full_node.set_no_of_peers(
            self.peers_less_than_full_node_danger_boundary, self.channel_set,
            self.logger)

        self.assertEqual(self.counter_channel.minor_count, 1)

    def test_set_no_of_peers_raises_no_alerts_if_decrease_for_full_node_if_outside_danger(
            self):
        self.full_node.set_no_of_peers(
            self.peers_more_than_full_node_danger_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.full_node.set_no_of_peers(
            self.peers_more_than_full_node_danger_boundary - 1,
            self.channel_set, self.logger)

        self.assertTrue(self.counter_channel.no_alerts())

    def test_set_no_of_peers_raises_major_alert_if_decrease_for_validator_if_inside_danger(
            self):
        self.validator.set_no_of_peers(self.peers_validator_danger_boundary,
                                       self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(
            self.peers_less_than_validator_danger_boundary, self.channel_set,
            self.logger)

        self.assertEqual(self.counter_channel.major_count, 1)

    def test_set_no_of_peers_raises_minor_alert_if_decrease_for_validator_if_outside_danger_inside_safe(
            self):
        self.validator.set_no_of_peers(self.peers_validator_safe_boundary,
                                       self.channel_set, self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(self.peers_validator_safe_boundary - 1,
                                       self.channel_set, self.logger)

        self.assertEqual(self.counter_channel.minor_count, 1)

    def test_set_no_of_peers_raises_no_alerts_if_decrease_for_validator_if_outside_safe(
            self):
        self.validator.set_no_of_peers(
            self.peers_more_than_validator_safe_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(
            self.peers_more_than_validator_safe_boundary - 1, self.channel_set,
            self.logger)

        self.assertEqual(self.counter_channel.minor_count, 0)
        self.assertEqual(self.counter_channel.major_count, 0)
        self.assertEqual(self.counter_channel.info_count, 0)
        self.assertEqual(self.counter_channel.error_count, 0)

    def test_set_no_of_peers_raises_info_alert_if_increase_for_validator_outside_safe_for_first_time(
            self):
        self.validator.set_no_of_peers(
            self.peers_less_than_validator_safe_boundary, self.channel_set,
            self.logger)
        self.counter_channel.reset()  # ignore previous alerts
        self.validator.set_no_of_peers(
            self.peers_more_than_validator_safe_boundary, self.channel_set,
            self.logger)

        self.assertEqual(self.counter_channel.minor_count, 0)
        self.assertEqual(self.counter_channel.major_count, 0)
        self.assertEqual(self.counter_channel.info_count, 1)
        self.assertEqual(self.counter_channel.error_count, 0)
Exemple #8
0
class TestNodeMonitorWithoutRedis(unittest.TestCase):
    def setUp(self) -> None:
        self.logger = logging.getLogger('dummy')
        self.monitor_name = 'testnodemonitor'
        self.counter_channel = CounterChannel(self.logger)
        self.channel_set = ChannelSet([self.counter_channel], TestInternalConf)
        self.node_monitor_max_catch_up_blocks = \
            TestInternalConf.node_monitor_max_catch_up_blocks
        self.redis = None
        self.archive_alerts_disabled = False
        self.data_sources = []
        self.chain = 'testchain'

        self.full_node_name = 'testfullnode'
        self.full_node_api_url = '123.123.123.11:9944'
        self.full_node_consensus_key = "ANDSAdisadjasdaANDAsa"
        self.full_node_tendermint_key = "ASFLNAFIISDANNSDAKKS2313AA"
        self.full_node_entity_public_key = "a98dabsfkjabfkjabsf9j"
        self.full_node_staking_address = "asdsasdsdsaasdsdaswwad"

        self.full_node = Node(
            self.full_node_name, self.full_node_api_url, None,
            NodeType.NON_VALIDATOR_FULL_NODE, '', self.chain, None, True,
            self.full_node_consensus_key, self.full_node_tendermint_key,
            self.full_node_staking_address, self.full_node_entity_public_key,
            TestInternalConf)

        self.full_node_monitor = NodeMonitor(
            self.monitor_name, self.channel_set, self.logger,
            self.node_monitor_max_catch_up_blocks, self.redis, self.full_node,
            self.archive_alerts_disabled, self.data_sources, TestInternalConf)

        self.validator_name = 'testvalidator'
        self.validator_api_url = '13.13.14.11:9944'
        self.validator_consensus_key = "KASDB01923udlakd19sad"
        self.validator_tendermint_key = "ALSFNF9)901jjelakNALKNDLKA"
        self.validator_node_public_key = "DFJGDF8G898fdghb98dg9wetg9we00w"
        self.validator_node_entity_public_key = "s12adsdghas9as0sa9dhnaskdlan"
        self.validator_node_staking_address = "jujuujsmjmsjmaklzsdjsdnanz"

        self.validator = Node(
            self.validator_name, self.validator_api_url, None,
            NodeType.VALIDATOR_FULL_NODE, self.validator_node_public_key,
            self.chain, None, True, self.validator_consensus_key,
            self.validator_tendermint_key, self.validator_node_staking_address,
            self.validator_node_entity_public_key, TestInternalConf)

        self.validator_monitor = NodeMonitor(
            self.monitor_name, self.channel_set, self.logger,
            self.node_monitor_max_catch_up_blocks, self.redis, self.validator,
            self.archive_alerts_disabled, self.data_sources, TestInternalConf)

        self.dummy_last_height_checked = 1000
        self.dummy_bonded_balance = scale_to_giga(5)
        self.dummy_debonding_balance = scale_to_giga(5)
        self.dummy_shares_balance = scale_to_giga(5)
        self.dummy_height_to_check = 1000
        self.dummy_no_of_peers = 100
        self.dummy_is_missing_blocks = False
        self.dummy_active = True
        self.dummy_finalized_block_height = 34535
        self.dummy_api_url_1 = "11.22.33.11:9944"
        self.dummy_api_url_2 = "11.22.33.12:9944"
        self.dummy_api_url_3 = "11.22.33.13:9944"
        self.dummy_api_url_4 = "11.22.33.14:9944"
        self.dummy_api_url_5 = "11.22.33.15:9944"
        self.dummy_node_name_1 = "testnode1"
        self.dummy_node_name_2 = "testnode2"
        self.dummy_node_name_3 = "testnode3"
        self.dummy_node_name_4 = "testnode4"
        self.dummy_node_name_5 = "testnode5"
        self.dummy_validator_node_name_2 = "testvalidatornode2"
        self.dummy_validator_node_name_3 = "testvalidatornode3"
        self.dummy_node_consensus_key_1 = "consensus_key_1"
        self.dummy_node_consensus_key_2 = "consensus_key_2"
        self.dummy_node_consensus_key_3 = "consensus_key_3"
        self.dummy_node_tendermint_key_1 = "consensus_key_1"
        self.dummy_node_tendermint_key_2 = "consensus_key_2"
        self.dummy_node_tendermint_key_3 = "consensus_key_3"
        self.dummy_node_entity_public_key_1 = "entity_key_1"
        self.dummy_node_entity_public_key_2 = "entity_key_2"
        self.dummy_node_entity_public_key_3 = "entity_key_3"
        self.dummy_node_staking_address_1 = "staking-key_1"
        self.dummy_node_staking_address_2 = "staking_key_2"
        self.dummy_node_staking_address_3 = "staking_key_3"
        self.dummy_full_node_1 = Node(
            name=self.dummy_node_name_1,
            api_url=self.dummy_api_url_1,
            prometheus_endpoint=None,
            node_type=NodeType.NON_VALIDATOR_FULL_NODE,
            node_public_key='',
            chain=self.chain,
            redis=None,
            is_archive_node=True,
            consensus_public_key='',
            tendermint_address_key='',
            staking_address='',
            entity_public_key='',
            internal_conf=TestInternalConf)
        self.dummy_full_node_2 = Node(
            name=self.dummy_node_name_2,
            api_url=self.dummy_api_url_2,
            prometheus_endpoint=None,
            node_type=NodeType.NON_VALIDATOR_FULL_NODE,
            node_public_key='',
            chain=self.chain,
            redis=None,
            is_archive_node=True,
            consensus_public_key='',
            tendermint_address_key='',
            staking_address='',
            entity_public_key='',
            internal_conf=TestInternalConf)
        self.dummy_full_node_3 = Node(
            name=self.dummy_node_name_3,
            api_url=self.dummy_api_url_3,
            prometheus_endpoint=None,
            node_type=NodeType.NON_VALIDATOR_FULL_NODE,
            node_public_key='',
            chain=self.chain,
            redis=None,
            is_archive_node=True,
            consensus_public_key='',
            tendermint_address_key='',
            staking_address='',
            entity_public_key='',
            internal_conf=TestInternalConf)
        self.dummy_full_node_4 = Node(
            name=self.dummy_node_name_5,
            api_url=self.dummy_api_url_5,
            prometheus_endpoint=None,
            node_type=NodeType.NON_VALIDATOR_FULL_NODE,
            node_public_key='',
            chain=self.chain,
            redis=None,
            is_archive_node=False,
            consensus_public_key='',
            tendermint_address_key='',
            staking_address='',
            entity_public_key='',
            internal_conf=TestInternalConf)

        self.dummy_take_event_owner = {
            "escrow": {
                "take": {
                    "owner": self.dummy_node_entity_public_key_1,
                    "tokens": "2000000000"
                }
            }
        }

        self.dummy_validator_node_1 = Node(
            name=self.dummy_node_name_4,
            api_url=self.dummy_api_url_4,
            prometheus_endpoint=None,
            node_type=NodeType.VALIDATOR_FULL_NODE,
            node_public_key=self.validator_node_public_key,
            chain=self.chain,
            redis=None,
            is_archive_node=True,
            consensus_public_key=self.dummy_node_consensus_key_1,
            tendermint_address_key=self.dummy_node_tendermint_key_1,
            staking_address=self.dummy_node_staking_address_1,
            entity_public_key=self.dummy_node_entity_public_key_1,
            internal_conf=TestInternalConf)

        self.dummy_validator_node_2 = Node(
            name=self.dummy_validator_node_name_2,
            api_url=self.dummy_api_url_4,
            prometheus_endpoint=None,
            node_type=NodeType.VALIDATOR_FULL_NODE,
            node_public_key=self.validator_node_public_key,
            chain=self.chain,
            redis=None,
            is_archive_node=True,
            consensus_public_key=self.dummy_node_consensus_key_2,
            tendermint_address_key=self.dummy_node_tendermint_key_2,
            staking_address=self.dummy_node_staking_address_2,
            entity_public_key=self.dummy_node_entity_public_key_2,
            internal_conf=TestInternalConf)

        self.dummy_validator_node_3 = Node(
            name=self.dummy_validator_node_name_3,
            api_url=self.dummy_api_url_4,
            prometheus_endpoint=None,
            node_type=NodeType.VALIDATOR_FULL_NODE,
            node_public_key=self.validator_node_public_key,
            chain=self.chain,
            redis=None,
            is_archive_node=True,
            consensus_public_key=self.dummy_node_consensus_key_3,
            tendermint_address_key=self.dummy_node_tendermint_key_3,
            staking_address=self.dummy_node_staking_address_3,
            entity_public_key=self.dummy_node_entity_public_key_3,
            internal_conf=TestInternalConf)

    def test_indirect_monitoring_data_sources_field_set_correctly(
            self) -> None:
        self.data_sources = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_full_node_4,
            self.dummy_validator_node_1
        ]
        test_monitor = NodeMonitor(self.monitor_name, self.channel_set,
                                   self.logger,
                                   self.node_monitor_max_catch_up_blocks,
                                   self.redis, self.validator,
                                   self.archive_alerts_disabled,
                                   self.data_sources, TestInternalConf)

        self.assertEqual(test_monitor.indirect_monitoring_data_sources,
                         self.data_sources)

    def test_archive_monitoring_data_sources_field_set_correctly(self) -> None:
        self.data_sources = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_full_node_4,
            self.dummy_validator_node_1
        ]
        test_monitor = NodeMonitor(self.monitor_name, self.channel_set,
                                   self.logger,
                                   self.node_monitor_max_catch_up_blocks,
                                   self.redis, self.validator,
                                   self.archive_alerts_disabled,
                                   self.data_sources, TestInternalConf)
        expected_result = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_validator_node_1
        ]

        self.assertEqual(test_monitor.archive_monitoring_data_sources,
                         expected_result)

    def test_is_catching_up_false_by_default(self) -> None:
        self.assertFalse(self.validator_monitor.is_catching_up())

    def test_is_indirect_monitoring_disabled_true_if_no_data_sources(
            self) -> None:
        self.assertTrue(self.validator_monitor.indirect_monitoring_disabled)

    def test_is_indirect_monitoring_disabled_false_if_data_sources_given(
            self) -> None:
        self.data_sources = [self.dummy_full_node_1, self.dummy_full_node_2]
        test_monitor = NodeMonitor(self.monitor_name, self.channel_set,
                                   self.logger,
                                   self.node_monitor_max_catch_up_blocks,
                                   self.redis, self.validator,
                                   self.archive_alerts_disabled,
                                   self.data_sources, TestInternalConf)

        self.assertFalse(test_monitor.indirect_monitoring_disabled)

    def test_last_height_checked_NONE_by_default(self) -> None:
        self.assertEqual(NONE, self.validator_monitor.last_height_checked)

    def test_no_live_archive_node_alert_sent_false_by_default(self) -> None:
        self.assertFalse(
            self.validator_monitor.no_live_archive_node_alert_sent)

    @patch(PING_NODE_FUNCTION, return_value=None)
    @patch(GET_WEB_SOCKETS_FUNCTION,
           return_value=["testnode1", "testnode2", "testnode3", "testnode4"])
    def test_data_source_chooses_an_online_full_node_connected_to_the_API(
            self, mock_get_web_sockets, _) -> None:
        self.dummy_full_node_1.set_as_down(self.channel_set, self.logger)
        self.dummy_validator_node_1.set_as_down(self.channel_set, self.logger)
        self.validator_monitor._indirect_monitoring_data_sources = [
            self.dummy_full_node_1, self.dummy_validator_node_1,
            self.dummy_full_node_2, self.dummy_full_node_3
        ]

        mock_get_web_sockets.return_value = [
            self.dummy_node_name_1, self.dummy_node_name_4,
            self.dummy_node_name_3, self.dummy_node_name_5
        ]

        node = self.validator_monitor.data_source_indirect

        self.assertEqual(node.name, self.dummy_node_name_3)

    @patch(PING_NODE_FUNCTION, return_value=None)
    @patch(GET_WEB_SOCKETS_FUNCTION,
           return_value=["testnode1", "testnode2", "testnode3", "testnode4"])
    def test_data_source_chooses_an_online_validator_node_connected_to_the_API(
            self, mock_get_web_sockets, _) -> None:

        self.dummy_full_node_1.set_as_down(self.channel_set, self.logger)
        self.dummy_full_node_2.set_as_down(self.channel_set, self.logger)

        self.validator_monitor._indirect_monitoring_data_sources = [
            self.dummy_full_node_1, self.dummy_validator_node_1,
            self.dummy_full_node_2, self.dummy_full_node_3
        ]

        mock_get_web_sockets.return_value = [
            self.dummy_node_name_1, self.dummy_node_name_4,
            self.dummy_node_name_3, self.dummy_node_name_5
        ]

        node = self.validator_monitor.data_source_indirect

        self.assertEqual(node.name, self.dummy_node_name_4)

    @patch(GET_WEB_SOCKETS_FUNCTION)
    def test_data_source_indirect_raises_exception_if_no_node_is_eligible_for_choosing(
            self, mock_get_web_sockets) -> None:
        self.dummy_full_node_1.set_as_down(self.channel_set, self.logger)
        self.dummy_full_node_3.set_as_down(self.channel_set, self.logger)

        self.validator_monitor._indirect_monitoring_data_sources = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_validator_node_1
        ]

        mock_get_web_sockets.return_value = [self.dummy_api_url_1]

        try:
            _ = self.validator_monitor.data_source_indirect
            self.fail('Expected NoLiveNodeConnectedWithAnApiServerException'
                      ' exception to be thrown.')
        except NoLiveNodeConnectedWithAnApiServerException:
            pass

    @patch(PING_NODE_FUNCTION, return_value=None)
    @patch(GET_WEB_SOCKETS_FUNCTION, return_value=["testnode1", "testnode5"])
    def test_data_source_archive_chooses_an_online_archive_full_node_connected_to_the_API(
            self, mock_get_web_sockets, _) -> None:
        self.dummy_full_node_1.set_as_down(self.channel_set, self.logger)
        self.dummy_full_node_3.set_as_down(self.channel_set, self.logger)
        self.validator_monitor._archive_monitoring_data_sources = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_validator_node_1,
            self.dummy_full_node_4
        ]

        mock_get_web_sockets.return_value = [
            self.dummy_node_name_1, self.dummy_node_name_5
        ]
        node = self.validator_monitor.data_source_archive

        self.assertEqual(node.name, self.dummy_node_name_5)

    @patch(PING_NODE_FUNCTION, return_value=None)
    @patch(GET_WEB_SOCKETS_FUNCTION,
           return_value=["testnode1", "testnode4", "testnode5"])
    def test_data_source_archive_chooses_an_online_archive_validator_node_connected_to_the_API(
            self, mock_get_web_sockets, _) -> None:

        self.dummy_full_node_1.set_as_down(self.channel_set, self.logger)
        self.dummy_full_node_3.set_as_down(self.channel_set, self.logger)
        self.dummy_full_node_2.set_as_down(self.channel_set, self.logger)

        self.validator_monitor._archive_monitoring_data_sources = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_validator_node_1,
            self.dummy_full_node_4
        ]

        mock_get_web_sockets.return_value = [
            self.dummy_node_name_1, self.dummy_node_name_4,
            self.dummy_node_name_5
        ]

        node = self.validator_monitor.data_source_archive

        self.assertEqual(node.name, self.dummy_node_name_4)

    @patch(GET_FINALIZED_HEAD_FUNCTION, return_value={"height": "523686"})
    @patch(PING_NODE_FUNCTION, return_value="pong")
    @patch(GET_SYNCING_FUNCTION, return_value="true")
    @patch(GET_PEERS_FUNCTION, return_value="92")
    def test_monitor_direct_sets_node_state_to_retrieved_data(
            self, _1, _2, _3, _4) -> None:
        self.validator_monitor.monitor_direct()

        self.assertFalse(self.validator.is_down)
        self.assertFalse(self.validator.is_syncing)
        self.assertEqual(self.validator.no_of_peers, 92)
        self.assertEqual(self.validator.finalized_block_height, 523686)

    @patch(GET_FINALIZED_HEAD_FUNCTION, return_value={"height": "523686"})
    @patch(PING_NODE_FUNCTION, return_value="pong")
    @patch(GET_SYNCING_FUNCTION, return_value="true")
    @patch(GET_PEERS_FUNCTION, return_value="92")
    @patch(PING_NODE_FUNCTION, return_value=None)
    def test_monitor_direct_connects_node_to_api_if_monitoring_successful(
            self, _1, _2, _3_, _4, _5) -> None:
        self.validator.disconnect_from_api(self.channel_set, self.logger)
        self.assertFalse(self.validator.is_connected_to_api_server)
        self.counter_channel.reset()
        self.validator_monitor.monitor_direct()

        self.assertTrue(self.validator.is_connected_to_api_server)

    @patch(GET_WEB_SOCKETS_FUNCTION)
    def test_data_source_archive_raises_exception_if_no_archive_node_is_eligible_for_choosing(
            self, mock_get_web_sockets) -> None:
        self.dummy_full_node_1.set_as_down(self.channel_set, self.logger)
        self.dummy_full_node_3.set_as_down(self.channel_set, self.logger)
        self.dummy_validator_node_1.set_as_down(self.channel_set, self.logger)
        self.validator_monitor._archive_monitoring_data_sources = [
            self.dummy_full_node_1, self.dummy_full_node_2,
            self.dummy_full_node_3, self.dummy_validator_node_1
        ]
        mock_get_web_sockets.return_value = [
            self.dummy_api_url_1, self.dummy_api_url_5
        ]

        try:
            _ = self.validator_monitor.data_source_archive
            self.fail('Expected '
                      'NoLiveArchiveNodeConnectedWithAnApiServerException'
                      ' exception to be thrown.')
        except NoLiveArchiveNodeConnectedWithAnApiServerException:
            pass

    def test_status_returns_as_expected_for_validator_monitor(self) -> None:
        self.validator_monitor._last_height_checked = \
            self.dummy_last_height_checked
        self.validator._bonded_balance = self.dummy_bonded_balance
        self.validator._debonding_balance = self.dummy_debonding_balance
        self.validator._shares_balance = self.dummy_shares_balance
        self.validator._no_of_peers = self.dummy_no_of_peers
        self.validator._active = self.dummy_active
        self.validator._finalized_block_height = \
            self.dummy_finalized_block_height
        self.validator._is_missing_blocks = self.dummy_is_missing_blocks

        expected_output = "bonded_balance={}, debonding_balance={}, " \
                          "shares_balance={}, "\
                          "is_syncing=False, " \
                          "no_of_peers={}, active={}, " \
                          "finalized_block_height={}, " \
                          "is_missing_blocks={}, " \
                          "last_height_checked={}".format(
            self.dummy_bonded_balance,
            self.dummy_debonding_balance,
            self.dummy_shares_balance,
            self.dummy_no_of_peers,
            self.dummy_active,
            self.dummy_finalized_block_height,
            self.dummy_is_missing_blocks,
            self.dummy_last_height_checked)
        self.assertEqual(expected_output, self.validator_monitor.status())

    def test_status_returns_as_expected_for_full_node_monitor(self) -> None:
        self.full_node._no_of_peers = self.dummy_no_of_peers
        self.full_node._finalized_block_height = \
            self.dummy_finalized_block_height

        expected_output = "bonded_balance={}, debonding_balance={}, " \
                          "shares_balance={}, " \
                          "is_syncing=False, " \
                          "no_of_peers={}, active={}, " \
                          "finalized_block_height={}, " \
                          "is_missing_blocks=False" \
                          .format(
            None, None, None, self.dummy_no_of_peers, None,
            self.dummy_finalized_block_height)

        self.assertEqual(expected_output, self.full_node_monitor.status())

    @patch(GET_FINALIZED_HEAD_FUNCTION, return_value={"height": "523686"})
    @patch(PING_NODE_FUNCTION, return_value="pong")
    @patch(GET_SYNCING_FUNCTION, return_value="true")
    @patch(GET_PEERS_FUNCTION, return_value="92")
    def test_monitor_direct_sets_node_state_to_retrieved_data(
            self, _1, _2, _3, _4) -> None:
        self.validator_monitor.monitor_direct()

        self.assertFalse(self.validator.is_down)
        self.assertFalse(self.validator.is_syncing)
        self.assertEqual(self.validator.no_of_peers, 92)
        self.assertEqual(self.validator.finalized_block_height, 523686)

    @patch(GET_FINALIZED_HEAD_FUNCTION, return_value={"height": "523686"})
    @patch(PING_NODE_FUNCTION, return_value="pong")
    @patch(GET_SYNCING_FUNCTION, return_value="true")
    @patch(GET_PEERS_FUNCTION, return_value="92")
    def test_monitor_direct_sets_API_as_up_if_monitoring_successful(
            self, _1, _2, _3, _4) -> None:
        self.validator_monitor.monitor_direct()

        self.assertFalse(self.validator_monitor.data_wrapper.is_api_down)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    @patch(GET_STAKING_ACCOUNT_FUNCTION,
           return_value={
               "escrow": {
                   "active": {
                       "balance": "9999999999993"
                   },
                   "debonding": {
                       "balance": "9999999999993"
                   }
               }
           })
    @patch(GET_SIGNED_BLOCKS_FUNCTION,
           return_value={
               "signatures": [
                   {
                       "validator_address":
                       "06140E5B1FE5D72BAA12AED7815A7E19BC7BDABA"
                   },
                   {
                       "validator_address":
                       "7C87340EFE4BE695E80099AE3B0CAE545381925D"
                   },
                   {
                       "validator_address":
                       "0D73013810841C092B8ACB1A9646F412B62EE14C"
                   },
               ]
           })
    @patch(GET_BLOCK_HEADER_AT_HEIGHT_FUNCTION,
           return_value={"height": "55666"})
    @patch(GET_CONSENSUS_BLOCK_FUNCTION, return_value={"height":"55666", \
    "meta": "asndiasbdiabsidjbasjiaslnlasndlkandlkasldknasdlknaskbda"})
    @patch(GET_SESSION_VALIDATORS_FUNCTION, return_value=[])
    @patch(GET_STAKING_DELEGATIONS_INFO_FUNCTION,
           return_value={
               "Xq2d4D43YmBdUAOd3q6A0n1kaHUY766RE24xznk1Sgc=": {
                   "shares": "27681494143232"
               }
           })
    def test_monitor_indirect_sets_API_up_when_validator_indirect_monitoring_succesfull(
            self, _1, _2, _3, _4, _5, _6, _7) -> None:
        with mock.patch(DATA_SOURCE_INDIRECT_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1
            self.validator_monitor.last_data_source_used = \
                self.dummy_full_node_1

            self.validator_monitor._archive_alerts_disabled = True
            self.validator_monitor.monitor_indirect()
            self.assertFalse(self.validator_monitor.data_wrapper.is_api_down)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    @patch(GET_STAKING_ACCOUNT_FUNCTION,
           return_value={
               "escrow": {
                   "active": {
                       "balance": "9999999999993"
                   },
                   "debonding": {
                       "balance": "9999999999993"
                   }
               }
           })
    @patch(GET_SIGNED_BLOCKS_FUNCTION,
           return_value={
               "signatures": [
                   {
                       "validator_address":
                       "06140E5B1FE5D72BAA12AED7815A7E19BC7BDABA"
                   },
                   {
                       "validator_address":
                       "7C87340EFE4BE695E80099AE3B0CAE545381925D"
                   },
                   {
                       "validator_address":
                       "0D73013810841C092B8ACB1A9646F412B62EE14C"
                   },
               ]
           })
    @patch(GET_BLOCK_HEADER_AT_HEIGHT_FUNCTION,
           return_value={"height": "55666"})
    @patch(GET_CONSENSUS_BLOCK_FUNCTION, return_value={"height":"55666", \
    "meta": "asndiasbdiabsidjbasjiaslnlasndlkandlkasldknasdlknaskbda"})
    @patch(GET_SESSION_VALIDATORS_FUNCTION, return_value=[])
    @patch(GET_STAKING_DELEGATIONS_INFO_FUNCTION,
           return_value={
               "Xq2d4D43YmBdUAOd3q6A0n1kaHUY766RE24xznk1Sgc=": {
                   "shares": "27681494143232"
               }
           })
    def test_monitor_indirect_connects_data_source_with_api_if_monitoring_succesfull(
            self, _1, _2, _3, _4, _5, _6, _7) -> None:
        with mock.patch(DATA_SOURCE_INDIRECT_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1
            self.validator_monitor.last_data_source_used = \
                self.dummy_full_node_1
            self.validator_monitor.last_data_source_used.disconnect_from_api(
                self.channel_set, self.logger)

            self.validator_monitor._archive_alerts_disabled = True
            self.validator_monitor.monitor_indirect()
            self.assertTrue(self.validator_monitor.last_data_source_used.
                            is_connected_to_api_server)

    def test_monitor_indirect_full_node_sets_values_as_expected(self) -> None:
        self.full_node_monitor.monitor_indirect()

        self.assertEqual(self.full_node_monitor.node.bonded_balance, 0)
        self.assertFalse(self.full_node_monitor.node.is_active)

    @patch(GET_STAKING_DELEGATIONS_INFO_FUNCTION,
           return_value={
               "Xq2d4D43YmBdUAOd3q6A0n1kaHUY766RE24xznk1Sgc=": {
                   "shares": "27681494143232"
               }
           })
    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    @patch(GET_STAKING_ACCOUNT_FUNCTION,
           return_value={
               "escrow": {
                   "active": {
                       "balance": "9999999999993"
                   },
                   "debonding": {
                       "balance": "9999999999993"
                   }
               }
           })
    @patch(GET_SIGNED_BLOCKS_FUNCTION,
           return_value={
               "signatures": [
                   {
                       "validator_address":
                       "06140E5B1FE5D72BAA12AED7815A7E19BC7BDABA"
                   },
                   {
                       "validator_address":
                       "7C87340EFE4BE695E80099AE3B0CAE545381925D"
                   },
                   {
                       "validator_address":
                       "0D73013810841C092B8ACB1A9646F412B62EE14C"
                   },
               ]
           })
    @patch(GET_BLOCK_HEADER_AT_HEIGHT_FUNCTION,
           return_value={"height": "55666"})
    @patch(GET_CONSENSUS_BLOCK_FUNCTION, return_value={"height":"55666", \
    "meta": "asndiasbdiabsidjbasjiaslnlasndlkandlkasldknasdlknaskbda"})
    @patch(GET_SESSION_VALIDATORS_FUNCTION, return_value=[])
    def test_monitor_indirect_validator_calls_monitor_archive_if_not_disabled(
            self, _1, _2, _3, _4, _5, _6, _7) -> None:
        with mock.patch(DATA_SOURCE_INDIRECT_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1
            self.validator_monitor._monitor_archive_state = MagicMock()

            self.validator_monitor._monitor_indirect_validator()
            self.assertEqual(
                self.validator_monitor._monitor_archive_state.call_count, 1)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    def test_monitor_archive_sets_catching_up_true_if_more_than_2_blocks_late(
            self, _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1

            # To make the monitor catch up
            archive_node_height = self.dummy_finalized_block_height + 4

            self.dummy_full_node_1.update_finalized_block_height(
                archive_node_height, self.logger, self.channel_set)
            self.validator_monitor._last_height_checked = \
                self.dummy_finalized_block_height
            self.validator_monitor._monitor_archive_state()

            self.assertTrue(self.validator_monitor._monitor_is_catching_up)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    def test_monitor_archive_sets_catching_up_false_if_less_than_2_blocks_late(
            self, _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1
            self.dummy_full_node_1.update_finalized_block_height(
                self.dummy_finalized_block_height, self.logger,
                self.channel_set)

            self.validator_monitor._monitor_archive_state()
            self.assertFalse(self.validator_monitor._monitor_is_catching_up)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    def test_monitor_archive_sets_catching_up_false_if_2_blocks_late(
            self, _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1
            self.dummy_full_node_1.update_finalized_block_height(
                self.dummy_finalized_block_height, self.logger,
                self.channel_set)
            self.validator_monitor._last_height_checked = \
                self.dummy_finalized_block_height - 2
            self.validator_monitor._monitor_archive_state()

            self.assertFalse(self.validator_monitor._monitor_is_catching_up)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    def test_monitor_archive_raises_info_alert_if_monitoring_round_successful_and_error_alert_sent(
            self, _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1

            self.validator_monitor._no_live_archive_node_alert_sent = True
            self.validator_monitor._monitor_archive_state()

            self.assertEqual(self.counter_channel.info_count, 1)
            self.assertFalse(
                self.validator_monitor._no_live_archive_node_alert_sent)
            self.assertIsInstance(self.counter_channel.latest_alert,
                                  FoundLiveArchiveNodeAgainAlert)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value=None)
    def test_monitor_archive_no_alerts_if_monitoring_round_successful_error_alert_not_sent_previously(
            self, _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1

            self.validator_monitor._monitor_archive_state()

            self.assertTrue(self.counter_channel.no_alerts())
            self.assertFalse(
                self.validator_monitor._no_live_archive_node_alert_sent)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value= \
            [{"escrow" : {"take" :
            {"owner" : "entity_key_1",
             "tokens": "2000000000"}}}])
    def test_monitor_archive_if_a_slashing_event_occurs_for_1_node(self,
                                                                   _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1

            self.data_sources = [self.dummy_validator_node_1]

            test_monitor = NodeMonitor(self.monitor_name, self.channel_set,
                                       self.logger,
                                       self.node_monitor_max_catch_up_blocks,
                                       self.redis, self.validator,
                                       self.archive_alerts_disabled,
                                       self.data_sources, TestInternalConf)

            # To make the monitor catch up
            archive_node_height = self.dummy_finalized_block_height + 4

            self.dummy_full_node_1.update_finalized_block_height(
                archive_node_height, self.logger, self.channel_set)

            test_monitor._last_height_checked = \
                self.dummy_finalized_block_height

            test_monitor._monitor_archive_state()

            self.assertEqual(self.counter_channel.critical_count, 1)

    @patch(GET_STAKING_EVENTS_BY_HEIGHT_FUNCTION, return_value= \
            [{"escrow" : {"take" :
            {"owner" : "entity_key_1",
             "tokens": "2000000000"}}},
             {"escrow" : {"take" :
            {"owner" : "entity_key_2",
             "tokens": "2000000000"}}},
             {"escrow" : {"take" :
            {"owner" : "entity_key_3",
             "tokens": "2000000000"}}}])
    def test_monitor_archive_if_a_slashing_event_occurs_for_3_nodes(
            self, _1) -> None:
        with mock.patch(DATA_SOURCE_ARCHIVE_PATH, new_callable=PropertyMock) \
                as mock_data_source_indirect:
            mock_data_source_indirect.return_value = self.dummy_full_node_1

            self.data_sources = [
                self.dummy_validator_node_1, self.dummy_validator_node_2,
                self.dummy_validator_node_3
            ]

            test_monitor = NodeMonitor(self.monitor_name, self.channel_set,
                                       self.logger,
                                       self.node_monitor_max_catch_up_blocks,
                                       self.redis, self.validator,
                                       self.archive_alerts_disabled,
                                       self.data_sources, TestInternalConf)

            # To make the monitor catch up
            archive_node_height = self.dummy_finalized_block_height + 4

            self.dummy_full_node_1.update_finalized_block_height(
                archive_node_height, self.logger, self.channel_set)

            test_monitor._last_height_checked = \
                self.dummy_finalized_block_height

            test_monitor._monitor_archive_state()

            self.assertEqual(self.counter_channel.critical_count, 3)