diff --git a/requirements.txt b/requirements.txt index 70d165d..48476ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,7 @@ adaptix typing_extensions aiohttp requests +requests-mock nose2 mypy pytest diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/requests/__init__.py b/tests/requests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/requests/conftest.py b/tests/requests/conftest.py new file mode 100644 index 0000000..9ee4254 --- /dev/null +++ b/tests/requests/conftest.py @@ -0,0 +1,14 @@ +import pytest +import requests_mock + +from dataclass_rest.http import requests + +@pytest.fixture +def session(): + return requests.Session() + + +@pytest.fixture +def mocker(session): + with requests_mock.Mocker(session=session, case_sensitive=True) as session_mock: + yield session_mock diff --git a/tests/requests/test_factory.py b/tests/requests/test_factory.py new file mode 100644 index 0000000..05bf77a --- /dev/null +++ b/tests/requests/test_factory.py @@ -0,0 +1,59 @@ +from dataclasses import dataclass +from enum import Enum + +from adaptix import Retort, NameStyle, name_mapping + +from dataclass_rest import patch +from dataclass_rest.http.requests import RequestsClient + + +class Selection(Enum): + ONE = "ONE" + TWO = "TWO" + + +@dataclass +class RequestBody: + int_param: int + selection: Selection + + +@dataclass +class ResponseBody: + int_param: int + selection: Selection + + +def test_body(session, mocker): + class Api(RequestsClient): + def _init_request_body_factory(self) -> Retort: + return Retort(recipe=[ + name_mapping(name_style=NameStyle.CAMEL), + ]) + + def _init_request_args_factory(self) -> Retort: + return Retort(recipe=[ + name_mapping(name_style=NameStyle.UPPER_DOT), + ]) + + def _init_response_body_factory(self) -> Retort: + return Retort(recipe=[ + name_mapping(name_style=NameStyle.LOWER_KEBAB), + ]) + + @patch("/post/") + def post_x(self, long_param: str, body: RequestBody) -> ResponseBody: + raise NotImplementedError() + + mocker.patch( + url="http://example.com/post/", + text="""{"int-param": 1, "selection": "TWO"}""", + ) + client = Api(base_url="http://example.com", session=session) + result = client.post_x( + long_param="hello", body=RequestBody(int_param=42, selection=Selection.ONE), + ) + assert result == ResponseBody(int_param=1, selection=Selection.TWO) + assert mocker.called_once + assert mocker.request_history[0].json() == {"intParam": 42, "selection": "ONE"} + assert mocker.request_history[0].query == "LONG.PARAM=hello" diff --git a/tests/requests/test_params.py b/tests/requests/test_params.py new file mode 100644 index 0000000..6b0b958 --- /dev/null +++ b/tests/requests/test_params.py @@ -0,0 +1,69 @@ +from dataclasses import dataclass +from typing import Optional + +from dataclass_rest import get, post +from dataclass_rest.http.requests import RequestsClient + + +def test_methods(session, mocker): + class Api(RequestsClient): + @get("/get") + def get_x(self) -> list[int]: + raise NotImplementedError() + + @post("/post") + def post_x(self) -> list[int]: + raise NotImplementedError() + + mocker.get("http://example.com/get", text="[1,2]") + mocker.post("http://example.com/post", text="[1,2,3]") + client = Api(base_url="http://example.com", session=session) + assert client.get_x() == [1, 2] + assert client.post_x() == [1, 2, 3] + + +def test_path_params(session, mocker): + class Api(RequestsClient): + @post("/post/{id}") + def post_x(self, id) -> list[int]: + raise NotImplementedError() + + mocker.post("http://example.com/post/1", text="[1]") + mocker.post("http://example.com/post/2", text="[1,2]") + client = Api(base_url="http://example.com", session=session) + assert client.post_x(1) == [1] + assert client.post_x(2) == [1, 2] + + +def test_query_params(session, mocker): + class Api(RequestsClient): + @post("/post/{id}") + def post_x(self, id: str, param: Optional[int]) -> list[int]: + raise NotImplementedError() + + mocker.post("http://example.com/post/x?", text="[0]") + mocker.post("http://example.com/post/x?param=1", text="[1]") + mocker.post("http://example.com/post/x?param=2", text="[1,2]") + client = Api(base_url="http://example.com", session=session) + assert client.post_x("x", None) == [0] + assert client.post_x("x", 1) == [1] + assert client.post_x("x", 2) == [1, 2] + + +@dataclass +class RequestBody: + x: int + y: str + + +def test_body(session, mocker): + class Api(RequestsClient): + @post("/post/") + def post_x(self, body: RequestBody) -> None: + raise NotImplementedError() + + mocker.post("http://example.com/post/", text="null") + client = Api(base_url="http://example.com", session=session) + assert client.post_x(RequestBody(x=1, y="test")) is None + assert mocker.called_once + assert mocker.request_history[0].json() == {"x": 1, "y": "test"}