From 8fa3e1b87d74f28f0d191885b437aea48b97f1d2 Mon Sep 17 00:00:00 2001 From: Albert Sola Date: Mon, 2 Feb 2026 17:23:35 +0000 Subject: [PATCH] MPT-16437 Reorganise resource commerce mixins structure --- mpt_api_client/resources/commerce/mixins.py | 97 ----------- .../resources/commerce/mixins/__init__.py | 18 ++ .../resources/commerce/mixins/render_mixin.py | 30 ++++ .../commerce/mixins/template_mixin.py | 31 ++++ .../commerce/mixins/terminate_mixin.py | 33 ++++ pyproject.toml | 1 - .../commerce/mixins/test_render_mixin.py | 56 +++++++ .../commerce/mixins/test_template_mixin.py | 57 +++++++ .../commerce/mixins/test_terminate_mixin.py | 89 ++++++++++ tests/unit/resources/commerce/test_mixins.py | 158 ------------------ 10 files changed, 314 insertions(+), 256 deletions(-) delete mode 100644 mpt_api_client/resources/commerce/mixins.py create mode 100644 mpt_api_client/resources/commerce/mixins/__init__.py create mode 100644 mpt_api_client/resources/commerce/mixins/render_mixin.py create mode 100644 mpt_api_client/resources/commerce/mixins/template_mixin.py create mode 100644 mpt_api_client/resources/commerce/mixins/terminate_mixin.py create mode 100644 tests/unit/resources/commerce/mixins/test_render_mixin.py create mode 100644 tests/unit/resources/commerce/mixins/test_template_mixin.py create mode 100644 tests/unit/resources/commerce/mixins/test_terminate_mixin.py delete mode 100644 tests/unit/resources/commerce/test_mixins.py diff --git a/mpt_api_client/resources/commerce/mixins.py b/mpt_api_client/resources/commerce/mixins.py deleted file mode 100644 index e04c9f1..0000000 --- a/mpt_api_client/resources/commerce/mixins.py +++ /dev/null @@ -1,97 +0,0 @@ -from mpt_api_client.models.model import ResourceData - - -class TerminateMixin[Model]: - """Terminate resource mixin.""" - - def terminate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Terminate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data - - Returns: - Terminated resource. - """ - return self._resource_action(resource_id, "POST", "terminate", json=resource_data) # type: ignore[attr-defined, no-any-return] - - -class RenderMixin[Model]: - """Render resource mixin.""" - - def render(self, resource_id: str) -> str: - """Render resource. - - Args: - resource_id: Resource ID - - Returns: - Rendered resource. - """ - response = self._resource_do_request(resource_id, action="render") # type: ignore[attr-defined] - return response.text # type: ignore[no-any-return] - - -class TemplateMixin[Model]: - """Template resource mixin.""" - - def template(self, resource_id: str) -> str: - """Get resource template. - - Args: - resource_id: Resource ID - - Returns: - Resource template. - """ - response = self._resource_do_request(resource_id, action="template") # type: ignore[attr-defined] - return response.text # type: ignore[no-any-return] - - -class AsyncTerminateMixin[Model]: - """Asynchronous terminate resource mixin.""" - - async def terminate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: - """Terminate resource. - - Args: - resource_id: Resource ID - resource_data: Resource data - - Returns: - Terminated resource. - """ - return await self._resource_action(resource_id, "POST", "terminate", json=resource_data) # type: ignore[attr-defined, no-any-return] - - -class AsyncRenderMixin[Model]: - """Asynchronous render resource mixin.""" - - async def render(self, resource_id: str) -> str: - """Render resource. - - Args: - resource_id: Resource ID - - Returns: - Rendered resource. - """ - response = await self._resource_do_request(resource_id, action="render") # type: ignore[attr-defined] - return response.text # type: ignore[no-any-return] - - -class AsyncTemplateMixin[Model]: - """Asynchronous template resource mixin.""" - - async def template(self, resource_id: str) -> str: - """Get resource template. - - Args: - resource_id: Resource ID - - Returns: - Resource template. - """ - response = await self._resource_do_request(resource_id, action="template") # type: ignore[attr-defined] - return response.text # type: ignore[no-any-return] diff --git a/mpt_api_client/resources/commerce/mixins/__init__.py b/mpt_api_client/resources/commerce/mixins/__init__.py new file mode 100644 index 0000000..6e2e8e3 --- /dev/null +++ b/mpt_api_client/resources/commerce/mixins/__init__.py @@ -0,0 +1,18 @@ +from mpt_api_client.resources.commerce.mixins.render_mixin import AsyncRenderMixin, RenderMixin +from mpt_api_client.resources.commerce.mixins.template_mixin import ( + AsyncTemplateMixin, + TemplateMixin, +) +from mpt_api_client.resources.commerce.mixins.terminate_mixin import ( + AsyncTerminateMixin, + TerminateMixin, +) + +__all__ = [ # noqa: WPS410 + "AsyncRenderMixin", + "AsyncTemplateMixin", + "AsyncTerminateMixin", + "RenderMixin", + "TemplateMixin", + "TerminateMixin", +] diff --git a/mpt_api_client/resources/commerce/mixins/render_mixin.py b/mpt_api_client/resources/commerce/mixins/render_mixin.py new file mode 100644 index 0000000..9b20462 --- /dev/null +++ b/mpt_api_client/resources/commerce/mixins/render_mixin.py @@ -0,0 +1,30 @@ +class RenderMixin[Model]: + """Render resource mixin.""" + + def render(self, resource_id: str) -> str: + """Render resource. + + Args: + resource_id: Resource ID + + Returns: + Rendered resource. + """ + response = self._resource_do_request(resource_id, action="render") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] + + +class AsyncRenderMixin[Model]: + """Asynchronous render resource mixin.""" + + async def render(self, resource_id: str) -> str: + """Render resource. + + Args: + resource_id: Resource ID + + Returns: + Rendered resource. + """ + response = await self._resource_do_request(resource_id, action="render") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] diff --git a/mpt_api_client/resources/commerce/mixins/template_mixin.py b/mpt_api_client/resources/commerce/mixins/template_mixin.py new file mode 100644 index 0000000..b9a7f95 --- /dev/null +++ b/mpt_api_client/resources/commerce/mixins/template_mixin.py @@ -0,0 +1,31 @@ +class TemplateMixin[Model]: + """Template resource mixin.""" + + def template(self, resource_id: str) -> str: + """Get resource template. + + Args: + resource_id: Resource ID + + Returns: + Resource template. + """ + response = self._resource_do_request(resource_id, action="template") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] + + +class AsyncTemplateMixin[Model]: + """Asynchronous template resource mixin.""" + + async def template(self, resource_id: str) -> str: + """Get resource template. + + Args: + resource_id: Resource ID + + Returns: + Resource template. + """ + # pylint: disable=duplicate-code + response = await self._resource_do_request(resource_id, action="template") # type: ignore[attr-defined] + return response.text # type: ignore[no-any-return] diff --git a/mpt_api_client/resources/commerce/mixins/terminate_mixin.py b/mpt_api_client/resources/commerce/mixins/terminate_mixin.py new file mode 100644 index 0000000..856ca76 --- /dev/null +++ b/mpt_api_client/resources/commerce/mixins/terminate_mixin.py @@ -0,0 +1,33 @@ +from mpt_api_client.models.model import ResourceData + + +class TerminateMixin[Model]: + """Terminate resource mixin.""" + + def terminate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Terminate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data + + Returns: + Terminated resource. + """ + return self._resource_action(resource_id, "POST", "terminate", json=resource_data) # type: ignore[attr-defined, no-any-return] + + +class AsyncTerminateMixin[Model]: + """Asynchronous terminate resource mixin.""" + + async def terminate(self, resource_id: str, resource_data: ResourceData | None = None) -> Model: + """Terminate resource. + + Args: + resource_id: Resource ID + resource_data: Resource data + + Returns: + Terminated resource. + """ + return await self._resource_action(resource_id, "POST", "terminate", json=resource_data) # type: ignore[attr-defined, no-any-return] diff --git a/pyproject.toml b/pyproject.toml index 2ddd0cd..dcbad80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -140,7 +140,6 @@ per-file-ignores = [ "tests/unit/resources/accounts/*.py: WPS204 WPS202 WPS210", "tests/unit/resources/catalog/test_products.py: WPS202 WPS210", "tests/unit/resources/commerce/*.py: WPS202 WPS204", - "tests/unit/resources/*/test_mixins.py: WPS118 WPS202 WPS204 WPS235", "tests/unit/test_mpt_client.py: WPS235", "tests/*: WPS432 WPS202", ] diff --git a/tests/unit/resources/commerce/mixins/test_render_mixin.py b/tests/unit/resources/commerce/mixins/test_render_mixin.py new file mode 100644 index 0000000..e368a2f --- /dev/null +++ b/tests/unit/resources/commerce/mixins/test_render_mixin.py @@ -0,0 +1,56 @@ +import httpx +import respx + +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.resources.commerce.mixins.render_mixin import AsyncRenderMixin, RenderMixin +from tests.unit.conftest import DummyModel + + +class DummyRenderService( + RenderMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "public/v1/dummy/render" + _model_class = DummyModel + + +class AsyncDummyRenderService( + AsyncRenderMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "public/v1/dummy/render" + _model_class = DummyModel + + +def test_render(http_client): + service = DummyRenderService(http_client=http_client) + rendered_content = "

Dummy Rendered Content

" + with respx.mock: + respx.get("https://api.example.com/public/v1/dummy/render/DUMMY-123/render").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "text/html"}, + content=rendered_content, + ) + ) + + result = service.render("DUMMY-123") + + assert result == rendered_content + + +async def test_async_render(async_http_client): + service = AsyncDummyRenderService(http_client=async_http_client) + rendered_content = "

Dummy Rendered Content

" + with respx.mock: + respx.get("https://api.example.com/public/v1/dummy/render/DUMMY-123/render").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "text/html"}, + content=rendered_content, + ) + ) + + result = await service.render("DUMMY-123") + + assert result == rendered_content diff --git a/tests/unit/resources/commerce/mixins/test_template_mixin.py b/tests/unit/resources/commerce/mixins/test_template_mixin.py new file mode 100644 index 0000000..945576d --- /dev/null +++ b/tests/unit/resources/commerce/mixins/test_template_mixin.py @@ -0,0 +1,57 @@ +import httpx +import respx + +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.resources.commerce.mixins.template_mixin import ( + AsyncTemplateMixin, + TemplateMixin, +) +from tests.unit.conftest import DummyModel + + +class DummyTemplateService( + TemplateMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "public/v1/dummy/template" + _model_class = DummyModel + + +class AsyncDummyTemplateService( + AsyncTemplateMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "public/v1/dummy/template" + _model_class = DummyModel + + +def test_template(http_client): + service = DummyTemplateService(http_client=http_client) + template_content = "

Dummy Template Content

" + with respx.mock: + respx.get("https://api.example.com/public/v1/dummy/template/DUMMY-123/template").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + content=template_content, + ) + ) + + result = service.template("DUMMY-123") + + assert result == template_content + + +async def test_async_template(async_http_client): + service = AsyncDummyTemplateService(http_client=async_http_client) + template_content = "

Dummy Template Content

" + with respx.mock: + respx.get("https://api.example.com/public/v1/dummy/template/DUMMY-123/template").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + content=template_content, + ) + ) + + result = await service.template("DUMMY-123") + + assert result == template_content diff --git a/tests/unit/resources/commerce/mixins/test_terminate_mixin.py b/tests/unit/resources/commerce/mixins/test_terminate_mixin.py new file mode 100644 index 0000000..ec10241 --- /dev/null +++ b/tests/unit/resources/commerce/mixins/test_terminate_mixin.py @@ -0,0 +1,89 @@ +import httpx +import respx + +from mpt_api_client.http import AsyncService, Service +from mpt_api_client.resources.commerce.mixins.terminate_mixin import ( + AsyncTerminateMixin, + TerminateMixin, +) +from tests.unit.conftest import DummyModel + + +class DummyTerminateService( + TerminateMixin[DummyModel], + Service[DummyModel], +): + _endpoint = "public/v1/dummy/terminate" + _model_class = DummyModel + + +class AsyncDummyTerminateService( + AsyncTerminateMixin[DummyModel], + AsyncService[DummyModel], +): + _endpoint = "public/v1/dummy/terminate" + _model_class = DummyModel + + +def test_terminate_with_data(http_client): + service = DummyTerminateService(http_client=http_client) + dummy_expected = {"id": "DUMMY-123", "status": "Terminated", "name": "Terminated DUMMY-123"} + with respx.mock: + respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-123/terminate").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=dummy_expected, + ) + ) + + result = service.terminate("DUMMY-123", {"name": "Terminated DUMMY-123"}) + + assert result.to_dict() == dummy_expected + + +def test_terminate(http_client): + service = DummyTerminateService(http_client=http_client) + dummy_expected = {"id": "DUMMY-124", "status": "Terminated", "name": "Terminated DUMMY-124"} + with respx.mock: + respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-124/terminate").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=dummy_expected, + ) + ) + + result = service.terminate("DUMMY-124") + + assert result.to_dict() == dummy_expected + + +async def test_async_terminate_with_data(async_http_client): + service = AsyncDummyTerminateService(http_client=async_http_client) + dummy_expected = {"id": "DUMMY-123", "status": "Terminated", "name": "Terminated DUMMY-123"} + with respx.mock: + respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-123/terminate").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=dummy_expected, + ) + ) + + result = await service.terminate("DUMMY-123", {"name": "Terminated DUMMY-123"}) + + assert result.to_dict() == dummy_expected + + +async def test_async_terminate(async_http_client): + service = AsyncDummyTerminateService(http_client=async_http_client) + dummy_expected = {"id": "DUMMY-124", "status": "Terminated", "name": "Terminated DUMMY-124"} + with respx.mock: + respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-124/terminate").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=dummy_expected, + ) + ) + + result = await service.terminate("DUMMY-124") + + assert result.to_dict() == dummy_expected diff --git a/tests/unit/resources/commerce/test_mixins.py b/tests/unit/resources/commerce/test_mixins.py deleted file mode 100644 index f19f4a0..0000000 --- a/tests/unit/resources/commerce/test_mixins.py +++ /dev/null @@ -1,158 +0,0 @@ -import httpx -import pytest -import respx - -from mpt_api_client.http import AsyncService, Service -from mpt_api_client.resources.commerce.mixins import ( - AsyncRenderMixin, - AsyncTerminateMixin, - RenderMixin, - TerminateMixin, -) -from tests.unit.conftest import DummyModel - - -class DummyTerminateService( - TerminateMixin[DummyModel], - Service[DummyModel], -): - _endpoint = "public/v1/dummy/terminate" - _model_class = DummyModel - - -class AsyncDummyTerminateService( - AsyncTerminateMixin[DummyModel], - AsyncService[DummyModel], -): - _endpoint = "public/v1/dummy/terminate" - _model_class = DummyModel - - -class DummyRenderService( - RenderMixin[DummyModel], - Service[DummyModel], -): - _endpoint = "public/v1/dummy/render" - _model_class = DummyModel - - -class AsyncDummyRenderService( - AsyncRenderMixin[DummyModel], - AsyncService[DummyModel], -): - _endpoint = "public/v1/dummy/render" - _model_class = DummyModel - - -@pytest.fixture -def dummy_terminate_service(http_client): - return DummyTerminateService(http_client=http_client) - - -@pytest.fixture -def async_dummy_terminate_service(async_http_client): - return AsyncDummyTerminateService(http_client=async_http_client) - - -@pytest.fixture -def dummy_render_service(http_client): - return DummyRenderService(http_client=http_client) - - -@pytest.fixture -def async_dummy_render_service(async_http_client): - return AsyncDummyRenderService(http_client=async_http_client) - - -def test_terminate_with_data(dummy_terminate_service): - dummy_expected = {"id": "DUMMY-123", "status": "Terminated", "name": "Terminated DUMMY-123"} - with respx.mock: - respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-123/terminate").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - json=dummy_expected, - ) - ) - - result = dummy_terminate_service.terminate("DUMMY-123", {"name": "Terminated DUMMY-123"}) - - assert result.to_dict() == dummy_expected - - -def test_terminate(dummy_terminate_service): - dummy_expected = {"id": "DUMMY-124", "status": "Terminated", "name": "Terminated DUMMY-124"} - with respx.mock: - respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-124/terminate").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - json=dummy_expected, - ) - ) - - result = dummy_terminate_service.terminate("DUMMY-124") - - assert result.to_dict() == dummy_expected - - -async def test_async_terminate_with_data(async_dummy_terminate_service): - dummy_expected = {"id": "DUMMY-123", "status": "Terminated", "name": "Terminated DUMMY-123"} - with respx.mock: - respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-123/terminate").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - json=dummy_expected, - ) - ) - - result = await async_dummy_terminate_service.terminate( - "DUMMY-123", {"name": "Terminated DUMMY-123"} - ) - - assert result.to_dict() == dummy_expected - - -async def test_async_terminate(async_dummy_terminate_service): - dummy_expected = {"id": "DUMMY-124", "status": "Terminated", "name": "Terminated DUMMY-124"} - with respx.mock: - respx.post("https://api.example.com/public/v1/dummy/terminate/DUMMY-124/terminate").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - json=dummy_expected, - ) - ) - - result = await async_dummy_terminate_service.terminate("DUMMY-124") - - assert result.to_dict() == dummy_expected - - -def test_render(dummy_render_service): - rendered_content = "

Dummy Rendered Content

" - with respx.mock: - respx.get("https://api.example.com/public/v1/dummy/render/DUMMY-123/render").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "text/html"}, - content=rendered_content, - ) - ) - - result = dummy_render_service.render("DUMMY-123") - - assert result == rendered_content - - -async def test_async_render(async_dummy_render_service): - rendered_content = "

Dummy Rendered Content

" - with respx.mock: - respx.get("https://api.example.com/public/v1/dummy/render/DUMMY-123/render").mock( - return_value=httpx.Response( - status_code=httpx.codes.OK, - headers={"content-type": "text/html"}, - content=rendered_content, - ) - ) - - result = await async_dummy_render_service.render("DUMMY-123") - - assert result == rendered_content