fix: remove unnecessary 'method' key from API request data and improve error handling for expired cookies and non-200 HTTP responses

This commit is contained in:
bohd4nx
2026-04-21 20:06:11 +03:00
parent 8c7423a6ab
commit 7923dff8b2
10 changed files with 53 additions and 5 deletions
+1 -1
View File
@@ -45,7 +45,7 @@ async def search_gifts(
FragmentAPIError: If the Fragment API returns an error.
UnexpectedError: For any other unexpected failure.
"""
data: dict[str, Any] = {"method": "searchAuctions", "type": "gifts", "query": query}
data: dict[str, Any] = {"type": "gifts", "query": query}
if collection is not None:
data["collection"] = collection
if sort is not None:
+1 -1
View File
@@ -37,7 +37,7 @@ async def search_numbers(
FragmentAPIError: If the Fragment API returns an error.
UnexpectedError: For any other unexpected failure.
"""
data: dict[str, Any] = {"method": "searchAuctions", "type": "numbers", "query": query}
data: dict[str, Any] = {"type": "numbers", "query": query}
if sort is not None:
data["sort"] = sort
if filter is not None:
+1 -1
View File
@@ -37,7 +37,7 @@ async def search_usernames(
FragmentAPIError: If the Fragment API returns an error.
UnexpectedError: For any other unexpected failure.
"""
data: dict[str, Any] = {"method": "searchAuctions", "type": "usernames", "query": query}
data: dict[str, Any] = {"type": "usernames", "query": query}
if sort is not None:
data["sort"] = sort
if filter is not None:
+1
View File
@@ -46,6 +46,7 @@ class CookieError(ClientError):
"Fragment cookies not found in {browser}: {keys}. "
"Make sure you are logged in to {url} and have connected your TON wallet in {browser}."
)
EXPIRED = "Fragment session cookie expired at {expires}. Log in to fragment.com in your browser and extract fresh cookies."
class FragmentAPIError(FragmentError):
+5
View File
@@ -61,6 +61,11 @@ def get_cookies_from_browser(browser: str = "chrome") -> CookieResult:
continue
break
if expires_iso:
expires_dt = datetime.fromisoformat(expires_iso)
if expires_dt < datetime.now(timezone.utc):
raise CookieError(CookieError.EXPIRED.format(expires=expires_iso))
return CookieResult(
cookies={k: cookie_map[k] for k in REQUIRED_COOKIE_KEYS},
expires=expires_iso,
+2
View File
@@ -111,6 +111,8 @@ async def fragment_request(
headers=headers,
data=data,
)
if resp.status_code != 200:
raise FragmentPageError(FragmentPageError.BAD_STATUS.format(status=resp.status_code, url=f"{FRAGMENT_BASE_URL}/api"))
return parse_json_response(resp, data.get("method", "request"))
+1 -1
View File
@@ -34,7 +34,7 @@ async def process_transaction(client: FragmentClient, transaction_data: dict[str
TransactionError: If the payload is malformed or the broadcast fails.
WalletError: If the wallet balance is too low or cannot be fetched.
"""
if "transaction" not in transaction_data or "messages" not in transaction_data["transaction"]:
if "transaction" not in transaction_data or not transaction_data["transaction"].get("messages"):
raise TransactionError(TransactionError.INVALID_PAYLOAD)
message = transaction_data["transaction"]["messages"][0]
+6
View File
@@ -103,6 +103,12 @@ async def test_invalid_payload_raises() -> None:
await process_transaction(_make_client(), {"transaction": {}})
@pytest.mark.asyncio
async def test_empty_messages_list_raises() -> None:
with pytest.raises(TransactionError):
await process_transaction(_make_client(), {"transaction": {"messages": []}})
@pytest.mark.asyncio
async def test_balance_check_failed_raises_wallet_error() -> None:
wallet = _make_wallet(balance_nanotons=1_000_000_000)
+20 -1
View File
@@ -1,10 +1,13 @@
"""Unit tests for FragmentClient.call() — raw Fragment API access."""
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, MagicMock, patch
import httpx
import pytest
from pyfragment import FragmentClient
from pyfragment.types import FragmentPageError
from pyfragment.utils.http import fragment_request
from tests.shared import FAKE_HASH, FAKE_RESPONSE
# client.call() mocked tests
@@ -61,3 +64,19 @@ async def test_call_merges_extra_data(client: FragmentClient) -> None:
_, _, _, sent_data = mock_request.call_args.args
assert sent_data == {"method": "anyMethod", "key": "value", "num": 7}
# fragment_request HTTP status tests
@pytest.mark.asyncio
async def test_fragment_request_non_200_raises() -> None:
"""fragment_request raises FragmentPageError on non-200 HTTP responses."""
response = MagicMock(spec=httpx.Response)
response.status_code = 429
session = AsyncMock(spec=httpx.AsyncClient)
session.post = AsyncMock(return_value=response)
with pytest.raises(FragmentPageError, match="429"):
await fragment_request(session, FAKE_HASH, {}, {"method": "anyMethod"})
+15
View File
@@ -106,6 +106,21 @@ def test_empty_cookie_value_treated_as_missing() -> None:
get_cookies_from_browser("chrome")
# expired cookie tests
def test_expired_cookie_raises() -> None:
expired_jar = [
{"name": "stel_ssid", "value": "abc123", "expires": "2020-01-01T00:00:00.000Z"},
{"name": "stel_dt", "value": "-120"},
{"name": "stel_token", "value": "tok_xyz"},
{"name": "stel_ton_token", "value": "ton_xyz"},
]
with patch(PATCH, _mock_rookiepy(expired_jar)):
with pytest.raises(CookieError, match="expired"):
get_cookies_from_browser("chrome")
# read failure tests