From ebd691687578dc61bda670e2c1921999918fcd94 Mon Sep 17 00:00:00 2001 From: Viktor Bozhinov <45173816+VKTB@users.noreply.github.com> Date: Wed, 30 Aug 2023 12:51:24 +0100 Subject: [PATCH] Unit test CatalogueItemService class #7 --- .../repositories/test_catalogue_category.py | 22 + test/unit/services/conftest.py | 18 + test/unit/services/test_catalogue_category.py | 38 +- test/unit/services/test_catalogue_item.py | 380 ++++++++++++++++++ 4 files changed, 446 insertions(+), 12 deletions(-) create mode 100644 test/unit/services/conftest.py create mode 100644 test/unit/services/test_catalogue_item.py diff --git a/test/unit/repositories/test_catalogue_category.py b/test/unit/repositories/test_catalogue_category.py index 278d6099..6e8582f3 100644 --- a/test/unit/repositories/test_catalogue_category.py +++ b/test/unit/repositories/test_catalogue_category.py @@ -27,6 +27,7 @@ def test_create(test_helpers, database_mock, catalogue_category_repository): Verify that the `create` method properly handles the catalogue category to be created, checks that there is not a duplicate catalogue category, and creates the catalogue category. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -37,6 +38,7 @@ def test_create(test_helpers, database_mock, catalogue_category_repository): parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `count_documents` to return 0 (no duplicate catalogue category found within the parent catalogue category) test_helpers.mock_count_documents(database_mock.catalogue_categories, 0) @@ -57,6 +59,7 @@ def test_create(test_helpers, database_mock, catalogue_category_repository): }, ) + # pylint: disable=duplicate-code created_catalogue_category = catalogue_category_repository.create( CatalogueCategoryIn( name=catalogue_category.name, @@ -68,6 +71,7 @@ def test_create(test_helpers, database_mock, catalogue_category_repository): catalogue_item_properties=catalogue_category.catalogue_item_properties, ) ) + # pylint: enable=duplicate-code database_mock.catalogue_categories.insert_one.assert_called_once_with( { @@ -137,6 +141,7 @@ def test_create_with_parent_id(test_helpers, database_mock, catalogue_category_r }, ) + # pylint: disable=duplicate-code created_catalogue_category = catalogue_category_repository.create( CatalogueCategoryIn( name=catalogue_category.name, @@ -148,6 +153,7 @@ def test_create_with_parent_id(test_helpers, database_mock, catalogue_category_r catalogue_item_properties=catalogue_category.catalogue_item_properties, ) ) + # pylint: enable=duplicate-code database_mock.catalogue_categories.insert_one.assert_called_once_with( { @@ -176,6 +182,7 @@ def test_create_with_nonexistent_parent_id(test_helpers, database_mock, catalogu Verify that the `create` method properly handles a catalogue category with a nonexistent parent ID, does not find a parent catalogue category with an ID specified by `parent_id`, and does not create the catalogue category. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -186,6 +193,7 @@ def test_create_with_nonexistent_parent_id(test_helpers, database_mock, catalogu parent_id=str(ObjectId()), catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `find_one` to not return a parent catalogue category document test_helpers.mock_find_one(database_mock.catalogue_categories, None) @@ -215,6 +223,7 @@ def test_create_with_duplicate_name_within_parent(test_helpers, database_mock, c Verify that the `create` method properly handles a catalogue category with a duplicate name, finds that there is a duplicate catalogue category, and does not create the catalogue category. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category B", @@ -228,6 +237,7 @@ def test_create_with_duplicate_name_within_parent(test_helpers, database_mock, c CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), ], ) + # pylint: enable=duplicate-code # Mock `find_one` to return the parent catalogue category document test_helpers.mock_find_one( @@ -247,6 +257,7 @@ def test_create_with_duplicate_name_within_parent(test_helpers, database_mock, c test_helpers.mock_count_documents(database_mock.catalogue_categories, 1) with pytest.raises(DuplicateRecordError) as exc: + # pylint: disable=duplicate-code catalogue_category_repository.create( CatalogueCategoryIn( name=catalogue_category.name, @@ -258,6 +269,7 @@ def test_create_with_duplicate_name_within_parent(test_helpers, database_mock, c catalogue_item_properties=catalogue_category.catalogue_item_properties, ) ) + # pylint: enable=duplicate-code assert str(exc.value) == "Duplicate catalogue category found within the parent catalogue category" database_mock.catalogue_categories.find_one.assert_called_once_with( {"_id": CustomObjectId(catalogue_category.parent_id)} @@ -345,6 +357,7 @@ def test_get(test_helpers, database_mock, catalogue_category_repository): Verify that the `get` method properly handles the retrieval of a catalogue category by ID. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -355,6 +368,7 @@ def test_get(test_helpers, database_mock, catalogue_category_repository): parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `find_one` to return a catalogue category document test_helpers.mock_find_one( @@ -411,6 +425,7 @@ def test_list(test_helpers, database_mock, catalogue_category_repository): Verify that the `list` method properly handles the retrieval of catalogue categories without filters. """ + # pylint: disable=duplicate-code catalogue_category_a = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -432,6 +447,7 @@ def test_list(test_helpers, database_mock, catalogue_category_repository): parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `find` to return a list of catalogue category documents test_helpers.mock_find( @@ -473,6 +489,7 @@ def test_list_with_path_filter(test_helpers, database_mock, catalogue_category_r Verify that the `list` method properly handles the retrieval of catalogue categories based on the provided path filter. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -483,6 +500,7 @@ def test_list_with_path_filter(test_helpers, database_mock, catalogue_category_r parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `find` to return a list of catalogue category documents test_helpers.mock_find( @@ -514,6 +532,7 @@ def test_list_with_parent_path_filter(test_helpers, database_mock, catalogue_cat Verify that the `list` method properly handles the retrieval of catalogue categories based on the provided parent path filter. """ + # pylint: disable=duplicate-code catalogue_category_a = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -535,6 +554,7 @@ def test_list_with_parent_path_filter(test_helpers, database_mock, catalogue_cat parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `find` to return a list of catalogue category documents test_helpers.mock_find( @@ -576,6 +596,7 @@ def test_list_with_path_and_parent_path_filters(test_helpers, database_mock, cat Verify that the `list` method properly handles the retrieval of catalogue categories based on the provided path and parent path filters. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category B", @@ -586,6 +607,7 @@ def test_list_with_path_and_parent_path_filters(test_helpers, database_mock, cat parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `find` to return a list of catalogue category documents test_helpers.mock_find( diff --git a/test/unit/services/conftest.py b/test/unit/services/conftest.py new file mode 100644 index 00000000..2a4836f8 --- /dev/null +++ b/test/unit/services/conftest.py @@ -0,0 +1,18 @@ +""" +Module for providing common test configuration and test fixtures. +""" +from unittest.mock import Mock + +import pytest + +from inventory_management_system_api.repositories.catalogue_category import CatalogueCategoryRepo + + +@pytest.fixture(name="catalogue_category_repository_mock") +def fixture_catalogue_category_repository_mock() -> Mock: + """ + Fixture to create a mock of the `CatalogueCategoryRepo` dependency. + + :return: Mocked CatalogueCategoryRepo instance. + """ + return Mock(CatalogueCategoryRepo) diff --git a/test/unit/services/test_catalogue_category.py b/test/unit/services/test_catalogue_category.py index f0aff710..128c2917 100644 --- a/test/unit/services/test_catalogue_category.py +++ b/test/unit/services/test_catalogue_category.py @@ -1,4 +1,3 @@ -# pylint: disable=duplicate-code """ Unit tests for the `CatalogueCategoryService` service. """ @@ -13,21 +12,10 @@ CatalogueCategoryIn, CatalogueItemProperty, ) -from inventory_management_system_api.repositories.catalogue_category import CatalogueCategoryRepo from inventory_management_system_api.schemas.catalogue_category import CatalogueCategoryPostRequestSchema from inventory_management_system_api.services.catalogue_category import CatalogueCategoryService -@pytest.fixture(name="catalogue_category_repository_mock") -def fixture_catalogue_category_repository_mock() -> Mock: - """ - Fixture to create a mock of the `CatalogueCategoryRepo` dependency. - - :return: Mocked CatalogueCategoryRepo instance. - """ - return Mock(CatalogueCategoryRepo) - - @pytest.fixture(name="catalogue_category_service") def fixture_catalogue_category_service(catalogue_category_repository_mock: Mock) -> CatalogueCategoryService: """ @@ -46,6 +34,7 @@ def test_create(catalogue_category_repository_mock, catalogue_category_service): Verify that the `create` method properly handles the catalogue category to be created, generates the code and paths, and calls the repository's create method. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -56,6 +45,7 @@ def test_create(catalogue_category_repository_mock, catalogue_category_service): parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `create` to return the created catalogue category catalogue_category_repository_mock.create.return_value = catalogue_category @@ -68,6 +58,7 @@ def test_create(catalogue_category_repository_mock, catalogue_category_service): ) ) + # pylint: disable=duplicate-code catalogue_category_repository_mock.create.assert_called_once_with( CatalogueCategoryIn( name=catalogue_category.name, @@ -79,6 +70,7 @@ def test_create(catalogue_category_repository_mock, catalogue_category_service): catalogue_item_properties=catalogue_category.catalogue_item_properties, ) ) + # pylint: enable=duplicate-code assert created_catalogue_category == catalogue_category @@ -88,6 +80,7 @@ def test_create_with_parent_id(catalogue_category_repository_mock, catalogue_cat Verify that the `create` method properly handles a catalogue category with a parent ID. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category B", @@ -101,8 +94,10 @@ def test_create_with_parent_id(catalogue_category_repository_mock, catalogue_cat CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), ], ) + # pylint: enable=duplicate-code # Mock `get` to return the parent catalogue category + # pylint: disable=duplicate-code catalogue_category_repository_mock.get.return_value = CatalogueCategoryOut( id=catalogue_category.parent_id, name="Category A", @@ -113,6 +108,7 @@ def test_create_with_parent_id(catalogue_category_repository_mock, catalogue_cat parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `create` to return the created catalogue category catalogue_category_repository_mock.create.return_value = catalogue_category @@ -126,6 +122,7 @@ def test_create_with_parent_id(catalogue_category_repository_mock, catalogue_cat ) catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.parent_id) + # pylint: disable=duplicate-code catalogue_category_repository_mock.create.assert_called_once_with( CatalogueCategoryIn( name=catalogue_category.name, @@ -137,6 +134,7 @@ def test_create_with_parent_id(catalogue_category_repository_mock, catalogue_cat catalogue_item_properties=catalogue_category.catalogue_item_properties, ) ) + # pylint: enable=duplicate-code assert created_catalogue_category == catalogue_category @@ -146,6 +144,7 @@ def test_create_with_whitespace_name(catalogue_category_repository_mock, catalog Verify that the `create` method trims the whitespace from the category name and handles it correctly. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name=" Category A ", @@ -159,6 +158,7 @@ def test_create_with_whitespace_name(catalogue_category_repository_mock, catalog CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), ], ) + # pylint: enable=duplicate-code # Mock `create` to return the created catalogue category catalogue_category_repository_mock.create.return_value = catalogue_category @@ -171,6 +171,7 @@ def test_create_with_whitespace_name(catalogue_category_repository_mock, catalog ) ) + # pylint: disable=duplicate-code catalogue_category_repository_mock.create.assert_called_once_with( CatalogueCategoryIn( name=catalogue_category.name, @@ -182,6 +183,7 @@ def test_create_with_whitespace_name(catalogue_category_repository_mock, catalog catalogue_item_properties=catalogue_category.catalogue_item_properties, ) ) + # pylint: enable=duplicate-code assert created_catalogue_category == catalogue_category @@ -201,6 +203,7 @@ def test_create_with_leaf_parent_catalogue_category(catalogue_category_repositor ) # Mock `get` to return the parent catalogue category + # pylint: disable=duplicate-code catalogue_category_repository_mock.get.return_value = CatalogueCategoryOut( id=catalogue_category.parent_id, name="Category A", @@ -214,6 +217,7 @@ def test_create_with_leaf_parent_catalogue_category(catalogue_category_repositor CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), ], ) + # pylint: enable=duplicate-code with pytest.raises(LeafCategoryError) as exc: catalogue_category_service.create( @@ -247,6 +251,7 @@ def test_get(catalogue_category_repository_mock, catalogue_category_service): Verify that the `get` method properly handles the retrieval of a catalogue category by ID. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -257,6 +262,7 @@ def test_get(catalogue_category_repository_mock, catalogue_category_service): parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `get` to return a catalogue category catalogue_category_repository_mock.get.return_value = catalogue_category @@ -290,6 +296,7 @@ def test_list(catalogue_category_repository_mock, catalogue_category_service): Verify that the `list` method properly handles the retrieval of catalogue categories without filters. """ + # pylint: disable=duplicate-code catalogue_category_a = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -311,6 +318,7 @@ def test_list(catalogue_category_repository_mock, catalogue_category_service): parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `list` to return a list of catalogue categories catalogue_category_repository_mock.list.return_value = [catalogue_category_a, catalogue_category_b] @@ -328,6 +336,7 @@ def test_list_with_path_filter(catalogue_category_repository_mock, catalogue_cat Verify that the `list` method properly handles the retrieval of catalogue categories based on the provided path filter. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -338,6 +347,7 @@ def test_list_with_path_filter(catalogue_category_repository_mock, catalogue_cat parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `list` to return a list of catalogue categories catalogue_category_repository_mock.list.return_value = [catalogue_category] @@ -355,6 +365,7 @@ def test_list_with_parent_path_filter(catalogue_category_repository_mock, catalo Verify that the `list` method properly handles the retrieval of catalogue categories based on the provided parent path filter. """ + # pylint: disable=duplicate-code catalogue_category_a = CatalogueCategoryOut( id=str(ObjectId()), name="Category A", @@ -365,6 +376,7 @@ def test_list_with_parent_path_filter(catalogue_category_repository_mock, catalo parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code catalogue_category_b = CatalogueCategoryOut( id=str(ObjectId()), @@ -393,6 +405,7 @@ def test_list_with_path_and_parent_path_filters(catalogue_category_repository_mo Verify that the `list` method properly handles the retrieval of catalogue categories based on the provided path and parent path filters. """ + # pylint: disable=duplicate-code catalogue_category = CatalogueCategoryOut( id=str(ObjectId()), name="Category B", @@ -403,6 +416,7 @@ def test_list_with_path_and_parent_path_filters(catalogue_category_repository_mo parent_id=None, catalogue_item_properties=[], ) + # pylint: enable=duplicate-code # Mock `list` to return a list of catalogue categories catalogue_category_repository_mock.list.return_value = [catalogue_category] diff --git a/test/unit/services/test_catalogue_item.py b/test/unit/services/test_catalogue_item.py new file mode 100644 index 00000000..6d42332a --- /dev/null +++ b/test/unit/services/test_catalogue_item.py @@ -0,0 +1,380 @@ +""" +Unit tests for the `CatalogueCategoryService` service. +""" +from unittest.mock import Mock + +import pytest +from bson import ObjectId + +from inventory_management_system_api.core.exceptions import ( + MissingRecordError, + NonLeafCategoryError, + MissingMandatoryCatalogueItemProperty, + InvalidCatalogueItemPropertyTypeError, +) +from inventory_management_system_api.models.catalogue_category import CatalogueCategoryOut, CatalogueItemProperty +from inventory_management_system_api.models.catalogue_item import CatalogueItemOut, Property, CatalogueItemIn +from inventory_management_system_api.repositories.catalogue_item import CatalogueItemRepo +from inventory_management_system_api.schemas.catalogue_item import ( + PropertyPostRequestSchema, + CatalogueItemPostRequestSchema, +) +from inventory_management_system_api.services.catalogue_item import CatalogueItemService + + +@pytest.fixture(name="catalogue_item_repository_mock") +def fixture_catalogue_item_repository_mock() -> Mock: + """ + Fixture to create a mock of the `CatalogueItemRepo` dependency. + + :return: Mocked CatalogueItemRepo instance. + """ + return Mock(CatalogueItemRepo) + + +@pytest.fixture(name="catalogue_item_service") +def fixture_catalogue_item_service( + catalogue_item_repository_mock: Mock, catalogue_category_repository_mock: Mock +) -> CatalogueItemService: + """ + Fixture to create a `CatalogueItemService` instance with a mocked `CatalogueItemRepo` and `CatalogueCategoryRepo` + dependencies. + + :param catalogue_item_repository_mock: Mocked `CatalogueItemRepo` instance. + :param catalogue_category_repository_mock: Mocked `CatalogueCategoryRepo` instance. + :return: `CatalogueItemService` instance with the mocked dependency. + """ + return CatalogueItemService(catalogue_item_repository_mock, catalogue_category_repository_mock) + + +def test_create(catalogue_item_repository_mock, catalogue_category_repository_mock, catalogue_item_service): + """ + Test creating a catalogue item. + + Verify that the `create` method properly handles the catalogue item to be created, checks that the catalogue + category exists and that it is a leaf category, checks for missing mandatory catalogue item properties, filters the + matching catalogue item properties, adds the units to the supplied properties, and validates the property values. + """ + # pylint: disable=duplicate-code + catalogue_item = CatalogueItemOut( + id=str(ObjectId()), + catalogue_category_id=str(ObjectId()), + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + Property(name="Property A", value=20, unit="mm"), + Property(name="Property B", value=False), + Property(name="Property C", value="20x15x10", unit="cm"), + ], + ) + + # Mock `get` to return the catalogue category + catalogue_category_repository_mock.get.return_value = CatalogueCategoryOut( + id=catalogue_item.catalogue_category_id, + name="Category A", + code="category-a", + is_leaf=True, + path="/category-a", + parent_path="/", + parent_id=None, + catalogue_item_properties=[ + CatalogueItemProperty(name="Property A", type="number", unit="mm", mandatory=False), + CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), + CatalogueItemProperty(name="Property C", type="string", unit="cm", mandatory=True), + ], + ) + # pylint: enable=duplicate-code + # Mock `create` to return the created catalogue item + catalogue_item_repository_mock.create.return_value = catalogue_item + + created_catalogue_item = catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_item.catalogue_category_id, + name=catalogue_item.name, + description=catalogue_item.description, + properties=[ + PropertyPostRequestSchema(name="Property A", value=20), + PropertyPostRequestSchema(name="Property B", value=False), + PropertyPostRequestSchema(name="Property C", value="20x15x10"), + ], + ) + ) + + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_item.catalogue_category_id) + # pylint: disable=duplicate-code + catalogue_item_repository_mock.create.assert_called_once_with( + CatalogueItemIn( + catalogue_category_id=catalogue_item.catalogue_category_id, + name=catalogue_item.name, + description=catalogue_item.description, + properties=catalogue_item.properties, + ) + ) + # pylint: enable=duplicate-code + assert created_catalogue_item == catalogue_item + + +def test_create_with_nonexistent_catalogue_category_id(catalogue_category_repository_mock, catalogue_item_service): + """ + Test creating a catalogue item with a nonexistent parent ID. + + Verify that the `create` method properly handles a catalogue item with a nonexistent catalogue category ID, does not + find a catalogue category with such ID, and does not create the catalogue item. + """ + catalogue_category_id = str(ObjectId()) + + # Mock `get` to not return a catalogue category + catalogue_category_repository_mock.get.return_value = None + + with pytest.raises(MissingRecordError) as exc: + catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_category_id, + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + PropertyPostRequestSchema(name="Property A", value=20), + PropertyPostRequestSchema(name="Property B", value=False), + PropertyPostRequestSchema(name="Property C", value="20x15x10"), + ], + ) + ) + assert str(exc.value) == f"No catalogue category found with ID: {catalogue_category_id}" + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category_id) + + +def test_create_in_non_leaf_catalogue_category(catalogue_category_repository_mock, catalogue_item_service): + """ + Test creating a catalogue item in a non-leaf catalogue category. + + Verify that the `create` method properly handles a catalogue item with a non-leaf catalogue category, checks that + the catalogue category exists, finds that the catalogue category is not a leaf category, and does not create the + catalogue item. + """ + # pylint: disable=duplicate-code + catalogue_category = CatalogueCategoryOut( + id=str(ObjectId()), + name="Category B", + code="category-b", + is_leaf=False, + path="/category-b", + parent_path="/", + parent_id=None, + catalogue_item_properties=[], + ) + # pylint: enable=duplicate-code + + # Mock `get` to return the catalogue category + catalogue_category_repository_mock.get.return_value = catalogue_category + + with pytest.raises(NonLeafCategoryError) as exc: + catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_category.id, + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + PropertyPostRequestSchema(name="Property A", value=20), + PropertyPostRequestSchema(name="Property B", value=False), + PropertyPostRequestSchema(name="Property C", value="20x15x10"), + ], + ) + ) + assert str(exc.value) == "Cannot add catalogue item to a non-leaf catalogue category" + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.id) + + +def test_create_with_missing_mandatory_properties(catalogue_category_repository_mock, catalogue_item_service): + """ + Test creating a catalogue item with missing mandatory catalogue item properties. + + Verify that the `create` method properly handles a catalogue item with missing mandatory properties, checks that + the catalogue category exists and that it is a leaf category, finds that there are missing mandatory catalogue item + properties, and does not create the catalogue item. + """ + # pylint: disable=duplicate-code + catalogue_category = CatalogueCategoryOut( + id=str(ObjectId()), + name="Category A", + code="category-a", + is_leaf=True, + path="/category-a", + parent_path="/", + parent_id=None, + catalogue_item_properties=[ + CatalogueItemProperty(name="Property A", type="number", unit="mm", mandatory=False), + CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), + CatalogueItemProperty(name="Property C", type="string", unit="cm", mandatory=True), + ], + ) + # pylint: enable=duplicate-code + + # Mock `get` to return the catalogue category + catalogue_category_repository_mock.get.return_value = catalogue_category + + with pytest.raises(MissingMandatoryCatalogueItemProperty) as exc: + catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_category.id, + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + PropertyPostRequestSchema(name="Property C", value="20x15x10"), + ], + ) + ) + assert ( + str(exc.value) + == f"Missing mandatory catalogue item property: '{catalogue_category.catalogue_item_properties[1].name}'" + ) + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.id) + + +def test_create_with_with_invalid_value_type_for_string_property( + catalogue_category_repository_mock, catalogue_item_service +): + """ + Test creating a catalogue item with invalid value type for a string catalogue item property. + + Verify that the `create` method properly handles a catalogue item with invalid value type for a string catalogue + item property, checks that the catalogue category exists and that it is a leaf category, checks that there are no + missing mandatory catalogue item properties, finds invalid value type for a string catalogue item property, and does + not create the catalogue item. + """ + catalogue_category = CatalogueCategoryOut( + id=str(ObjectId()), + name="Category A", + code="category-a", + is_leaf=True, + path="/category-a", + parent_path="/", + parent_id=None, + catalogue_item_properties=[ + CatalogueItemProperty(name="Property A", type="number", unit="mm", mandatory=False), + CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), + CatalogueItemProperty(name="Property C", type="string", unit="cm", mandatory=True), + ], + ) + + # Mock `get` to return the catalogue category + catalogue_category_repository_mock.get.return_value = catalogue_category + + with pytest.raises(InvalidCatalogueItemPropertyTypeError) as exc: + catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_category.id, + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + PropertyPostRequestSchema(name="Property A", value=20), + PropertyPostRequestSchema(name="Property B", value=False), + PropertyPostRequestSchema(name="Property C", value=True), + ], + ) + ) + assert ( + str(exc.value) + == f"Invalid value type for catalogue item property '{catalogue_category.catalogue_item_properties[2].name}'. " + "Expected type: string." + ) + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.id) + + +def test_create_with_with_invalid_value_type_for_number_property( + catalogue_category_repository_mock, catalogue_item_service +): + """ + Test creating a catalogue item with invalid value type for a number catalogue item property. + + Verify that the `create` method properly handles a catalogue item with invalid value type for a number catalogue + item property, checks that the catalogue category exists and that it is a leaf category, checks that there are no + missing mandatory catalogue item properties, finds invalid value type for a number catalogue item property, and does + not create the catalogue item. + """ + catalogue_category = CatalogueCategoryOut( + id=str(ObjectId()), + name="Category A", + code="category-a", + is_leaf=True, + path="/category-a", + parent_path="/", + parent_id=None, + catalogue_item_properties=[ + CatalogueItemProperty(name="Property A", type="number", unit="mm", mandatory=False), + CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), + CatalogueItemProperty(name="Property C", type="string", unit="cm", mandatory=True), + ], + ) + + # Mock `get` to return the catalogue category + catalogue_category_repository_mock.get.return_value = catalogue_category + + with pytest.raises(InvalidCatalogueItemPropertyTypeError) as exc: + catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_category.id, + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + PropertyPostRequestSchema(name="Property A", value="20"), + PropertyPostRequestSchema(name="Property B", value=False), + PropertyPostRequestSchema(name="Property C", value="20x15x10"), + ], + ) + ) + assert ( + str(exc.value) + == f"Invalid value type for catalogue item property '{catalogue_category.catalogue_item_properties[0].name}'. " + "Expected type: number." + ) + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.id) + + +def test_create_with_with_invalid_value_type_for_boolean_property( + catalogue_category_repository_mock, catalogue_item_service +): + """ + Test creating a catalogue item with invalid value type for a boolean catalogue item property. + + Verify that the `create` method properly handles a catalogue item with invalid value type for a boolean catalogue + item property, checks that the catalogue category exists and that it is a leaf category, checks that there are no + missing mandatory catalogue item properties, finds invalid value type for a boolean catalogue item property, and + does not create the catalogue item. + """ + catalogue_category = CatalogueCategoryOut( + id=str(ObjectId()), + name="Category A", + code="category-a", + is_leaf=True, + path="/category-a", + parent_path="/", + parent_id=None, + catalogue_item_properties=[ + CatalogueItemProperty(name="Property A", type="number", unit="mm", mandatory=False), + CatalogueItemProperty(name="Property B", type="boolean", mandatory=True), + CatalogueItemProperty(name="Property C", type="string", unit="cm", mandatory=True), + ], + ) + + # Mock `get` to return the catalogue category + catalogue_category_repository_mock.get.return_value = catalogue_category + + with pytest.raises(InvalidCatalogueItemPropertyTypeError) as exc: + catalogue_item_service.create( + CatalogueItemPostRequestSchema( + catalogue_category_id=catalogue_category.id, + name="Catalogue Item A", + description="This is Catalogue Item A", + properties=[ + PropertyPostRequestSchema(name="Property A", value=20), + PropertyPostRequestSchema(name="Property B", value="False"), + PropertyPostRequestSchema(name="Property C", value="20x15x10"), + ], + ) + ) + assert ( + str(exc.value) + == f"Invalid value type for catalogue item property '{catalogue_category.catalogue_item_properties[1].name}'. " + "Expected type: boolean." + ) + catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.id)