"""Unit tests for logic.py.""" from unittest.mock import MagicMock from owsresponse import response import pytest from owsrequest import error_response from features import logic as features_logic from features.constants import context as context_constants from features.constants import error from features.models import feature as feature_model from features.models import split_feature from features.models import feature_variant as variant_model from features.models import ows_users from features.utils import parser @pytest.fixture def parsed_features(): """Return array of parsed feature objects.""" variant_1 = MagicMock(variant_model.FeatureVariant) variant_1.name = 'variant_1' feature_1 = MagicMock(feature_model.Feature) feature_1.get_active_variant.return_value = 'variant_1' feature_1.variants = [variant_1] feature_2 = MagicMock(feature_model.Feature) feature_2.get_active_variant.return_value = 'variant_2' return {'feature_1': feature_1, 'feature_2': feature_2} @pytest.mark.parametrize( ('feature_name', 'user_id', 'ows_users_response', 'expected_result'), [ # oa user, feature 2 = force variant for user ( 'feature_2', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), response.Response(status=200, message='variant_1'), ), # alw user, feature 2 = force variant for vendor ( 'feature_2', 'alw:555', MagicMock( return_value=response.Response( status=200, message={ 'type': 'alw', 'account': {'vendor_id': 16055, 'subaccount_id': None}, }, ) ), response.Response(status=200, message='variant_1'), ), # ows_users response not ok ( 'feature_1', 'oa:123', MagicMock( return_value=response.create_error_response( status=500, code='pizza', message='party' ) ), response.Response(status=200, message='variant_1'), ), # feature does not exist ( 'feature_77777777777', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ), ), ], ) def test_get_single_feature_for_user( monkeypatch, feature_name, user_id, ows_users_response, expected_result ): """Verify that get_feature_with_user_context returns valid data.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/features.yml'), ) monkeypatch.setattr(ows_users, 'get_user_details_by_user_id', ows_users_response) result = features_logic.get_single_feature_with_user_context(feature_name, user_id) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ('feature_name', 'profile_type', 'profile_id', 'identity_id', 'expected_result'), [ # feature_1: profile_type and profile_id ( 'feature_1', 'ArtistProfile', 555, None, response.Response(status=200, message='variant_1'), ), # feature_2: profile_type and profile_id ( 'feature_2', 'ArtistProfile', 555, None, response.Response(status=200, message='variant_2'), ), # feature_1: identity_id ( 'feature_1', None, None, '5d542f411234a40db1f64010', response.Response(status=200, message='variant_2'), ), # feature_2: identity_id ( 'feature_2', None, None, '5d542f411234a40db1f64010', response.Response(status=200, message='variant_1'), ), # feature_1: all 3 ( 'feature_1', 'ArtistProfile', 555, '5d542f411234a40db1f64010', response.Response(status=200, message='variant_1'), ), # feature_2: all 3 ( 'feature_2', 'ArtistProfile', 555, '5d542f411234a40db1f64010', response.Response(status=200, message='variant_1'), ), # feature_1: none ( 'feature_2', None, None, None, response.Response(status=200, message='variant_2'), ), # feature_1: bad headers, just profile_type ( 'feature_2', 'ArtistProfile', None, None, response.create_error_response(code='whatever', message='whatever'), ), # feature does not exist ( 'pizza', 'ArtistProfile', 555, None, response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ), ), ], ) def test_get_single_feature_for_profile( monkeypatch, feature_name, profile_type, profile_id, identity_id, expected_result ): """Test logic.get_single_feature_with_profile_context.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/profile_features.yml'), ) result = features_logic.get_single_feature_with_profile_context( feature_name, profile_type, profile_id, identity_id ) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ('user_id', 'vendor_id', 'subaccount_id', 'ows_users_response', 'expected_result'), [ # oa user, feature 2 = force variant for user ( 'oa:123', None, None, MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_1'} ), ), # alw user only, feature 2 = force variant for vendor ( 'alw:555', None, None, MagicMock( return_value=response.Response( status=200, message={ 'type': 'alw', 'account': {'vendor_id': 16055, 'subaccount_id': None}, }, ) ), response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_1'} ), ), # user validation error ( 'alw:555', None, None, MagicMock( return_value=response.create_error_response( status=500, code='pizza', message='party' ) ), response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_2'} ), ), ], ) def test_get_all_features_with_user_context( monkeypatch, user_id, vendor_id, subaccount_id, ows_users_response, expected_result ): """Test getting all features with user context.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/features.yml'), ) monkeypatch.setattr(ows_users, 'get_user_details_by_user_id', ows_users_response) result = features_logic.get_all_features_with_user_context( user_id, subaccount_id, vendor_id ) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ('vendor_id', 'subaccount_id', 'expected_result'), [ # Without a subaccount id or vendor id. ( None, None, response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_2'} ), ), # With vendor id. feature 2 = force variant for vendor ( 7123, None, response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_1'} ), ), ], ) def test_get_all_features_without_user_context( monkeypatch, vendor_id, subaccount_id, expected_result ): """Test getting fall features without a user context.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/features.yml'), ) result = features_logic.get_all_features_without_user_context( subaccount_id, vendor_id ) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ('profile_type', 'profile_id', 'identity_id', 'expected_result'), [ # profile_type and profile_id only ( 'ArtistProfile', 555, None, response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_2'} ), ), # identity_id only ( None, None, '5d542f411234a40db1f64010', response.Response( status=200, message={'feature_1': 'variant_2', 'feature_2': 'variant_1'} ), ), # all 3 (no current validation on this) ( 'ArtistProfile', 555, '5d542f411234a40db1f64010', response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_1'} ), ), # just profile_type ( 'ArtistProfile', None, None, error_response.create_error_incomplete_profile_headers(), ), # just profile_id (None, 555, None, error_response.create_error_incomplete_profile_headers()), # just profile_id, identity_id ( None, 555, '5d542f411234a40db1f64010', error_response.create_error_incomplete_profile_headers(), ), ], ) def test_get_all_features_with_profile_context( monkeypatch, profile_type, profile_id, identity_id, expected_result ): """Test get_all_features_with_profile_context.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/profile_features.yml'), ) result = features_logic.get_all_features_with_profile_context( profile_type, profile_id, identity_id ) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ( 'feature_name', 'orchard_user_id', 'ows_users_response', 'split_response', 'expected_result', ), [ # oa user, feature 2 = force variant for user ( 'feature_2', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), ), # alw user, feature 1 ( 'feature_1', 'alw:555', MagicMock( return_value=response.Response( status=200, message={ 'type': 'alw', 'account': {'vendor_id': 16055, 'subaccount_id': None}, }, ) ), MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), ), # oa user, feature 3 = for user, from split.io ( 'feature_3', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock(return_value=context_constants.SPLIT_ENABLED), response.Response(status=200, message=context_constants.FEATURE_ENABLED), ), # oa user, feature 3 = for user, from split.io ( 'feature_3', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock(return_value=context_constants.SPLIT_DISABLED), response.Response(status=200, message=context_constants.FEATURE_DISABLED), ), # oa user, feature 4 = force variant for user, from split.io ( 'feature_4', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock(return_value=context_constants.SPLIT_ENABLED), response.Response(status=200, message=context_constants.FEATURE_ENABLED), ), # alw user, feature 3 = force variant for vendor, from split.io ( 'feature_3', 'alw:555', MagicMock( return_value=response.Response( status=200, message={ 'type': 'alw', 'account': {'vendor_id': 16055, 'subaccount_id': None}, }, ) ), MagicMock(return_value='on'), response.Response(status=200, message='enabled'), ), # ows_users response not ok ( 'feature_1', 'oa:123', MagicMock( return_value=response.create_error_response( status=500, code='pizza', message='party' ) ), MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), ), # feature does not exist ( 'feature_77777777777', 'oa:123', MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ), ), ], ) def test_get_single_split_feature_for_user( monkeypatch, feature_name, orchard_user_id, ows_users_response, split_response, expected_result, ): """Verify that get_single_split_feature_with_user_context returns valid data from split.io or features.yml.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/features.yml'), ) monkeypatch.setattr(ows_users, 'get_user_details_by_user_id', ows_users_response) monkeypatch.setattr(split_feature, 'get_split_feature', split_response) result = features_logic.get_single_split_feature_with_user_context( feature_name, orchard_user_id ) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ( 'feature_name', 'profile_type', 'profile_id', 'identity_id', 'split_response', 'expected_result', 'split_call', ), [ # feature_1: profile_type and profile_id ( 'feature_1', 'ArtistProfile', 555, None, MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), { 'attributes': {'profile_type_and_id': 'ArtistProfile:555'}, 'split_name': 'feature_1', 'traffic_type': 'ArtistProfile:555', }, ), # feature_2: profile_type and profile_id ( 'feature_2', 'ArtistProfile', 555, None, MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_2'), { 'attributes': {'profile_type_and_id': 'ArtistProfile:555'}, 'split_name': 'feature_2', 'traffic_type': 'ArtistProfile:555', }, ), # feature_1: identity_id ( 'feature_1', None, None, '5d542f411234a40db1f64010', MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_2'), { 'attributes': {'identity_id': '5d542f411234a40db1f64010'}, 'split_name': 'feature_1', 'traffic_type': '5d542f411234a40db1f64010', }, ), # feature_2: identity_id ( 'feature_2', None, None, '5d542f411234a40db1f64010', MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), { 'attributes': {'identity_id': '5d542f411234a40db1f64010'}, 'split_name': 'feature_2', 'traffic_type': '5d542f411234a40db1f64010', }, ), # feature_1: all 3 ( 'feature_1', 'ArtistProfile', 555, '5d542f411234a40db1f64010', MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), { 'attributes': { 'identity_id': '5d542f411234a40db1f64010', 'profile_type_and_id': 'ArtistProfile:555', }, # noqa 'split_name': 'feature_1', 'traffic_type': '5d542f411234a40db1f64010', }, ), # feature_2: all 3 ( 'feature_2', 'ArtistProfile', 555, '5d542f411234a40db1f64010', MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_1'), { 'attributes': { 'identity_id': '5d542f411234a40db1f64010', 'profile_type_and_id': 'ArtistProfile:555', }, # noqa 'split_name': 'feature_2', 'traffic_type': '5d542f411234a40db1f64010', }, ), # feature_1: none ( 'feature_2', None, None, None, MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.Response(status=200, message='variant_2'), {'attributes': {}, 'split_name': 'feature_2', 'traffic_type': 'Anonymous'}, ), # feature_1: profile_type and profile_id, from split.io ( 'feature_1', 'ArtistProfile', 556, None, MagicMock(return_value=context_constants.FEATURE_ENABLED), response.Response(status=200, message=context_constants.FEATURE_ENABLED), { 'attributes': {'profile_type_and_id': 'ArtistProfile:556'}, 'split_name': 'feature_1', 'traffic_type': 'ArtistProfile:556', }, ), # feature_2: profile_type and profile_id, from split.io ( 'feature_2', 'ArtistProfile', 556, None, MagicMock(return_value=context_constants.SPLIT_DISABLED), response.Response(status=200, message=context_constants.FEATURE_DISABLED), { 'attributes': {'profile_type_and_id': 'ArtistProfile:556'}, 'split_name': 'feature_2', 'traffic_type': 'ArtistProfile:556', }, ), # feature_1: identity_id, from split.io ( 'feature_1', None, None, '5d542f411234a40db1f64011', MagicMock(return_value=context_constants.SPLIT_DISABLED), response.Response(status=200, message=context_constants.FEATURE_DISABLED), { 'attributes': {'identity_id': '5d542f411234a40db1f64011'}, 'split_name': 'feature_1', 'traffic_type': '5d542f411234a40db1f64011', }, ), # feature_2: identity_id, from split.io ( 'feature_2', None, None, '5d542f411234a40db1f64011', MagicMock(return_value=context_constants.FEATURE_ENABLED), response.Response(status=200, message=context_constants.FEATURE_ENABLED), { 'attributes': {'identity_id': '5d542f411234a40db1f64011'}, 'split_name': 'feature_2', 'traffic_type': '5d542f411234a40db1f64011', }, ), # feature_1: all 3, from split.io ( 'feature_1', 'ArtistProfile', 556, '5d542f411234a40db1f64011', MagicMock(return_value=context_constants.FEATURE_ENABLED), response.Response(status=200, message=context_constants.FEATURE_ENABLED), { 'attributes': { 'identity_id': '5d542f411234a40db1f64011', 'profile_type_and_id': 'ArtistProfile:556', }, # noqa 'split_name': 'feature_1', 'traffic_type': '5d542f411234a40db1f64011', }, ), # feature_2: all 3, from split.io ( 'feature_2', 'ArtistProfile', 556, '5d542f411234a40db1f64011', MagicMock(return_value=context_constants.FEATURE_ENABLED), response.Response(status=200, message=context_constants.FEATURE_ENABLED), { 'attributes': { 'identity_id': '5d542f411234a40db1f64011', 'profile_type_and_id': 'ArtistProfile:556', }, # noqa 'split_name': 'feature_2', 'traffic_type': '5d542f411234a40db1f64011', }, ), # feature_3: none, from split.io ( 'feature_3', None, None, None, MagicMock(return_value=context_constants.SPLIT_DISABLED), response.Response(status=200, message=context_constants.FEATURE_DISABLED), {'attributes': {}, 'split_name': 'feature_3', 'traffic_type': 'Anonymous'}, ), # feature does not exist ( 'pizza', 'ArtistProfile', 555, None, MagicMock(return_value=context_constants.SPLIT_NOT_AVAILABLE), response.create_error_response( code=error.ERROR_CODE_FEATURE_DOES_NOT_EXIST, message=error.ERROR_MESSAGE_FEATURE_DOES_NOT_EXIST, status=404, ), { 'attributes': {'profile_type_and_id': 'ArtistProfile:555'}, 'split_name': 'pizza', 'traffic_type': 'ArtistProfile:555', }, ), ], ) def test_get_single_split_feature_for_profile( monkeypatch, feature_name, profile_type, profile_id, identity_id, split_response, expected_result, split_call, ): """Verify that get_single_split_feature_with_profile_context returns valid data from split.io or features.yml.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/profile_features.yml'), ) monkeypatch.setattr(split_feature, 'get_split_feature', split_response) result = features_logic.get_single_split_feature_with_profile_context( feature_name, profile_type, profile_id, identity_id ) assert result.status == expected_result.status assert result.message == expected_result.message if split_call: split_feature.get_split_feature.assert_called_once_with(**split_call) else: assert split_feature.get_split_feature.call_count == 0 @pytest.mark.parametrize( ( 'user_id', 'vendor_id', 'subaccount_id', 'ows_users_response', 'split_response', 'expected_result', ), [ # oa user, feature 2 = force variant for user, from split.io ( 'oa:234', None, None, MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock( return_value={ 'feature_2': context_constants.SPLIT_DISABLED, 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_1', 'feature_2': context_constants.FEATURE_DISABLED, 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), ), # oa user, feature 2 = force variant for user ( 'oa:123', None, None, MagicMock( return_value=response.Response( status=200, message={ 'type': 'oa', 'account': {'vendor_id': None, 'subaccount_id': None}, }, ) ), MagicMock( return_value={ 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_1', 'feature_2': 'variant_1', 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), ), # alw user only, feature 1 = force variant for vendor, from split.io ( 'alw:555', None, None, MagicMock( return_value=response.Response( status=200, message={ 'type': 'alw', 'account': {'vendor_id': 16055, 'subaccount_id': None}, }, ) ), MagicMock( return_value={ 'feature_1': context_constants.SPLIT_DISABLED, 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': context_constants.FEATURE_DISABLED, 'feature_2': 'variant_1', 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), ), # no subaccount id or vendor id. ( None, None, None, None, MagicMock( return_value={ 'feature_3': context_constants.SPLIT_ENABLED, 'feature_4': context_constants.SPLIT_DISABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_1', 'feature_2': 'variant_2', 'feature_3': context_constants.FEATURE_ENABLED, 'feature_4': context_constants.FEATURE_DISABLED, }, ), ), # no features in split.io ( 'alw:557', None, None, MagicMock( return_value=response.Response( status=200, message={ 'type': 'alw', 'account': {'vendor_id': 22, 'subaccount_id': None}, }, ) ), MagicMock(return_value={}), response.Response( status=200, message={'feature_1': 'variant_1', 'feature_2': 'variant_2'} ), ), ], ) def test_get_all_split_features_with_user_context( monkeypatch, user_id, vendor_id, subaccount_id, ows_users_response, split_response, expected_result, ): """Test getting all features with user context.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/features.yml'), ) monkeypatch.setattr(ows_users, 'get_user_details_by_user_id', ows_users_response) monkeypatch.setattr(split_feature, 'get_all_split_features', split_response) result = features_logic.get_all_split_features_with_user_context( user_id, subaccount_id, vendor_id ) assert result.status == expected_result.status assert result.message == expected_result.message @pytest.mark.parametrize( ( 'profile_type', 'profile_id', 'identity_id', 'split_response', 'expected_result', 'split_call', ), [ # profile_type and profile_id only, from split.io ( 'ArtistProfile', 556, None, MagicMock( return_value={ 'feature_2': context_constants.SPLIT_ENABLED, 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_2', 'feature_2': context_constants.FEATURE_ENABLED, 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), { 'attributes': {'profile_type_and_id': 'ArtistProfile:556'}, 'traffic_type': 'ArtistProfile:556', }, ), # profile_type and profile_id only ( 'ArtistProfile', 555, None, MagicMock( return_value={ 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_1', 'feature_2': 'variant_2', 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), { 'attributes': {'profile_type_and_id': 'ArtistProfile:555'}, 'traffic_type': 'ArtistProfile:555', }, ), # identity_id only, from split.io ( None, None, '5d542f411234a40db1f64011', MagicMock( return_value={ 'feature_1': context_constants.SPLIT_ENABLED, 'feature_2': context_constants.SPLIT_ENABLED, 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': context_constants.FEATURE_ENABLED, 'feature_2': context_constants.FEATURE_ENABLED, 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), { 'attributes': {'identity_id': '5d542f411234a40db1f64011'}, 'traffic_type': '5d542f411234a40db1f64011', }, ), # identity_id only ( None, None, '5d542f411234a40db1f64010', MagicMock( return_value={ 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_2', 'feature_2': 'variant_1', 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), { 'attributes': {'identity_id': '5d542f411234a40db1f64010'}, 'traffic_type': '5d542f411234a40db1f64010', }, ), # all 3 (no current validation on this), from split.io ( 'ArtistProfile', 556, '5d542f411234a40db1f64011', MagicMock( return_value={ 'feature_1': context_constants.SPLIT_DISABLED, 'feature_2': context_constants.SPLIT_DISABLED, 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': context_constants.FEATURE_DISABLED, 'feature_2': context_constants.FEATURE_DISABLED, 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), { 'attributes': { 'identity_id': '5d542f411234a40db1f64011', 'profile_type_and_id': 'ArtistProfile:556', }, # noqa 'traffic_type': '5d542f411234a40db1f64011', }, ), # all 3 (no current validation on this) ( 'ArtistProfile', 555, '5d542f411234a40db1f64010', MagicMock( return_value={ 'feature_3': context_constants.SPLIT_DISABLED, 'feature_4': context_constants.SPLIT_ENABLED, } ), response.Response( status=200, message={ 'feature_1': 'variant_1', 'feature_2': 'variant_1', 'feature_3': context_constants.FEATURE_DISABLED, 'feature_4': context_constants.FEATURE_ENABLED, }, ), { 'attributes': { 'identity_id': '5d542f411234a40db1f64010', 'profile_type_and_id': 'ArtistProfile:555', }, # noqa 'traffic_type': '5d542f411234a40db1f64010', }, ), ], ) def test_get_all_split_features_with_profile_context( monkeypatch, profile_type, profile_id, identity_id, split_response, expected_result, split_call, ): """Test get_all_features_with_profile_context.""" monkeypatch.setattr( features_logic, 'features', parser.parse_features_from_yaml_file('tests/fixtures/profile_features.yml'), ) monkeypatch.setattr(split_feature, 'get_all_split_features', split_response) result = features_logic.get_all_split_features_with_profile_context( profile_type, profile_id, identity_id ) assert result.status == expected_result.status assert result.message == expected_result.message if split_call: split_feature.get_all_split_features.assert_called_once_with(**split_call) else: assert split_feature.get_all_split_features.call_count == 0 @pytest.mark.parametrize( ('attributes', 'expected'), [ ( # no headers {}, 'Anonymous', ), ( # legacy grass headers without uuid. { 'user_id': 'alw:41128', 'profile_type_and_id': 'LabelProfile:41128', 'vendor_id': '25189', }, # noqa 'LabelProfile:41128', ), ( # legacy grass headers with uuid. { 'user_id': 'alw:41128', 'profile_type_and_id': 'LabelProfile:41128', 'vendor_id': '25189', 'identity_uuid': 'aaae-3bba-95d8dbaf515d', }, # noqa 'aaae-3bba-95d8dbaf515d', ), ( # OA grass headers. {'user_id': 'oa:123', 'profile_type_and_id': 'OrchAdminProfile:123'}, 'OrchAdminProfile:123', ), ( # profile user with uuid. { 'profile_type_and_id': 'InsightsProfile:21', 'identity_id': '5d64e5cc51c1480dc75a27ef', 'identity_uuid': 'aaae-3bba-95d8dbaf515d', }, # noqa 'aaae-3bba-95d8dbaf515d', ), ( # profile user without uuid. {'identity_id': '5d64e5cc51c1480dc75a27ef'}, '5d64e5cc51c1480dc75a27ef', ), ], ) def test_get_traffic_type_identifier(attributes, expected): """Test _get_traffic_type_identifier.""" assert features_logic._get_traffic_type_identifier(attributes) == expected