Skip to content

Commit ba9a457

Browse files
committed
test: fix currency hex encoding and refactor
Issue #610 fixed: /search/transactions now requires hex-encoded currency symbols. Updated all tests to comply with this change and fixed several validation issues. Changes: - Add symbol_hex field to network_test_data.yaml for native assets - Convert test_native_asset_filtering_by_ascii_symbol to negative test (expects error 5059) - Unskip test_currency_filter_with_hex_encoded_symbol (issue #610 now fixed) - Update all currency search tests to use hex symbols instead of ASCII - Fix test assertions to validate ALL transactions contain filtered asset (not just "at least one") - Correct tokenBundle validation (native assets live there, not in operation amounts) - Update peer discovery test for Genesis mode (peer-snapshot.json) with time-based validation - Refactor token_registry tests: convert assertion helpers to pytest fixtures - Fix registry metadata extraction to handle v2 API structure ({value, source} objects) - Fix test_rosetta_metadata_matches_registry to compare Rosetta vs Registry (not vs config) - Add "domain" to valid peer types (Genesis peer-snapshot.json support)
1 parent c2a4002 commit ba9a457

File tree

6 files changed

+241
-164
lines changed

6 files changed

+241
-164
lines changed

tests/data-endpoints/network_test_data.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ preprod:
3030
- name: "tTEURO"
3131
description: "Test Euro stablecoin on preprod - 11,081 transactions"
3232
symbol: "tTEURO"
33+
symbol_hex: "74544555524f" # Symbol in hexadecimal
3334
decimals: 6
3435
policy_id: "e68f1cea19752d1292b4be71b7f5d2b3219a15859c028f7454f66cdf"
3536

tests/data-endpoints/test_network_endpoints.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for Rosetta /network endpoints."""
22

3+
import time
34
import pytest
45
import allure
56

@@ -65,18 +66,32 @@ def test_returns_current_network_status(self, client, network):
6566
assert sync_current >= current_index, "sync progress cannot trail current block"
6667
assert sync_current <= sync_target, "sync progress cannot exceed target"
6768

68-
peers = data.get("peers")
69-
assert isinstance(peers, list) and peers, "Peers list must not be empty"
69+
peers = data.get("peers", [])
70+
71+
if not peers:
72+
max_wait_seconds = 300
73+
retry_interval = 30
74+
for _ in range(max_wait_seconds // retry_interval):
75+
time.sleep(retry_interval)
76+
peers = client.network_status(network=network).json().get("peers", [])
77+
if peers:
78+
break
79+
80+
assert isinstance(peers, list), "Peers must be a list"
81+
assert peers, "Peers list empty after 5 minutes (peer discovery initial delay)"
82+
7083
for peer in peers:
7184
peer_id = peer.get("peer_id")
7285
metadata = peer.get("metadata")
86+
7387
assert isinstance(peer_id, str) and ":" in peer_id, (
7488
"peer_id must include hostname and port"
7589
)
7690
assert isinstance(metadata, dict) and metadata.get("type") in {
7791
"IPv4",
7892
"IPv6",
79-
}
93+
"domain",
94+
}, f"Peer type must be IPv4, IPv6, or domain (Genesis mode), got: {metadata.get('type')}"
8095

8196
# Error handling tests moved to test_error_handling.py
8297

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,38 @@
11
"""Peer discovery TTL validation (v1.4.0)."""
2-
import os
2+
import time
33
import pytest
44
import allure
5-
from pathlib import Path
6-
7-
import yaml
85

96

107
@allure.feature("Peer Discovery")
118
@allure.story("Dynamic Peer List")
129
class TestPeerDiscovery:
1310
"""Validate dynamic peer discovery with TTL."""
1411

15-
@pytest.mark.nightly
12+
@pytest.mark.weekly
1613
@pytest.mark.requires_peer_discovery
1714
@pytest.mark.slow
18-
@pytest.mark.skip(reason="Peer discovery takes too long to populate dynamic peers (#619)")
19-
def test_peer_list_contains_dynamic_entries(self, client, network, has_peer_discovery):
20-
"""Verify peer discovery returns peers beyond the static bootstrap list."""
15+
@pytest.mark.skip(reason="Peer discovery refresh takes 65+ minutes - reserved for weekly tests (#619)")
16+
def test_peer_list_changes_over_time(self, client, network, has_peer_discovery):
17+
"""Verify peer discovery refreshes and peers change over time (60 min refresh cycle)."""
2118
if not has_peer_discovery:
2219
pytest.skip("Peer discovery not enabled")
2320

24-
repo_root = Path(__file__).resolve().parents[2]
25-
topology_file = repo_root / "config" / "node" / network / "topology.json"
26-
assert topology_file.exists(), f"Bootstrap topology file not found: {topology_file}"
27-
28-
bootstrap_hosts = set()
29-
try:
30-
topology = yaml.safe_load(topology_file.read_text(encoding="utf-8"))
31-
for peer in topology.get("bootstrapPeers", []):
32-
host = peer.get("address")
33-
if host:
34-
bootstrap_hosts.add(host)
35-
except Exception as exc:
36-
pytest.fail(f"Unable to parse bootstrap topology {topology_file}: {exc}")
37-
assert bootstrap_hosts, f"Bootstrap topology {topology_file} does not define any bootstrap peers"
21+
# Get initial peer list
22+
peers_t0 = client.network_status(network=network).json().get("peers", [])
23+
assert peers_t0, "Initial peer list should not be empty"
3824

39-
peers = client.network_status(network=network).json().get("peers", [])
40-
peer_hosts = set()
41-
for peer in peers:
42-
peer_id = peer.get("peer_id", "")
43-
if ":" in peer_id:
44-
host = peer_id.split(":", 1)[0]
45-
if host:
46-
peer_hosts.add(host)
25+
# Wait for peer discovery refresh cycle (60 min + 5 min buffer)
26+
wait_minutes = 65
27+
print(f"Waiting {wait_minutes} minutes for peer discovery refresh...")
28+
time.sleep(wait_minutes * 60)
4729

48-
assert peer_hosts, "Peer discovery should return peers with peer_id host values"
30+
# Get peer list after refresh
31+
peers_t1 = client.network_status(network=network).json().get("peers", [])
32+
assert peers_t1, "Peer list after refresh should not be empty"
4933

50-
dynamic_hosts = peer_hosts - bootstrap_hosts
51-
assert dynamic_hosts, (
52-
"Peer discovery did not return any dynamic peers beyond bootstrap list. "
53-
f"Bootstrap hosts: {sorted(bootstrap_hosts)}; Reported peers: {sorted(peer_hosts)}"
34+
# Verify peers changed (dynamic discovery is working)
35+
assert peers_t0 != peers_t1, (
36+
f"Peers should change after {wait_minutes} minutes (discovery refresh cycle). "
37+
f"T0 peers: {len(peers_t0)}, T1 peers: {len(peers_t1)}"
5438
)

tests/data-endpoints/test_search_transactions.py

Lines changed: 71 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -659,70 +659,75 @@ def test_ada_filter_case_insensitive(self, client, network, symbol):
659659
@allure.story("Currency Filtering")
660660
@pytest.mark.pruning_compatible
661661
def test_native_asset_filtering_by_ascii_symbol(self, client, network, network_data, is_pruned_instance):
662-
"""Currency filter works with ASCII symbols (backwards compat in v1.3.3)."""
662+
"""Currency filter with ASCII symbols should return error (negative test for v1.4.1+)."""
663663
asset = network_data["assets"][0]
664-
hex_symbol = asset["symbol"].encode().hex().lower() # Lowercase hex
665664

666-
# Search by ASCII symbol (backwards compat)
665+
# Search by ASCII symbol (should return error in v1.4.1+)
667666
response = client.search_transactions(
668667
network=network,
669668
currency={"symbol": asset["symbol"], "decimals": asset["decimals"]},
670669
)
671-
assert response.status_code == 200
672-
673-
# Verify returns transactions
674-
txs = response.json()["transactions"]
675-
assert len(txs) > 0, "Currency filter should return transactions"
676670

677-
# Verify transactions contain the asset (in metadata.tokenBundle)
678-
for tx in txs:
679-
found_asset = False
680-
for op in tx["transaction"]["operations"]:
681-
# Check metadata.tokenBundle for native assets
682-
if "metadata" in op and "tokenBundle" in op["metadata"]:
683-
for bundle in op["metadata"]["tokenBundle"]:
684-
for token in bundle.get("tokens", []):
685-
if token["currency"]["symbol"].lower() == hex_symbol:
686-
found_asset = True
687-
break
671+
# API should return error for non-hex-encoded symbols
672+
assert response.status_code == 500, (
673+
f"Expected error status code when using ASCII symbol '{asset['symbol']}'"
674+
)
688675

689-
assert found_asset, (
690-
f"Transaction must contain native asset with hex symbol {hex_symbol}"
691-
)
676+
error_data = response.json()
677+
assert error_data["code"] == 5059, "Expected error code 5059 for non-hex symbol"
678+
assert "hex-encoded" in error_data["message"].lower(), (
679+
"Error message should mention hex encoding requirement"
680+
)
681+
assert asset["symbol"] in error_data.get("details", {}).get("message", ""), (
682+
f"Error details should mention the invalid symbol '{asset['symbol']}'"
683+
)
692684

693685
@allure.feature("Search Transactions")
694686
@allure.story("Currency Filtering")
695-
@pytest.mark.skip(
696-
reason="Currency filter + limit parameter causes timeout (#615)"
697-
)
687+
# TODO: investigate and update the ticket; seems to be working now, although response time is ~2min
688+
# @pytest.mark.skip(
689+
# reason="Currency filter + limit parameter causes timeout (#615)"
690+
# )
698691
def test_currency_filter_with_limit_parameter(self, client, network, network_data):
699692
"""Currency filter with explicit limit should not cause timeout."""
700693
asset = network_data["assets"][0]
701694

702695
# This combination causes 600s+ timeout in v1.3.3
703696
response = client.search_transactions(
704697
network=network,
705-
currency={"symbol": asset["symbol"], "decimals": asset["decimals"]},
698+
currency={"symbol": asset["symbol_hex"], "decimals": asset["decimals"]},
706699
limit=1,
707700
)
708701
assert response.status_code == 200
709702

710703
txs = response.json()["transactions"]
711-
assert len(txs) <= 1, "Should respect limit parameter"
704+
assert len(txs) == 1, "Should respect limit parameter"
705+
706+
# Verify the returned transaction contains the filtered asset
707+
tx = txs[0]
708+
currencies_in_tx = []
709+
for op in tx["transaction"]["operations"]:
710+
if "metadata" in op and "tokenBundle" in op["metadata"]:
711+
for bundle in op["metadata"]["tokenBundle"]:
712+
for token in bundle.get("tokens", []):
713+
currencies_in_tx.append(token["currency"]["symbol"].lower())
714+
715+
assert asset["symbol_hex"].lower() in currencies_in_tx, (
716+
f"Transaction must contain filtered asset {asset['symbol_hex']}. "
717+
f"Found currencies: {currencies_in_tx}"
718+
)
712719

713720
@allure.feature("Search Transactions")
714721
@allure.story("Currency Filtering")
715-
@pytest.mark.skip(
716-
reason="Hex symbol search not working until v1.4.x (#610)"
717-
)
718-
def test_currency_filter_with_hex_encoded_symbol(self, client, network, network_data):
719-
"""Currency filter should accept hex-encoded symbols (canonical format in v1.4.x+)."""
722+
@pytest.mark.pruning_compatible
723+
def test_currency_filter_with_hex_encoded_symbol(self, client, network, network_data, is_pruned_instance):
724+
"""Currency filter accepts hex-encoded symbols (canonical format in v1.4.x+, issue #610 fixed)."""
720725
asset = network_data["assets"][0]
721726

722-
# Convert ASCII symbol to hex (canonical format)
723-
hex_symbol = asset["symbol"].encode().hex().upper() # "tTEURO" → "74544555524F"
727+
# Use hex-encoded symbol from test data (canonical format)
728+
hex_symbol = asset["symbol_hex"] # e.g., "74544555524f"
724729

725-
# Search by hex-encoded symbol (will work in v1.4.x when #610 is fixed)
730+
# Search by hex-encoded symbol (supported in v1.4.x+ after #610 fix)
726731
response = client.search_transactions(
727732
network=network,
728733
currency={"symbol": hex_symbol, "decimals": asset["decimals"]},
@@ -732,49 +737,59 @@ def test_currency_filter_with_hex_encoded_symbol(self, client, network, network_
732737
txs = response.json()["transactions"]
733738
assert len(txs) > 0, "Hex symbol search should return transactions"
734739

735-
# Verify transactions contain the asset
740+
# Verify ALL transactions contain the filtered asset (currency filter was applied)
736741
for tx in txs:
737-
currencies = [
738-
op["amount"]["currency"]["symbol"]
739-
for op in tx.get("operations", [])
740-
if "amount" in op and "currency" in op["amount"]
741-
]
742-
# In v1.4.x+, API will return hex symbols
743-
assert hex_symbol in currencies, \
744-
"Currency filter with hex symbol should return matching transactions"
742+
# Collect all currency symbols from tokenBundle across all operations
743+
currencies_in_tx = []
744+
for op in tx["transaction"]["operations"]:
745+
# Native assets are always in tokenBundle (they sit in UTXOs with ADA)
746+
if "metadata" in op and "tokenBundle" in op["metadata"]:
747+
for bundle in op["metadata"]["tokenBundle"]:
748+
for token in bundle.get("tokens", []):
749+
currencies_in_tx.append(token["currency"]["symbol"].lower())
750+
751+
# Assert filtered currency is in this transaction
752+
assert hex_symbol.lower() in currencies_in_tx, (
753+
f"Transaction must contain native asset with hex symbol {hex_symbol} in tokenBundle. "
754+
f"Found currencies: {currencies_in_tx}"
755+
)
745756

746757
@allure.feature("Search Transactions")
747758
@allure.story("Currency Filtering")
748759
@pytest.mark.pruning_compatible
749760
def test_native_asset_filtering_with_policy_id(self, client, network, network_data, is_pruned_instance):
750-
"""Test currency filtering with metadata.policyId."""
761+
"""Test currency filtering with hex-encoded symbol and metadata.policyId."""
751762
asset = network_data["assets"][0]
752763

753-
# Search by asset with policyId in metadata
764+
# Search by hex-encoded asset symbol with policyId in metadata
754765
response = client.search_transactions(
755766
network=network,
756767
currency={
757-
"symbol": asset["symbol"],
768+
"symbol": asset["symbol_hex"],
758769
"decimals": asset["decimals"],
759770
"metadata": {"policyId": asset["policy_id"]}
760771
},
761772
)
762773
assert response.status_code == 200
763774

764-
# Verify all returned transactions contain this specific asset with policyId
775+
# Verify ALL transactions contain the filtered asset with matching policyId and symbol
765776
txs = response.json()["transactions"]
766777
assert len(txs) > 0, "Currency filter with policyId should return transactions"
767778

768779
for tx in txs:
769-
# Check metadata.tokenBundle for matching policyId
770-
found_asset = False
780+
# Collect all (policyId, symbol) pairs from tokenBundle across all operations
781+
assets_in_tx = []
771782
for op in tx["transaction"]["operations"]:
772783
if "metadata" in op and "tokenBundle" in op["metadata"]:
773784
for bundle in op["metadata"]["tokenBundle"]:
774-
if bundle.get("policyId") == asset["policy_id"]:
775-
found_asset = True
776-
break
777-
778-
assert found_asset, (
779-
f"Transaction must contain asset with policyId {asset['policy_id']}"
785+
policy_id = bundle.get("policyId")
786+
for token in bundle.get("tokens", []):
787+
symbol = token["currency"]["symbol"].lower()
788+
assets_in_tx.append((policy_id, symbol))
789+
790+
# Assert filtered asset (policyId + symbol) is in this transaction
791+
expected_asset = (asset["policy_id"], asset["symbol_hex"].lower())
792+
assert expected_asset in assets_in_tx, (
793+
f"Transaction must contain asset with policyId {asset['policy_id']} "
794+
f"and symbol {asset['symbol_hex']}. Found assets: {assets_in_tx}"
780795
)

tests/data-endpoints/test_smoke_network_data.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def test_configured_assets_have_transactions(self, client, network, network_data
5454
# Note: Do NOT add limit parameter - currency filter + limit causes timeout (#615)
5555
response = client.search_transactions(
5656
network=network,
57-
currency={"symbol": asset["symbol"], "decimals": asset["decimals"]},
57+
currency={"symbol": asset["symbol_hex"], "decimals": asset["decimals"]},
5858
)
5959
assert response.status_code == 200
6060

@@ -64,6 +64,24 @@ def test_configured_assets_have_transactions(self, client, network, network_data
6464
f"Network may have changed or asset data is wrong - update network_test_data.yaml"
6565
)
6666

67+
# Verify ALL transactions contain the filtered asset (currency filter was applied)
68+
for tx in txs:
69+
# Collect all currency symbols from tokenBundle across all operations
70+
currencies_in_tx = []
71+
for op in tx["transaction"]["operations"]:
72+
# Native assets are always in tokenBundle (they sit in UTXOs with ADA)
73+
if "metadata" in op and "tokenBundle" in op["metadata"]:
74+
for bundle in op["metadata"]["tokenBundle"]:
75+
for token in bundle.get("tokens", []):
76+
currencies_in_tx.append(token["currency"]["symbol"].lower())
77+
78+
# Assert filtered currency is in this transaction
79+
assert asset["symbol_hex"].lower() in currencies_in_tx, (
80+
f"Asset '{asset['name']}' with hex symbol {asset['symbol_hex']} not found in transaction. "
81+
f"Currency filter should return only transactions containing the filtered asset. "
82+
f"Found currencies: {currencies_in_tx}"
83+
)
84+
6785
def test_asset_policy_ids_are_valid(self, client, network, network_data):
6886
"""Asset policy IDs should be valid hex strings."""
6987
for asset in network_data["assets"]:

0 commit comments

Comments
 (0)