Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions barte/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ def get_instance(cls) -> "BarteClient":
)
return cls._instance

def _extract_error_data(
self, json_response: Union[Dict[str, Any], List[Any]]
) -> Optional[Dict[str, Any]]:
"""Extract error data from API response if present."""
if isinstance(json_response, dict) and "errors" in json_response:
return json_response

if isinstance(json_response, list) and json_response:
first_item = json_response[0]
if isinstance(first_item, dict) and "errors" in first_item:
return first_item

return None

def _request(
self,
method: str,
Expand Down Expand Up @@ -105,9 +119,9 @@ def _request(
response.raise_for_status()
return None

if isinstance(json_response, dict) and "errors" in json_response:
if error_data := self._extract_error_data(json_response):
error_response = from_dict(
data_class=ErrorResponse, data=json_response, config=DACITE_CONFIG
data_class=ErrorResponse, data=error_data, config=DACITE_CONFIG
)
error_response.raise_exception(response=response)

Expand Down
2 changes: 1 addition & 1 deletion barte/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ class ErrorMetadata:
@dataclass
class ErrorResponse:
errors: List[ErrorItem]
metadata: ErrorMetadata
metadata: Optional[ErrorMetadata] = None

def raise_exception(self, response=None):
error = self.errors[0]
Expand Down
37 changes: 37 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,22 @@ def mock_refund_error_response_with_charge_uuid():
}


@pytest.fixture
def mock_partial_refund_list_error_response():
"""Error response in list format (used by partial_refund endpoint)"""
return [
{
"errors": [
{
"code": "BAR-3002",
"title": "Não é possível estornar esse valor.",
"description": "Valor solicitado é maior que o valor disponível na cobrança.",
}
]
}
]


@pytest.fixture
def mock_charge_response():
return {
Expand Down Expand Up @@ -874,3 +890,24 @@ def test_request_handles_list_response(self, mock_request, barte_client):
assert result == list_response
assert isinstance(result, list)
assert len(result) == 2

@patch("barte.client.requests.Session.request")
def test_partial_refund_charge_with_list_error_response(
self, mock_request, barte_client, mock_partial_refund_list_error_response
):
"""Test partial refund charge returns BarteError when API returns error in list format"""
mock_request.return_value.json.return_value = (
mock_partial_refund_list_error_response
)
mock_request.return_value.raise_for_status = Mock()

with pytest.raises(BarteError) as exc_info:
barte_client.partial_refund_charge(
"d54f6553-8bcf-4376-a995-aaffb6d29492", value=Decimal("101.00")
)

assert exc_info.value.code == "BAR-3002"
assert (
exc_info.value.message
== "Valor solicitado é maior que o valor disponível na cobrança."
)