Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion e2e_config.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
48 changes: 48 additions & 0 deletions mpt_api_client/resources/commerce/orders.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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.

Expand Down
19 changes: 12 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions tests/e2e/commerce/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
72 changes: 72 additions & 0 deletions tests/e2e/commerce/order/conftest.py
Original file line number Diff line number Diff line change
@@ -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
141 changes: 141 additions & 0 deletions tests/e2e/commerce/order/test_async_order.py
Original file line number Diff line number Diff line change
@@ -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)
Loading