1+ """Test suite for constants module."""
2+
3+ import pytest
4+ from solana_ai_registries .constants import (
5+ A2AMPL_BASE_UNIT ,
6+ A2AMPL_TOKEN_MINT_DEVNET ,
7+ A2AMPL_TOKEN_MINT_MAINNET ,
8+ AGENT_REGISTRY_PROGRAM_ID ,
9+ DEFAULT_DEVNET_RPC ,
10+ DEFAULT_MAINNET_RPC ,
11+ MAX_AGENT_DESCRIPTION_LEN ,
12+ MAX_AGENT_ID_LEN ,
13+ MAX_AGENT_NAME_LEN ,
14+ MAX_SERVER_ID_LEN ,
15+ MAX_SERVER_NAME_LEN ,
16+ MCP_SERVER_REGISTRY_PROGRAM_ID ,
17+ a2ampl_to_base_units ,
18+ base_units_to_a2ampl ,
19+ get_token_mint_for_cluster ,
20+ validate_string_length ,
21+ validate_url ,
22+ )
23+
24+
25+ class TestConstants :
26+ """Test constant values."""
27+
28+ def test_program_ids_are_strings (self ) -> None :
29+ """Test that program IDs are valid strings."""
30+ assert isinstance (AGENT_REGISTRY_PROGRAM_ID , str )
31+ assert isinstance (MCP_SERVER_REGISTRY_PROGRAM_ID , str )
32+ assert len (AGENT_REGISTRY_PROGRAM_ID ) > 0
33+ assert len (MCP_SERVER_REGISTRY_PROGRAM_ID ) > 0
34+
35+ def test_rpc_endpoints_are_urls (self ) -> None :
36+ """Test that RPC endpoints are valid URLs."""
37+ assert isinstance (DEFAULT_MAINNET_RPC , str )
38+ assert isinstance (DEFAULT_DEVNET_RPC , str )
39+ assert DEFAULT_MAINNET_RPC .startswith ("https://" )
40+ assert DEFAULT_DEVNET_RPC .startswith ("https://" )
41+
42+ def test_token_mints_are_strings (self ) -> None :
43+ """Test that token mints are valid strings."""
44+ assert isinstance (A2AMPL_TOKEN_MINT_MAINNET , str )
45+ assert isinstance (A2AMPL_TOKEN_MINT_DEVNET , str )
46+ assert len (A2AMPL_TOKEN_MINT_MAINNET ) > 0
47+ assert len (A2AMPL_TOKEN_MINT_DEVNET ) > 0
48+
49+ def test_length_constants_are_positive (self ) -> None :
50+ """Test that length constants are positive integers."""
51+ assert isinstance (MAX_AGENT_ID_LEN , int )
52+ assert isinstance (MAX_AGENT_NAME_LEN , int )
53+ assert isinstance (MAX_AGENT_DESCRIPTION_LEN , int )
54+ assert isinstance (MAX_SERVER_ID_LEN , int )
55+ assert isinstance (MAX_SERVER_NAME_LEN , int )
56+ assert MAX_AGENT_ID_LEN > 0
57+ assert MAX_AGENT_NAME_LEN > 0
58+ assert MAX_AGENT_DESCRIPTION_LEN > 0
59+ assert MAX_SERVER_ID_LEN > 0
60+ assert MAX_SERVER_NAME_LEN > 0
61+
62+ def test_a2ampl_base_unit (self ) -> None :
63+ """Test A2AMPL base unit constant."""
64+ assert isinstance (A2AMPL_BASE_UNIT , int )
65+ assert A2AMPL_BASE_UNIT == 10 ** 9 # 9 decimal places
66+
67+
68+ class TestA2AMPLConversion :
69+ """Test A2AMPL conversion functions."""
70+
71+ def test_a2ampl_to_base_units (self ) -> None :
72+ """Test converting A2AMPL to base units."""
73+ assert a2ampl_to_base_units (1.0 ) == 1_000_000_000
74+ assert a2ampl_to_base_units (100.5 ) == 100_500_000_000
75+ assert a2ampl_to_base_units (0.000000001 ) == 1 # Smallest unit
76+ assert a2ampl_to_base_units (0.0 ) == 0
77+
78+ def test_base_units_to_a2ampl (self ) -> None :
79+ """Test converting base units to A2AMPL."""
80+ assert base_units_to_a2ampl (1_000_000_000 ) == 1.0
81+ assert base_units_to_a2ampl (100_500_000_000 ) == 100.5
82+ assert base_units_to_a2ampl (1 ) == 0.000000001 # Smallest unit
83+ assert base_units_to_a2ampl (0 ) == 0.0
84+
85+ def test_conversion_round_trip (self ) -> None :
86+ """Test that conversions are reversible."""
87+ amounts = [1.0 , 100.5 , 0.123456789 , 1000.0 ]
88+ for amount in amounts :
89+ base_units = a2ampl_to_base_units (amount )
90+ converted_back = base_units_to_a2ampl (base_units )
91+ assert abs (converted_back - amount ) < 1e-9 # Allow for floating point precision
92+
93+
94+ class TestClusterTokenMint :
95+ """Test cluster token mint resolution."""
96+
97+ def test_mainnet_clusters (self ) -> None :
98+ """Test mainnet cluster variations."""
99+ assert get_token_mint_for_cluster ("mainnet" ) == A2AMPL_TOKEN_MINT_MAINNET
100+ assert get_token_mint_for_cluster ("mainnet-beta" ) == A2AMPL_TOKEN_MINT_MAINNET
101+
102+ def test_devnet_testnet_clusters (self ) -> None :
103+ """Test devnet and testnet clusters."""
104+ assert get_token_mint_for_cluster ("devnet" ) == A2AMPL_TOKEN_MINT_DEVNET
105+ assert get_token_mint_for_cluster ("testnet" ) == A2AMPL_TOKEN_MINT_DEVNET
106+
107+ def test_unsupported_cluster (self ) -> None :
108+ """Test unsupported cluster raises error."""
109+ with pytest .raises (ValueError ) as exc_info :
110+ get_token_mint_for_cluster ("unsupported" )
111+ assert "Unsupported cluster: unsupported" in str (exc_info .value )
112+
113+ def test_empty_cluster (self ) -> None :
114+ """Test empty cluster raises error."""
115+ with pytest .raises (ValueError ) as exc_info :
116+ get_token_mint_for_cluster ("" )
117+ assert "Unsupported cluster:" in str (exc_info .value )
118+
119+
120+ class TestStringValidation :
121+ """Test string validation functions."""
122+
123+ def test_validate_string_length_valid (self ) -> None :
124+ """Test string validation with valid lengths."""
125+ # These should not raise exceptions
126+ validate_string_length ("" , 10 , "test_field" )
127+ validate_string_length ("hello" , 10 , "test_field" )
128+ validate_string_length ("exactly10c" , 10 , "test_field" )
129+
130+ def test_validate_string_length_too_long (self ) -> None :
131+ """Test string validation with too long strings."""
132+ with pytest .raises (ValueError ) as exc_info :
133+ validate_string_length ("this_is_too_long" , 10 , "test_field" )
134+ assert "test_field exceeds maximum length: 16 > 10" in str (exc_info .value )
135+
136+ def test_validate_string_length_zero_max (self ) -> None :
137+ """Test string validation with zero max length."""
138+ with pytest .raises (ValueError ) as exc_info :
139+ validate_string_length ("x" , 0 , "test_field" )
140+ assert "test_field exceeds maximum length: 1 > 0" in str (exc_info .value )
141+
142+ # Empty string should be valid for zero max length
143+ validate_string_length ("" , 0 , "test_field" )
144+
145+
146+ class TestURLValidation :
147+ """Test URL validation functions."""
148+
149+ def test_validate_url_valid_urls (self ) -> None :
150+ """Test URL validation with valid URLs."""
151+ valid_urls = [
152+ "https://example.com" ,
153+ "http://localhost:8080" ,
154+ "https://api.example.com/v1/endpoint" ,
155+ "https://subdomain.example.com:443/path?query=value" ,
156+ "ipfs://QmExampleHash" ,
157+ "ar://ExampleArweaveHash" ,
158+ "https://" , # Minimal valid https URL
159+ "http://" , # Minimal valid http URL
160+ ]
161+ for url in valid_urls :
162+ # Should not raise exceptions
163+ validate_url (url , "test_url" )
164+
165+ def test_validate_url_invalid_urls (self ) -> None :
166+ """Test URL validation with invalid URLs."""
167+ invalid_urls = [
168+ "not-a-url" ,
169+ "ftp://example.com" , # Only http/https/ipfs/ar allowed
170+ "" ,
171+ "javascript:alert('xss')" ,
172+ ]
173+ for url in invalid_urls :
174+ with pytest .raises (ValueError ) as exc_info :
175+ validate_url (url , "test_url" )
176+ assert "test_url must start with one of" in str (exc_info .value )
177+
178+ def test_validate_url_none_value (self ) -> None :
179+ """Test URL validation with None value."""
180+ # This would be caught by type checking, but test the behavior
181+ with pytest .raises (AttributeError ):
182+ validate_url (None , "test_url" ) # type: ignore
0 commit comments