"""Test for logic application module.""" from unittest.mock import MagicMock, patch import pytest from owsresponse.response import Response from account.constants.features import FEATURES from account.logic.application import subaccount as subaccount_app_logic from account.models import ( application as app_model, feature as feature_model, subaccount as subaccount_model, types, ) @pytest.fixture() def subaccount_attribute_fixture() -> dict[str, types.SubaccountAttributes]: """Fixture for subaccount attributes.""" return { # theorchard, migrated_to_abacus=False '313edc35-853c-4a57-9cb8-c19cb265f39b': { 'subaccount_id': 62281, 'uuid': '313edc35-853c-4a57-9cb8-c19cb265f39b', 'vendor_attributes': { 'vendor_id': 21899, 'uuid': '69270e27-b666-4f60-b73e-11f6821a7b0a', 'migrated_to_abacus': False, 'company_brand': {'id': 2, 'name': 'theorchard'}, 'features': [ FEATURES.AUDIO_RELEASE_BUILDER, FEATURES.MUSIC_VIDEO_RELEASE_BUILDER, FEATURES.ARTIST_BUILDER, FEATURES.MARKETPLACE, FEATURES.ACCOUNTING, FEATURES.VENDOR_IDENTIFIERS, FEATURES.ANALYTICS, FEATURES.PROJECT_MODE, FEATURES.PHYSICAL_PRODUCT_BUILDER, FEATURES.SOURCE_OF_STREAM, FEATURES.WORKSTATION_PHYSICAL_REPORTING, FEATURES.ADVERTISING_CAMPAIGN_CONTROL, FEATURES.ORCHARD_ADVERTISING, FEATURES.SHOW_GROSS_ACCOUNTING, FEATURES.WORKSTATION_BULK_UPLOAD, FEATURES.ORCHARDGO_BANNER, FEATURES.ORCHARDGO_NEW_BANNER, FEATURES.WORKSTATION_ANALYTICS_OVERVIEW, ], }, }, # theorchard, migrated_to_abacus=True '415af73a-8f57-4546-a485-19c65c185eee': { 'subaccount_id': 78257, 'uuid': '415af73a-8f57-4546-a485-19c65c185eee', 'vendor_attributes': { 'vendor_id': 34536, 'uuid': '1ee1ebda-42f2-4183-84d2-25df213a7db6', 'migrated_to_abacus': True, 'company_brand': {'id': 2, 'name': 'theorchard'}, 'features': [ FEATURES.AUDIO_RELEASE_BUILDER, FEATURES.MUSIC_VIDEO_RELEASE_BUILDER, FEATURES.ARTIST_BUILDER, FEATURES.MARKETPLACE, FEATURES.ACCOUNTING, FEATURES.VENDOR_IDENTIFIERS, FEATURES.ANALYTICS, FEATURES.PROJECT_MODE, FEATURES.PHYSICAL_PRODUCT_BUILDER, FEATURES.SOURCE_OF_STREAM, FEATURES.WORKSTATION_PHYSICAL_REPORTING, FEATURES.ADVERTISING_CAMPAIGN_CONTROL, FEATURES.ORCHARD_ADVERTISING, FEATURES.SHOW_GROSS_ACCOUNTING, FEATURES.WORKSTATION_BULK_UPLOAD, FEATURES.ORCHARDGO_BANNER, FEATURES.ORCHARDGO_NEW_BANNER, FEATURES.WORKSTATION_ANALYTICS_OVERVIEW, ], }, }, # msk '1fab3fe8-c9a6-488a-9a96-959734a93997': { 'subaccount_id': 72651, 'uuid': '1fab3fe8-c9a6-488a-9a96-959734a93997', 'vendor_attributes': { 'vendor_id': 70969, 'uuid': '54239904-8a4f-497f-8c84-9e8a2d75113e', 'migrated_to_abacus': False, 'company_brand': {'id': 6, 'name': 'msk'}, 'features': [ FEATURES.AUDIO_RELEASE_BUILDER, FEATURES.MUSIC_VIDEO_RELEASE_BUILDER, FEATURES.MARKETPLACE, FEATURES.ACCOUNTING, FEATURES.ANALYTICS, FEATURES.PERFORMANCE_RIGHTS, FEATURES.LOCALIZATIONS, FEATURES.PROJECT_MODE, FEATURES.PHYSICAL_PRODUCT_BUILDER, FEATURES.SOURCE_OF_STREAM, FEATURES.PHYSICAL_UPC_GENERATION, FEATURES.WORKSTATION_PHYSICAL_REPORTING, FEATURES.ADVERTISING_CAMPAIGN_CONTROL, FEATURES.ORCHARD_ADVERTISING, FEATURES.FACEBOOK_CAMPAIGN_BOOST, ], }, }, # awal 'e60daea0-d00c-4df3-bb5e-22889f54ff34': { 'subaccount_id': 666, 'uuid': 'e60daea0-d00c-4df3-bb5e-22889f54ff34', 'vendor_attributes': { 'vendor_id': 666, 'uuid': 'd1b4975f-d2e8-463c-8630-0316216fd876', 'migrated_to_abacus': True, 'company_brand': {'id': 5, 'name': 'awal'}, 'features': [FEATURES.ACCOUNTING], }, }, # knr 'ef087858-8d26-4dab-9612-358cd1a36eab': { 'subaccount_id': 666666, 'uuid': 'ef087858-8d26-4dab-9612-358cd1a36eab', 'vendor_attributes': { 'vendor_id': 666666, 'uuid': '5e953248-0694-49a1-97ca-bf720e829dff', 'migrated_to_abacus': True, 'company_brand': {'id': 1, 'name': 'knr'}, 'features': [FEATURES.ACCOUNTING], }, }, # sme '5a36f341-b63b-11ef-b6a8-0affe2ca6553': { 'subaccount_id': 96276, 'uuid': '5a36f341-b63b-11ef-b6a8-0affe2ca6553', 'vendor_attributes': { 'vendor_id': 791217, 'uuid': '59cb86fb-b63b-11ef-b6a8-0affe2ca6553', 'migrated_to_abacus': False, 'company_brand': {'id': 4, 'name': 'sme'}, 'features': [FEATURES.ACCOUNTING], }, }, } @patch('account.logic.application.subaccount.get_insights_app_for_subaccount') @patch('account.logic.application.subaccount.get_ws_analytics_app_for_subaccount') @patch('account.logic.application.subaccount.get_customer_accounting_app_for_subaccount') @patch('account.logic.application.subaccount.get_ws_catalog_app_for_subaccount') @patch('account.logic.application.subaccount.get_settings_app_for_subaccount') def test_generate_subaccount_app_check_map( mock_get_settings_app_for_subaccount, mock_get_ws_catalog_app_for_subaccount, mock_get_customer_accounting_app_for_subaccount, mock_get_ws_analytics_app_for_subaccount, mock_get_insights_app_for_subaccount, subaccount_attribute_fixture, ): """Test generate_subaccount_app_check_map.""" subaccount_attributes = subaccount_attribute_fixture['313edc35-853c-4a57-9cb8-c19cb265f39b'] result = subaccount_app_logic.generate_subaccount_app_check_map(subaccount_attributes) assert isinstance(result, dict) assert len(result) == len(subaccount_app_logic.ENABLED_APPLICATIONS) for app_id in subaccount_app_logic.ENABLED_APPLICATIONS: assert app_id in result assert callable(result[app_id]) result[app_id]() if app_id == app_model.INSIGHTS_APP: mock_get_insights_app_for_subaccount.assert_called_with(subaccount_attributes) elif app_id == app_model.WORKSTATION_ANALYTICS_APP: mock_get_ws_analytics_app_for_subaccount.assert_called_with(subaccount_attributes) elif app_id == app_model.CUSTOMER_ACCOUNTING_APP: mock_get_customer_accounting_app_for_subaccount.assert_called_with( subaccount_attributes ) elif app_id == app_model.WORKSTATION_CATALOG_APP: mock_get_ws_catalog_app_for_subaccount.assert_called_with(subaccount_attributes) elif app_id == app_model.SETTINGS_APP: mock_get_settings_app_for_subaccount.assert_called_with(subaccount_attributes) def test_get_enabled_subaccount_applications_with_workstation_apps(subaccount_attribute_fixture): """Test get_enabled_subaccount_applications for a subaccount with workstation apps.""" applications = subaccount_app_logic.get_enabled_subaccount_applications( subaccount_attribute_fixture['313edc35-853c-4a57-9cb8-c19cb265f39b'] ) assert applications == [ { 'application_id': 'INSIGHTS_APP', 'roles': ['INSIGHTS_BASE_ROLE'], 'url': 'https://insights.theorchard.com', }, { 'application_id': 'WORKSTATION_ANALYTICS_APP', 'roles': ['WORKSTATION_ANALYTICS_BASE_ROLE'], 'url': 'https://workstation.theorchard.com/analytics', }, { 'application_id': 'WORKSTATION_CATALOG_APP', 'roles': ['WORKSTATION_CATALOG_ROLE'], 'url': 'https://workstation.theorchard.com/catalog', }, { 'application_id': 'SETTINGS_APP', 'roles': ['SETTINGS_BASE_ROLE', 'WORKSTATION_ADMIN_ROLE'], 'url': 'https://settings.theorchard.com', }, ] def test_get_enabled_subaccount_applications_without_workstation_apps(subaccount_attribute_fixture): """Test get_enabled_subaccount_applications for a subaccount without workstation apps.""" applications = subaccount_app_logic.get_enabled_subaccount_applications( subaccount_attribute_fixture['e60daea0-d00c-4df3-bb5e-22889f54ff34'] ) assert applications == [ { 'application_id': 'CUSTOMER_ACCOUNTING_APP', 'roles': ['CUSTOMER_ACCOUNTING_BASE_ROLE'], 'url': 'https://accounting.awal.com', }, { 'application_id': 'INSIGHTS_APP', 'roles': ['INSIGHTS_BASE_ROLE'], 'url': 'https://insights.awal.com', }, { 'application_id': 'SETTINGS_APP', 'roles': ['SETTINGS_BASE_ROLE'], 'url': 'https://settings.awal.com', }, ] def test_get_subaccount_applications_existing_subaccount(monkeypatch): """Test get_subaccount_applications for existing subaccount.""" monkeypatch.setattr( subaccount_model, 'lookup_subaccount_and_vendor_and_company_brand_by_uuid', MagicMock( return_value=( {'subaccount_id': 78257, 'uuid': '415af73a-8f57-4546-a485-19c65c185eee'}, { 'vendor_id': 34536, 'uuid': '1ee1ebda-42f2-4183-84d2-25df213a7db6', 'migrated_to_abacus': True, }, {'id': 2, 'name': 'theorchard'}, ) ), ) monkeypatch.setattr( feature_model, 'get_enabled_features_for_vendor_with_session', MagicMock( return_value=[ {'feature_id': 1, 'feature_name': 'Audio Release Builder'}, {'feature_id': 2, 'feature_name': 'Music Video Release Builder'}, {'feature_id': 5, 'feature_name': 'Marketplace'}, {'feature_id': 6, 'feature_name': 'Accounting'}, {'feature_id': 13, 'feature_name': 'Project Mode'}, {'feature_id': 14, 'feature_name': 'Physical Product Builder'}, {'feature_id': 18, 'feature_name': 'Physical UPC Generation'}, {'feature_id': 19, 'feature_name': 'Workstation Physical Reporting'}, ] ), ) response = subaccount_app_logic.get_subaccount_applications( '415af73a-8f57-4546-a485-19c65c185eee' ) assert isinstance(response, Response) assert response.status == 200 assert response.message == { 'applications': [ { 'application_id': 'CUSTOMER_ACCOUNTING_APP', 'roles': ['CUSTOMER_ACCOUNTING_BASE_ROLE'], 'url': 'https://accounting.theorchard.com', }, { 'application_id': 'INSIGHTS_APP', 'roles': ['INSIGHTS_BASE_ROLE'], 'url': 'https://insights.theorchard.com', }, { 'application_id': 'WORKSTATION_ANALYTICS_APP', 'roles': ['WORKSTATION_ANALYTICS_BASE_ROLE'], 'url': 'https://workstation.theorchard.com/analytics', }, { 'application_id': 'WORKSTATION_CATALOG_APP', 'roles': ['WORKSTATION_CATALOG_ROLE'], 'url': 'https://workstation.theorchard.com/catalog', }, { 'application_id': 'SETTINGS_APP', 'roles': ['SETTINGS_BASE_ROLE', 'WORKSTATION_ADMIN_ROLE'], 'url': 'https://settings.theorchard.com', }, ] } def test_get_subaccount_applications_non_existing_subaccount(monkeypatch): """Test get_subaccount_applications for non-existing subaccount.""" monkeypatch.setattr( subaccount_model, 'lookup_subaccount_and_vendor_and_company_brand_by_uuid', MagicMock(return_value=None), ) response = subaccount_app_logic.get_subaccount_applications('bogus-uuid') assert isinstance(response, Response) assert response.status == 404 assert response.errors['message'] == 'Subaccount not found' assert response.errors['code'] == 'not_found_error' @pytest.mark.parametrize( 'subaccount_uuid, expected', [ pytest.param( '313edc35-853c-4a57-9cb8-c19cb265f39b', { 'application_id': 'INSIGHTS_APP', 'roles': ['INSIGHTS_BASE_ROLE'], 'url': 'https://insights.theorchard.com', }, ) ], ) def test_get_insights_app_for_subaccount( subaccount_attribute_fixture: dict[str, types.SubaccountAttributes], subaccount_uuid: str, expected: types.TenantApplication | None, ): """Test get_insights_app_for_subaccount.""" subaccount_attributes = subaccount_attribute_fixture[subaccount_uuid] result = subaccount_app_logic.get_insights_app_for_subaccount(subaccount_attributes) assert result == expected @pytest.mark.parametrize( 'subaccount_uuid, expected', [ pytest.param( '313edc35-853c-4a57-9cb8-c19cb265f39b', { 'application_id': 'WORKSTATION_ANALYTICS_APP', 'roles': ['WORKSTATION_ANALYTICS_BASE_ROLE'], 'url': 'https://workstation.theorchard.com/analytics', }, ) ], ) def test_get_ws_analytics_app_for_subaccount( subaccount_attribute_fixture: dict[str, types.SubaccountAttributes], subaccount_uuid: str, expected: types.TenantApplication | None, ): """Test get_ws_analytics_app_for_subaccount.""" subaccount_attributes = subaccount_attribute_fixture[subaccount_uuid] result = subaccount_app_logic.get_ws_analytics_app_for_subaccount(subaccount_attributes) assert result == expected @pytest.mark.parametrize( 'subaccount_uuid, expected', [ pytest.param( '415af73a-8f57-4546-a485-19c65c185eee', { 'application_id': 'CUSTOMER_ACCOUNTING_APP', 'roles': ['CUSTOMER_ACCOUNTING_BASE_ROLE'], 'url': 'https://accounting.theorchard.com', }, ) ], ) def test_get_customer_accounting_app_for_subaccount( subaccount_attribute_fixture: dict[str, types.SubaccountAttributes], subaccount_uuid: str, expected: types.TenantApplication | None, ): """Test get_customer_accounting_app_for_subaccount.""" subaccount_attributes = subaccount_attribute_fixture[subaccount_uuid] result = subaccount_app_logic.get_customer_accounting_app_for_subaccount(subaccount_attributes) assert result == expected @pytest.mark.parametrize( 'subaccount_uuid, expected', [ pytest.param( '313edc35-853c-4a57-9cb8-c19cb265f39b', { 'application_id': 'WORKSTATION_CATALOG_APP', 'roles': ['WORKSTATION_CATALOG_ROLE'], 'url': 'https://workstation.theorchard.com/catalog', }, ) ], ) def test_get_ws_catalog_app_for_subaccount( subaccount_attribute_fixture: dict[str, types.SubaccountAttributes], subaccount_uuid: str, expected: types.TenantApplication | None, ): """Test get_ws_catalog_app_for_subaccount.""" subaccount_attributes = subaccount_attribute_fixture[subaccount_uuid] result = subaccount_app_logic.get_ws_catalog_app_for_subaccount(subaccount_attributes) assert result == expected @patch('account.models.subaccount.lookup_subaccounts_and_vendors_and_company_brands_by_uuids') @patch('account.models.feature.get_enabled_features_for_vendors_with_session') @patch('account.models.feature.convert_features_to_list') @patch('account.logic.application.subaccount.get_enabled_subaccount_applications') def test_get_bulk_subaccount_applications_success( mock_get_enabled_subaccount_applications: MagicMock, mock_convert_features_to_list: MagicMock, mock_get_enabled_features_for_vendors_with_session: MagicMock, mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids: MagicMock, ) -> None: """Test get_bulk_subaccount_applications with successful results.""" subaccount_uuids = ['uuid1', 'uuid2'] subaccount_data_1 = {'subaccount_id': 123, 'uuid': 'uuid1'} subaccount_data_2 = {'subaccount_id': 456, 'uuid': 'uuid2'} vendor_data_1 = {'vendor_id': 789, 'uuid': 'vendor1', 'migrated_to_abacus': True} vendor_data_2 = {'vendor_id': 101112, 'uuid': 'vendor2', 'migrated_to_abacus': True} company_brand = {'id': 1, 'name': 'theorchard'} features = [FEATURES.ACCOUNTING] applications = [ { 'application_id': app_model.CUSTOMER_ACCOUNTING_APP, 'roles': [app_model.CUSTOMER_ACCOUNTING_BASE_ROLE], 'url': 'https://accounting.theorchard.com', } ] mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids.return_value = { 'uuid1': (subaccount_data_1, vendor_data_1, company_brand), 'uuid2': (subaccount_data_2, vendor_data_2, company_brand), } mock_get_enabled_features_for_vendors_with_session.return_value = { 789: features, 101112: features, } mock_convert_features_to_list.return_value = features mock_get_enabled_subaccount_applications.return_value = applications result = subaccount_app_logic.get_bulk_subaccount_applications(subaccount_uuids) assert 'subaccounts' in result assert len(result['subaccounts']) == 2 assert result['subaccounts'][0]['uuid'] == 'uuid1' assert result['subaccounts'][1]['uuid'] == 'uuid2' assert result['subaccounts'][0]['applications'] == applications assert result['subaccounts'][1]['applications'] == applications @patch('account.models.subaccount.lookup_subaccounts_and_vendors_and_company_brands_by_uuids') @patch('account.models.feature.get_enabled_features_for_vendors_with_session') @patch('account.models.feature.convert_features_to_list') @patch('account.logic.application.subaccount.get_enabled_subaccount_applications') def test_get_bulk_subaccount_applications_subaccount_not_found( mock_get_enabled_subaccount_applications: MagicMock, mock_convert_features_to_list: MagicMock, mock_get_enabled_features_for_vendors_with_session: MagicMock, mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids: MagicMock, ) -> None: """Test get_bulk_subaccount_applications when subaccount is not found.""" subaccount_uuids = ['uuid1', 'uuid_not_found'] subaccount_data = {'subaccount_id': 123, 'uuid': 'uuid1'} vendor_data = {'vendor_id': 789, 'uuid': 'vendor1', 'migrated_to_abacus': True} company_brand = {'id': 1, 'name': 'theorchard'} features = [FEATURES.ACCOUNTING] applications = [ { 'application_id': app_model.CUSTOMER_ACCOUNTING_APP, 'roles': [app_model.CUSTOMER_ACCOUNTING_BASE_ROLE], 'url': 'https://accounting.theorchard.com', } ] mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids.return_value = { 'uuid1': (subaccount_data, vendor_data, company_brand), } mock_get_enabled_features_for_vendors_with_session.return_value = { 789: features, } mock_convert_features_to_list.return_value = features mock_get_enabled_subaccount_applications.return_value = applications result = subaccount_app_logic.get_bulk_subaccount_applications(subaccount_uuids) assert 'subaccounts' in result assert len(result['subaccounts']) == 2 assert result['subaccounts'][0]['uuid'] == 'uuid1' assert result['subaccounts'][1] is None @patch('account.models.subaccount.lookup_subaccounts_and_vendors_and_company_brands_by_uuids') @patch('account.models.feature.get_enabled_features_for_vendors_with_session') @patch('account.models.feature.convert_features_to_list') @patch('account.logic.application.subaccount.get_enabled_subaccount_applications') def test_get_bulk_subaccount_applications_features_not_found( mock_get_enabled_subaccount_applications: MagicMock, mock_convert_features_to_list: MagicMock, mock_get_enabled_features_for_vendors_with_session: MagicMock, mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids: MagicMock, ) -> None: """Test get_bulk_subaccount_applications when features are not found for a vendor.""" subaccount_uuids = ['uuid1', 'uuid2'] subaccount_data_1 = {'subaccount_id': 123, 'uuid': 'uuid1'} subaccount_data_2 = {'subaccount_id': 456, 'uuid': 'uuid2'} vendor_data_1 = {'vendor_id': 789, 'uuid': 'vendor1', 'migrated_to_abacus': True} vendor_data_2 = {'vendor_id': 101112, 'uuid': 'vendor2', 'migrated_to_abacus': True} company_brand = {'id': 1, 'name': 'theorchard'} features = [FEATURES.ACCOUNTING] applications = [ { 'application_id': app_model.CUSTOMER_ACCOUNTING_APP, 'roles': [app_model.CUSTOMER_ACCOUNTING_BASE_ROLE], 'url': 'https://accounting.theorchard.com', } ] # subaccount 1 has features, subaccount 2 doesn't mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids.return_value = { 'uuid1': (subaccount_data_1, vendor_data_1, company_brand), 'uuid2': (subaccount_data_2, vendor_data_2, company_brand), } mock_get_enabled_features_for_vendors_with_session.return_value = { 789: features, 101112: None, # No features for vendor 2 } mock_convert_features_to_list.return_value = features mock_get_enabled_subaccount_applications.return_value = applications result = subaccount_app_logic.get_bulk_subaccount_applications(subaccount_uuids) assert 'subaccounts' in result assert len(result['subaccounts']) == 2 assert result['subaccounts'][0]['uuid'] == 'uuid1' assert result['subaccounts'][1] is None # Should be None when features are None @patch('account.models.subaccount.lookup_subaccounts_and_vendors_and_company_brands_by_uuids') @patch('account.models.feature.get_enabled_features_for_vendors_with_session') def test_get_bulk_subaccount_applications_mixed_scenarios( mock_get_enabled_features_for_vendors_with_session: MagicMock, mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids: MagicMock, ) -> None: """Test get_bulk_subaccount_applications with mixed scenarios.""" subaccount_uuids = ['uuid_valid', 'uuid_not_found', 'uuid_no_features'] subaccount_data_1 = {'subaccount_id': 123, 'uuid': 'uuid_valid'} subaccount_data_3 = {'subaccount_id': 789, 'uuid': 'uuid_no_features'} vendor_data_1 = {'vendor_id': 111, 'uuid': 'vendor1', 'migrated_to_abacus': True} vendor_data_3 = {'vendor_id': 333, 'uuid': 'vendor3', 'migrated_to_abacus': True} company_brand = {'id': 1, 'name': 'theorchard'} features = [{'feature_id': 6, 'feature_name': 'Accounting'}] mock_lookup_subaccounts_and_vendors_and_company_brands_by_uuids.return_value = { 'uuid_valid': (subaccount_data_1, vendor_data_1, company_brand), 'uuid_no_features': (subaccount_data_3, vendor_data_3, company_brand), } mock_get_enabled_features_for_vendors_with_session.return_value = { 111: features, 333: None, # No features for vendor 3 } result = subaccount_app_logic.get_bulk_subaccount_applications(subaccount_uuids) assert 'subaccounts' in result assert len(result['subaccounts']) == 3 # Find the valid uuid in the response valid_entry = next( (item for item in result['subaccounts'] if item and item['uuid'] == 'uuid_valid'), None ) assert valid_entry is not None assert 'applications' in valid_entry # Count None entries for not found and no features none_count = sum(1 for item in result['subaccounts'] if item is None) assert none_count == 2