diff --git a/e2e_config.test.json b/e2e_config.test.json index b0b7aa4..d0c4127 100644 --- a/e2e_config.test.json +++ b/e2e_config.test.json @@ -32,6 +32,7 @@ "commerce.product.id": "PRD-1767-7355", "commerce.product.item.id": "ITM-1767-7355-0001", "commerce.product.listing.id": "LST-5489-0806", - "commerce.product.template.id": "TPL-1767-7355-0003", + "commerce.product.template.id": "TPL-1767-7355-0002", + "commerce.user.id": "USR-4303-2348", "notifications.message.id": "MSG-0000-6215-1019-0139" } diff --git a/mpt_api_client/resources/commerce/orders.py b/mpt_api_client/resources/commerce/orders.py index 6b97ba5..c599e74 100644 --- a/mpt_api_client/resources/commerce/orders.py +++ b/mpt_api_client/resources/commerce/orders.py @@ -105,6 +105,30 @@ def template(self, resource_id: str) -> str: response = self._resource_do_request(resource_id, "GET", "template") return response.text + def render(self, resource_id: str) -> str: + """Render the order template for the given order ID. + + Args: + resource_id: Order resource ID + + Returns: + Rendered order as HTML. + """ + response = self._resource_do_request(resource_id, "GET", "render") + return response.text + + def quote(self, resource_id: str, resource_data: ResourceData | None = None) -> Order: + """Quote the order. + + Args: + resource_id: Order resource ID + resource_data: Order data will be updated + + Returns: + Quoted order resource. + """ + return self._resource_action(resource_id, "POST", "quote", json=resource_data) + def subscriptions(self, order_id: str) -> OrderSubscriptionsService: """Get the subscription service for the given Order id. @@ -223,6 +247,30 @@ async def template(self, resource_id: str) -> str: response = await self._resource_do_request(resource_id, "GET", "template") return response.text + async def render(self, resource_id: str) -> str: + """Render the order template for the given order ID. + + Args: + resource_id: Order resource ID + + Returns: + Rendered order as HTML. + """ + response = await self._resource_do_request(resource_id, "GET", "render") + return response.text + + async def quote(self, resource_id: str, resource_data: ResourceData | None = None) -> Order: + """Quote the order. + + Args: + resource_id: Order resource ID + resource_data: Order data will be updated + + Returns: + Quoted order resource. + """ + return await self._resource_action(resource_id, "POST", "quote", json=resource_data) + def subscriptions(self, order_id: str) -> AsyncOrderSubscriptionsService: """Get the subscription service for the given Order id. diff --git a/pyproject.toml b/pyproject.toml index aed35e1..bdb1bd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,31 +111,36 @@ select = ["AAA", "E999", "WPS"] show-source = true statistics = false per-file-ignores = [ - "mpt_api_client/mpt_client.py: WPS214 WPS235", "mpt_api_client/http/mixins.py: WPS202 WPS204 WPS235", - "mpt_api_client/resources/*: WPS215", "mpt_api_client/models/model.py: WPS215 WPS110", + "mpt_api_client/mpt_client.py: WPS214 WPS235", + "mpt_api_client/resources/*: WPS215", "mpt_api_client/resources/accounts/*.py: WPS202 WPS215 WPS214 WPS235 WPS453", "mpt_api_client/resources/billing/*.py: WPS202 WPS204 WPS214 WPS215", "mpt_api_client/resources/catalog/*.py: WPS110 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/mixins.py: WPS110 WPS202 WPS214 WPS215 WPS235", "mpt_api_client/resources/catalog/products.py: WPS204 WPS214 WPS215 WPS235", "mpt_api_client/rql/query_builder.py: WPS110 WPS115 WPS210 WPS214", + "tests/e2e/accounts/*.py: WPS430 WPS202", + "tests/e2e/catalog/*.py: WPS202 WPS421", + "tests/e2e/catalog/items/*.py: WPS110 WPS202", + "tests/e2e/commerce/*.py: WPS204 WPS453", + "tests/e2e/commerce/agreement/*.py: WPS202", + "tests/e2e/commerce/agreement/attachment/*.py: WPS202", + "tests/e2e/commerce/order/*.py: WPS202 WPS204", "tests/unit/http/test_async_service.py: WPS204 WPS202", "tests/unit/http/test_service.py: WPS204 WPS202", "tests/unit/http/test_mixins.py: WPS204 WPS202 WPS210", - "tests/unit/resources/catalog/test_products.py: WPS202 WPS210", "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/e2e/accounts/*.py: WPS430 WPS202", - "tests/e2e/catalog/*.py: WPS202 WPS421", - "tests/e2e/catalog/items/*.py: WPS110 WPS202", "tests/seed/catalog/test_product.py: WPS202 WPS204 WPS219", "tests/*: WPS432 WPS202", "seed/*: WPS404", "seed/accounts/*.py: WPS204 WPS404 WPS453", - "seed/catalog/product.py: WPS202 WPS204 WPS217 WPS201 WPS213 WPS404" + "seed/catalog/product.py: WPS202 WPS204 WPS217 WPS201 WPS213 WPS404", ] [tool.ruff] diff --git a/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py b/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py index 2071a4b..a226b7d 100644 --- a/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py +++ b/tests/e2e/commerce/agreement/attachment/test_async_agreement_attachment.py @@ -52,7 +52,7 @@ async def test_filter_agreement_attachments(agreement_attachments, agreement_att assert len(result) == 1 -def test_create_agreement_attachment(created_agreement_attachment): +def test_create_order_agreement_attachment(created_agreement_attachment): result = created_agreement_attachment assert result is not None diff --git a/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py b/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py index 8343260..bcc9cba 100644 --- a/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py +++ b/tests/e2e/commerce/agreement/attachment/test_sync_agreement_attachment.py @@ -54,7 +54,7 @@ def test_filter_agreement_attachments(agreement_attachments, agreement_attachmen assert len(result) == 1 -def test_create_agreement_attachment(created_agreement_attachment): +def test_create_order_agreement_attachment(created_agreement_attachment): result = created_agreement_attachment assert result is not None diff --git a/tests/e2e/commerce/conftest.py b/tests/e2e/commerce/conftest.py index 18693e5..df69632 100644 --- a/tests/e2e/commerce/conftest.py +++ b/tests/e2e/commerce/conftest.py @@ -9,3 +9,23 @@ def agreement_id(e2e_config): @pytest.fixture def commerce_product_id(e2e_config): return e2e_config["commerce.product.id"] + + +@pytest.fixture +def order_id(e2e_config): + return e2e_config["commerce.order.id"] + + +@pytest.fixture +def commerce_item_id(e2e_config): + return e2e_config["commerce.product.item.id"] + + +@pytest.fixture +def commerce_product_template_id(e2e_config): + return e2e_config["commerce.product.template.id"] + + +@pytest.fixture +def commerce_user_id(e2e_config): + return e2e_config["commerce.user.id"] diff --git a/tests/e2e/commerce/order/conftest.py b/tests/e2e/commerce/order/conftest.py new file mode 100644 index 0000000..2ef681d --- /dev/null +++ b/tests/e2e/commerce/order/conftest.py @@ -0,0 +1,72 @@ +import pytest +from freezegun import freeze_time + + +@pytest.fixture +def invalid_order_id(): + return "ORD-0000-0000" + + +@pytest.fixture +@freeze_time("2025-12-01T10:00:00.000Z") +def order_factory(licensee_id, commerce_product_id, commerce_item_id, authorization_id): + def factory( + notes: str = "E2E Created Order", + line_item_period: str = "1y", + line_item_commitment: str = "1y", + line_quantity: int = 10, + ): + return { + "agreement": { + "licensee": {"id": licensee_id}, + "product": {"id": commerce_product_id}, + }, + "authorization": {"id": authorization_id}, + "type": "Purchase", + "status": "Draft", + "parameters": { + "ordering": [], + "fulfillment": [], + }, + "notes": notes, + "lines": [ + { + "item": { + "id": commerce_item_id, + "terms": { + "model": "quantity", + "period": line_item_period, + "commitment": line_item_commitment, + }, + }, + "price": { + "currency": "USD", + "unitSP": 15, + "SPx1": None, + "SPxM": 1.25, + "SPxY": 15, + }, + "quantity": line_quantity, + } + ], + } + + return factory + + +@pytest.fixture +def order_subscription_factory(): + def factory( + line_id: str, + name: str = "E2E Created Order Subscription", + ): + return { + "name": name, + "parameters": {"fulfillment": []}, + "lines": [{"id": line_id}], + "terms": {"model": "quantity", "period": "1y", "commitment": "1y"}, + "autoRenew": True, + "template": None, + } + + return factory diff --git a/tests/e2e/commerce/order/test_async_order.py b/tests/e2e/commerce/order/test_async_order.py new file mode 100644 index 0000000..b1d9154 --- /dev/null +++ b/tests/e2e/commerce/order/test_async_order.py @@ -0,0 +1,141 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError +from mpt_api_client.rql.query_builder import RQLQuery + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +async def created_order(async_mpt_client, order_factory): + new_order_request_data = order_factory() + + return await async_mpt_client.commerce.orders.create(new_order_request_data) + + +@pytest.fixture +async def processed_order(async_mpt_client, created_order): + return await async_mpt_client.commerce.orders.process(created_order.id) + + +async def test_get_order_by_id(async_mpt_client, order_id): + result = await async_mpt_client.commerce.orders.get(order_id) + + assert result is not None + + +async def test_list_orders(async_mpt_client): + limit = 10 + + result = await async_mpt_client.commerce.orders.fetch_page(limit=limit) + + assert len(result) > 0 + + +async def test_get_order_by_id_not_found(async_mpt_client, invalid_order_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + await async_mpt_client.commerce.orders.get(invalid_order_id) + + +async def test_filter_orders(async_mpt_client, order_id): + select_fields = ["-authorization"] + filtered_orders = ( + async_mpt_client.commerce.orders.filter(RQLQuery(id=order_id)) + .filter(RQLQuery(type="Purchase")) + .select(*select_fields) + ) + + result = [order async for order in filtered_orders.iterate()] + + assert len(result) == 1 + + +def test_create_order(created_order): + result = created_order + + assert result is not None + + +async def test_update_order(async_mpt_client, created_order, order_factory): + update_data = order_factory(notes="E2E Updated Order Notes") + + result = await async_mpt_client.commerce.orders.update(created_order.id, update_data) + + assert result is not None + + +async def test_process_order(async_mpt_client, created_order): + result = await async_mpt_client.commerce.orders.process(created_order.id) + + assert result is not None + + +async def test_query_order(async_mpt_vendor, async_mpt_client, created_order): + processed_order = await async_mpt_client.commerce.orders.process(created_order.id) + + result = await async_mpt_vendor.commerce.orders.query(processed_order.id) + + assert result is not None + + +async def test_complete_order(async_mpt_vendor, order_subscription_factory, processed_order): + processed_order_lines = processed_order.lines + line_id = processed_order_lines[0].id + order_subscription = order_subscription_factory(line_id) + order_subscriptions = async_mpt_vendor.commerce.orders.subscriptions(processed_order.id) + await order_subscriptions.create(order_subscription) + + result = await async_mpt_vendor.commerce.orders.complete(processed_order.id) + + assert result is not None + + +async def test_fail_order(async_mpt_vendor, processed_order): + fail_order_data = { + "statusNotes": { + "message": "Failing order for E2E test", + "id": processed_order.id, + } + } + + result = await async_mpt_vendor.commerce.orders.fail(processed_order.id, fail_order_data) + + assert result is not None + + +async def test_delete_draft_order(async_mpt_client, created_order): + await async_mpt_client.commerce.orders.delete(created_order.id) + + +async def test_quote_order(async_mpt_client, created_order): + result = await async_mpt_client.commerce.orders.quote(created_order.id) + + assert result is not None + + +async def test_validate_order(async_mpt_client, created_order): + result = await async_mpt_client.commerce.orders.validate(created_order.id) + + assert result is not None + + +async def test_order_template(async_mpt_client, created_order): + result = await async_mpt_client.commerce.orders.template(created_order.id) + + assert result is not None + + +async def test_order_render(async_mpt_client, created_order): + result = await async_mpt_client.commerce.orders.render(created_order.id) + + assert result is not None + + +async def test_notify_order(async_mpt_client, created_order, commerce_user_id): + user_data = { + "userId": commerce_user_id, + "notifyMe": False, + } + await async_mpt_client.commerce.orders.quote(created_order.id) + + await async_mpt_client.commerce.orders.notify(created_order.id, user_data) diff --git a/tests/e2e/commerce/order/test_sync_order.py b/tests/e2e/commerce/order/test_sync_order.py new file mode 100644 index 0000000..d7ffee9 --- /dev/null +++ b/tests/e2e/commerce/order/test_sync_order.py @@ -0,0 +1,144 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError +from mpt_api_client.rql.query_builder import RQLQuery + +pytestmark = [pytest.mark.flaky] + + +@pytest.fixture +def created_order(mpt_client, order_factory): + new_order_request_data = order_factory() + + return mpt_client.commerce.orders.create(new_order_request_data) + + +@pytest.fixture +def processed_order(mpt_client, created_order): + return mpt_client.commerce.orders.process(created_order.id) + + +def test_get_order_by_id(mpt_client, order_id): + result = mpt_client.commerce.orders.get(order_id) + + assert result is not None + + +def test_list_orders(mpt_client): + limit = 10 + + result = mpt_client.commerce.orders.fetch_page(limit=limit) + + assert len(result) > 0 + + +def test_get_order_by_id_not_found(mpt_client, invalid_order_id): + with pytest.raises(MPTAPIError, match=r"404 Not Found"): + mpt_client.commerce.orders.get(invalid_order_id) + + +def test_filter_orders(mpt_client, order_id): + select_fields = ["-authorization"] + filtered_orders = ( + mpt_client.commerce.orders.filter(RQLQuery(id=order_id)) + .filter(RQLQuery(type="Purchase")) + .select(*select_fields) + ) + + result = list(filtered_orders.iterate()) + + assert len(result) == 1 + + +def test_create_order(created_order): + result = created_order + + assert result is not None + + +def test_update_order(mpt_client, created_order, order_factory): + update_data = order_factory(notes="E2E Updated Order Notes") + + result = mpt_client.commerce.orders.update(created_order.id, update_data) + + assert result is not None + + +def test_process_order(mpt_client, created_order): + result = mpt_client.commerce.orders.process(created_order.id) + + assert result is not None + + +def test_query_order(mpt_vendor, mpt_client, created_order): + processed_order = mpt_client.commerce.orders.process(created_order.id) + + result = mpt_vendor.commerce.orders.query(processed_order.id) + + assert result is not None + + +def test_complete_order(mpt_vendor, order_subscription_factory, processed_order): + processed_order_lines = processed_order.lines + line_id = processed_order_lines[0].id + order_subscription = order_subscription_factory(line_id) + order_subscriptions = mpt_vendor.commerce.orders.subscriptions(processed_order.id) + order_subscriptions.create(order_subscription) + + result = mpt_vendor.commerce.orders.complete(processed_order.id) + + assert result is not None + + +def test_fail_order(mpt_vendor, processed_order): + fail_order_data = { + "statusNotes": { + "message": "Failing order for E2E test", + "id": processed_order.id, + } + } + + result = mpt_vendor.commerce.orders.fail(processed_order.id, fail_order_data) + + assert result is not None + + +def test_delete_draft_order(mpt_client, created_order): + result = created_order + + mpt_client.commerce.orders.delete(result.id) + + +def test_quote_order(mpt_client, created_order): + result = mpt_client.commerce.orders.quote(created_order.id) + + assert result is not None + + +def test_validate_order(mpt_client, created_order): + result = mpt_client.commerce.orders.validate(created_order.id) + + assert result is not None + + +def test_order_template(mpt_client, created_order): + result = mpt_client.commerce.orders.template(created_order.id) + + assert result is not None + + +def test_order_render(mpt_client, created_order): + result = mpt_client.commerce.orders.render(created_order.id) + + assert result is not None + + +def test_notify_order(mpt_client, created_order, commerce_user_id): + user_data = { + "userId": commerce_user_id, + "notifyMe": False, + } + + result = mpt_client.commerce.orders.quote(created_order.id) + + mpt_client.commerce.orders.notify(result.id, user_data) diff --git a/tests/unit/resources/commerce/test_agreements.py b/tests/unit/resources/commerce/test_agreements.py index 0de58f1..6fe2380 100644 --- a/tests/unit/resources/commerce/test_agreements.py +++ b/tests/unit/resources/commerce/test_agreements.py @@ -15,7 +15,7 @@ async def test_async_template(async_http_client): with respx.mock: respx.get("https://api.example.com/public/v1/commerce/agreements/AGR-123/template").mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "text/markdown"}, content=template_content, ) @@ -33,7 +33,7 @@ def test_template(http_client): "https://api.example.com/public/v1/commerce/agreements/AGR-123/template" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "text/markdown"}, content="# Order Template\n\nThis is a markdown template.", ) @@ -54,7 +54,7 @@ def test_render(http_client): "https://api.example.com/public/v1/commerce/agreements/AGR-123/render" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "text/html"}, content=rendered_content, ) @@ -75,7 +75,7 @@ async def test_async_render(async_http_client): "https://api.example.com/public/v1/commerce/agreements/AGR-123/render" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "text/html"}, content=rendered_content, ) diff --git a/tests/unit/resources/commerce/test_assets.py b/tests/unit/resources/commerce/test_assets.py index 6250f4e..31a6526 100644 --- a/tests/unit/resources/commerce/test_assets.py +++ b/tests/unit/resources/commerce/test_assets.py @@ -21,7 +21,7 @@ async def test_async_render(async_assets_service): with respx.mock: respx.get("https://api.example.com/public/v1/commerce/assets/ASSET-123/render").mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": APPLICATION_JSON}, json=render_response, ) @@ -39,7 +39,7 @@ def test_render(assets_service): "https://api.example.com/public/v1/commerce/assets/ASSET-123/render" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": APPLICATION_JSON}, json=render_response, ) @@ -59,7 +59,7 @@ def test_terminate(assets_service): "https://api.example.com/public/v1/commerce/assets/ASSET-123/terminate" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": APPLICATION_JSON}, json=response_data, ) @@ -79,7 +79,7 @@ async def test_async_terminate(async_assets_service): "https://api.example.com/public/v1/commerce/assets/ASSET-123/terminate" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": APPLICATION_JSON}, json=response_data, ) diff --git a/tests/unit/resources/commerce/test_orders.py b/tests/unit/resources/commerce/test_orders.py index 2d5126a..14cfb00 100644 --- a/tests/unit/resources/commerce/test_orders.py +++ b/tests/unit/resources/commerce/test_orders.py @@ -31,6 +31,7 @@ def async_orders_service(async_http_client): ("query", {"id": "ORD-123", "status": "update"}), ("complete", {"id": "ORD-123", "status": "update"}), ("fail", {"id": "ORD-123", "status": "update"}), + ("quote", {"id": "ORD-123", "status": "update"}), ], ) def test_custom_resource_actions(orders_service, action, input_status): @@ -41,7 +42,7 @@ def test_custom_resource_actions(orders_service, action, input_status): f"https://api.example.com/public/v1/commerce/orders/ORD-123/{action}" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "application/json"}, json=response_expected_data, ) @@ -64,6 +65,7 @@ def test_custom_resource_actions(orders_service, action, input_status): ("query", None), ("complete", None), ("fail", None), + ("quote", None), ], ) def test_custom_resource_actions_no_data(orders_service, action, input_status): @@ -74,7 +76,7 @@ def test_custom_resource_actions_no_data(orders_service, action, input_status): f"https://api.example.com/public/v1/commerce/orders/ORD-123/{action}" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "application/json"}, json=response_expected_data, ) @@ -95,7 +97,7 @@ def test_notify(orders_service): "https://api.example.com/public/v1/commerce/orders/ORD-123/notify" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "application/json"}, content='{"status": "notified"}', ) @@ -115,7 +117,7 @@ def test_template(orders_service): "https://api.example.com/public/v1/commerce/orders/ORD-123/template" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "text/markdown"}, content="# Order Template\n\nThis is a markdown template.", ) @@ -128,6 +130,25 @@ def test_template(orders_service): assert result == "# Order Template\n\nThis is a markdown template." +def test_render(orders_service): + with respx.mock: + mock_route = respx.get( + "https://api.example.com/public/v1/commerce/orders/ORD-123/render" + ).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "text/markdown"}, + content="# Order Render\n\nThis is a markdown render.", + ) + ) + + result = orders_service.render("ORD-123") + + assert mock_route.called + assert mock_route.call_count == 1 + assert result == "# Order Render\n\nThis is a markdown render." + + @pytest.mark.parametrize( ("action", "input_status"), [ @@ -136,6 +157,7 @@ def test_template(orders_service): ("query", {"id": "ORD-123", "status": "update"}), ("complete", {"id": "ORD-123", "status": "update"}), ("fail", {"id": "ORD-123", "status": "update"}), + ("quote", {"id": "ORD-123", "status": "update"}), ], ) async def test_async_custom_resource_actions(async_orders_service, action, input_status): @@ -146,7 +168,7 @@ async def test_async_custom_resource_actions(async_orders_service, action, input f"https://api.example.com/public/v1/commerce/orders/ORD-123/{action}" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "application/json"}, json=response_expected_data, ) @@ -169,6 +191,7 @@ async def test_async_custom_resource_actions(async_orders_service, action, input ("query", None), ("complete", None), ("fail", None), + ("quote", None), ], ) async def test_async_custom_resource_actions_nodata(async_orders_service, action, input_status): @@ -179,7 +202,7 @@ async def test_async_custom_resource_actions_nodata(async_orders_service, action f"https://api.example.com/public/v1/commerce/orders/ORD-123/{action}" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "application/json"}, json=response_expected_data, ) @@ -200,7 +223,7 @@ async def test_async_notify(async_orders_service): "https://api.example.com/public/v1/commerce/orders/ORD-123/notify" ).mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "application/json"}, content='{"status": "notified"}', ) @@ -219,7 +242,7 @@ async def test_async_template(async_orders_service): with respx.mock: respx.get("https://api.example.com/public/v1/commerce/orders/ORD-123/template").mock( return_value=httpx.Response( - status_code=200, + status_code=httpx.codes.OK, headers={"content-type": "text/markdown"}, content=template_content, ) @@ -230,6 +253,22 @@ async def test_async_template(async_orders_service): assert result == template_content +async def test_async_render(async_orders_service): + render_content = "# Order Render\n\nThis is a markdown render." + with respx.mock: + respx.get("https://api.example.com/public/v1/commerce/orders/ORD-123/render").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "text/markdown"}, + content=render_content, + ) + ) + + result = await async_orders_service.render("ORD-123") + + assert result == render_content + + def test_subscription_service(http_client): orders_service = OrdersService(http_client=http_client)