@@ -85,8 +85,23 @@ def mock_settings(monkeypatch):
8585@pytest .fixture  
8686def  mock_httpx_client (monkeypatch ):
8787    mock_client  =  AsyncMock ()
88-     mock_client .get  =  AsyncMock ()
89-     mock_client .post  =  AsyncMock ()
88+ 
89+     # Create mock response objects that return actual values, not coroutines 
90+     mock_get_response  =  Mock ()
91+     mock_post_response  =  Mock ()
92+ 
93+     # Set up the mock responses to return actual values, not coroutines 
94+     mock_get_response .status_code  =  200 
95+     mock_get_response .json .return_value  =  {}
96+     mock_get_response .raise_for_status .return_value  =  None 
97+ 
98+     mock_post_response .status_code  =  200 
99+     mock_post_response .json .return_value  =  {}
100+     mock_post_response .raise_for_status .return_value  =  None 
101+ 
102+     # Configure the client methods 
103+     mock_client .get .return_value  =  mock_get_response 
104+     mock_client .post .return_value  =  mock_post_response 
90105
91106    # Create a mock context manager 
92107    mock_context  =  AsyncMock ()
@@ -203,3 +218,159 @@ def test_get_solana_asset_proof_error(mock_httpx_client):
203218        client .get ("/api/nft/v1/getSolanaAssetProof?token_address=invalid_token" )
204219
205220    assert  str (e .value ) ==  "Alchemy API error: Token not found" 
221+ 
222+ 
223+ def  test_get_simplehash_nfts_by_owner (mock_httpx_client , mock_settings ):
224+     mock_response  =  {
225+         "ownedNfts" : [MOCK_NFT_ALCHEMY_RESPONSE ],
226+         "totalCount" : 1 ,
227+         "pageKey" : None ,
228+     }
229+ 
230+     # Configure the mock response 
231+     mock_httpx_client .get .return_value .json .return_value  =  mock_response 
232+ 
233+     response  =  client .get (
234+         "/simplehash/api/v0/nfts/owners?wallet_addresses=0x123&chains=ethereum" 
235+     )
236+     assert  response .status_code  ==  200 
237+     data  =  response .json ()
238+ 
239+     sh_response  =  SimpleHashNFTResponse .model_validate (data )
240+     assert  len (sh_response .nfts ) ==  1 
241+     nft  =  sh_response .nfts [0 ]
242+     assert  nft .chain  ==  "ethereum" 
243+     assert  nft .contract_address  ==  "0x123" 
244+     assert  nft .token_id  ==  "1" 
245+     assert  nft .name  ==  "Mock NFT #1" 
246+     assert  nft .description  ==  "A mock NFT description" 
247+     assert  nft .image_url  ==  "https://example.com/cached.jpg" 
248+     assert  nft .background_color  is  None 
249+     assert  nft .external_url  is  None 
250+     assert  nft .contract .type  ==  "ERC721" 
251+     assert  nft .contract .name  ==  "MockNFT" 
252+     assert  nft .contract .symbol  ==  "MOCK" 
253+     assert  nft .collection .name  ==  "MockNFT" 
254+     assert  nft .collection .spam_score  ==  0 
255+     attributes  =  nft .extra_metadata .attributes 
256+     assert  len (attributes ) ==  2 
257+     assert  attributes [0 ].trait_type  ==  "Color" 
258+     assert  attributes [0 ].value  ==  "Red" 
259+     assert  attributes [1 ].trait_type  ==  "Shape" 
260+     assert  attributes [1 ].value  ==  "Round" 
261+ 
262+ 
263+ def  test_get_simplehash_nfts_by_owner_multiple_chains (mock_httpx_client , mock_settings ):
264+     mock_response  =  {
265+         "ownedNfts" : [MOCK_NFT_ALCHEMY_RESPONSE ],
266+         "totalCount" : 1 ,
267+         "pageKey" : None ,
268+     }
269+ 
270+     mock_httpx_client .get .return_value .json .return_value  =  mock_response 
271+ 
272+     response  =  client .get (
273+         "/simplehash/api/v0/nfts/owners?wallet_addresses=0x123&chains=ethereum,polygon" 
274+     )
275+     assert  response .status_code  ==  200 
276+     data  =  response .json ()
277+     sh_response  =  SimpleHashNFTResponse .model_validate (data )
278+     # Should get 2 NFTs - one from Ethereum and one from Polygon 
279+     assert  len (sh_response .nfts ) ==  2 
280+ 
281+ 
282+ def  test_get_simplehash_nfts_by_owner_with_cursor (mock_httpx_client , mock_settings ):
283+     mock_response  =  {
284+         "ownedNfts" : [MOCK_NFT_ALCHEMY_RESPONSE ],
285+         "totalCount" : 1 ,
286+         "pageKey" : "next_page_key" ,
287+     }
288+ 
289+     mock_httpx_client .get .return_value .json .return_value  =  mock_response 
290+ 
291+     response  =  client .get (
292+         "/simplehash/api/v0/nfts/owners?wallet_addresses=0x123&chains=ethereum&cursor=page123" 
293+     )
294+     assert  response .status_code  ==  200 
295+     data  =  response .json ()
296+     sh_response  =  SimpleHashNFTResponse .model_validate (data )
297+     assert  len (sh_response .nfts ) ==  1 
298+     assert  sh_response .next_cursor  ==  "next_page_key" 
299+ 
300+ 
301+ def  test_get_simplehash_compressed_nft_proof (mock_httpx_client , mock_settings ):
302+     mock_response  =  {
303+         "result" : {
304+             "proof" : ["hash1" , "hash2" , "hash3" ],
305+             "root" : "root_hash" ,
306+             "tree_id" : "tree_123" ,
307+             "node_index" : 42 ,
308+             "leaf" : "leaf_hash" ,
309+             "status" : "finalized" ,
310+         },
311+         "error" : None ,
312+     }
313+ 
314+     mock_httpx_client .post .return_value .json .return_value  =  mock_response 
315+ 
316+     response  =  client .get ("/simplehash/api/v0/nfts/proof/solana/mint123" )
317+     assert  response .status_code  ==  200 
318+     data  =  response .json ()
319+     sh_response  =  SolanaAssetMerkleProof .model_validate (data )
320+     assert  sh_response .root  ==  "root_hash" 
321+     assert  sh_response .tree_id  ==  "tree_123" 
322+     assert  sh_response .node_index  ==  42 
323+     assert  sh_response .leaf  ==  "leaf_hash" 
324+     assert  sh_response .proof  ==  ["hash1" , "hash2" , "hash3" ]
325+ 
326+ 
327+ def  test_get_simplehash_nfts_by_ids_solana (mock_httpx_client , mock_settings ):
328+     mock_solana_asset  =  {
329+         "id" : "mint123" ,
330+         "interface" : "ProgrammableNFT" ,
331+         "content" : {
332+             "metadata" : {
333+                 "name" : "Mock Solana NFT" ,
334+                 "symbol" : "MSN" ,
335+                 "description" : "A mock Solana NFT" ,
336+                 "attributes" : [],
337+             },
338+             "links" : {
339+                 "image" : "https://example.com/solana-image.jpg" ,
340+                 "external_url" : "https://example.com" ,
341+             },
342+             "json_uri" : "https://example.com/metadata/solana.json" ,
343+         },
344+         "grouping" : [],
345+         "mutable" : False ,
346+         "burnt" : False ,
347+     }
348+ 
349+     mock_response  =  {
350+         "result" : [mock_solana_asset ],
351+     }
352+ 
353+     mock_httpx_client .post .return_value .json .return_value  =  mock_response 
354+ 
355+     response  =  client .get ("/simplehash/api/v0/nfts/assets?nft_ids=solana.mint123" )
356+     assert  response .status_code  ==  200 
357+     data  =  response .json ()
358+     sh_response  =  SimpleHashNFTResponse .model_validate (data )
359+     assert  len (sh_response .nfts ) ==  1 
360+ 
361+ 
362+ def  test_get_simplehash_nfts_by_ids (mock_httpx_client , mock_settings ):
363+     mock_response  =  {
364+         "nfts" : [MOCK_NFT_ALCHEMY_RESPONSE ],
365+     }
366+ 
367+     mock_httpx_client .post .return_value .json .return_value  =  mock_response 
368+ 
369+     response  =  client .get (
370+         "/simplehash/api/v0/nfts/assets?nft_ids=ethereum.0x123.456,polygon.0x789.101112" 
371+     )
372+     assert  response .status_code  ==  200 
373+     data  =  response .json ()
374+     sh_response  =  SimpleHashNFTResponse .model_validate (data )
375+     # Should get 2 NFTs - one from Ethereum and one from Polygon 
376+     assert  len (sh_response .nfts ) ==  2 
0 commit comments