Skip to content

Commit f4a9c48

Browse files
authored
[Tests] AsyncSubtensor (Part 3) (#2409)
* added tests from `AsyncSubtensor.get_netuids_for_hotkey` until `AsyncSubtensor.neurons_lite` * ruff
1 parent ff17b6a commit f4a9c48

File tree

2 files changed

+334
-3
lines changed

2 files changed

+334
-3
lines changed

bittensor/core/async_subtensor.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -677,9 +677,6 @@ async def get_hyperparameter(
677677
reuse_block_hash=reuse_block,
678678
)
679679

680-
if result is None:
681-
return None
682-
683680
return result
684681

685682
async def filter_netuids_by_registered_hotkeys(

tests/unit_tests/test_async_subtensor.py

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,3 +629,337 @@ async def test_get_total_stake_for_hotkey(subtensor, mocker):
629629
)
630630
mocked_substrate_query_multiple.assert_called_once()
631631
assert result == {0: async_subtensor.Balance(1)}
632+
633+
634+
@pytest.mark.parametrize(
635+
"records, response",
636+
[([(0, True), (1, False), (3, False), (3, True)], [0, 3]), ([], [])],
637+
ids=["with records", "empty-records"],
638+
)
639+
@pytest.mark.asyncio
640+
async def test_get_netuids_for_hotkey(subtensor, mocker, records, response):
641+
"""Tests get_netuids_for_hotkey method."""
642+
# Preps
643+
fake_result = mocker.AsyncMock(autospec=list)
644+
fake_result.records = records
645+
fake_result.__aiter__.return_value = iter(records)
646+
647+
mocked_substrate_query_map = mocker.AsyncMock(
648+
autospec=async_subtensor.AsyncSubstrateInterface.query_map,
649+
return_value=fake_result,
650+
)
651+
652+
subtensor.substrate.query_map = mocked_substrate_query_map
653+
fake_hotkey_ss58 = "hotkey_58"
654+
fake_block_hash = None
655+
656+
# Call
657+
result = await subtensor.get_netuids_for_hotkey(
658+
hotkey_ss58=fake_hotkey_ss58, block_hash=fake_block_hash, reuse_block=True
659+
)
660+
661+
# Assertions
662+
mocked_substrate_query_map.assert_called_once_with(
663+
module="SubtensorModule",
664+
storage_function="IsNetworkMember",
665+
params=[fake_hotkey_ss58],
666+
block_hash=fake_block_hash,
667+
reuse_block_hash=True,
668+
)
669+
assert result == response
670+
671+
672+
@pytest.mark.asyncio
673+
async def test_subnet_exists(subtensor, mocker):
674+
"""Tests subnet_exists method ."""
675+
# Preps
676+
fake_netuid = 1
677+
fake_block_hash = "block_hash"
678+
fake_reuse_block_hash = True
679+
680+
mocked_substrate_query = mocker.AsyncMock(
681+
autospec=async_subtensor.AsyncSubstrateInterface.query
682+
)
683+
subtensor.substrate.query = mocked_substrate_query
684+
685+
# Call
686+
result = await subtensor.subnet_exists(
687+
netuid=fake_netuid,
688+
block_hash=fake_block_hash,
689+
reuse_block=fake_reuse_block_hash,
690+
)
691+
692+
# Asserts
693+
mocked_substrate_query.assert_called_once_with(
694+
module="SubtensorModule",
695+
storage_function="NetworksAdded",
696+
params=[fake_netuid],
697+
block_hash=fake_block_hash,
698+
reuse_block_hash=fake_reuse_block_hash,
699+
)
700+
assert result == mocked_substrate_query.return_value
701+
702+
703+
@pytest.mark.asyncio
704+
async def test_get_hyperparameter_happy_path(subtensor, mocker):
705+
"""Tests get_hyperparameter method with happy path."""
706+
# Preps
707+
fake_param_name = "param_name"
708+
fake_netuid = 1
709+
fake_block_hash = "block_hash"
710+
fake_reuse_block_hash = True
711+
712+
# kind of fake subnet exists
713+
mocked_subtensor_subnet_exists = mocker.AsyncMock(return_value=True)
714+
subtensor.subnet_exists = mocked_subtensor_subnet_exists
715+
716+
mocked_substrate_query = mocker.AsyncMock(
717+
autospec=async_subtensor.AsyncSubstrateInterface.query
718+
)
719+
subtensor.substrate.query = mocked_substrate_query
720+
721+
# Call
722+
result = await subtensor.get_hyperparameter(
723+
param_name=fake_param_name,
724+
netuid=fake_netuid,
725+
block_hash=fake_block_hash,
726+
reuse_block=fake_reuse_block_hash,
727+
)
728+
729+
# Assertions
730+
mocked_subtensor_subnet_exists.assert_called_once()
731+
mocked_substrate_query.assert_called_once_with(
732+
module="SubtensorModule",
733+
storage_function=fake_param_name,
734+
params=[fake_netuid],
735+
block_hash=fake_block_hash,
736+
reuse_block_hash=fake_reuse_block_hash,
737+
)
738+
assert result == mocked_substrate_query.return_value
739+
740+
741+
@pytest.mark.asyncio
742+
async def test_get_hyperparameter_if_subnet_does_not_exist(subtensor, mocker):
743+
"""Tests get_hyperparameter method if subnet does not exist."""
744+
# Preps
745+
# kind of fake subnet doesn't exist
746+
mocked_subtensor_subnet_exists = mocker.AsyncMock(return_value=False)
747+
subtensor.subnet_exists = mocked_subtensor_subnet_exists
748+
749+
mocked_substrate_query = mocker.AsyncMock(
750+
autospec=async_subtensor.AsyncSubstrateInterface.query
751+
)
752+
subtensor.substrate.query = mocked_substrate_query
753+
754+
# Call
755+
result = await subtensor.get_hyperparameter(mocker.Mock(), mocker.Mock())
756+
757+
# Assertions
758+
mocked_subtensor_subnet_exists.assert_called_once()
759+
mocked_substrate_query.assert_not_called()
760+
assert result is None
761+
762+
763+
@pytest.mark.parametrize(
764+
"all_netuids, filter_for_netuids, response",
765+
[([1, 2], [3, 4], []), ([1, 2], [1, 3], [1]), ([1, 2], None, [1, 2])],
766+
ids=[
767+
"all arguments -> no comparison",
768+
"all arguments -> is comparison",
769+
"not filter_for_netuids",
770+
],
771+
)
772+
@pytest.mark.asyncio
773+
async def test_filter_netuids_by_registered_hotkeys(
774+
subtensor, mocker, all_netuids, filter_for_netuids, response
775+
):
776+
"""Tests filter_netuids_by_registered_hotkeys method."""
777+
# Preps
778+
fake_wallet_1 = mocker.Mock(autospec=async_subtensor.Wallet)
779+
fake_wallet_1.hotkey.ss58_address = "ss58_address_1"
780+
fake_wallet_2 = mocker.Mock(autospec=async_subtensor.Wallet)
781+
fake_wallet_2.hotkey.ss58_address = "ss58_address_2"
782+
783+
fake_all_netuids = all_netuids
784+
fake_filter_for_netuids = filter_for_netuids
785+
fake_all_hotkeys = [fake_wallet_1, fake_wallet_2]
786+
fake_block_hash = "fake_block_hash"
787+
fake_reuse_block = True
788+
789+
mocked_get_netuids_for_hotkey = mocker.AsyncMock(
790+
# returned subnets list
791+
return_value=[1, 2]
792+
)
793+
subtensor.get_netuids_for_hotkey = mocked_get_netuids_for_hotkey
794+
795+
# Call
796+
797+
result = await subtensor.filter_netuids_by_registered_hotkeys(
798+
all_netuids=fake_all_netuids,
799+
filter_for_netuids=fake_filter_for_netuids,
800+
all_hotkeys=fake_all_hotkeys,
801+
block_hash=fake_block_hash,
802+
reuse_block=fake_reuse_block,
803+
)
804+
805+
# Asserts
806+
mocked_get_netuids_for_hotkey.call_count = len(fake_all_netuids)
807+
assert mocked_get_netuids_for_hotkey.mock_calls == [
808+
mocker.call(
809+
w.hotkey.ss58_address,
810+
block_hash=fake_block_hash,
811+
reuse_block=fake_reuse_block,
812+
)
813+
for w in fake_all_hotkeys
814+
]
815+
assert result == response
816+
817+
818+
@pytest.mark.asyncio
819+
async def test_get_existential_deposit_happy_path(subtensor, mocker):
820+
"""Tests get_existential_deposit method."""
821+
# Preps
822+
fake_block_hash = "block_hash"
823+
fake_reuse_block_hash = True
824+
825+
mocked_substrate_get_constant = mocker.AsyncMock(return_value=1)
826+
subtensor.substrate.get_constant = mocked_substrate_get_constant
827+
828+
spy_balance_from_rao = mocker.spy(async_subtensor.Balance, "from_rao")
829+
830+
# Call
831+
result = await subtensor.get_existential_deposit(
832+
block_hash=fake_block_hash, reuse_block=fake_reuse_block_hash
833+
)
834+
835+
# Asserts
836+
mocked_substrate_get_constant.assert_awaited_once()
837+
mocked_substrate_get_constant.assert_called_once_with(
838+
module_name="Balances",
839+
constant_name="ExistentialDeposit",
840+
block_hash=fake_block_hash,
841+
reuse_block_hash=fake_reuse_block_hash,
842+
)
843+
spy_balance_from_rao.assert_called_once_with(
844+
mocked_substrate_get_constant.return_value
845+
)
846+
assert result == async_subtensor.Balance(mocked_substrate_get_constant.return_value)
847+
848+
849+
@pytest.mark.asyncio
850+
async def test_get_existential_deposit_raise_exception(subtensor, mocker):
851+
"""Tests get_existential_deposit method raise Exception."""
852+
# Preps
853+
fake_block_hash = "block_hash"
854+
fake_reuse_block_hash = True
855+
856+
mocked_substrate_get_constant = mocker.AsyncMock(return_value=None)
857+
subtensor.substrate.get_constant = mocked_substrate_get_constant
858+
859+
spy_balance_from_rao = mocker.spy(async_subtensor.Balance, "from_rao")
860+
861+
# Call
862+
with pytest.raises(Exception):
863+
await subtensor.get_existential_deposit(
864+
block_hash=fake_block_hash, reuse_block=fake_reuse_block_hash
865+
)
866+
867+
# Asserts
868+
mocked_substrate_get_constant.assert_awaited_once()
869+
mocked_substrate_get_constant.assert_called_once_with(
870+
module_name="Balances",
871+
constant_name="ExistentialDeposit",
872+
block_hash=fake_block_hash,
873+
reuse_block_hash=fake_reuse_block_hash,
874+
)
875+
spy_balance_from_rao.assert_not_called()
876+
877+
878+
@pytest.mark.asyncio
879+
async def test_neurons(subtensor, mocker):
880+
"""Tests neurons method."""
881+
# Preps
882+
fake_netuid = 1
883+
fake_block_hash = "block_hash"
884+
fake_neurons = [mocker.Mock(), mocker.Mock()]
885+
fake_weights = [(1, [(10, 20), (30, 40)]), (2, [(50, 60), (70, 80)])]
886+
fake_bonds = [(1, [(10, 20), (30, 40)]), (2, [(50, 60), (70, 80)])]
887+
888+
mocked_neurons_lite = mocker.AsyncMock(return_value=fake_neurons)
889+
subtensor.neurons_lite = mocked_neurons_lite
890+
891+
mocked_weights = mocker.AsyncMock(return_value=fake_weights)
892+
subtensor.weights = mocked_weights
893+
894+
mocked_bonds = mocker.AsyncMock(return_value=fake_bonds)
895+
subtensor.bonds = mocked_bonds
896+
897+
mocked_neuron_info_method = mocker.Mock()
898+
async_subtensor.NeuronInfo.from_weights_bonds_and_neuron_lite = (
899+
mocked_neuron_info_method
900+
)
901+
902+
# Call
903+
result = await subtensor.neurons(netuid=fake_netuid, block_hash=fake_block_hash)
904+
905+
# Asserts
906+
mocked_neurons_lite.assert_awaited_once()
907+
mocked_neurons_lite.assert_called_once_with(
908+
netuid=fake_netuid, block_hash=fake_block_hash
909+
)
910+
mocked_weights.assert_awaited_once()
911+
mocked_weights.assert_called_once_with(
912+
netuid=fake_netuid, block_hash=fake_block_hash
913+
)
914+
mocked_bonds.assert_awaited_once()
915+
mocked_bonds.assert_called_once_with(netuid=fake_netuid, block_hash=fake_block_hash)
916+
assert result == [
917+
mocked_neuron_info_method.return_value for _ in range(len(fake_neurons))
918+
]
919+
920+
921+
@pytest.mark.parametrize(
922+
"fake_hex_bytes_result, response",
923+
[(None, []), ("0xaabbccdd", b"\xaa\xbb\xcc\xdd")],
924+
ids=["none", "with data"],
925+
)
926+
@pytest.mark.asyncio
927+
async def test_neurons_lite(subtensor, mocker, fake_hex_bytes_result, response):
928+
"""Tests neurons_lite method."""
929+
# Preps
930+
fake_netuid = 1
931+
fake_block_hash = "block_hash"
932+
fake_reuse_block_hash = True
933+
934+
mocked_query_runtime_api = mocker.AsyncMock(return_value=fake_hex_bytes_result)
935+
subtensor.query_runtime_api = mocked_query_runtime_api
936+
937+
mocked_neuron_info_lite_list_from_vec_u8 = mocker.Mock()
938+
async_subtensor.NeuronInfoLite.list_from_vec_u8 = (
939+
mocked_neuron_info_lite_list_from_vec_u8
940+
)
941+
942+
# Call
943+
result = await subtensor.neurons_lite(
944+
netuid=fake_netuid,
945+
block_hash=fake_block_hash,
946+
reuse_block=fake_reuse_block_hash,
947+
)
948+
949+
# Assertions
950+
mocked_query_runtime_api.assert_awaited_once()
951+
mocked_query_runtime_api.assert_called_once_with(
952+
runtime_api="NeuronInfoRuntimeApi",
953+
method="get_neurons_lite",
954+
params=[fake_netuid],
955+
block_hash=fake_block_hash,
956+
reuse_block=fake_reuse_block_hash,
957+
)
958+
if fake_hex_bytes_result:
959+
mocked_neuron_info_lite_list_from_vec_u8.assert_called_once_with(
960+
bytes.fromhex(fake_hex_bytes_result[2:])
961+
)
962+
assert result == mocked_neuron_info_lite_list_from_vec_u8.return_value
963+
else:
964+
mocked_neuron_info_lite_list_from_vec_u8.assert_not_called()
965+
assert result == []

0 commit comments

Comments
 (0)