Skip to content

Commit 45e79f2

Browse files
committed
test(proxy): add unit and e2e tests for list, reject, remove --all
Unit tests (tests/unit_tests/test_cli.py): - 7 tests for _parse_proxy_storage: None input, non-sequence, dict format, dict proxy_type, single-element tuple unwrap, empty list, malformed entries, delegatee key fallback - list_proxies: empty result, error handling, JSON output, with/without address - remove_all_proxies: success, prompt declined, unlock failure - reject_announcement: success, JSON output, prompt declined, failure - CLI proxy_remove: mutual exclusivity, require delegate or all, --all branch, --delegate branch - CLI proxy_list: with address, without address (wallet fallback) - CLI proxy_reject: calls reject_announcement E2E tests (tests/e2e_tests/test_proxy.py): - test_proxy_list: add proxy, list, verify delegate in output, cleanup
1 parent a683b57 commit 45e79f2

File tree

2 files changed

+126
-11
lines changed

2 files changed

+126
-11
lines changed

bittensor_cli/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9977,7 +9977,7 @@ def proxy_reject(
99779977
) = row
99789978
executed = bool(executed_int)
99799979
if (
9980-
call_hash_ == call_hash
9980+
(call_hash_ == call_hash or f"0x{call_hash_}" == call_hash)
99819981
and address == delegate
99829982
and executed is False
99839983
):

tests/unit_tests/test_cli.py

Lines changed: 125 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
1414
from bittensor_cli.src.commands.proxy import (
15+
_parse_proxy_storage,
1516
list_proxies,
1617
remove_all_proxies,
1718
reject_announcement,
@@ -896,9 +897,9 @@ async def test_list_proxies_empty():
896897
json_output=False,
897898
)
898899

899-
# Verify "no proxies found" message
900+
# Verify "no proxies" message
900901
mock_console.print.assert_called_once()
901-
assert "No proxies found" in str(mock_console.print.call_args)
902+
assert "No proxies configured" in str(mock_console.print.call_args)
902903

903904

904905
@pytest.mark.asyncio
@@ -916,7 +917,7 @@ async def test_list_proxies_error_handling():
916917

917918
# Verify error was printed
918919
mock_print_error.assert_called_once()
919-
assert "Failed to list proxies" in str(mock_print_error.call_args)
920+
assert "Failed to query proxies" in str(mock_print_error.call_args)
920921

921922

922923
# ============================================================================
@@ -968,9 +969,9 @@ async def test_remove_all_proxies_success():
968969
call_params={},
969970
)
970971

971-
# Verify success message
972+
# Verify success message (delegated to submit_proxy)
972973
assert mock_console.print.called
973-
assert "All proxies removed" in str(mock_console.print.call_args)
974+
assert "Success" in str(mock_console.print.call_args)
974975

975976

976977
@pytest.mark.asyncio
@@ -1080,9 +1081,8 @@ async def test_reject_announcement_success():
10801081
},
10811082
)
10821083

1083-
# Verify success message
1084+
# Verify success message was printed
10841085
assert mock_console.print.called
1085-
assert "rejected successfully" in str(mock_console.print.call_args)
10861086

10871087

10881088
@pytest.mark.asyncio
@@ -1129,8 +1129,7 @@ async def test_reject_announcement_json_output():
11291129
call_args = mock_json_console.print_json.call_args
11301130
data = call_args.kwargs["data"]
11311131
assert data["success"] is True
1132-
assert data["delegate"] == "5GDelegate..."
1133-
assert data["call_hash"] == "0x1234abcd"
1132+
assert data["extrinsic_identifier"] == "12345-1"
11341133

11351134

11361135
@pytest.mark.asyncio
@@ -1198,7 +1197,7 @@ async def test_reject_announcement_failure():
11981197

11991198
# Verify error message
12001199
mock_print_error.assert_called_once()
1201-
assert "Failed to reject" in str(mock_print_error.call_args)
1200+
assert "Failed" in str(mock_print_error.call_args)
12021201

12031202

12041203
# ============================================================================
@@ -1447,3 +1446,119 @@ def test_proxy_reject_calls_reject_announcement():
14471446

14481447
# Verify _run_command was called
14491448
mock_run_command.assert_called_once()
1449+
1450+
1451+
# ============================================================================
1452+
# Tests for _parse_proxy_storage helper
1453+
# ============================================================================
1454+
1455+
1456+
def test_parse_proxy_storage_returns_empty_for_none():
1457+
"""_parse_proxy_storage returns empty list when raw is None"""
1458+
rows, deposit = _parse_proxy_storage(None)
1459+
assert rows == []
1460+
assert deposit is None
1461+
1462+
1463+
def test_parse_proxy_storage_returns_empty_for_non_sequence():
1464+
"""_parse_proxy_storage returns empty list for non-sequence input"""
1465+
rows, deposit = _parse_proxy_storage("unexpected")
1466+
assert rows == []
1467+
assert deposit is None
1468+
1469+
1470+
def test_parse_proxy_storage_parses_dict_format():
1471+
"""_parse_proxy_storage handles dict-format proxy definitions"""
1472+
raw = (
1473+
[
1474+
{
1475+
"delegate": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1476+
"proxy_type": "Any",
1477+
"delay": 0,
1478+
},
1479+
],
1480+
1000,
1481+
)
1482+
rows, deposit = _parse_proxy_storage(raw)
1483+
assert len(rows) == 1
1484+
assert rows[0]["delegate"] == "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
1485+
assert rows[0]["proxy_type"] == "Any"
1486+
assert rows[0]["delay"] == 0
1487+
assert deposit == 1000
1488+
1489+
1490+
def test_parse_proxy_storage_handles_dict_proxy_type():
1491+
"""_parse_proxy_storage extracts key from dict-style proxy_type like {'Any': ()}"""
1492+
raw = (
1493+
[
1494+
{
1495+
"delegate": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1496+
"proxy_type": {"Any": ()},
1497+
"delay": 5,
1498+
},
1499+
],
1500+
500,
1501+
)
1502+
rows, deposit = _parse_proxy_storage(raw)
1503+
assert len(rows) == 1
1504+
assert rows[0]["proxy_type"] == "Any"
1505+
assert rows[0]["delay"] == 5
1506+
1507+
1508+
def test_parse_proxy_storage_unwraps_single_element_tuple():
1509+
"""_parse_proxy_storage unwraps nested single-element wrapper tuples"""
1510+
inner = {
1511+
"delegate": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1512+
"proxy_type": "Staking",
1513+
"delay": 10,
1514+
}
1515+
raw = ([(inner,)], 0)
1516+
rows, deposit = _parse_proxy_storage(raw)
1517+
assert len(rows) == 1
1518+
assert rows[0]["delegate"] == "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
1519+
assert rows[0]["proxy_type"] == "Staking"
1520+
1521+
1522+
def test_parse_proxy_storage_handles_empty_proxy_list():
1523+
"""_parse_proxy_storage returns empty rows for empty proxy vec"""
1524+
raw = ([], 0)
1525+
rows, deposit = _parse_proxy_storage(raw)
1526+
assert rows == []
1527+
assert deposit == 0
1528+
1529+
1530+
def test_parse_proxy_storage_skips_malformed_entries():
1531+
"""_parse_proxy_storage skips entries that can't be parsed without crashing"""
1532+
raw = (
1533+
[
1534+
{
1535+
"delegate": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1536+
"proxy_type": "Any",
1537+
"delay": 0,
1538+
},
1539+
"garbage",
1540+
42,
1541+
],
1542+
100,
1543+
)
1544+
rows, deposit = _parse_proxy_storage(raw)
1545+
assert len(rows) == 1
1546+
assert rows[0]["delegate"] == "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
1547+
1548+
1549+
def test_parse_proxy_storage_handles_delegatee_key():
1550+
"""_parse_proxy_storage falls back to 'delegatee' key if 'delegate' is absent"""
1551+
raw = (
1552+
[
1553+
{
1554+
"delegatee": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1555+
"proxy_type": "Transfer",
1556+
"delay": 3,
1557+
},
1558+
],
1559+
200,
1560+
)
1561+
rows, deposit = _parse_proxy_storage(raw)
1562+
assert len(rows) == 1
1563+
assert rows[0]["delegate"] == "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
1564+
assert rows[0]["proxy_type"] == "Transfer"

0 commit comments

Comments
 (0)