Skip to content

Commit c61e420

Browse files
committed
Feat: AuthenticatedVoucher integrations
1 parent 7ca3034 commit c61e420

File tree

2 files changed

+173
-0
lines changed

2 files changed

+173
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from typing import TYPE_CHECKING, Optional, overload
2+
3+
from typing_extensions import override
4+
5+
from aleph.sdk.types import Voucher
6+
7+
from .voucher import Vouchers
8+
9+
if TYPE_CHECKING:
10+
from aleph.sdk.client.abstract import AuthenticatedAlephClient
11+
12+
13+
class AuthenticatedVoucher(Vouchers):
14+
"""
15+
This service is same logic than Vouchers but allow to don't pass address
16+
to use account address
17+
"""
18+
19+
def __init__(self, client: "AuthenticatedAlephClient"):
20+
super().__init__(client)
21+
22+
@overload
23+
def _resolve_address(self, address: str) -> str: ...
24+
25+
@overload
26+
def _resolve_address(self, address: None) -> str: ...
27+
28+
@override
29+
def _resolve_address(self, address: Optional[str] = None) -> str:
30+
"""
31+
Resolve the address to use. Prefer the provided address, fallback to account.
32+
"""
33+
if address:
34+
return address
35+
if self._client.account:
36+
return self._client.account.get_address()
37+
38+
raise ValueError("No address provided and no account configured")
39+
40+
@override
41+
async def get_vouchers(self, address: Optional[str] = None) -> list[Voucher]:
42+
"""
43+
Retrieve all vouchers for the account / specific address, across EVM and Solana chains.
44+
"""
45+
address = address or self._client.account.get_address()
46+
return await super().get_vouchers(address=address)
47+
48+
@override
49+
async def get_evm_vouchers(self, address: Optional[str] = None) -> list[Voucher]:
50+
"""
51+
Retrieve vouchers specific to EVM chains for a specific address.
52+
"""
53+
address = address or self._client.account.get_address()
54+
return await super().get_evm_vouchers(address=address)
55+
56+
@override
57+
async def get_solana_vouchers(self, address: Optional[str] = None) -> list[Voucher]:
58+
"""
59+
Fetch Solana vouchers for a specific address.
60+
"""
61+
address = address or self._client.account.get_address()
62+
return await super().get_solana_vouchers(address=address)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
from unittest.mock import AsyncMock, MagicMock, patch
2+
3+
import pytest
4+
5+
from aleph.sdk.client.services.authenticated_voucher import AuthenticatedVoucher
6+
7+
from .test_voucher import (
8+
MOCK_ADDRESS,
9+
MOCK_METADATA,
10+
MOCK_SOLANA_ADDRESS,
11+
MOCK_SOLANA_REGISTRY,
12+
MOCK_VOUCHER_ID,
13+
)
14+
15+
16+
def test_resolve_address_with_argument():
17+
client = MagicMock()
18+
service = AuthenticatedVoucher(client=client)
19+
assert service._resolve_address(address="custom-address") == "custom-address"
20+
21+
22+
def test_resolve_address_with_account_fallback():
23+
mock_account = MagicMock()
24+
mock_account.get_address.return_value = MOCK_ADDRESS
25+
26+
client = MagicMock()
27+
client.account = mock_account
28+
29+
service = AuthenticatedVoucher(client=client)
30+
assert service._resolve_address(address=None) == MOCK_ADDRESS
31+
mock_account.get_address.assert_called_once()
32+
33+
34+
def test_resolve_address_no_address_no_account():
35+
client = MagicMock()
36+
client.account = None
37+
38+
service = AuthenticatedVoucher(client=client)
39+
40+
with pytest.raises(
41+
ValueError, match="No address provided and no account configured"
42+
):
43+
service._resolve_address(address=None)
44+
45+
46+
@pytest.mark.asyncio
47+
async def test_get_vouchers_fallback_to_account(
48+
make_mock_aiohttp_session, mock_post_response
49+
):
50+
mock_account = MagicMock()
51+
mock_account.get_address.return_value = MOCK_ADDRESS
52+
53+
mock_client = MagicMock()
54+
mock_client.account = mock_account
55+
mock_client.get_posts = AsyncMock(return_value=mock_post_response)
56+
57+
service = AuthenticatedVoucher(client=mock_client)
58+
59+
metadata_session = make_mock_aiohttp_session(MOCK_METADATA)
60+
61+
with patch("aiohttp.ClientSession", return_value=metadata_session):
62+
vouchers = await service.get_vouchers()
63+
64+
assert len(vouchers) == 1
65+
assert vouchers[0].name == MOCK_METADATA["name"]
66+
mock_account.get_address.assert_called_once()
67+
68+
69+
@pytest.mark.asyncio
70+
async def test_get_evm_vouchers_fallback_to_account(
71+
make_mock_aiohttp_session, mock_post_response
72+
):
73+
mock_account = MagicMock()
74+
mock_account.get_address.return_value = MOCK_ADDRESS
75+
76+
mock_client = MagicMock()
77+
mock_client.account = mock_account
78+
mock_client.get_posts = AsyncMock(return_value=mock_post_response)
79+
80+
service = AuthenticatedVoucher(client=mock_client)
81+
82+
metadata_session = make_mock_aiohttp_session(MOCK_METADATA)
83+
84+
with patch("aiohttp.ClientSession", return_value=metadata_session):
85+
vouchers = await service.get_evm_vouchers()
86+
87+
assert len(vouchers) == 1
88+
assert vouchers[0].id == MOCK_VOUCHER_ID
89+
90+
91+
@pytest.mark.asyncio
92+
async def test_get_solana_vouchers_fallback_to_account(make_mock_aiohttp_session):
93+
mock_account = MagicMock()
94+
mock_account.get_address.return_value = MOCK_SOLANA_ADDRESS
95+
96+
mock_client = MagicMock()
97+
mock_client.account = mock_account
98+
99+
service = AuthenticatedVoucher(client=mock_client)
100+
101+
registry_session = make_mock_aiohttp_session(MOCK_SOLANA_REGISTRY)
102+
metadata_session = make_mock_aiohttp_session(MOCK_METADATA)
103+
104+
with patch(
105+
"aiohttp.ClientSession", side_effect=[registry_session, metadata_session]
106+
):
107+
vouchers = await service.get_solana_vouchers()
108+
109+
assert len(vouchers) == 1
110+
assert vouchers[0].id == "solticket123"
111+
assert vouchers[0].name == MOCK_METADATA["name"]

0 commit comments

Comments
 (0)