diff --git a/postgrest_py/base_request_builder.py b/postgrest_py/base_request_builder.py index 6ec18100..18cff5cd 100644 --- a/postgrest_py/base_request_builder.py +++ b/postgrest_py/base_request_builder.py @@ -123,9 +123,9 @@ def get_count_from_http_request_response( content_range_header: Optional[str] = request_response.headers.get( "content-range" ) - if is_count_in_prefer_header and content_range_header: - return cls.get_count_from_content_range_header(content_range_header) - return None + if not (is_count_in_prefer_header and content_range_header): + return None + return cls.get_count_from_content_range_header(content_range_header) @classmethod def from_http_request_response( diff --git a/tests/_async/test_request_builder.py b/tests/_async/test_request_builder.py index 85339b01..09ff9bcf 100644 --- a/tests/_async/test_request_builder.py +++ b/tests/_async/test_request_builder.py @@ -1,6 +1,10 @@ +from typing import Any, Dict, List + import pytest +from httpx import Request, Response from postgrest_py import AsyncRequestBuilder +from postgrest_py.base_request_builder import APIResponse from postgrest_py.types import CountMethod from postgrest_py.utils import AsyncClient @@ -114,3 +118,191 @@ def test_delete_with_count(self, request_builder: AsyncRequestBuilder): ] assert builder.http_method == "DELETE" assert builder.json == {} + + +@pytest.fixture +def api_response_with_error() -> Dict[str, Any]: + return { + "message": "Route GET:/countries?select=%2A not found", + "error": "Not Found", + "statusCode": 404, + } + + +@pytest.fixture +def api_response() -> List[Dict[str, Any]]: + return [ + { + "id": 1, + "name": "Bonaire, Sint Eustatius and Saba", + "iso2": "BQ", + "iso3": "BES", + "local_name": None, + "continent": None, + }, + { + "id": 2, + "name": "Curaçao", + "iso2": "CW", + "iso3": "CUW", + "local_name": None, + "continent": None, + }, + ] + + +@pytest.fixture +def content_range_header_with_count() -> str: + return "0-1/2" + + +@pytest.fixture +def content_range_header_without_count() -> str: + return "0-1" + + +@pytest.fixture +def prefer_header_with_count() -> str: + return "count=exact" + + +@pytest.fixture +def prefer_header_without_count() -> str: + return "random prefer header" + + +@pytest.fixture +def request_response_without_prefer_header() -> Response: + return Response( + status_code=200, request=Request(method="GET", url="http://example.com") + ) + + +@pytest.fixture +def request_response_with_prefer_header_without_count( + prefer_header_without_count: str, +) -> Response: + return Response( + status_code=200, + request=Request( + method="GET", + url="http://example.com", + headers={"prefer": prefer_header_without_count}, + ), + ) + + +@pytest.fixture +def request_response_with_prefer_header_with_count_and_content_range( + prefer_header_with_count: str, content_range_header_with_count: str +) -> Response: + return Response( + status_code=200, + headers={"content-range": content_range_header_with_count}, + request=Request( + method="GET", + url="http://example.com", + headers={"prefer": prefer_header_with_count}, + ), + ) + + +@pytest.fixture +def request_response_with_data( + prefer_header_with_count: str, + content_range_header_with_count: str, + api_response: List[Dict[str, Any]], +) -> Response: + return Response( + status_code=200, + headers={"content-range": content_range_header_with_count}, + json=api_response, + request=Request( + method="GET", + url="http://example.com", + headers={"prefer": prefer_header_with_count}, + ), + ) + + +class TestApiResponse: + def test_response_raises_when_api_error( + self, api_response_with_error: Dict[str, Any] + ): + with pytest.raises(ValueError): + APIResponse(data=api_response_with_error) + + def test_parses_valid_response_only_data(self, api_response: List[Dict[str, Any]]): + result = APIResponse(data=api_response) + assert result.data == api_response + + def test_parses_valid_response_data_and_count( + self, api_response: List[Dict[str, Any]] + ): + count = len(api_response) + result = APIResponse(data=api_response, count=count) + assert result.data == api_response + assert result.count == count + + def test_get_count_from_content_range_header_with_count( + self, content_range_header_with_count: str + ): + assert ( + APIResponse.get_count_from_content_range_header( + content_range_header_with_count + ) + == 2 + ) + + def test_get_count_from_content_range_header_without_count( + self, content_range_header_without_count: str + ): + assert ( + APIResponse.get_count_from_content_range_header( + content_range_header_without_count + ) + is None + ) + + def test_is_count_in_prefer_header_true(self, prefer_header_with_count: str): + assert APIResponse.is_count_in_prefer_header(prefer_header_with_count) + + def test_is_count_in_prefer_header_false(self, prefer_header_without_count: str): + assert not APIResponse.is_count_in_prefer_header(prefer_header_without_count) + + def test_get_count_from_http_request_response_without_prefer_header( + self, request_response_without_prefer_header: Response + ): + assert ( + APIResponse.get_count_from_http_request_response( + request_response_without_prefer_header + ) + is None + ) + + def test_get_count_from_http_request_response_with_prefer_header_without_count( + self, request_response_with_prefer_header_without_count: Response + ): + assert ( + APIResponse.get_count_from_http_request_response( + request_response_with_prefer_header_without_count + ) + is None + ) + + def test_get_count_from_http_request_response_with_count_and_content_range( + self, request_response_with_prefer_header_with_count_and_content_range: Response + ): + assert ( + APIResponse.get_count_from_http_request_response( + request_response_with_prefer_header_with_count_and_content_range + ) + == 2 + ) + + def test_from_http_request_response_constructor( + self, request_response_with_data: Response, api_response: List[Dict[str, Any]] + ): + result = APIResponse.from_http_request_response(request_response_with_data) + assert result.data == api_response + assert result.count == 2 diff --git a/tests/_sync/test_request_builder.py b/tests/_sync/test_request_builder.py index 4fb38500..48a93ae2 100644 --- a/tests/_sync/test_request_builder.py +++ b/tests/_sync/test_request_builder.py @@ -1,6 +1,10 @@ +from typing import Any, Dict, List + import pytest +from httpx import Request, Response from postgrest_py import SyncRequestBuilder +from postgrest_py.base_request_builder import APIResponse from postgrest_py.types import CountMethod from postgrest_py.utils import SyncClient @@ -114,3 +118,191 @@ def test_delete_with_count(self, request_builder: SyncRequestBuilder): ] assert builder.http_method == "DELETE" assert builder.json == {} + + +@pytest.fixture +def api_response_with_error() -> Dict[str, Any]: + return { + "message": "Route GET:/countries?select=%2A not found", + "error": "Not Found", + "statusCode": 404, + } + + +@pytest.fixture +def api_response() -> List[Dict[str, Any]]: + return [ + { + "id": 1, + "name": "Bonaire, Sint Eustatius and Saba", + "iso2": "BQ", + "iso3": "BES", + "local_name": None, + "continent": None, + }, + { + "id": 2, + "name": "Curaçao", + "iso2": "CW", + "iso3": "CUW", + "local_name": None, + "continent": None, + }, + ] + + +@pytest.fixture +def content_range_header_with_count() -> str: + return "0-1/2" + + +@pytest.fixture +def content_range_header_without_count() -> str: + return "0-1" + + +@pytest.fixture +def prefer_header_with_count() -> str: + return "count=exact" + + +@pytest.fixture +def prefer_header_without_count() -> str: + return "random prefer header" + + +@pytest.fixture +def request_response_without_prefer_header() -> Response: + return Response( + status_code=200, request=Request(method="GET", url="http://example.com") + ) + + +@pytest.fixture +def request_response_with_prefer_header_without_count( + prefer_header_without_count: str, +) -> Response: + return Response( + status_code=200, + request=Request( + method="GET", + url="http://example.com", + headers={"prefer": prefer_header_without_count}, + ), + ) + + +@pytest.fixture +def request_response_with_prefer_header_with_count_and_content_range( + prefer_header_with_count: str, content_range_header_with_count: str +) -> Response: + return Response( + status_code=200, + headers={"content-range": content_range_header_with_count}, + request=Request( + method="GET", + url="http://example.com", + headers={"prefer": prefer_header_with_count}, + ), + ) + + +@pytest.fixture +def request_response_with_data( + prefer_header_with_count: str, + content_range_header_with_count: str, + api_response: List[Dict[str, Any]], +) -> Response: + return Response( + status_code=200, + headers={"content-range": content_range_header_with_count}, + json=api_response, + request=Request( + method="GET", + url="http://example.com", + headers={"prefer": prefer_header_with_count}, + ), + ) + + +class TestApiResponse: + def test_response_raises_when_api_error( + self, api_response_with_error: Dict[str, Any] + ): + with pytest.raises(ValueError): + APIResponse(data=api_response_with_error) + + def test_parses_valid_response_only_data(self, api_response: List[Dict[str, Any]]): + result = APIResponse(data=api_response) + assert result.data == api_response + + def test_parses_valid_response_data_and_count( + self, api_response: List[Dict[str, Any]] + ): + count = len(api_response) + result = APIResponse(data=api_response, count=count) + assert result.data == api_response + assert result.count == count + + def test_get_count_from_content_range_header_with_count( + self, content_range_header_with_count: str + ): + assert ( + APIResponse.get_count_from_content_range_header( + content_range_header_with_count + ) + == 2 + ) + + def test_get_count_from_content_range_header_without_count( + self, content_range_header_without_count: str + ): + assert ( + APIResponse.get_count_from_content_range_header( + content_range_header_without_count + ) + is None + ) + + def test_is_count_in_prefer_header_true(self, prefer_header_with_count: str): + assert APIResponse.is_count_in_prefer_header(prefer_header_with_count) + + def test_is_count_in_prefer_header_false(self, prefer_header_without_count: str): + assert not APIResponse.is_count_in_prefer_header(prefer_header_without_count) + + def test_get_count_from_http_request_response_without_prefer_header( + self, request_response_without_prefer_header: Response + ): + assert ( + APIResponse.get_count_from_http_request_response( + request_response_without_prefer_header + ) + is None + ) + + def test_get_count_from_http_request_response_with_prefer_header_without_count( + self, request_response_with_prefer_header_without_count: Response + ): + assert ( + APIResponse.get_count_from_http_request_response( + request_response_with_prefer_header_without_count + ) + is None + ) + + def test_get_count_from_http_request_response_with_count_and_content_range( + self, request_response_with_prefer_header_with_count_and_content_range: Response + ): + assert ( + APIResponse.get_count_from_http_request_response( + request_response_with_prefer_header_with_count_and_content_range + ) + == 2 + ) + + def test_from_http_request_response_constructor( + self, request_response_with_data: Response, api_response: List[Dict[str, Any]] + ): + result = APIResponse.from_http_request_response(request_response_with_data) + assert result.data == api_response + assert result.count == 2