diff --git a/src/nene2/http/pagination.py b/src/nene2/http/pagination.py index 1ddd94d..0f4a06c 100644 --- a/src/nene2/http/pagination.py +++ b/src/nene2/http/pagination.py @@ -34,10 +34,19 @@ def parse( Raises ValidationException (→ 422) when values are out of range. """ params = request.query_params - limit = int(params.get("limit", default_limit)) - offset = int(params.get("offset", 0)) errors: list[ValidationError] = [] + try: + limit = int(params.get("limit", default_limit)) + except ValueError: + errors.append(ValidationError("limit", "limit must be an integer.", "invalid")) + limit = default_limit + try: + offset = int(params.get("offset", 0)) + except ValueError: + errors.append(ValidationError("offset", "offset must be an integer.", "invalid")) + offset = 0 + if limit < 1 or limit > max_limit: errors.append( ValidationError( diff --git a/tests/nene2/http/test_pagination.py b/tests/nene2/http/test_pagination.py index ecbb315..d18cb26 100644 --- a/tests/nene2/http/test_pagination.py +++ b/tests/nene2/http/test_pagination.py @@ -43,6 +43,34 @@ def test_limit_out_of_range_raises() -> None: assert exc_info.value.errors[0].field == "limit" +def test_non_integer_limit_raises_validation_exception() -> None: + mock_request = MagicMock(spec=Request) + mock_request.query_params = {"limit": "abc"} + with pytest.raises(ValidationException) as exc_info: + PaginationQueryParser.parse(mock_request) + assert exc_info.value.errors[0].field == "limit" + assert exc_info.value.errors[0].code == "invalid" + + +def test_non_integer_offset_raises_validation_exception() -> None: + mock_request = MagicMock(spec=Request) + mock_request.query_params = {"offset": "xyz"} + with pytest.raises(ValidationException) as exc_info: + PaginationQueryParser.parse(mock_request) + assert exc_info.value.errors[0].field == "offset" + assert exc_info.value.errors[0].code == "invalid" + + +def test_both_invalid_collects_all_errors() -> None: + mock_request = MagicMock(spec=Request) + mock_request.query_params = {"limit": "abc", "offset": "xyz"} + with pytest.raises(ValidationException) as exc_info: + PaginationQueryParser.parse(mock_request) + fields = [e.field for e in exc_info.value.errors] + assert "limit" in fields + assert "offset" in fields + + def test_pagination_response_without_total() -> None: r = PaginationResponse(items=[{"id": 1}], limit=20, offset=0) d = r.to_dict()