From 9f357b8ad88ff8bda1c9b559aefd3b9756316313 Mon Sep 17 00:00:00 2001 From: Albert Sola Date: Mon, 16 Mar 2026 11:42:20 +0000 Subject: [PATCH] MPT-19057: add first-level field annotations to commerce resource models Add typed field annotations (str | None, bool | None, BaseModel | None, list[BaseModel] | None) to all commerce resource Model subclasses: - Agreement, AgreementAttachment (agreements.py, agreements_attachments.py) - Asset (assets.py) - Order (orders.py) - OrdersAsset (orders_asset.py) - OrderSubscription (orders_subscription.py) - Subscription (subscriptions.py) Fields are derived from openapi-dev.json first-level properties. id is inherited from Model and skipped. $meta is skipped (invalid identifier). camelCase API names are converted to snake_case Python attributes. All fields are | None since no fields appear in the OpenAPI required array. Add corresponding unit tests for each model covering: - primitive field round-trip (to_dict) - nested object fields resolved as BaseModel instances - optional fields absent when not in input data --- .../resources/commerce/agreements.py | 57 ++++++++++- .../commerce/agreements_attachments.py | 11 ++- mpt_api_client/resources/commerce/assets.py | 35 ++++++- mpt_api_client/resources/commerce/orders.py | 65 +++++++++++- .../resources/commerce/orders_asset.py | 25 ++++- .../resources/commerce/orders_subscription.py | 37 ++++++- .../resources/commerce/subscriptions.py | 47 ++++++++- .../resources/commerce/test_agreements.py | 84 +++++++++++++++- .../commerce/test_agreements_attachments.py | 31 ++++++ tests/unit/resources/commerce/test_assets.py | 60 +++++++++++- .../commerce/test_order_subcription.py | 46 --------- .../commerce/test_order_subscription.py | 98 +++++++++++++++++++ tests/unit/resources/commerce/test_orders.py | 91 +++++++++++++++++ .../resources/commerce/test_orders_asset.py | 44 +++++++++ .../resources/commerce/test_subscriptions.py | 66 +++++++++++++ 15 files changed, 742 insertions(+), 55 deletions(-) delete mode 100644 tests/unit/resources/commerce/test_order_subcription.py create mode 100644 tests/unit/resources/commerce/test_order_subscription.py diff --git a/mpt_api_client/resources/commerce/agreements.py b/mpt_api_client/resources/commerce/agreements.py index e95ffbc1..ecbc5b58 100644 --- a/mpt_api_client/resources/commerce/agreements.py +++ b/mpt_api_client/resources/commerce/agreements.py @@ -6,6 +6,7 @@ ManagedResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.agreements_attachments import ( AgreementsAttachmentService, AsyncAgreementsAttachmentService, @@ -19,7 +20,61 @@ class Agreement(Model): - """Agreement resource.""" + """Agreement resource. + + Attributes: + icon: URL or identifier for the agreement icon. + status: Agreement status. + name: Agreement name. + start_date: Agreement start date. + end_date: Agreement end date. + listing: Reference to the listing. + authorization: Reference to the authorization. + vendor: Reference to the vendor account. + client: Reference to the client account. + price: Price information. + template: Reference to the template. + error: Error information. + lines: List of agreement lines. + assets: List of assets. + subscriptions: List of subscriptions. + parameters: Agreement parameters. + licensee: Reference to the licensee. + buyer: Reference to the buyer. + seller: Reference to the seller. + product: Reference to the product. + external_ids: External identifiers. + split: Split billing information. + terms_and_conditions: List of terms and conditions. + certificates: List of certificates. + audit: Audit information. + """ + + icon: str | None + status: str | None + name: str | None + start_date: str | None + end_date: str | None + listing: BaseModel | None + authorization: BaseModel | None + vendor: BaseModel | None + client: BaseModel | None + price: BaseModel | None + template: BaseModel | None + error: BaseModel | None + lines: list[BaseModel] | None + assets: list[BaseModel] | None + subscriptions: list[BaseModel] | None + parameters: BaseModel | None # noqa: WPS110 + licensee: BaseModel | None + buyer: BaseModel | None + seller: BaseModel | None + product: BaseModel | None + external_ids: BaseModel | None + split: BaseModel | None + terms_and_conditions: list[BaseModel] | None + certificates: list[BaseModel] | None + audit: BaseModel | None class AgreementsServiceConfig: diff --git a/mpt_api_client/resources/commerce/agreements_attachments.py b/mpt_api_client/resources/commerce/agreements_attachments.py index c1cfe30d..5489c5d3 100644 --- a/mpt_api_client/resources/commerce/agreements_attachments.py +++ b/mpt_api_client/resources/commerce/agreements_attachments.py @@ -14,10 +14,19 @@ UpdateMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel class AgreementAttachment(Model): - """Agreement attachment resource.""" + """Agreement attachment resource. + + Attributes: + attachment: Attachment metadata. + file: File reference or URL. + """ + + attachment: BaseModel | None + file: str | None # noqa: WPS110 class AgreementsAttachmentServiceConfig: diff --git a/mpt_api_client/resources/commerce/assets.py b/mpt_api_client/resources/commerce/assets.py index 898477ff..0afa28df 100644 --- a/mpt_api_client/resources/commerce/assets.py +++ b/mpt_api_client/resources/commerce/assets.py @@ -10,6 +10,7 @@ UpdateMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.mixins import ( AsyncRenderMixin, AsyncTerminateMixin, @@ -19,7 +20,39 @@ class Asset(Model): - """Asset resource.""" + """Asset resource. + + Attributes: + name: Asset name. + status: Asset status. + external_ids: External identifiers. + price: Price information. + template: Reference to the template. + parameters: Asset parameters. + terms: Reference to terms and conditions. + agreement: Reference to the agreement. + product: Reference to the product. + price_list: Reference to the price list. + listing: Reference to the listing. + licensee: Reference to the licensee. + lines: List of asset lines. + audit: Audit information. + """ + + name: str | None + status: str | None + external_ids: BaseModel | None + price: BaseModel | None + template: BaseModel | None + parameters: BaseModel | None # noqa: WPS110 + terms: BaseModel | None + agreement: BaseModel | None + product: BaseModel | None + price_list: BaseModel | None + listing: BaseModel | None + licensee: BaseModel | None + lines: list[BaseModel] | None + audit: BaseModel | None class AssetTemplate(Model): diff --git a/mpt_api_client/resources/commerce/orders.py b/mpt_api_client/resources/commerce/orders.py index 1e9f8334..3203edc9 100644 --- a/mpt_api_client/resources/commerce/orders.py +++ b/mpt_api_client/resources/commerce/orders.py @@ -9,6 +9,7 @@ ManagedResourceMixin, ) from mpt_api_client.models import Model, ResourceData +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.mixins import ( AsyncRenderMixin, AsyncTemplateMixin, @@ -26,7 +27,69 @@ class Order(Model): - """Order resource.""" + """Order resource. + + Attributes: + type: Order type. + status: Order status. + notes: Order notes. + comments: Order comments. + default_markup_source: Default markup source. + status_notes: Status notes details. + template: Reference to the template. + listing: Reference to the listing. + authorization: Reference to the authorization. + agreement: Reference to the agreement. + assignee: Reference to the assignee. + external_ids: External identifiers. + price: Price information. + lines: List of order lines. + subscriptions: List of subscriptions. + assets: List of assets. + parameters: Order parameters. + error: Error information. + product: Reference to the product. + client: Reference to the client account. + licensee: Reference to the licensee. + buyer: Reference to the buyer. + seller: Reference to the seller. + vendor: Reference to the vendor account. + bill_to: Bill-to address. + pricing_policy: Reference to the pricing policy. + terms_and_conditions: List of terms and conditions. + certificates: List of certificates. + audit: Audit information. + """ + + type: str | None + status: str | None + notes: str | None + comments: str | None + default_markup_source: str | None + status_notes: BaseModel | None + template: BaseModel | None + listing: BaseModel | None + authorization: BaseModel | None + agreement: BaseModel | None + assignee: BaseModel | None + external_ids: BaseModel | None + price: BaseModel | None + lines: list[BaseModel] | None + subscriptions: list[BaseModel] | None + assets: list[BaseModel] | None + parameters: BaseModel | None # noqa: WPS110 + error: BaseModel | None + product: BaseModel | None + client: BaseModel | None + licensee: BaseModel | None + buyer: BaseModel | None + seller: BaseModel | None + vendor: BaseModel | None + bill_to: BaseModel | None + pricing_policy: BaseModel | None + terms_and_conditions: list[BaseModel] | None + certificates: list[BaseModel] | None + audit: BaseModel | None class OrdersServiceConfig: diff --git a/mpt_api_client/resources/commerce/orders_asset.py b/mpt_api_client/resources/commerce/orders_asset.py index 9d29058b..595951d6 100644 --- a/mpt_api_client/resources/commerce/orders_asset.py +++ b/mpt_api_client/resources/commerce/orders_asset.py @@ -6,11 +6,34 @@ ManagedResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.mixins import AsyncRenderMixin, RenderMixin class OrdersAsset(Model): - """Orders Asset resource.""" + """Orders Asset resource. + + Attributes: + name: Asset name. + status: Asset status. + external_ids: External identifiers. + price: Price information. + template: Reference to the template. + parameters: Asset parameters. + terms: Reference to terms and conditions. + lines: List of asset lines. + audit: Audit information. + """ + + name: str | None + status: str | None + external_ids: BaseModel | None + price: BaseModel | None + template: BaseModel | None + parameters: BaseModel | None # noqa: WPS110 + terms: BaseModel | None + lines: list[BaseModel] | None + audit: BaseModel | None class OrdersAssetServiceConfig: diff --git a/mpt_api_client/resources/commerce/orders_subscription.py b/mpt_api_client/resources/commerce/orders_subscription.py index f7e7ea14..619dcea9 100644 --- a/mpt_api_client/resources/commerce/orders_subscription.py +++ b/mpt_api_client/resources/commerce/orders_subscription.py @@ -6,10 +6,45 @@ ManagedResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel class OrderSubscription(Model): - """Order Subscription resource.""" + """Order Subscription resource. + + Attributes: + name: Subscription name. + status: Subscription status. + start_date: Subscription start date. + termination_date: Subscription termination date. + commitment_date: Subscription commitment date. + auto_renew: Whether the subscription auto-renews. + external_ids: External identifiers. + terms: Reference to terms and conditions. + product: Reference to the product. + parameters: Subscription parameters. + agreement: Reference to the agreement. + price: Price information. + template: Reference to the template. + lines: List of subscription lines. + audit: Audit information. + """ + + name: str | None + status: str | None + start_date: str | None + termination_date: str | None + commitment_date: str | None + auto_renew: bool | None + external_ids: BaseModel | None + terms: BaseModel | None + product: BaseModel | None + parameters: BaseModel | None # noqa: WPS110 + agreement: BaseModel | None + price: BaseModel | None + template: BaseModel | None + lines: list[BaseModel] | None + audit: BaseModel | None class OrderSubscriptionsServiceConfig: diff --git a/mpt_api_client/resources/commerce/subscriptions.py b/mpt_api_client/resources/commerce/subscriptions.py index f111a99e..135dcfbc 100644 --- a/mpt_api_client/resources/commerce/subscriptions.py +++ b/mpt_api_client/resources/commerce/subscriptions.py @@ -13,6 +13,7 @@ UpdateMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.mixins import ( AsyncRenderMixin, AsyncTerminateMixin, @@ -22,7 +23,51 @@ class Subscription(Model): - """Subscription resource.""" + """Subscription resource. + + Attributes: + name: Subscription name. + status: Subscription status. + start_date: Subscription start date. + termination_date: Subscription termination date. + commitment_date: Subscription commitment date. + split_status: Split billing status. + auto_renew: Whether the subscription auto-renews. + external_ids: External identifiers. + terms: Reference to terms and conditions. + product: Reference to the product. + price: Price information. + parameters: Subscription parameters. + agreement: Reference to the agreement. + buyer: Reference to the buyer. + licensee: Reference to the licensee. + seller: Reference to the seller. + split: Split billing information. + template: Reference to the template. + lines: List of subscription lines. + audit: Audit information. + """ + + name: str | None + status: str | None + start_date: str | None + termination_date: str | None + commitment_date: str | None + split_status: str | None + auto_renew: bool | None + external_ids: BaseModel | None + terms: BaseModel | None + product: BaseModel | None + price: BaseModel | None + parameters: BaseModel | None # noqa: WPS110 + agreement: BaseModel | None + buyer: BaseModel | None + licensee: BaseModel | None + seller: BaseModel | None + split: BaseModel | None + template: BaseModel | None + lines: list[BaseModel] | None + audit: BaseModel | None class SubscriptionsServiceConfig: diff --git a/tests/unit/resources/commerce/test_agreements.py b/tests/unit/resources/commerce/test_agreements.py index 0ca3d994..f04bf039 100644 --- a/tests/unit/resources/commerce/test_agreements.py +++ b/tests/unit/resources/commerce/test_agreements.py @@ -2,7 +2,12 @@ import pytest import respx -from mpt_api_client.resources.commerce.agreements import AgreementsService, AsyncAgreementsService +from mpt_api_client.models.model import BaseModel +from mpt_api_client.resources.commerce.agreements import ( + Agreement, + AgreementsService, + AsyncAgreementsService, +) from mpt_api_client.resources.commerce.agreements_attachments import ( AgreementsAttachmentService, AsyncAgreementsAttachmentService, @@ -80,3 +85,80 @@ def test_async_mixins_present(async_http_client, method): result = hasattr(service, method) assert result is True + + +@pytest.fixture +def agreement_data(): + return { + "id": "AGR-001", + "icon": "https://example.com/icon.png", + "status": "Active", + "name": "My Agreement", + "startDate": "2024-01-01", + "endDate": "2025-01-01", + "listing": {"id": "LST-001"}, + "authorization": {"id": "AUT-001"}, + "vendor": {"id": "ACC-001"}, + "client": {"id": "ACC-002"}, + "price": {"total": 100}, + "template": {"id": "TPL-001"}, + "error": {"message": "some error"}, + "lines": [{"id": "LIN-001"}], + "assets": [{"id": "ASS-001"}], + "subscriptions": [{"id": "SUB-001"}], + "parameters": {"fulfillment": []}, + "licensee": {"id": "ACC-003"}, + "buyer": {"id": "ACC-004"}, + "seller": {"id": "ACC-005"}, + "product": {"id": "PRD-001"}, + "externalIds": {"vendor": "ext-001"}, + "split": {"type": "none"}, + "termsAndConditions": [{"id": "TAC-001"}], + "certificates": [{"id": "CRT-001"}], + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +def test_agreement_primitive_fields(agreement_data): + result = Agreement(agreement_data) + + assert result.to_dict() == agreement_data + + +def test_agreement_nested_party_fields(agreement_data): # noqa: WPS218 + result = Agreement(agreement_data) + + assert isinstance(result.listing, BaseModel) + assert isinstance(result.authorization, BaseModel) + assert isinstance(result.vendor, BaseModel) + assert isinstance(result.client, BaseModel) + assert isinstance(result.price, BaseModel) + + +def test_agreement_nested_relation_fields(agreement_data): # noqa: WPS218 + result = Agreement(agreement_data) + + assert isinstance(result.template, BaseModel) + assert isinstance(result.error, BaseModel) + assert isinstance(result.parameters, BaseModel) + assert isinstance(result.licensee, BaseModel) + assert isinstance(result.buyer, BaseModel) + + +def test_agreement_nested_identity_fields(agreement_data): # noqa: WPS218 + result = Agreement(agreement_data) + + assert isinstance(result.seller, BaseModel) + assert isinstance(result.product, BaseModel) + assert isinstance(result.external_ids, BaseModel) + assert isinstance(result.split, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_agreement_optional_fields_absent(): + result = Agreement({"id": "AGR-001"}) + + assert result.id == "AGR-001" + assert not hasattr(result, "name") + assert not hasattr(result, "status") + assert not hasattr(result, "audit") diff --git a/tests/unit/resources/commerce/test_agreements_attachments.py b/tests/unit/resources/commerce/test_agreements_attachments.py index 96eb0044..c282cc56 100644 --- a/tests/unit/resources/commerce/test_agreements_attachments.py +++ b/tests/unit/resources/commerce/test_agreements_attachments.py @@ -1,6 +1,8 @@ import pytest +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.agreements_attachments import ( + AgreementAttachment, AgreementsAttachmentService, AsyncAgreementsAttachmentService, ) @@ -44,3 +46,32 @@ def test_async_methods_present(async_attachment_service, method: str) -> None: result = hasattr(async_attachment_service, method) assert result is True + + +@pytest.fixture +def attachment_data(): + return { + "id": "ATT-001", + "attachment": {"name": "document.pdf", "size": 1024}, + "file": "https://example.com/files/document.pdf", + } + + +def test_attachment_primitive_fields(attachment_data): + result = AgreementAttachment(attachment_data) + + assert result.to_dict() == attachment_data + + +def test_attachment_nested_fields(attachment_data): + result = AgreementAttachment(attachment_data) + + assert isinstance(result.attachment, BaseModel) + + +def test_attachment_optional_fields_absent(): + result = AgreementAttachment({"id": "ATT-001"}) + + assert result.id == "ATT-001" + assert not hasattr(result, "attachment") + assert not hasattr(result, "file") diff --git a/tests/unit/resources/commerce/test_assets.py b/tests/unit/resources/commerce/test_assets.py index 39418719..d59eb804 100644 --- a/tests/unit/resources/commerce/test_assets.py +++ b/tests/unit/resources/commerce/test_assets.py @@ -1,6 +1,7 @@ import pytest -from mpt_api_client.resources.commerce.assets import AssetService, AsyncAssetService +from mpt_api_client.models.model import BaseModel +from mpt_api_client.resources.commerce.assets import Asset, AssetService, AsyncAssetService @pytest.fixture @@ -25,3 +26,60 @@ def test_async_assets_service_methods(async_assets_service, method): result = hasattr(async_assets_service, method) assert result is True + + +@pytest.fixture +def asset_data(): + return { + "id": "ASS-001", + "name": "My Asset", + "status": "Active", + "externalIds": {"vendor": "ext-001"}, + "price": {"total": 100}, + "template": {"id": "TPL-001"}, + "parameters": {"fulfillment": []}, + "terms": {"id": "TRM-001"}, + "agreement": {"id": "AGR-001"}, + "product": {"id": "PRD-001"}, + "priceList": {"id": "PRL-001"}, + "listing": {"id": "LST-001"}, + "licensee": {"id": "ACC-001"}, + "lines": [{"id": "LIN-001"}], + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +def test_asset_primitive_fields(asset_data): + result = Asset(asset_data) + + assert result.to_dict() == asset_data + + +def test_asset_nested_pricing_fields(asset_data): # noqa: WPS218 + result = Asset(asset_data) + + assert isinstance(result.external_ids, BaseModel) + assert isinstance(result.price, BaseModel) + assert isinstance(result.template, BaseModel) + assert isinstance(result.parameters, BaseModel) + assert isinstance(result.terms, BaseModel) + + +def test_asset_nested_relation_fields(asset_data): # noqa: WPS218 + result = Asset(asset_data) + + assert isinstance(result.agreement, BaseModel) + assert isinstance(result.product, BaseModel) + assert isinstance(result.price_list, BaseModel) + assert isinstance(result.listing, BaseModel) + assert isinstance(result.licensee, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_asset_optional_fields_absent(): + result = Asset({"id": "ASS-001"}) + + assert result.id == "ASS-001" + assert not hasattr(result, "name") + assert not hasattr(result, "status") + assert not hasattr(result, "audit") diff --git a/tests/unit/resources/commerce/test_order_subcription.py b/tests/unit/resources/commerce/test_order_subcription.py deleted file mode 100644 index 7f1d8935..00000000 --- a/tests/unit/resources/commerce/test_order_subcription.py +++ /dev/null @@ -1,46 +0,0 @@ -import pytest - -from mpt_api_client.resources.commerce.orders_subscription import ( - AsyncOrderSubscriptionsService, - OrderSubscriptionsService, -) - - -@pytest.fixture -def subscription_service(http_client): - return OrderSubscriptionsService( - http_client=http_client, endpoint_params={"order_id": "ORD-123"} - ) - - -@pytest.fixture -def async_subscription_service(async_http_client): - return AsyncOrderSubscriptionsService( - http_client=async_http_client, endpoint_params={"order_id": "ORD-123"} - ) - - -@pytest.mark.parametrize("method", ["get", "create", "delete", "update"]) -def test_mixins_present(subscription_service, method): - result = hasattr(subscription_service, method) - - assert result is True - - -@pytest.mark.parametrize("method", ["get", "create", "delete", "update"]) -def test_async_mixins_present(async_subscription_service, method): - result = hasattr(async_subscription_service, method) - - assert result is True - - -def test_endpoint(subscription_service): - result = subscription_service.path == "/public/v1/commerce/orders/ORD-123/subscriptions" - - assert result is True - - -def test_async_endpoint(async_subscription_service): - result = async_subscription_service.path == "/public/v1/commerce/orders/ORD-123/subscriptions" - - assert result is True diff --git a/tests/unit/resources/commerce/test_order_subscription.py b/tests/unit/resources/commerce/test_order_subscription.py new file mode 100644 index 00000000..b99ecacb --- /dev/null +++ b/tests/unit/resources/commerce/test_order_subscription.py @@ -0,0 +1,98 @@ +import pytest + +from mpt_api_client.models.model import BaseModel +from mpt_api_client.resources.commerce.orders_subscription import ( + AsyncOrderSubscriptionsService, + OrderSubscription, + OrderSubscriptionsService, +) + + +@pytest.fixture +def subscription_service(http_client): + return OrderSubscriptionsService( + http_client=http_client, endpoint_params={"order_id": "ORD-123"} + ) + + +@pytest.fixture +def async_subscription_service(async_http_client): + return AsyncOrderSubscriptionsService( + http_client=async_http_client, endpoint_params={"order_id": "ORD-123"} + ) + + +@pytest.mark.parametrize("method", ["get", "create", "delete", "update"]) +def test_mixins_present(subscription_service, method): + result = hasattr(subscription_service, method) + + assert result is True + + +@pytest.mark.parametrize("method", ["get", "create", "delete", "update"]) +def test_async_mixins_present(async_subscription_service, method): + result = hasattr(async_subscription_service, method) + + assert result is True + + +def test_endpoint(subscription_service): + result = subscription_service.path == "/public/v1/commerce/orders/ORD-123/subscriptions" + + assert result is True + + +def test_async_endpoint(async_subscription_service): + result = async_subscription_service.path == "/public/v1/commerce/orders/ORD-123/subscriptions" + + assert result is True + + +@pytest.fixture +def order_subscription_data(): + return { + "id": "SUB-001", + "name": "Order Subscription", + "status": "Active", + "startDate": "2024-01-01", + "terminationDate": "2025-01-01", + "commitmentDate": "2024-06-01", + "autoRenew": True, + "externalIds": {"vendor": "ext-001"}, + "terms": {"id": "TRM-001"}, + "product": {"id": "PRD-001"}, + "parameters": {"fulfillment": []}, + "agreement": {"id": "AGR-001"}, + "price": {"total": 100}, + "template": {"id": "TPL-001"}, + "lines": [{"id": "LIN-001"}], + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +def test_order_subscription_primitive_fields(order_subscription_data): + result = OrderSubscription(order_subscription_data) + + assert result.to_dict() == order_subscription_data + + +def test_order_subscription_nested_fields(order_subscription_data): # noqa: WPS218 + result = OrderSubscription(order_subscription_data) + + assert isinstance(result.external_ids, BaseModel) + assert isinstance(result.terms, BaseModel) + assert isinstance(result.product, BaseModel) + assert isinstance(result.parameters, BaseModel) + assert isinstance(result.agreement, BaseModel) + assert isinstance(result.price, BaseModel) + assert isinstance(result.template, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_order_subscription_fields_absent(): + result = OrderSubscription({"id": "SUB-001"}) + + assert result.id == "SUB-001" + assert not hasattr(result, "name") + assert not hasattr(result, "status") + assert not hasattr(result, "audit") diff --git a/tests/unit/resources/commerce/test_orders.py b/tests/unit/resources/commerce/test_orders.py index 76f830ea..aec18f4a 100644 --- a/tests/unit/resources/commerce/test_orders.py +++ b/tests/unit/resources/commerce/test_orders.py @@ -2,6 +2,7 @@ import pytest import respx +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.orders import AsyncOrdersService, Order, OrdersService from mpt_api_client.resources.commerce.orders_asset import ( AsyncOrdersAssetService, @@ -247,3 +248,93 @@ def test_async_mixins_present(async_orders_service, method): result = hasattr(async_orders_service, method) assert result is True + + +@pytest.fixture +def order_data(): + return { + "id": "ORD-001", + "type": "Purchase", + "status": "Processing", + "notes": "Some notes", + "comments": "Some comments", + "defaultMarkupSource": "standard", + "statusNotes": {"reason": "pending review"}, + "template": {"id": "TPL-001"}, + "listing": {"id": "LST-001"}, + "authorization": {"id": "AUT-001"}, + "agreement": {"id": "AGR-001"}, + "assignee": {"id": "USR-001"}, + "externalIds": {"vendor": "ext-001"}, + "price": {"total": 100}, + "lines": [{"id": "LIN-001"}], + "subscriptions": [{"id": "SUB-001"}], + "assets": [{"id": "ASS-001"}], + "parameters": {"fulfillment": []}, + "error": {"message": "some error"}, + "product": {"id": "PRD-001"}, + "client": {"id": "ACC-001"}, + "licensee": {"id": "ACC-002"}, + "buyer": {"id": "ACC-003"}, + "seller": {"id": "ACC-004"}, + "vendor": {"id": "ACC-005"}, + "billTo": {"address": "123 Main St"}, + "pricingPolicy": {"id": "PPL-001"}, + "termsAndConditions": [{"id": "TAC-001"}], + "certificates": [{"id": "CRT-001"}], + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +def test_order_primitive_fields(order_data): + result = Order(order_data) + + assert result.to_dict() == order_data + + +def test_order_nested_header_fields(order_data): # noqa: WPS218 + result = Order(order_data) + + assert isinstance(result.status_notes, BaseModel) + assert isinstance(result.template, BaseModel) + assert isinstance(result.listing, BaseModel) + assert isinstance(result.authorization, BaseModel) + assert isinstance(result.agreement, BaseModel) + + +def test_order_nested_pricing_fields(order_data): # noqa: WPS218 + result = Order(order_data) + + assert isinstance(result.assignee, BaseModel) + assert isinstance(result.external_ids, BaseModel) + assert isinstance(result.price, BaseModel) + assert isinstance(result.parameters, BaseModel) + assert isinstance(result.error, BaseModel) + + +def test_order_nested_party_fields(order_data): # noqa: WPS218 + result = Order(order_data) + + assert isinstance(result.product, BaseModel) + assert isinstance(result.client, BaseModel) + assert isinstance(result.licensee, BaseModel) + assert isinstance(result.buyer, BaseModel) + assert isinstance(result.seller, BaseModel) + + +def test_order_nested_billing_fields(order_data): # noqa: WPS218 + result = Order(order_data) + + assert isinstance(result.vendor, BaseModel) + assert isinstance(result.bill_to, BaseModel) + assert isinstance(result.pricing_policy, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_order_optional_fields_absent(): + result = Order({"id": "ORD-001"}) + + assert result.id == "ORD-001" + assert not hasattr(result, "type") + assert not hasattr(result, "status") + assert not hasattr(result, "audit") diff --git a/tests/unit/resources/commerce/test_orders_asset.py b/tests/unit/resources/commerce/test_orders_asset.py index 03213e9a..437db968 100644 --- a/tests/unit/resources/commerce/test_orders_asset.py +++ b/tests/unit/resources/commerce/test_orders_asset.py @@ -1,7 +1,9 @@ import pytest +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.orders_asset import ( AsyncOrdersAssetService, + OrdersAsset, OrdersAssetService, ) @@ -42,3 +44,45 @@ def test_async_endpoint(async_asset_service): result = async_asset_service.path == "/public/v1/commerce/orders/ORD-123/assets" assert result is True + + +@pytest.fixture +def orders_asset_data(): + return { + "id": "ASS-001", + "name": "Order Asset", + "status": "Active", + "externalIds": {"vendor": "ext-001"}, + "price": {"total": 100}, + "template": {"id": "TPL-001"}, + "parameters": {"fulfillment": []}, + "terms": {"id": "TRM-001"}, + "lines": [{"id": "LIN-001"}], + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +def test_orders_asset_primitive_fields(orders_asset_data): + result = OrdersAsset(orders_asset_data) + + assert result.to_dict() == orders_asset_data + + +def test_orders_asset_nested_fields(orders_asset_data): # noqa: WPS218 + result = OrdersAsset(orders_asset_data) + + assert isinstance(result.external_ids, BaseModel) + assert isinstance(result.price, BaseModel) + assert isinstance(result.template, BaseModel) + assert isinstance(result.parameters, BaseModel) + assert isinstance(result.terms, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_orders_asset_optional_fields_absent(): + result = OrdersAsset({"id": "ASS-001"}) + + assert result.id == "ASS-001" + assert not hasattr(result, "name") + assert not hasattr(result, "status") + assert not hasattr(result, "audit") diff --git a/tests/unit/resources/commerce/test_subscriptions.py b/tests/unit/resources/commerce/test_subscriptions.py index 47e03304..6aa5a392 100644 --- a/tests/unit/resources/commerce/test_subscriptions.py +++ b/tests/unit/resources/commerce/test_subscriptions.py @@ -2,8 +2,10 @@ import pytest import respx +from mpt_api_client.models.model import BaseModel from mpt_api_client.resources.commerce.subscriptions import ( AsyncSubscriptionsService, + Subscription, SubscriptionsService, ) @@ -98,3 +100,67 @@ def test_render(subscriptions_service): result = subscriptions_service.render("SUB-123") assert result == template_content + + +@pytest.fixture +def subscription_data(): + return { + "id": "SUB-001", + "name": "My Subscription", + "status": "Active", + "startDate": "2024-01-01", + "terminationDate": "2025-01-01", + "commitmentDate": "2024-06-01", + "splitStatus": "none", + "autoRenew": True, + "externalIds": {"vendor": "ext-001"}, + "terms": {"id": "TRM-001"}, + "product": {"id": "PRD-001"}, + "price": {"total": 100}, + "parameters": {"fulfillment": []}, + "agreement": {"id": "AGR-001"}, + "buyer": {"id": "ACC-001"}, + "licensee": {"id": "ACC-002"}, + "seller": {"id": "ACC-003"}, + "split": {"type": "none"}, + "template": {"id": "TPL-001"}, + "lines": [{"id": "LIN-001"}], + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +def test_subscription_primitive_fields(subscription_data): + result = Subscription(subscription_data) + + assert result.to_dict() == subscription_data + + +def test_subscription_nested_core_fields(subscription_data): # noqa: WPS218 + result = Subscription(subscription_data) + + assert isinstance(result.external_ids, BaseModel) + assert isinstance(result.terms, BaseModel) + assert isinstance(result.product, BaseModel) + assert isinstance(result.price, BaseModel) + assert isinstance(result.parameters, BaseModel) + assert isinstance(result.agreement, BaseModel) + + +def test_subscription_nested_party_fields(subscription_data): # noqa: WPS218 + result = Subscription(subscription_data) + + assert isinstance(result.buyer, BaseModel) + assert isinstance(result.licensee, BaseModel) + assert isinstance(result.seller, BaseModel) + assert isinstance(result.split, BaseModel) + assert isinstance(result.template, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_subscription_optional_fields_absent(): + result = Subscription({"id": "SUB-001"}) + + assert result.id == "SUB-001" + assert not hasattr(result, "name") + assert not hasattr(result, "status") + assert not hasattr(result, "audit")