diff --git a/e2e_config.test.json b/e2e_config.test.json index 5d0bde00..b999baef 100644 --- a/e2e_config.test.json +++ b/e2e_config.test.json @@ -67,5 +67,6 @@ "notifications.category.id": "NTC-6157-0397", "notifications.message.id": "MSG-0000-6215-1019-0139", "notifications.subscriber.id": "NTS-0829-7123-7123", - "integration.extension.id": "EXT-6587-4477" + "integration.extension.id": "EXT-6587-4477", + "integration.term.id": "ETC-6587-4477-0062" } diff --git a/mpt_api_client/resources/integration/extension_term_variants.py b/mpt_api_client/resources/integration/extension_term_variants.py index a8b8061a..364758f2 100644 --- a/mpt_api_client/resources/integration/extension_term_variants.py +++ b/mpt_api_client/resources/integration/extension_term_variants.py @@ -1,13 +1,52 @@ from mpt_api_client.http import AsyncService, Service from mpt_api_client.http.mixins import ( AsyncCollectionMixin, + AsyncCreateFileMixin, + AsyncModifiableResourceMixin, CollectionMixin, + CreateFileMixin, + ModifiableResourceMixin, ) from mpt_api_client.models import Model +from mpt_api_client.models.model import BaseModel +from mpt_api_client.resources.integration.mixins import ( + AsyncPublishableMixin, + PublishableMixin, +) class ExtensionTermVariant(Model): - """Extension Term Variant resource (stub).""" + """Extension Term Variant resource. + + Attributes: + name: Variant name. + revision: Revision number. + type: Variant type (Online or File). + asset_url: URL to the variant asset for Online type. + language_code: Language code for this variant. + description: Variant description. + status: Variant status (Draft, Published, Unpublished, Deleted). + filename: Original file name for File type. + size: File size in bytes for File type. + content_type: MIME content type of the file. + term: Reference to the parent term. + file_id: Identifier of the uploaded file. + audit: Audit information (created, updated, published, unpublished). + """ + + name: str | None + revision: int | None + type: str | None + asset_url: str | None + language_code: str | None + description: str | None + status: str | None + filename: str | None + size: int | None + content_type: str | None + term: BaseModel | None + file_id: str | None + audit: BaseModel | None class ExtensionTermVariantsServiceConfig: @@ -16,19 +55,27 @@ class ExtensionTermVariantsServiceConfig: _endpoint = "/public/v1/integration/extensions/{extension_id}/terms/{term_id}/variants" _model_class = ExtensionTermVariant _collection_key = "data" + _upload_file_key = "file" + _upload_data_key = "variant" class ExtensionTermVariantsService( + PublishableMixin[ExtensionTermVariant], + CreateFileMixin[ExtensionTermVariant], + ModifiableResourceMixin[ExtensionTermVariant], CollectionMixin[ExtensionTermVariant], Service[ExtensionTermVariant], ExtensionTermVariantsServiceConfig, ): - """Sync service for extension term variants (stub).""" + """Sync service for extensions/{extensionId}/terms/{termId}/variants endpoint.""" class AsyncExtensionTermVariantsService( + AsyncPublishableMixin[ExtensionTermVariant], + AsyncCreateFileMixin[ExtensionTermVariant], + AsyncModifiableResourceMixin[ExtensionTermVariant], AsyncCollectionMixin[ExtensionTermVariant], AsyncService[ExtensionTermVariant], ExtensionTermVariantsServiceConfig, ): - """Async service for extension term variants (stub).""" + """Async service for extensions/{extensionId}/terms/{termId}/variants endpoint.""" diff --git a/tests/e2e/integration/extension_term_variants/__init__.py b/tests/e2e/integration/extension_term_variants/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/e2e/integration/extension_term_variants/conftest.py b/tests/e2e/integration/extension_term_variants/conftest.py new file mode 100644 index 00000000..500337cb --- /dev/null +++ b/tests/e2e/integration/extension_term_variants/conftest.py @@ -0,0 +1,58 @@ +import pytest + +from mpt_api_client.exceptions import MPTAPIError + + +@pytest.fixture(scope="session") +def extension_id(e2e_config): + return e2e_config["integration.extension.id"] + + +@pytest.fixture(scope="session") +def term_id(e2e_config): + return e2e_config["integration.term.id"] + + +@pytest.fixture +def extension_term_variants_service(mpt_vendor, extension_id, term_id): + return mpt_vendor.integration.extensions.terms(extension_id).variants(term_id) + + +@pytest.fixture +def async_extension_term_variants_service(async_mpt_vendor, extension_id, term_id): + return async_mpt_vendor.integration.extensions.terms(extension_id).variants(term_id) + + +@pytest.fixture +def variant_data(short_uuid): + return { + "type": "File", + "assetUrl": "", + "languageCode": "en-US", + "name": f"e2e - please delete {short_uuid}", + "description": "Created by automated E2E tests. Safe to delete.", + } + + +@pytest.fixture +def created_variant(extension_term_variants_service, variant_data, pdf_fd): + variant = extension_term_variants_service.create(variant_data, file=pdf_fd) + + yield variant + + try: + extension_term_variants_service.delete(variant.id) + except MPTAPIError as error: + print(f"TEARDOWN - Unable to delete variant {variant.id}: {error.title}") # noqa: WPS421 + + +@pytest.fixture +async def async_created_variant(async_extension_term_variants_service, variant_data, pdf_fd): + variant = await async_extension_term_variants_service.create(variant_data, file=pdf_fd) + + yield variant + + try: + await async_extension_term_variants_service.delete(variant.id) + except MPTAPIError as error: + print(f"TEARDOWN - Unable to delete variant {variant.id}: {error.title}") # noqa: WPS421 diff --git a/tests/e2e/integration/extension_term_variants/test_async_extension_term_variants.py b/tests/e2e/integration/extension_term_variants/test_async_extension_term_variants.py new file mode 100644 index 00000000..296faa8e --- /dev/null +++ b/tests/e2e/integration/extension_term_variants/test_async_extension_term_variants.py @@ -0,0 +1,57 @@ +import pytest + +from tests.e2e.helper import assert_async_service_filter_with_iterate + +pytestmark = [ + pytest.mark.flaky, +] + + +def test_create_extension_term_variant(async_created_variant, variant_data): + result = async_created_variant.name + + assert result == variant_data["name"] + + +async def test_filter_extension_term_variants( + async_extension_term_variants_service, async_created_variant +): + await assert_async_service_filter_with_iterate( + async_extension_term_variants_service, async_created_variant.id, None + ) # act + + +async def test_update_extension_term_variant( + async_extension_term_variants_service, async_created_variant, short_uuid +): + update_data = {"name": f"e2e updated {short_uuid}"} + + result = await async_extension_term_variants_service.update( + async_created_variant.id, update_data + ) + + assert result.name == update_data["name"] + + +async def test_publish_extension_term_variant( + async_extension_term_variants_service, async_created_variant +): + result = await async_extension_term_variants_service.publish(async_created_variant.id) + + assert result.status == "Published" + + +async def test_unpublish_extension_term_variant( + async_extension_term_variants_service, async_created_variant +): + await async_extension_term_variants_service.publish(async_created_variant.id) + + result = await async_extension_term_variants_service.unpublish(async_created_variant.id) + + assert result.status == "Unpublished" + + +async def test_delete_extension_term_variant( + async_extension_term_variants_service, async_created_variant +): + await async_extension_term_variants_service.delete(async_created_variant.id) # act diff --git a/tests/e2e/integration/extension_term_variants/test_sync_extension_term_variants.py b/tests/e2e/integration/extension_term_variants/test_sync_extension_term_variants.py new file mode 100644 index 00000000..4849fcbb --- /dev/null +++ b/tests/e2e/integration/extension_term_variants/test_sync_extension_term_variants.py @@ -0,0 +1,47 @@ +import pytest + +from tests.e2e.helper import assert_service_filter_with_iterate + +pytestmark = [ + pytest.mark.flaky, +] + + +def test_create_extension_term_variant(created_variant, variant_data): + result = created_variant.name + + assert result == variant_data["name"] + + +def test_filter_extension_term_variants(extension_term_variants_service, created_variant): + assert_service_filter_with_iterate( + extension_term_variants_service, created_variant.id, None + ) # act + + +def test_update_extension_term_variant( + extension_term_variants_service, created_variant, short_uuid +): + update_data = {"name": f"e2e updated {short_uuid}"} + + result = extension_term_variants_service.update(created_variant.id, update_data) + + assert result.name == update_data["name"] + + +def test_publish_extension_term_variant(extension_term_variants_service, created_variant): + result = extension_term_variants_service.publish(created_variant.id) + + assert result.status == "Published" + + +def test_unpublish_extension_term_variant(extension_term_variants_service, created_variant): + extension_term_variants_service.publish(created_variant.id) + + result = extension_term_variants_service.unpublish(created_variant.id) + + assert result.status == "Unpublished" + + +def test_delete_extension_term_variant(extension_term_variants_service, created_variant): + extension_term_variants_service.delete(created_variant.id) # act diff --git a/tests/unit/resources/integration/test_extension_term_variants.py b/tests/unit/resources/integration/test_extension_term_variants.py new file mode 100644 index 00000000..ea714c17 --- /dev/null +++ b/tests/unit/resources/integration/test_extension_term_variants.py @@ -0,0 +1,174 @@ +from typing import Any + +import httpx +import pytest +import respx + +from mpt_api_client.models.model import BaseModel +from mpt_api_client.resources.integration.extension_term_variants import ( + AsyncExtensionTermVariantsService, + ExtensionTermVariant, + ExtensionTermVariantsService, +) +from mpt_api_client.resources.integration.extension_terms import ExtensionTermsService + +BASE_URL = "https://api.example.com" +VARIANTS_URL = f"{BASE_URL}/public/v1/integration/extensions/EXT-001/terms/TERM-001/variants" + + +@pytest.fixture +def variants_service(http_client: Any) -> ExtensionTermVariantsService: + return ExtensionTermVariantsService( + http_client=http_client, + endpoint_params={"extension_id": "EXT-001", "term_id": "TERM-001"}, + ) + + +@pytest.fixture +def async_variants_service(async_http_client: Any) -> AsyncExtensionTermVariantsService: + return AsyncExtensionTermVariantsService( + http_client=async_http_client, + endpoint_params={"extension_id": "EXT-001", "term_id": "TERM-001"}, + ) + + +@pytest.fixture +def terms_service(http_client: Any) -> ExtensionTermsService: + return ExtensionTermsService( + http_client=http_client, endpoint_params={"extension_id": "EXT-001"} + ) + + +@pytest.fixture +def variant_data() -> dict: + return { + "id": "TRV-001", + "name": "English Variant", + "revision": 1, + "type": "File", + "assetUrl": None, + "languageCode": "en-US", + "description": "English language variant", + "status": "Draft", + "filename": "terms.pdf", + "size": 2048, + "contentType": "application/pdf", + "term": {"id": "TERM-001"}, + "fileId": "FILE-001", + "audit": {"created": {"at": "2024-01-01T00:00:00Z"}}, + } + + +@pytest.mark.parametrize( + "method", + ["get", "create", "update", "delete", "publish", "unpublish", "iterate"], +) +def test_mixins_present(variants_service: ExtensionTermVariantsService, method: str) -> None: + result = hasattr(variants_service, method) + + assert result is True + + +@pytest.mark.parametrize( + "method", + ["get", "create", "update", "delete", "publish", "unpublish", "iterate"], +) +def test_async_mixins_present( + async_variants_service: AsyncExtensionTermVariantsService, method: str +) -> None: + result = hasattr(async_variants_service, method) + + assert result is True + + +def test_extension_term_variant_primitive_fields(variant_data: dict) -> None: + result = ExtensionTermVariant(variant_data) + + assert result.to_dict() == variant_data + + +def test_extension_term_variant_nested_fields(variant_data: dict) -> None: + result = ExtensionTermVariant(variant_data) + + assert isinstance(result.term, BaseModel) + assert isinstance(result.audit, BaseModel) + + +def test_extension_term_variant_create( + variants_service: ExtensionTermVariantsService, tmp_path: Any +) -> None: + variant_payload = { + "Type": "File", + "Name": "English Variant", + "LanguageCode": "en-US", + "Description": "English language variant", + } + response_data = {"id": "TRV-001", "name": "English Variant", "status": "Draft"} + with respx.mock: + mock_route = respx.post(VARIANTS_URL).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + headers={"content-type": "application/json"}, + json=response_data, + ) + ) + file_path = tmp_path / "terms.pdf" + file_path.write_bytes(b"fake-pdf-content") + + with file_path.open("rb") as fp: + result = variants_service.create(variant_payload, fp) + + assert mock_route.call_count == 1 + assert result.to_dict() == response_data + + +def test_extension_term_variant_get( + variants_service: ExtensionTermVariantsService, variant_data: dict +) -> None: + with respx.mock: + respx.get(f"{VARIANTS_URL}/TRV-001").mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=variant_data, + ) + ) + + result = variants_service.get("TRV-001") + + assert result.to_dict() == variant_data + + +def test_extension_term_variants_list( + variants_service: ExtensionTermVariantsService, variant_data: dict +) -> None: + response_data = { + "data": [variant_data], + "$meta": {"pagination": {"total": 1, "offset": 0, "limit": 100}}, + } + with respx.mock: + respx.get(VARIANTS_URL).mock( + return_value=httpx.Response( + status_code=httpx.codes.OK, + json=response_data, + ) + ) + + result = variants_service.fetch_page() + + assert result[0].to_dict() == variant_data + + +def test_extension_term_variants_service_endpoint( + variants_service: ExtensionTermVariantsService, +) -> None: + result = variants_service.path + + assert result == "/public/v1/integration/extensions/EXT-001/terms/TERM-001/variants" + + +def test_extension_terms_variants_accessor(terms_service: ExtensionTermsService) -> None: + result = terms_service.variants("TERM-001") + + assert isinstance(result, ExtensionTermVariantsService) + assert result.http_client == terms_service.http_client + assert result.endpoint_params == {"extension_id": "EXT-001", "term_id": "TERM-001"}