diff --git a/seed/accounts/accounts.py b/seed/accounts/accounts.py index 389a61c..858fba0 100644 --- a/seed/accounts/accounts.py +++ b/seed/accounts/accounts.py @@ -1,18 +1,31 @@ import logging +import os +from mpt_api_client.resources.accounts.account import Account from seed.accounts.api_tokens import seed_api_token from seed.accounts.buyer import seed_buyer from seed.accounts.licensee import seed_licensee from seed.accounts.module import seed_module from seed.accounts.seller import seed_seller from seed.accounts.user_group import seed_user_group +from seed.helper import init_resource logger = logging.getLogger(__name__) -async def seed_accounts() -> None: # noqa: WPS217 +async def get_account() -> Account: # noqa: RUF029 async for compatibility purposes with init_resource + """Get account ID from environment variable.""" + account_id = os.getenv("CLIENT_ACCOUNT_ID") + if not account_id: + raise ValueError("CLIENT_ACCOUNT_ID environment variable is required") + return Account({"id": account_id}) + + +async def seed_accounts() -> None: # noqa: WPS213 WPS217 """Seed accounts data including account.""" logger.debug("Seeding accounts ...") + await init_resource("accounts.account.id", get_account) + await seed_seller() await seed_buyer() await seed_module() diff --git a/seed/accounts/api_tokens.py b/seed/accounts/api_tokens.py index 4dfd984..4673576 100644 --- a/seed/accounts/api_tokens.py +++ b/seed/accounts/api_tokens.py @@ -1,5 +1,4 @@ import logging -import os from dependency_injector.wiring import Provide, inject @@ -7,38 +6,18 @@ from mpt_api_client.resources.accounts.api_tokens import ApiToken from seed.container import Container from seed.context import Context +from seed.helper import init_resource, require_context_id logger = logging.getLogger(__name__) -@inject -async def get_api_token( - context: Context = Provide[Container.context], - mpt_ops: AsyncMPTClient = Provide[Container.mpt_operations], -) -> ApiToken | None: - """Get API token from context or fetch from API.""" - api_token_id = context.get_string("accounts.api_token.id") - if not api_token_id: - return None - try: - api_token = context.get_resource("accounts.api_token", api_token_id) - except ValueError: - api_token = None - if not isinstance(api_token, ApiToken): - api_token = await mpt_ops.accounts.api_tokens.get(api_token_id) - context.set_resource("accounts.api_token", api_token) - context["accounts.api_token.id"] = api_token.id - return api_token - return api_token - - @inject def build_api_token_data( context: Context = Provide[Container.context], ) -> dict[str, object]: """Get API token data dictionary for creation.""" - account_id = os.getenv("CLIENT_ACCOUNT_ID") - module_id = context.get_string("accounts.module.id") + account_id = require_context_id(context, "accounts.account.id", "creating API token") + module_id = require_context_id(context, "accounts.module.id", "creating API token") return { "account": {"id": account_id}, "name": "E2E Seeded API Token", @@ -49,27 +28,16 @@ def build_api_token_data( @inject -async def init_api_token( - context: Context = Provide[Container.context], +async def create_api_token( mpt_ops: AsyncMPTClient = Provide[Container.mpt_operations], ) -> ApiToken: - """Get or create API token.""" - api_token = await get_api_token(context=context, mpt_ops=mpt_ops) - if api_token is None: - logger.debug("Creating API token ...") - api_token_data = build_api_token_data(context=context) - api_token = await mpt_ops.accounts.api_tokens.create(api_token_data) - context.set_resource("accounts.api_token", api_token) - context["accounts.api_token.id"] = api_token.id - logger.info("API token created: %s", api_token.id) - else: - logger.info("API token found: %s", api_token.id) - return api_token + """Creates an API token.""" + api_token_data = build_api_token_data() + return await mpt_ops.accounts.api_tokens.create(api_token_data) -@inject async def seed_api_token() -> None: """Seed API token.""" logger.debug("Seeding API token ...") - await init_api_token() + await init_resource("accounts.api_token.id", create_api_token) logger.debug("Seeding API token completed.") diff --git a/seed/accounts/buyer.py b/seed/accounts/buyer.py index d3840e6..c8a6781 100644 --- a/seed/accounts/buyer.py +++ b/seed/accounts/buyer.py @@ -1,5 +1,4 @@ import logging -import os from dependency_injector.wiring import Provide, inject @@ -7,41 +6,16 @@ from mpt_api_client.resources.accounts.buyers import Buyer from seed.container import Container from seed.context import Context +from seed.helper import init_resource, require_context_id from seed.static.static import ICON logger = logging.getLogger(__name__) -@inject -async def get_buyer( - context: Context = Provide[Container.context], - mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> Buyer | None: - """Get buyer from context or fetch from API.""" - buyer_id = context.get_string("accounts.buyer.id") - if not buyer_id: - return None - try: - buyer = context.get_resource("accounts.buyer", buyer_id) - except ValueError: - buyer = None - if not isinstance(buyer, Buyer): - buyer = await mpt_operations.accounts.buyers.get(buyer_id) - context.set_resource("accounts.buyer", buyer) - context["accounts.buyer.id"] = buyer.id - return buyer - return buyer - - -@inject def build_buyer_data(context: Context = Provide[Container.context]) -> dict[str, object]: """Build buyer data dictionary for creation.""" - buyer_account_id = os.getenv("CLIENT_ACCOUNT_ID") - if not buyer_account_id: - raise ValueError("CLIENT_ACCOUNT_ID environment variable is required") - seller_id = context.get_string("accounts.seller.id") - if not seller_id: - raise ValueError("accounts.seller.id missing from context; seed seller before buyer.") + buyer_account_id = require_context_id(context, "accounts.account.id", "creating buyer") + seller_id = require_context_id(context, "accounts.seller.id", "creating buyer") return { "name": "E2E Seeded Buyer", "account": {"id": buyer_account_id}, @@ -62,31 +36,18 @@ def build_buyer_data(context: Context = Provide[Container.context]) -> dict[str, @inject -async def init_buyer( +async def create_buyer( context: Context = Provide[Container.context], mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], ) -> Buyer: - """Get or create buyer.""" - buyer = await get_buyer(context=context, mpt_operations=mpt_operations) - if buyer is None: - buyer_data = build_buyer_data(context=context) - logger.debug("Creating buyer ...") - with ICON.open("rb") as icon_fd: - created = await mpt_operations.accounts.buyers.create(buyer_data, file=icon_fd) - if isinstance(created, Buyer): - context.set_resource("accounts.buyer", created) - context["accounts.buyer.id"] = created.id - logger.info("Buyer created: %s", created.id) - return created - logger.warning("Buyer creation failed") # type: ignore[unreachable] - raise ValueError("Buyer creation failed") - logger.info("Buyer found: %s", buyer.id) - return buyer + """Creates a buyer.""" + buyer_data = build_buyer_data(context=context) + with ICON.open("rb") as icon_fd: + return await mpt_operations.accounts.buyers.create(buyer_data, file=icon_fd) -@inject async def seed_buyer() -> None: """Seed buyer.""" logger.debug("Seeding buyer ...") - await init_buyer() + await init_resource("accounts.buyer.id", create_buyer) logger.debug("Seeding buyer completed.") diff --git a/seed/accounts/licensee.py b/seed/accounts/licensee.py index b8a8274..c418fdd 100644 --- a/seed/accounts/licensee.py +++ b/seed/accounts/licensee.py @@ -1,5 +1,4 @@ import logging -import os from dependency_injector.wiring import Provide, inject @@ -7,49 +6,22 @@ from mpt_api_client.resources.accounts.licensees import Licensee from seed.container import Container from seed.context import Context +from seed.helper import init_resource, require_context_id from seed.static.static import ICON logger = logging.getLogger(__name__) -@inject -async def get_licensee( - context: Context = Provide[Container.context], - mpt_client: AsyncMPTClient = Provide[Container.mpt_client], -) -> Licensee | None: - """Get licensee from context or fetch from API.""" - licensee_id = context.get_string("accounts.licensee.id") - if not licensee_id: - return None - try: - licensee = context.get_resource("accounts.licensee", licensee_id) - except ValueError: - licensee = None - if not isinstance(licensee, Licensee): - licensee = await mpt_client.accounts.licensees.get(licensee_id) - context.set_resource("accounts.licensee", licensee) - context["accounts.licensee.id"] = licensee.id - return licensee - return licensee - - @inject def build_licensee_data( # noqa: WPS238 context: Context = Provide[Container.context], ) -> dict[str, object]: """Get licensee data dictionary for creation.""" - account_id = os.getenv("CLIENT_ACCOUNT_ID") - if not account_id: - raise ValueError("CLIENT_ACCOUNT_ID environment variable is required") - seller_id = context.get_string("accounts.seller.id") - if not seller_id: - raise ValueError("Seller ID is required in context") - buyer_id = context.get_string("accounts.buyer.id") - if not buyer_id: - raise ValueError("Buyer ID is required in context") - group = context.get_resource("accounts.user_group") - if group is None: - raise ValueError("User group is required in context") + account_id = require_context_id(context, "accounts.account.id", "creating licensee") + seller_id = require_context_id(context, "accounts.seller.id", "create licensee") + buyer_id = require_context_id(context, "accounts.buyer.id", "create licensee") + user_group_id = require_context_id(context, "accounts.user_group.id", "create licensee") + licensee_type = "Client" return { "name": "E2E Seeded Licensee", @@ -65,7 +37,7 @@ def build_licensee_data( # noqa: WPS238 "buyer": {"id": buyer_id}, "account": {"id": account_id}, "eligibility": {"client": True, "partner": False}, - "groups": [{"id": group.id}], + "groups": [{"id": user_group_id}], "type": licensee_type, "status": "Enabled", "defaultLanguage": "en-US", @@ -73,31 +45,15 @@ def build_licensee_data( # noqa: WPS238 @inject -async def init_licensee( - context: Context = Provide[Container.context], - mpt_client: AsyncMPTClient = Provide[Container.mpt_client], -) -> Licensee: - """Get or create licensee.""" - licensee = await get_licensee(context=context, mpt_client=mpt_client) - if licensee is None: - licensee_data = build_licensee_data(context=context) - logger.debug("Creating licensee ...") - with ICON.open("rb") as icon_file: - created = await mpt_client.accounts.licensees.create(licensee_data, file=icon_file) - if isinstance(created, Licensee): - context.set_resource("accounts.licensee", created) - context["accounts.licensee.id"] = created.id - logger.info("Licensee created: %s", created.id) - return created - logger.warning("Licensee creation failed") # type: ignore[unreachable] - raise ValueError("Licensee creation failed") - logger.info("Licensee found: %s", licensee.id) - return licensee +async def create_licensee(mpt_client: AsyncMPTClient = Provide[Container.mpt_client]) -> Licensee: + """Create licensee.""" + licensee_data = build_licensee_data() + with ICON.open("rb") as icon_fd: + return await mpt_client.accounts.licensees.create(licensee_data, file=icon_fd) -@inject async def seed_licensee() -> None: """Seed licensee.""" logger.debug("Seeding licensee ...") - await init_licensee() + await init_resource("accounts.licensee.id", create_licensee) logger.info("Seeding licensee completed.") diff --git a/seed/accounts/module.py b/seed/accounts/module.py index 5c9efa3..03735e3 100644 --- a/seed/accounts/module.py +++ b/seed/accounts/module.py @@ -6,67 +6,29 @@ from mpt_api_client.resources.accounts.modules import Module from mpt_api_client.rql.query_builder import RQLQuery from seed.container import Container -from seed.context import Context +from seed.helper import init_resource logger = logging.getLogger(__name__) @inject -async def get_module( - context: Context = Provide[Container.context], +async def find_module( mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> Module | None: - """Get module from context or fetch from API.""" - module_id = context.get_string("accounts.module.id") - if not module_id: - return None - try: - module = context.get_resource("accounts.module", module_id) - except ValueError: - module = None - if not isinstance(module, Module): - module = await mpt_operations.accounts.modules.get(module_id) - context.set_resource("accounts.module", module) - context["accounts.module.id"] = module.id - return module - return module +) -> Module: + """Selects an existing module to use for seeding purposes. + Currently selects the "Access Management" module from the marketplace. + """ + filtered = mpt_operations.accounts.modules.filter(RQLQuery(name="Access Management")) + modules = [module async for module in filtered.iterate()] -@inject -async def refresh_module( - context: Context = Provide[Container.context], - mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> Module | None: - """Refresh module in context (always fetch).""" - module = await get_module(context=context, mpt_operations=mpt_operations) - if module is None: - filtered_modules = mpt_operations.accounts.modules.filter( - RQLQuery(name="Access Management") - ) - modules = [mod async for mod in filtered_modules.iterate()] - if modules: - first_module = modules[0] - if isinstance(first_module, Module): - context["accounts.module.id"] = first_module.id - context.set_resource("accounts.module", first_module) - return first_module - logger.warning("First module is not a Module instance.") # type: ignore[unreachable] - return None - logger.warning("Module 'Access Management' not found.") - return None - return module + if not modules: + raise ValueError("Module 'Access Management' not found.") + return modules[0] -@inject -async def seed_module() -> Module: +async def seed_module() -> None: """Seed module.""" logger.debug("Seeding module ...") - existing_module = await get_module() - if existing_module is None: - refreshed = await refresh_module() - logger.debug("Seeding module completed.") - if refreshed is None: - raise ValueError("Could not seed module: no valid Module found.") - return refreshed + await init_resource("accounts.module.id", find_module) logger.debug("Seeding module completed.") - return existing_module diff --git a/seed/accounts/seller.py b/seed/accounts/seller.py index 6159918..610de87 100644 --- a/seed/accounts/seller.py +++ b/seed/accounts/seller.py @@ -6,32 +6,11 @@ from mpt_api_client import AsyncMPTClient from mpt_api_client.resources.accounts.sellers import Seller from seed.container import Container -from seed.context import Context +from seed.helper import init_resource logger = logging.getLogger(__name__) -@inject -async def get_seller( - context: Context = Provide[Container.context], - mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> Seller | None: - """Get seller from context or fetch from API.""" - seller_id = context.get_string("accounts.seller.id") - if not seller_id: - return None - try: - seller = context.get_resource("accounts.seller", seller_id) - except ValueError: - seller = None - if not isinstance(seller, Seller): - seller = await mpt_operations.accounts.sellers.get(seller_id) - context.set_resource("accounts.seller", seller) - context["accounts.seller.id"] = seller.id - return seller - return seller - - def build_seller_data(external_id: str | None = None) -> dict[str, object]: """Get seller data dictionary for creation.""" if external_id is None: @@ -51,29 +30,16 @@ def build_seller_data(external_id: str | None = None) -> dict[str, object]: @inject -async def init_seller( - context: Context = Provide[Container.context], +async def create_seller( mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> Seller | None: - """Get or create seller. Returns Seller if successful, None otherwise.""" - seller = await get_seller() - if seller is None: - logger.debug("Creating seller ...") - seller_data = build_seller_data() - created = await mpt_operations.accounts.sellers.create(seller_data) - if isinstance(created, Seller): - context.set_resource("accounts.seller", created) - context["accounts.seller.id"] = created.id - logger.info("Seller created: %s", created.id) - return created - logger.warning("Seller creation failed") # type: ignore[unreachable] - return None - logger.info("Seller already exists: %s", seller.id) - return seller +) -> Seller: + """Creates a seller.""" + seller_data = build_seller_data() + return await mpt_operations.accounts.sellers.create(seller_data) async def seed_seller() -> None: """Seed seller.""" logger.debug("Seeding seller ...") - await init_seller() + await init_resource("accounts.seller.id", create_seller) logger.debug("Seeding seller completed.") diff --git a/seed/accounts/user_group.py b/seed/accounts/user_group.py index db064ca..ccb8e9d 100644 --- a/seed/accounts/user_group.py +++ b/seed/accounts/user_group.py @@ -1,6 +1,5 @@ # mypy: disable-error-code=unreachable import logging -import os from dependency_injector.wiring import Provide, inject @@ -8,40 +7,18 @@ from mpt_api_client.resources.accounts.user_groups import UserGroup from seed.container import Container from seed.context import Context +from seed.helper import init_resource, require_context_id logger = logging.getLogger(__name__) -@inject -async def get_user_group( - context: Context = Provide[Container.context], - mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> UserGroup | None: - """Get user group from context or fetch from API.""" - user_group_id = context.get_string("accounts.user_group.id") - if not user_group_id: - return None - try: - user_group = context.get_resource("accounts.user_group", user_group_id) - except ValueError: - user_group = None - if not isinstance(user_group, UserGroup): - user_group = await mpt_operations.accounts.user_groups.get(user_group_id) - context.set_resource("accounts.user_group", user_group) - context["accounts.user_group.id"] = user_group.id - return user_group - return user_group - - @inject def build_user_group_data( context: Context = Provide[Container.context], ) -> dict[str, object]: """Get user group data dictionary for creation.""" - account_id = os.getenv("CLIENT_ACCOUNT_ID") - if not account_id: - raise ValueError("CLIENT_ACCOUNT_ID environment variable is required") - module_id = context.get_string("accounts.module.id") + account_id = require_context_id(context, "accounts.account.id", "create user group") + module_id = require_context_id(context, "accounts.module.id", "create user group") return { "name": "E2E Seeded User Group", "account": {"id": account_id}, @@ -53,33 +30,16 @@ def build_user_group_data( @inject -async def init_user_group( - context: Context = Provide[Container.context], +async def create_user_group( mpt_operations: AsyncMPTClient = Provide[Container.mpt_operations], -) -> UserGroup | None: - """Get or create user group.""" - user_group = await get_user_group(context=context, mpt_operations=mpt_operations) - if user_group is not None: - logger.info("User group already exists: %s", user_group.id) - return user_group - - logger.debug("Creating user group ...") - user_group_data = build_user_group_data(context=context) - created = await mpt_operations.accounts.user_groups.create(user_group_data) - if isinstance(created, UserGroup): - context.set_resource("accounts.user_group", created) - context["accounts.user_group.id"] = created.id - logger.info("User group created: %s", created.id) - return created +) -> UserGroup: + """Creates a user group.""" + user_group_data = build_user_group_data() + return await mpt_operations.accounts.user_groups.create(user_group_data) - logger.warning("User group creation failed") - return None - -@inject -async def seed_user_group() -> UserGroup | None: +async def seed_user_group() -> None: """Seed user group.""" logger.debug("Seeding user group ...") - user_group = await init_user_group() + await init_resource("accounts.user_group.id", create_user_group) logger.debug("Seeding user group completed.") - return user_group diff --git a/seed/static/static.py b/seed/static/static.py index 6b3258b..0ff8164 100644 --- a/seed/static/static.py +++ b/seed/static/static.py @@ -2,6 +2,6 @@ ICON = pathlib.Path(__file__).parent / "FIL-9920-4780-9379.png" -LOGO = pathlib.Path(__file__).parent / "FIL-9920-4780-9379.png" +LOGO = pathlib.Path(__file__).parent / "logo.png" PDF = pathlib.Path(__file__).parent / "empty.pdf" diff --git a/tests/seed/accounts/test_accounts.py b/tests/seed/accounts/test_accounts.py index 64aa1ac..60fa9cf 100644 --- a/tests/seed/accounts/test_accounts.py +++ b/tests/seed/accounts/test_accounts.py @@ -1,25 +1,33 @@ -from seed.accounts.accounts import seed_accounts +import pytest + +from mpt_api_client.resources.accounts.account import Account +from seed.accounts.accounts import get_account, seed_accounts + + +async def test_get_account_fails(monkeypatch): + # when env var is missing -> raises + monkeypatch.delenv("CLIENT_ACCOUNT_ID", raising=False) + with pytest.raises(ValueError): + await get_account() + + +async def test_get_account_success(monkeypatch): + # when env var is present -> returns Account with same id + monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-123") + account = await get_account() + assert isinstance(account, Account) + assert account.id == "ACC-123" async def test_seed_accounts(mocker): # noqa: WPS210 - mock_seed_seller = mocker.patch( - "seed.accounts.accounts.seed_seller", new_callable=mocker.AsyncMock - ) - mock_seed_buyer = mocker.patch( - "seed.accounts.accounts.seed_buyer", new_callable=mocker.AsyncMock - ) - mock_seed_module = mocker.patch( - "seed.accounts.accounts.seed_module", new_callable=mocker.AsyncMock - ) - mock_seed_api_token = mocker.patch( - "seed.accounts.accounts.seed_api_token", new_callable=mocker.AsyncMock - ) - mock_seed_user_group = mocker.patch( - "seed.accounts.accounts.seed_user_group", new_callable=mocker.AsyncMock - ) - mock_seed_licensee = mocker.patch( - "seed.accounts.accounts.seed_licensee", new_callable=mocker.AsyncMock - ) + mock_init_resource = mocker.patch("seed.accounts.accounts.init_resource", autospec=True) + + mock_seed_seller = mocker.patch("seed.accounts.accounts.seed_seller", autospec=True) + mock_seed_buyer = mocker.patch("seed.accounts.accounts.seed_buyer", autospec=True) + mock_seed_module = mocker.patch("seed.accounts.accounts.seed_module", autospec=True) + mock_seed_api_token = mocker.patch("seed.accounts.accounts.seed_api_token", autospec=True) + mock_seed_user_group = mocker.patch("seed.accounts.accounts.seed_user_group", autospec=True) + mock_seed_licensee = mocker.patch("seed.accounts.accounts.seed_licensee", autospec=True) await seed_accounts() # act mocks = [ mock_seed_seller, @@ -28,6 +36,7 @@ async def test_seed_accounts(mocker): # noqa: WPS210 mock_seed_api_token, mock_seed_user_group, mock_seed_licensee, + mock_init_resource, ] for mock in mocks: - mock.assert_called_once() + mock.assert_awaited_once() diff --git a/tests/seed/accounts/test_api_token.py b/tests/seed/accounts/test_api_token.py deleted file mode 100644 index ffbeafa..0000000 --- a/tests/seed/accounts/test_api_token.py +++ /dev/null @@ -1,73 +0,0 @@ -import pytest - -from mpt_api_client.resources.accounts.api_tokens import ApiToken, AsyncApiTokensService -from seed.accounts.api_tokens import ( - build_api_token_data, - get_api_token, - init_api_token, - seed_api_token, -) - - -@pytest.fixture -def api_token(): - return ApiToken({"id": "TOK-123", "name": "Test Token"}) - - -@pytest.fixture -def api_tokens_service(mocker): - return mocker.Mock(spec=AsyncApiTokensService) - - -async def test_get_api_token(context, operations_client, api_token, api_tokens_service): - context["accounts.api_token.id"] = api_token.id - api_tokens_service.get.return_value = api_token - operations_client.accounts.api_tokens = api_tokens_service - - result = await get_api_token(context=context, mpt_ops=operations_client) - - assert result == api_token - assert context.get_resource("accounts.api_token", api_token.id) == api_token - - -async def test_get_api_token_without_id(context): - result = await get_api_token(context=context) - assert result is None - - -async def test_init_api_token(context, operations_client, api_tokens_service, api_token, mocker): - api_tokens_service.create.return_value = api_token - operations_client.accounts.api_tokens = api_tokens_service - mock_get_api_token = mocker.patch( - "seed.accounts.api_tokens.get_api_token", new_callable=mocker.AsyncMock - ) - mock_build_api_token_data = mocker.patch("seed.accounts.api_tokens.build_api_token_data") - mock_get_api_token.return_value = None - mock_build_api_token_data.return_value = {"any": "payload"} - result = await init_api_token(context=context, mpt_ops=operations_client) - assert result == api_token - api_tokens_service.create.assert_called_once() - - -def test_build_api_token_data(context, monkeypatch): - monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") - context["accounts.module.id"] = "MOD-456" - expected_data = { - "account": {"id": "ACC-1086-6867"}, - "name": "E2E Seeded API Token", - "description": "This is a seeded API token for end-to-end testing.", - "icon": "", - "modules": [{"id": "MOD-456"}], - } - - result = build_api_token_data(context=context) - - assert result == expected_data - - -async def test_seed_api_token(mocker): - mock_init_api_token = mocker.patch( - "seed.accounts.api_tokens.init_api_token", new_callable=mocker.AsyncMock - ) - await seed_api_token() # act - mock_init_api_token.assert_awaited_once() diff --git a/tests/seed/accounts/test_api_tokens.py b/tests/seed/accounts/test_api_tokens.py new file mode 100644 index 0000000..7fbf95e --- /dev/null +++ b/tests/seed/accounts/test_api_tokens.py @@ -0,0 +1,50 @@ +import pytest + +from mpt_api_client.resources.accounts.api_tokens import ApiToken +from seed.accounts.api_tokens import ( + build_api_token_data, + create_api_token, + seed_api_token, +) + + +@pytest.fixture +def api_token(): + return ApiToken({"id": "TOK-123", "name": "Test Token"}) + + +def test_build_api_token_data(context, monkeypatch): + monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") + context["accounts.module.id"] = "MOD-456" + context["accounts.account.id"] = "ACC-1086-6867" + expected_data = { + "account": {"id": "ACC-1086-6867"}, + "name": "E2E Seeded API Token", + "description": "This is a seeded API token for end-to-end testing.", + "icon": "", + "modules": [{"id": "MOD-456"}], + } + + result = build_api_token_data(context=context) + + assert result == expected_data + + +async def test_create_api_token(mocker, api_token, operations_client): + create = mocker.AsyncMock(return_value=api_token) + operations_client.accounts.api_tokens.create = create + build_api_token_data = mocker.patch( + "seed.accounts.api_tokens.build_api_token_data", autospec=True + ) + + result = await create_api_token(operations_client) + + assert result == api_token + build_api_token_data.assert_called_once() + create.assert_awaited_once() + + +async def test_seed_api_token(mocker): + init_resource = mocker.patch("seed.accounts.api_tokens.init_resource", autospec=True) + await seed_api_token() # act + init_resource.assert_awaited_once() diff --git a/tests/seed/accounts/test_buyer.py b/tests/seed/accounts/test_buyer.py index d54b316..55e2679 100644 --- a/tests/seed/accounts/test_buyer.py +++ b/tests/seed/accounts/test_buyer.py @@ -1,7 +1,7 @@ import pytest -from mpt_api_client.resources.accounts.buyers import AsyncBuyersService, Buyer -from seed.accounts.buyer import build_buyer_data, get_buyer, init_buyer, seed_buyer +from mpt_api_client.resources.accounts.buyers import Buyer +from seed.accounts.buyer import build_buyer_data, create_buyer, seed_buyer from seed.context import Context @@ -10,27 +10,6 @@ def buyer(): return Buyer({"id": "BUY-123", "name": "Test Buyer"}) -@pytest.fixture -def buyers_service(mocker): - return mocker.Mock(spec=AsyncBuyersService) - - -async def test_get_buyer(context: Context, operations_client, buyer, buyers_service): - context["accounts.buyer.id"] = buyer.id - buyers_service.get.return_value = buyer - operations_client.accounts.buyers = buyers_service - - result = await get_buyer(context=context, mpt_operations=operations_client) - - assert result == buyer - assert context.get_resource("accounts.buyer", buyer.id) == buyer - - -async def test_get_buyer_without_id(context: Context): - result = await get_buyer(context=context) - assert result is None - - def test_build_buyer_data(context: Context): context["accounts.account.id"] = "ACC-1086-6867" context["accounts.seller.id"] = "SEL-9999-9999" @@ -59,36 +38,19 @@ def test_build_buyer_data(context: Context): assert result == expected_data -async def test_init_buyer(context: Context, operations_client, buyers_service, buyer, mocker): - buyers_service.create.return_value = buyer - operations_client.accounts.buyers = buyers_service - mock_get_buyer = mocker.patch("seed.accounts.buyer.get_buyer", new_callable=mocker.AsyncMock) - mock_get_buyer.return_value = buyer - result = await init_buyer(context=context, mpt_operations=operations_client) - assert result == buyer - buyers_service.create.assert_not_called() +async def test_create_buyer(mocker, context: Context, operations_client, buyer): + context["accounts.seller.id"] = "SEL-9999-9999" + context["accounts.account.id"] = "ACC-1086-6867" + operations_client.accounts.buyers.create = mocker.AsyncMock(return_value=buyer) + result = await create_buyer(context, operations_client) -async def test_init_buyer_create_new( # noqa: WPS211 - context: Context, - operations_client, - buyers_service, - buyer, - monkeypatch, - fs, -): - buyers_service.create.return_value = buyer - operations_client.accounts.buyers = buyers_service - monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") - context["accounts.seller.id"] = "SEL-9999-9999" - fs.create_file("/fake/path/buyer.txt", contents=b"fake_buyer_bytes") - fs.create_file("/mpt_api_client/seed/data/logo.png", contents=b"fake_icon_bytes") - result = await init_buyer(context=context, mpt_operations=operations_client) assert result == buyer - buyers_service.create.assert_called_once() async def test_seed_buyer(mocker): - mock_init_buyer = mocker.patch("seed.accounts.buyer.init_buyer", new_callable=mocker.AsyncMock) + init_resource = mocker.patch("seed.accounts.buyer.init_resource", autospec=True) + await seed_buyer() # act - mock_init_buyer.assert_awaited_once() + + init_resource.assert_awaited_once() diff --git a/tests/seed/accounts/test_licensee.py b/tests/seed/accounts/test_licensee.py index d52809b..7a8e652 100644 --- a/tests/seed/accounts/test_licensee.py +++ b/tests/seed/accounts/test_licensee.py @@ -1,8 +1,8 @@ import pytest from mpt_api_client.models.model import Model -from mpt_api_client.resources.accounts.licensees import AsyncLicenseesService, Licensee -from seed.accounts.licensee import build_licensee_data, get_licensee, init_licensee, seed_licensee +from mpt_api_client.resources.accounts.licensees import Licensee +from seed.accounts.licensee import build_licensee_data, create_licensee, seed_licensee from seed.context import Context @@ -28,33 +28,13 @@ def licensee(): }) -@pytest.fixture -def licensees_service(mocker): - return mocker.Mock(spec=AsyncLicenseesService) - - -async def test_get_licensee(context: Context, client_client, licensee, licensees_service): - context["accounts.licensee.id"] = licensee.id - licensees_service.get.return_value = licensee - client_client.accounts.licensees = licensees_service - - result = await get_licensee(context=context, mpt_client=client_client) - - assert result == licensee - assert context.get_resource("accounts.licensee", licensee.id) == licensee - - -async def test_get_licensee_without_id(context: Context): - licensee = await get_licensee(context=context) - assert licensee is None - - def test_build_licensee_data(context: Context, monkeypatch): monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") context["accounts.user_group.id"] = "UG-123" context.set_resource("accounts.user_group", Model({"id": "UG-123"})) context["accounts.seller.id"] = "SEL-123" context["accounts.buyer.id"] = "BUY-123" + context["accounts.account.id"] = "ACC-1086-6867" expected = { "name": "E2E Seeded Licensee", "address": { @@ -80,48 +60,20 @@ def test_build_licensee_data(context: Context, monkeypatch): assert result == expected -async def test_init_licensee( # noqa: WPS211 - context: Context, client_client, licensees_service, licensee, monkeypatch, mocker, fs -): - licensees_service.create.return_value = licensee - client_client.accounts.licensees = licensees_service - monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") - context["accounts.user_group.id"] = "UG-123" - context.set_resource("accounts.user_group", Model({"id": "UG-123"})) - context["accounts.seller.id"] = "SEL-123" - context["accounts.buyer.id"] = "BUY-123" - mock_get_licensee = mocker.patch( - "seed.accounts.licensee.get_licensee", new_callable=mocker.AsyncMock - ) - mocker.patch( - "seed.accounts.licensee.build_licensee_data", return_value=build_licensee_data(context) - ) - mock_get_licensee.return_value = None - fs.create_file("/mpt_api_client/seed/data/logo.png", contents=b"fake_icon_bytes") - result = await init_licensee(context=context, mpt_client=client_client) - assert result == licensee - licensees_service.create.assert_called_once() +async def test_create_licensee(mocker, operations_client, licensee): + create_mock = mocker.AsyncMock(return_value=licensee) + operations_client.accounts.licensees.create = create_mock + mocker.patch("seed.accounts.licensee.build_licensee_data") + result = await create_licensee(operations_client) -async def test_init_licensee_create_new( # noqa: WPS211 - context: Context, client_client, licensees_service, licensee, monkeypatch, fs, mocker -): - licensees_service.create.return_value = licensee - client_client.accounts.licensees = licensees_service - monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") - context["accounts.user_group.id"] = "UG-123" - context.set_resource("accounts.user_group", Model({"id": "UG-123"})) - context["accounts.seller.id"] = "SEL-123" - context["accounts.buyer.id"] = "BUY-123" - fs.create_file("/mpt_api_client/seed/data/logo.png", contents=b"fake_icon_bytes") - result = await init_licensee(context=context, mpt_client=client_client) assert result == licensee - licensees_service.create.assert_called_once() + create_mock.assert_awaited_once() async def test_seed_licensee(mocker): - mock_init_licensee = mocker.patch( - "seed.accounts.licensee.init_licensee", new_callable=mocker.AsyncMock - ) + init_resource = mocker.patch("seed.accounts.licensee.init_resource", autospec=True) + await seed_licensee() # act - mock_init_licensee.assert_awaited_once() + + init_resource.assert_awaited_once() diff --git a/tests/seed/accounts/test_module.py b/tests/seed/accounts/test_module.py index 432d1dc..a41cd4b 100644 --- a/tests/seed/accounts/test_module.py +++ b/tests/seed/accounts/test_module.py @@ -1,13 +1,12 @@ import pytest from mpt_api_client.resources.accounts.modules import AsyncModulesService, Module -from seed.accounts.module import get_module, refresh_module, seed_module -from seed.context import Context +from seed.accounts.module import find_module, seed_module class DummyAsyncIterator: - def __init__(self, modules): - self._iterator = iter(modules) + def __init__(self, items): # noqa: WPS110 + self._iterator = iter(items) # noqa: WPS110 def __aiter__(self): return self @@ -33,61 +32,34 @@ def async_iter(iter_items): yield from iter_items -async def test_get_module(context: Context, operations_client, module, modules_service): - context["accounts.module.id"] = module.id - modules_service.get.return_value = module +async def test_find_module(operations_client, module, modules_service, mocker): operations_client.accounts.modules = modules_service - - result = await get_module(context=context, mpt_operations=operations_client) - - assert result == module - assert context.get_resource("accounts.module", module.id) == module - - -async def test_get_module_without_id(context: Context): - result = await get_module(context=context) - assert result is None - - -async def test_refresh_module(context: Context, operations_client, modules_service, module, mocker): - modules_service.refresh = mocker.AsyncMock(return_value=module) - operations_client.accounts.modules = modules_service - context["accounts.module.id"] = module.id - mock_get_module = mocker.patch("seed.accounts.module.get_module", new_callable=mocker.AsyncMock) - mocker.patch("seed.accounts.module.Module", new=Module) mocker.patch.object( operations_client.accounts.modules, "filter", return_value=mocker.AsyncMock() ) - mock_get_module.return_value = None modules_filter = operations_client.accounts.modules.filter modules_filter.return_value.iterate = lambda: DummyAsyncIterator([module]) - result = await refresh_module(context=context, mpt_operations=operations_client) + + result = await find_module(mpt_operations=operations_client) + assert result == module -async def test_refresh_module_get_new( - context: Context, operations_client, modules_service, module, mocker -): - modules_service.refresh = mocker.AsyncMock(return_value=module) +async def test_find_module_not_found(operations_client, modules_service, mocker): operations_client.accounts.modules = modules_service - context["accounts.module.id"] = module.id - mock_get_module = mocker.patch("seed.accounts.module.get_module", new_callable=mocker.AsyncMock) - mocker.patch("seed.accounts.module.Module", new=Module) mocker.patch.object( operations_client.accounts.modules, "filter", return_value=mocker.AsyncMock() ) - mock_get_module.return_value = None modules_filter = operations_client.accounts.modules.filter - modules_filter.return_value.iterate = lambda: DummyAsyncIterator([module]) - result = await refresh_module(context=context, mpt_operations=operations_client) - assert result == module + modules_filter.return_value.iterate = lambda: DummyAsyncIterator([]) + + with pytest.raises(ValueError, match=r"Module 'Access Management' not found."): + await find_module(mpt_operations=operations_client) async def test_seed_module(mocker): - mock_get_module = mocker.patch("seed.accounts.module.get_module", new_callable=mocker.AsyncMock) - mock_refresh_module = mocker.patch( - "seed.accounts.module.refresh_module", new_callable=mocker.AsyncMock - ) - mock_get_module.return_value = None - await seed_module() - mock_refresh_module.assert_awaited_once() + init_resource = mocker.patch("seed.accounts.module.init_resource", autospec=True) + + await seed_module() # act + + init_resource.assert_awaited_once() diff --git a/tests/seed/accounts/test_seller.py b/tests/seed/accounts/test_seller.py index 5d53fc7..084d2e2 100644 --- a/tests/seed/accounts/test_seller.py +++ b/tests/seed/accounts/test_seller.py @@ -1,8 +1,11 @@ import pytest -from mpt_api_client.resources.accounts.sellers import AsyncSellersService, Seller -from seed.accounts.seller import build_seller_data, get_seller, init_seller, seed_seller -from seed.context import Context +from mpt_api_client.resources.accounts.sellers import Seller +from seed.accounts.seller import ( + build_seller_data, + create_seller, + seed_seller, +) @pytest.fixture @@ -10,27 +13,6 @@ def seller(): return Seller({"id": "SEL-123", "name": "Test Seller"}) -@pytest.fixture -def sellers_service(mocker): - return mocker.Mock(spec=AsyncSellersService) - - -async def test_get_seller(context: Context, operations_client, seller, sellers_service): - context["accounts.seller.id"] = seller.id - sellers_service.get.return_value = seller - operations_client.accounts.sellers = sellers_service - - result = await get_seller(context=context, mpt_operations=operations_client) - - assert result == seller - assert context.get_resource("accounts.seller", seller.id) == seller - - -async def test_get_seller_without_id(context: Context): - result = await get_seller(context=context) - assert result is None - - def test_build_seller_data(): external_id = "test-external-id" seller_data = { @@ -51,32 +33,20 @@ def test_build_seller_data(): assert result == seller_data -async def test_init_seller(context: Context, operations_client, sellers_service, seller, mocker): - sellers_service.create.return_value = seller - operations_client.accounts.sellers = sellers_service - mocker.patch("seed.accounts.seller.get_seller", return_value=None) - result = await init_seller(context=context, mpt_operations=operations_client) - assert result == seller - sellers_service.create.assert_called_once() +async def test_create_seller(mocker, operations_client): + seller = Seller({"id": "SEL-123"}) + create_mock = mocker.AsyncMock(return_value=seller) + operations_client.accounts.sellers.create = create_mock + result = await create_seller(operations_client) -async def test_init_seller_create_new( - context: Context, operations_client, sellers_service, seller, mocker -): - sellers_service.create.return_value = seller - operations_client.accounts.sellers = sellers_service - mocker.patch("seed.accounts.seller.get_seller", return_value=None) - mocker.patch( - "seed.accounts.seller.build_seller_data", return_value=build_seller_data("test-external-id") - ) - result = await init_seller(context, mpt_operations=operations_client) assert result == seller - sellers_service.create.assert_called_once() + create_mock.assert_called_once() async def test_seed_seller(mocker): - mock_init_seller = mocker.patch( - "seed.accounts.seller.init_seller", new_callable=mocker.AsyncMock - ) + init_resource = mocker.patch("seed.accounts.seller.init_resource", autospec=True) + await seed_seller() # act - mock_init_seller.assert_awaited_once() + + init_resource.assert_awaited_once() diff --git a/tests/seed/accounts/test_user_group.py b/tests/seed/accounts/test_user_group.py index 21d40bb..86ba815 100644 --- a/tests/seed/accounts/test_user_group.py +++ b/tests/seed/accounts/test_user_group.py @@ -1,10 +1,9 @@ import pytest -from mpt_api_client.resources.accounts.user_groups import AsyncUserGroupsService, UserGroup +from mpt_api_client.resources.accounts.user_groups import UserGroup from seed.accounts.user_group import ( build_user_group_data, - get_user_group, - init_user_group, + create_user_group, seed_user_group, ) from seed.context import Context @@ -15,49 +14,10 @@ def user_group(): return UserGroup({"id": "UG-123", "name": "Test User Group"}) -@pytest.fixture -def user_groups_service(mocker): - return mocker.Mock(spec=AsyncUserGroupsService) - - -async def test_get_user_group(context: Context, operations_client, user_group, user_groups_service): - context["accounts.user_group.id"] = user_group.id - user_groups_service.get.return_value = user_group - operations_client.accounts.user_groups = user_groups_service - - result = await get_user_group(context=context, mpt_operations=operations_client) - - assert result == user_group - assert context.get_resource("accounts.user_group", user_group.id) == user_group - - -async def test_get_user_group_without_id(context: Context): - result = await get_user_group(context=context) - assert result is None - - -async def test_init_user_group( # noqa: WPS211 - context: Context, operations_client, user_groups_service, user_group, monkeypatch, mocker -): - user_groups_service.create.return_value = user_group - operations_client.accounts.user_groups = user_groups_service - monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") - context["accounts.module.id"] = "MOD-456" - mocker.patch( - "seed.accounts.user_group.get_user_group", new_callable=mocker.AsyncMock, return_value=None - ) - mocker.patch( - "seed.accounts.user_group.build_user_group_data", - return_value=build_user_group_data(context), - ) - result = await init_user_group(context=context, mpt_operations=operations_client) - assert result == user_group - user_groups_service.create.assert_called_once() - - def test_build_user_group_data(context: Context, monkeypatch): monkeypatch.setenv("CLIENT_ACCOUNT_ID", "ACC-1086-6867") context["accounts.module.id"] = "MOD-456" + context["accounts.account.id"] = "ACC-1086-6867" expected_data = { "name": "E2E Seeded User Group", "account": {"id": "ACC-1086-6867"}, @@ -72,9 +32,20 @@ def test_build_user_group_data(context: Context, monkeypatch): assert result == expected_data +async def test_create_user_group(mocker, operations_client, user_group): + create_mock = mocker.AsyncMock(return_value=user_group) + operations_client.accounts.user_groups.create = create_mock + mocker.patch("seed.accounts.user_group.build_user_group_data") + + result = await create_user_group(operations_client) + + assert result == user_group + create_mock.assert_awaited_once() + + async def test_seed_user_group(mocker): - mock_init_user_group = mocker.patch( - "seed.accounts.user_group.init_user_group", new_callable=mocker.AsyncMock - ) + init_resource = mocker.patch("seed.accounts.user_group.init_resource", autospec=True) + await seed_user_group() # act - mock_init_user_group.assert_awaited_once() + + init_resource.assert_awaited_once_with("accounts.user_group.id", create_user_group) diff --git a/tests/seed/catalog/test_authorization.py b/tests/seed/catalog/test_authorization.py index 1b43c47..2c2bee6 100644 --- a/tests/seed/catalog/test_authorization.py +++ b/tests/seed/catalog/test_authorization.py @@ -32,9 +32,9 @@ async def test_create_authorization(mocker, operations_client, context_with_data async def test_seed_authorization(mocker): init_resource = mocker.patch( "seed.catalog.authorization.init_resource", - new_callable=mocker.AsyncMock, + autospec=True, ) await seed_authorization() - init_resource.assert_called_once_with("catalog.authorization.id", create_authorization) + init_resource.assert_awaited_once_with("catalog.authorization.id", create_authorization) diff --git a/tests/seed/catalog/test_catalog.py b/tests/seed/catalog/test_catalog.py index c5dc0e9..adda5c1 100644 --- a/tests/seed/catalog/test_catalog.py +++ b/tests/seed/catalog/test_catalog.py @@ -1,20 +1,18 @@ -from unittest.mock import AsyncMock, patch +from unittest.mock import patch from seed.catalog.catalog import seed_catalog async def test_seed_catalog() -> None: with ( - patch("seed.catalog.catalog.seed_product", new_callable=AsyncMock) as seed_product, - patch( - "seed.catalog.catalog.seed_authorization", new_callable=AsyncMock - ) as seed_authorization, - patch("seed.catalog.catalog.seed_price_list", new_callable=AsyncMock) as seed_price_list, - patch("seed.catalog.catalog.seed_listing", new_callable=AsyncMock) as seed_listing, + patch("seed.catalog.catalog.seed_product", autospec=True) as seed_product, + patch("seed.catalog.catalog.seed_authorization", autospec=True) as seed_authorization, + patch("seed.catalog.catalog.seed_price_list", autospec=True) as seed_price_list, + patch("seed.catalog.catalog.seed_listing", autospec=True) as seed_listing, ): await seed_catalog() # act - seed_product.assert_called_once() - seed_authorization.assert_called_once() - seed_price_list.assert_called_once() - seed_listing.assert_called_once() + seed_product.assert_awaited_once() + seed_authorization.assert_awaited_once() + seed_price_list.assert_awaited_once() + seed_listing.assert_awaited_once() diff --git a/tests/seed/catalog/test_listing.py b/tests/seed/catalog/test_listing.py index 085c2c6..2b236cc 100644 --- a/tests/seed/catalog/test_listing.py +++ b/tests/seed/catalog/test_listing.py @@ -34,8 +34,7 @@ async def test_create_listing(mocker, operations_client, context_with_data): # async def test_seed_listing(mocker, context_with_data): init_resource = mocker.patch( "seed.catalog.listing.init_resource", - new_callable=mocker.AsyncMock, - return_value=mocker.Mock(id="lst-999"), + autospec=True, ) await seed_listing() diff --git a/tests/seed/catalog/test_price_list.py b/tests/seed/catalog/test_price_list.py index e62aefd..722478b 100644 --- a/tests/seed/catalog/test_price_list.py +++ b/tests/seed/catalog/test_price_list.py @@ -26,8 +26,7 @@ async def test_create_price_list(mocker, operations_client, context_with_product async def test_seed_price_list_create(mocker): init_resource = mocker.patch( "seed.catalog.price_list.init_resource", - new_callable=mocker.AsyncMock, - return_value=mocker.Mock(id="pl-999"), + autospec=True, ) await seed_price_list() diff --git a/tests/seed/test_seed_api.py b/tests/seed/test_seed_api.py index c2d4875..6f05917 100644 --- a/tests/seed/test_seed_api.py +++ b/tests/seed/test_seed_api.py @@ -20,8 +20,8 @@ def context_file_path(tmp_path): async def test_seed_api_success(mock_context, mocker): - mock_seed_catalog = mocker.patch("seed.seed_api.seed_catalog", new_callable=mocker.AsyncMock) - mock_seed_accounts = mocker.patch("seed.seed_api.seed_accounts", new_callable=mocker.AsyncMock) + mock_seed_catalog = mocker.patch("seed.seed_api.seed_catalog", autospec=True) + mock_seed_accounts = mocker.patch("seed.seed_api.seed_accounts", autospec=True) mock_context_file = mocker.patch("seed.seed_api.context_file") load = mocker.patch("seed.seed_api.load_context") save = mocker.patch("seed.seed_api.save_context") @@ -32,6 +32,6 @@ async def test_seed_api_success(mock_context, mocker): await seed_api(context=mock_context) load.assert_called_once() - mock_seed_catalog.assert_called_once() - mock_seed_accounts.assert_called_once() + mock_seed_catalog.assert_awaited_once() + mock_seed_accounts.assert_awaited_once() save.assert_called_once()