Example #1
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())
Example #2
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)
Example #3
0
class BlockchainMonitor(Monitor):
    def __init__(self,
                 monitor_name: str,
                 blockchain: Blockchain,
                 channels: ChannelSet,
                 logger: logging.Logger,
                 redis: Optional[RedisApi],
                 data_sources: List[Node],
                 polkadot_api_endpoint: str,
                 internal_conf: InternalConfig = InternalConf):
        super().__init__(monitor_name, channels, logger, redis, internal_conf)

        self._blockchain = blockchain
        self.data_sources = data_sources
        self._data_wrapper = PolkadotApiWrapper(logger, polkadot_api_endpoint)

        self.last_data_source_used = None

        self._redis_alive_key_timeout = \
            self._internal_conf.redis_blockchain_monitor_alive_key_timeout

    @property
    def data_wrapper(self) -> PolkadotApiWrapper:
        return self._data_wrapper

    @property
    def blockchain(self) -> Blockchain:
        return self._blockchain

    def save_state(self) -> None:
        # If Redis is enabled save the current time, indicating that the monitor
        # was alive at this time.
        if self.redis_enabled:
            self.logger.debug('Saving %s state', self._monitor_name)

            # Set alive key (to be able to query latest update from Telegram)
            key = Keys.get_blockchain_monitor_alive(self.monitor_name)
            until = timedelta(seconds=self._redis_alive_key_timeout)
            self.redis.set_for(key, str(datetime.now().timestamp()), until)

    @property
    def data_source(self) -> Node:
        nodes_connected_to_an_api = \
            self.data_wrapper.get_web_sockets_connected_to_an_api()
        # Get one of the nodes to use as data source
        for n in self.data_sources:
            if n.ws_url in nodes_connected_to_an_api and not n.is_down:
                self.last_data_source_used = n
                self._data_wrapper.ping_node(n.ws_url)
                return n
        raise NoLiveNodeConnectedWithAnApiServerException()

    def status(self) -> str:
        return self.blockchain.status()

    def _check_for_new_referendums(self, new_referendum_count: int) -> None:

        if self.blockchain.referendum_count is None:
            self.blockchain.set_referendum_count(new_referendum_count,
                                                 self.channels, self.logger)
            return

        while self.blockchain.referendum_count < new_referendum_count:
            referendum_info = self._data_wrapper.get_referendum_info_of(
                self.data_source.ws_url, self.blockchain.referendum_count)
            self.blockchain.set_referendum_count(
                self.blockchain.referendum_count + 1, self.channels,
                self.logger, referendum_info)

    def monitor(self) -> None:
        # Get new data.
        new_referendum_count = self._data_wrapper.get_referendum_count(
            self.data_source.ws_url)
        new_council_prop_count = self._data_wrapper.get_council_proposal_count(
            self.data_source.ws_url)
        new_public_prop_count = self._data_wrapper.get_public_proposal_count(
            self.data_source.ws_url)
        session_validators = self._data_wrapper.get_session_validators(
            self.data_source.ws_url)
        new_validator_set_size = len(session_validators)

        # Check for referendums
        self._logger.debug('%s referendum_count: %s', self.blockchain,
                           new_referendum_count)
        self._check_for_new_referendums(new_referendum_count)

        # Set council prop count
        self._logger.debug('%s council_prop_count: %s', self.blockchain,
                           new_council_prop_count)
        self.blockchain.set_council_prop_count(new_council_prop_count,
                                               self.channels, self.logger)

        # Set public prop count
        self._logger.debug('%s public_prop_count: %s', self.blockchain,
                           new_public_prop_count)
        self.blockchain.set_public_prop_count(new_public_prop_count,
                                              self.channels, self.logger)

        # Set validator set size
        self._logger.debug('%s validator_set_size: %s', self.blockchain,
                           new_validator_set_size)
        self.blockchain.set_validator_set_size(new_validator_set_size,
                                               self.channels, self.logger)

        # Set API as up
        self.data_wrapper.set_api_as_up(self.monitor_name, self.channels)

        self.logger.info('%s status: %s', self._monitor_name, self.status())