From 218c9dec3eb9c8118ac8ae60a5b2ef03c8360e95 Mon Sep 17 00:00:00 2001 From: Mauro de Carvalho Date: Mon, 1 Dec 2025 10:18:36 -0300 Subject: [PATCH 1/5] fix: updating raise_for_status --- barte/client.py | 47 +++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/barte/client.py b/barte/client.py index c995898..38a90de 100644 --- a/barte/client.py +++ b/barte/client.py @@ -75,7 +75,7 @@ def _request( path: str, params: Optional[Dict[str, Any]] = None, json: Optional[Dict[str, Any]] = None, - ) -> Dict[str, Any]: + ) -> Union[Dict[str, Any], List[Any], None]: """ Private method to centralize HTTP requests. @@ -86,32 +86,41 @@ def _request( json: JSON body for POST, PATCH requests. Returns: - The response JSON as a dictionary. + The response JSON as a dictionary or list. Raises: - HTTPError: If the HTTP request returned an unsuccessful status code. + BarteError: If the API returns an error response with Barte error codes. + HTTPError: If the HTTP request returned an unsuccessful status code + without a structured error response. """ url = f"{self.base_url}{path}" response = self.session.request(method, url, params=params, json=json) - response.raise_for_status() if response.status_code == 204: return None - return response.json() + try: + json_response = response.json() + except ValueError: + response.raise_for_status() + return None + + if isinstance(json_response, dict) and "errors" in json_response: + error_response = from_dict( + data_class=ErrorResponse, data=json_response, config=DACITE_CONFIG + ) + error_response.raise_exception(response=response) + + if not response.ok: + response.raise_for_status() + + return json_response def create_order(self, data: Union[Dict[str, Any], OrderPayload]) -> Order: """Create a new order""" if isinstance(data, OrderPayload): data = asdict(data) json_response = self._request("POST", "/v2/orders", json=data) - - if "errors" in json_response: - error_response = from_dict( - data_class=ErrorResponse, data=json_response, config=DACITE_CONFIG - ) - error_response.raise_exception(response=json_response) - return from_dict(data_class=Order, data=json_response, config=DACITE_CONFIG) def get_charge(self, charge_id: str) -> Charge: @@ -176,13 +185,6 @@ def refund_charge(self, charge_id: str, as_fraud: Optional[bool] = False) -> Cha json_response = self._request( "PATCH", f"/v2/charges/{charge_id}/refund", json={"asFraud": as_fraud} ) - - if "errors" in json_response: - error_response = from_dict( - data_class=ErrorResponse, data=json_response, config=DACITE_CONFIG - ) - error_response.raise_exception(response=json_response) - return from_dict(data_class=Charge, data=json_response, config=DACITE_CONFIG) def partial_refund_charge( @@ -196,13 +198,6 @@ def partial_refund_charge( json_response = self._request( "PATCH", f"/v2/charges/partial-refund/{charge_id}", json={"value": value} ) - - if isinstance(json_response, dict) and "errors" in json_response: - error_response = from_dict( - data_class=ErrorResponse, data=json_response, config=DACITE_CONFIG - ) - error_response.raise_exception(response=json_response) - return [ from_dict(data_class=PartialRefund, data=item, config=DACITE_CONFIG) for item in json_response From 4ead23f89351fa7d76b073801aed1a33869959e9 Mon Sep 17 00:00:00 2001 From: Mauro de Carvalho Date: Mon, 1 Dec 2025 12:01:57 -0300 Subject: [PATCH 2/5] test: adding more tests to client --- tests/test_client.py | 84 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index a9d60a6..890464e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -791,3 +791,87 @@ def test_refund_charge_with_error_and_charge_uuid( assert exc_info.value.code == "BAR-7010" assert exc_info.value.message == "Não foi possível realizar o reembolso" assert exc_info.value.charge_uuid == "abc123-charge-uuid" + + @patch("barte.client.requests.Session.request") + def test_request_raises_barte_error_on_http_error_with_error_body( + self, mock_request, barte_client, mock_order_error_response + ): + """Test _request raises BarteError when API returns HTTP error with structured error body""" + mock_response = Mock() + mock_response.status_code = 400 + mock_response.ok = False + mock_response.json.return_value = mock_order_error_response + mock_request.return_value = mock_response + + with pytest.raises(BarteError) as exc_info: + barte_client._request("POST", "/v2/orders", json={}) + + assert exc_info.value.code == "BAR-7005" + assert exc_info.value.message == "Erro no Pagamento" + assert ( + exc_info.value.action + == "Verifique os detalhes da transação e/ou contate a central do seu cartão" + ) + assert exc_info.value.charge_uuid == "c4e5bf04-7dd3-42bd-9904-f46c8ed43b3c" + + @patch("barte.client.requests.Session.request") + def test_request_raises_http_error_on_http_error_without_error_body( + self, mock_request, barte_client + ): + """Test _request raises HTTPError when API returns HTTP error without structured error body""" + from requests.exceptions import HTTPError + + mock_response = Mock() + mock_response.status_code = 500 + mock_response.ok = False + mock_response.json.return_value = {"message": "Internal Server Error"} + mock_response.raise_for_status.side_effect = HTTPError("500 Server Error") + mock_request.return_value = mock_response + + with pytest.raises(HTTPError): + barte_client._request("GET", "/v2/orders") + + @patch("barte.client.requests.Session.request") + def test_request_raises_http_error_on_invalid_json(self, mock_request, barte_client): + """Test _request raises HTTPError when response is not valid JSON""" + from requests.exceptions import HTTPError + + mock_response = Mock() + mock_response.status_code = 500 + mock_response.json.side_effect = ValueError("No JSON object could be decoded") + mock_response.raise_for_status.side_effect = HTTPError("500 Server Error") + mock_request.return_value = mock_response + + with pytest.raises(HTTPError): + barte_client._request("GET", "/v2/orders") + + @patch("barte.client.requests.Session.request") + def test_request_returns_none_on_204(self, mock_request, barte_client): + """Test _request returns None when API returns 204 No Content""" + mock_response = Mock() + mock_response.status_code = 204 + mock_request.return_value = mock_response + + result = barte_client._request("DELETE", "/v2/charges/123") + + assert result is None + mock_response.json.assert_not_called() + + @patch("barte.client.requests.Session.request") + def test_request_handles_list_response(self, mock_request, barte_client): + """Test _request correctly handles list JSON responses""" + list_response = [ + {"uuid": "item1", "value": 100}, + {"uuid": "item2", "value": 200}, + ] + mock_response = Mock() + mock_response.status_code = 200 + mock_response.ok = True + mock_response.json.return_value = list_response + mock_request.return_value = mock_response + + result = barte_client._request("GET", "/v2/charges/partial-refund/123") + + assert result == list_response + assert isinstance(result, list) + assert len(result) == 2 From 04ec34a4a8f9d464d5136b8501cfc47550ed5516 Mon Sep 17 00:00:00 2001 From: Mauro de Carvalho Date: Mon, 1 Dec 2025 12:04:07 -0300 Subject: [PATCH 3/5] fix: updating HTTPError import --- tests/test_client.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_client.py b/tests/test_client.py index 890464e..ab3abea 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -4,12 +4,12 @@ import pytest from dacite import from_dict +from requests.exceptions import HTTPError from barte import BarteClient, CardToken, Charge, PartialRefund, PixCharge from barte.exceptions import BarteError from barte.models import DACITE_CONFIG, InstallmentOption, Order - @pytest.fixture def barte_client(): client = BarteClient(api_key="test_key", environment="sandbox") @@ -819,8 +819,6 @@ def test_request_raises_http_error_on_http_error_without_error_body( self, mock_request, barte_client ): """Test _request raises HTTPError when API returns HTTP error without structured error body""" - from requests.exceptions import HTTPError - mock_response = Mock() mock_response.status_code = 500 mock_response.ok = False @@ -834,8 +832,6 @@ def test_request_raises_http_error_on_http_error_without_error_body( @patch("barte.client.requests.Session.request") def test_request_raises_http_error_on_invalid_json(self, mock_request, barte_client): """Test _request raises HTTPError when response is not valid JSON""" - from requests.exceptions import HTTPError - mock_response = Mock() mock_response.status_code = 500 mock_response.json.side_effect = ValueError("No JSON object could be decoded") From 1f91cc66ef015f383e29aa7ec109bb7c40317de6 Mon Sep 17 00:00:00 2001 From: Mauro de Carvalho Date: Mon, 1 Dec 2025 12:30:46 -0300 Subject: [PATCH 4/5] style: apply PEP8 --- tests/test_client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_client.py b/tests/test_client.py index ab3abea..6665ede 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -10,6 +10,7 @@ from barte.exceptions import BarteError from barte.models import DACITE_CONFIG, InstallmentOption, Order + @pytest.fixture def barte_client(): client = BarteClient(api_key="test_key", environment="sandbox") @@ -830,7 +831,9 @@ def test_request_raises_http_error_on_http_error_without_error_body( barte_client._request("GET", "/v2/orders") @patch("barte.client.requests.Session.request") - def test_request_raises_http_error_on_invalid_json(self, mock_request, barte_client): + def test_request_raises_http_error_on_invalid_json( + self, mock_request, barte_client + ): """Test _request raises HTTPError when response is not valid JSON""" mock_response = Mock() mock_response.status_code = 500 From d7007eafe5b70dfdd0e24e95d199e8e153bf9ec5 Mon Sep 17 00:00:00 2001 From: Mauro de Carvalho Date: Mon, 1 Dec 2025 14:09:04 -0300 Subject: [PATCH 5/5] fix: removing unnecessary conditional check --- barte/client.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/barte/client.py b/barte/client.py index 38a90de..4251868 100644 --- a/barte/client.py +++ b/barte/client.py @@ -111,8 +111,7 @@ def _request( ) error_response.raise_exception(response=response) - if not response.ok: - response.raise_for_status() + response.raise_for_status() return json_response