def test_investment_project_syncs_when_adviser_changes(opensearch_with_signals, field): """ Tests that when an adviser is updated, investment projects related to that adviser are resynced. """ adviser = AdviserFactory() project = InvestmentProjectFactory(**{field: adviser}) adviser.dit_team = TeamFactory() adviser.save() opensearch_with_signals.indices.refresh() result = search_investment_project_by_id(project.pk) assert result.hits.total.value == 1 assert result.hits[0][field]['dit_team']['id'] == str(adviser.dit_team.id) assert result.hits[0][field]['dit_team']['name'] == adviser.dit_team.name
def test_investment_project_syncs_when_team_member_adviser_changes(es_with_signals, team_member): """ Tests that when an adviser that is a team member of an investment project is updated, the related investment project is resynced. """ adviser = team_member.adviser adviser.dit_team = TeamFactory() adviser.save() es_with_signals.indices.refresh() result = get_search_by_entity_query( InvestmentProject, term='', filter_data={'id': team_member.investment_project.pk}, ).execute() assert result.hits.total == 1 assert result.hits[0]['team_members'][0]['dit_team']['id'] == str(adviser.dit_team.id) assert result.hits[0]['team_members'][0]['dit_team']['name'] == adviser.dit_team.name
def test_can_update_subscription(self): """Test that notification subscription can be updated.""" project = InvestmentProjectFactory() user = create_test_user(dit_team=TeamFactory()) notification_choice = InvestmentNotificationSubscription.EstimatedLandDateNotification InvestmentNotificationSubscriptionFactory( investment_project=project, adviser=user, estimated_land_date=[ notification_choice.ESTIMATED_LAND_DATE_30, notification_choice.ESTIMATED_LAND_DATE_60, ], ) api_client = self.create_api_client(user=user) url = reverse( 'api-v3:investment:notification:notification-subscription', kwargs={ 'project_pk': project.pk, }, ) notification_choice = InvestmentNotificationSubscription.EstimatedLandDateNotification response = api_client.post( url, data={ 'estimated_land_date': [ str(notification_choice.ESTIMATED_LAND_DATE_30), ], }) assert response.status_code == status.HTTP_200_OK subscription = InvestmentNotificationSubscription.objects.get( investment_project_id=project.pk, adviser_id=user.pk, ) assert subscription.estimated_land_date == [ notification_choice.ESTIMATED_LAND_DATE_30, ]
def test_investment_project_syncs_when_team_member_adviser_changes( opensearch_with_signals, team_member, ): """ Tests that when an adviser that is a team member of an investment project is updated, the related investment project is resynced. """ adviser = team_member.adviser adviser.dit_team = TeamFactory() adviser.save() opensearch_with_signals.indices.refresh() result = search_investment_project_by_id(team_member.investment_project.pk) assert result.hits.total.value == 1 assert result.hits[0]['team_members'][0]['dit_team']['id'] == str( adviser.dit_team.id) assert result.hits[0]['team_members'][0]['dit_team'][ 'name'] == adviser.dit_team.name
def test_investment_project_syncs_when_adviser_changes(es_with_signals, field): """ Tests that when an adviser is updated, investment projects related to that adviser are resynced. """ adviser = AdviserFactory() project = InvestmentProjectFactory(**{field: adviser}) adviser.dit_team = TeamFactory() adviser.save() es_with_signals.indices.refresh() result = get_search_by_entity_query( InvestmentProject, term='', filter_data={'id': project.pk}, ).execute() assert result.hits.total == 1 assert result.hits[0][field]['dit_team']['id'] == str(adviser.dit_team.id) assert result.hits[0][field]['dit_team']['name'] == adviser.dit_team.name
def test_view_returns_200(self): """ Tests view returns 200 """ permission = Permission.objects.get(codename='view_permissionmodel') permission_group = GroupFactory() permission_group.permissions.add(permission) team = TeamFactory() team.role.groups.add(permission_group) user = create_test_user(dit_team=team) token = self.get_token(user=user) request = factory.get( '/', data={}, content_type='application/json', Authorization=f'Bearer {token}', ) my_view = PermissionModelViewset.as_view(actions={'get': 'list'}, ) response = my_view(request) assert response.status_code == status.HTTP_200_OK
def test_restricted_user_cannot_add_non_associated_investment_project_interaction(self): """ Test that a restricted user cannot add an interaction for a non-associated investment project. """ project_creator = AdviserFactory() project = InvestmentProjectFactory(created_by=project_creator) requester = create_test_user( permission_codenames=[InteractionPermission.add_associated_investmentproject], dit_team=TeamFactory(), # different dit team from the project creator ) url = reverse('api-v3:interaction:collection') api_client = self.create_api_client(user=requester) response = api_client.post( url, data={ 'kind': Interaction.KINDS.interaction, 'company': CompanyFactory().pk, 'contacts': [ContactFactory().pk], 'communication_channel': random_obj_for_model(CommunicationChannel).pk, 'subject': 'whatever', 'date': date.today().isoformat(), 'dit_adviser': requester.pk, 'notes': 'hello', 'investment_project': project.pk, 'service': Service.trade_enquiry.value.id, 'dit_team': Team.healthcare_uk.value.id, 'was_policy_feedback_provided': False, }, ) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == { 'investment_project': [ "You don't have permission to add an interaction for this " 'investment project.', ], }
def test_basic_search_no_permissions(self, es_with_collector): """Tests model permissions enforcement in basic search for a user with no permissions.""" user = create_test_user(permission_codenames=[], dit_team=TeamFactory()) api_client = self.create_api_client(user=user) SimpleModel.objects.create(name='test') es_with_collector.flush_and_refresh() url = reverse('api-v3:search:basic') response = api_client.get( url, data={ 'term': '', 'entity': 'simplemodel', }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 0 assert len(response_data['aggregations']) == 0
def test_search_event_lead_team(self, es_with_collector): """Tests lead_team filter.""" url = reverse('api-v3:search:event') team = TeamFactory() EventFactory.create_batch(5) EventFactory.create_batch(5, lead_team_id=team.id) es_with_collector.flush_and_refresh() response = self.api_client.post( url, data={ 'lead_team': team.id, }, ) assert response.status_code == status.HTTP_200_OK assert response.data['count'] == 5 assert len(response.data['results']) == 5 team_ids = [result['lead_team']['id'] for result in response.data['results']] assert all(team_id == str(team.id) for team_id in team_ids)
def test_permissions(self, es_with_collector, permission, permission_entity, entity): """ Tests model permissions enforcement in basic search. TODO: we should test permissions relevant to a specific search app in the tests for that search app, and remove this test. """ user = create_test_user(permission_codenames=[permission], dit_team=TeamFactory()) api_client = self.create_api_client(user=user) InvestmentProjectFactory(created_by=user) CompanyFactory() ContactFactory() EventFactory() CompanyInteractionFactory() OrderFactory() es_with_collector.flush_and_refresh() url = reverse('api-v3:search:basic') response = api_client.get( url, data={ 'term': '', 'entity': entity, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert (response_data['count'] == 0) == (permission_entity != entity) assert len(response_data['aggregations']) == 1 assert response_data['aggregations'][0]['entity'] == permission_entity
def test_who_am_i_authenticated(self): """Who am I.""" permission_names = [ 'view_lorem', 'view_ipsum', 'add_cats', ] content_type = ContentType.objects.first() permissions = PermissionFactory.create_batch( len(permission_names), codename=factory.Iterator(permission_names), content_type=content_type, ) group = GroupFactory() group.permissions.add(permissions[0]) role = TeamRoleFactory(name='Test Role') team = TeamFactory(name='Test Team', role=role) team.role.groups.add(group) user_test = create_test_user(dit_team=team) user_test.user_permissions.set(permissions[1:]) api_client = self.create_api_client(user=user_test) url = reverse('who_am_i') response = api_client.get(url) assert response.status_code == status.HTTP_200_OK response_data = response.json() if 'permissions' in response_data: response_data['permissions'].sort() expected_permissions = [ f'{content_type.app_label}.add_cats', f'{content_type.app_label}.view_ipsum', f'{content_type.app_label}.view_lorem', ] assert response_data == { 'id': str(user_test.id), 'name': user_test.name, 'last_login': None, 'first_name': user_test.first_name, 'last_name': user_test.last_name, 'email': user_test.email, 'contact_email': user_test.contact_email, 'telephone_number': user_test.telephone_number, 'dit_team': { 'id': str(team.id), 'name': 'Test Team', 'role': { 'id': str(role.id), 'name': 'Test Role', }, 'uk_region': { 'id': str(team.uk_region_id), 'name': 'East Midlands', }, 'country': { 'id': str(team.country_id), 'name': 'France', }, 'disabled_on': None, }, 'permissions': expected_permissions, }
class TestInteractionCSVRowForm: """Tests for InteractionCSVRowForm.""" @pytest.mark.parametrize( 'data,errors', ( # kind blank ( { 'kind': '' }, { 'kind': ['This field is required.'] }, ), # kind invalid ( { 'kind': 'invalid' }, { 'kind': [ 'Select a valid choice. invalid is not one of the available choices.' ] }, ), # date blank ( { 'date': '' }, { 'date': ['This field is required.'] }, ), # invalid date ( { 'date': '08/31/2020' }, { 'date': ['Enter a valid date.'] }, ), # invalid contact_email ( { 'contact_email': 'invalid' }, { 'contact_email': ['Enter a valid email address.'] }, ), # blank adviser_1 ( { 'adviser_1': '' }, { 'adviser_1': ['This field is required.'] }, ), # adviser_1 doesn't exist ( { 'adviser_1': 'Non-existent adviser' }, { 'adviser_1': [ADVISER_NOT_FOUND_MESSAGE] }, ), # multiple matching values for adviser_1 ( { 'adviser_1': lambda: AdviserFactory.create_batch( 2, first_name='Pluto', last_name='Doris', )[0].name, }, { 'adviser_1': [MULTIPLE_ADVISERS_FOUND_MESSAGE] }, ), # adviser_1 and team_1 mismatch ( { 'adviser_1': lambda: AdviserFactory( first_name='Pluto', last_name='Doris', dit_team__name='Team Advantage', ).name, 'team_1': lambda: TeamFactory(name='Team Disadvantage', ).name, }, { 'adviser_1': [ADVISER_WITH_TEAM_NOT_FOUND_MESSAGE] }, ), # adviser_2 doesn't exist ( { 'adviser_2': 'Non-existent adviser' }, { 'adviser_2': [ADVISER_NOT_FOUND_MESSAGE] }, ), # multiple matching values for adviser_2 ( { 'adviser_2': lambda: AdviserFactory.create_batch( 2, first_name='Pluto', last_name='Doris', )[0].name, }, { 'adviser_2': [MULTIPLE_ADVISERS_FOUND_MESSAGE] }, ), # adviser_2 and team_2 mismatch ( { 'adviser_2': lambda: AdviserFactory( first_name='Pluto', last_name='Doris', dit_team__name='Team Advantage', ).name, 'team_2': lambda: TeamFactory(name='Team Disadvantage', ).name, }, { 'adviser_2': [ADVISER_WITH_TEAM_NOT_FOUND_MESSAGE] }, ), # service doesn't exist ( { 'service': 'Non-existent service' }, { 'service': [ 'Select a valid choice. That choice is not one of the available choices.', ], }, ), # service is disabled ( { 'service': lambda: _random_service(disabled=True).name, }, { 'service': [OBJECT_DISABLED_MESSAGE], }, ), # Multiple matching services ( { 'service': lambda: ServiceFactory.create_batch(2, name='Duplicate')[0] .name, }, { 'service': ['There is more than one matching service.'], }, ), # communication_channel doesn't exist ( { 'communication_channel': 'Non-existent communication channel' }, { 'communication_channel': [ 'Select a valid choice. That choice is not one of the available choices.', ], }, ), # Multiple matching communication channels ( { 'communication_channel': lambda: CommunicationChannelFactory.create_batch( 2, name='Duplicate', )[0].name, }, { 'communication_channel': [ 'There is more than one matching communication channel.', ], }, ), # communication_channel is disabled ( { 'communication_channel': lambda: _random_communication_channel(disabled=True, ). name, }, { 'communication_channel': [OBJECT_DISABLED_MESSAGE], }, ), # event_id invalid ( { 'event_id': 'non_existent_event_id' }, { 'event_id': [ "'non_existent_event_id' is not a valid UUID.", ], }, ), # event_id is for a disabled event ( { 'event_id': lambda: str(DisabledEventFactory().pk), }, { 'event_id': [OBJECT_DISABLED_MESSAGE], }, ), # event_id non-existent ( { 'event_id': '00000000-0000-0000-0000-000000000000' }, { 'event_id': [ 'Select a valid choice. That choice is not one of the available ' 'choices.', ], }, ), # cannot specify event_id for an interaction ( { 'kind': Interaction.KINDS.interaction, 'event_id': lambda: str(EventFactory().pk), }, { 'event_id': [INTERACTION_CANNOT_HAVE_AN_EVENT_MESSAGE], }, ), ), ) def test_validation_errors(self, data, errors): """Test validation for various fields.""" adviser = AdviserFactory(first_name='Neptune', last_name='Doris') service = _random_service() resolved_data = { 'kind': 'interaction', 'date': '01/01/2018', 'adviser_1': adviser.name, 'contact_email': '*****@*****.**', 'service': service.name, **_resolve_data(data), } form = InteractionCSVRowForm(data=resolved_data) assert form.errors == errors @pytest.mark.parametrize( 'field,input_value,expected_value', ( # UK date format without leading zeroes ( 'date', '1/2/2013', date(2013, 2, 1), ), # UK date format with leading zeroes ( 'date', '03/04/2015', date(2015, 4, 3), ), # ISO date format ( 'date', '2016-05-04', date(2016, 5, 4), ), # Subject ( 'subject', 'A subject', 'A subject', ), # Notes (trailing blank lines are stripped) ( 'notes', 'Notes with\nmultiple lines\n', 'Notes with\nmultiple lines', ), ), ) def test_simple_value_cleaning(self, field, input_value, expected_value): """Test the conversion and cleaning of various non-relationship fields.""" adviser = AdviserFactory(first_name='Neptune', last_name='Doris') service = _random_service() resolved_data = { 'kind': 'interaction', 'date': '01/01/2018', 'adviser_1': adviser.name, 'contact_email': '*****@*****.**', 'service': service.name, field: input_value, } form = InteractionCSVRowForm(data=resolved_data) assert not form.errors assert form.cleaned_data[field] == expected_value @pytest.mark.parametrize( 'kind', (Interaction.KINDS.interaction, Interaction.KINDS.service_delivery), ) @pytest.mark.parametrize( 'field,object_creator,input_transformer', ( # adviser_1 look-up (same case) ( 'adviser_1', lambda: AdviserFactory( first_name='Pluto', last_name='Doris', ), lambda obj: obj.name, ), # adviser_1 look-up (case-insensitive) ( 'adviser_1', lambda: AdviserFactory( first_name='Pluto', last_name='Doris', ), lambda obj: obj.name.upper(), ), # adviser_2 look-up (same case) ( 'adviser_1', lambda: AdviserFactory( first_name='Pluto', last_name='Doris', ), lambda obj: obj.name, ), # adviser_2 look-up (case-insensitive) ( 'adviser_1', lambda: AdviserFactory( first_name='Pluto', last_name='Doris', ), lambda obj: obj.name.upper(), ), # service look-up (same case) ( 'service', lambda: ServiceFactory(name='UNIQUE EXPORT DEAL'), lambda obj: obj.name, ), # service look-up (case-insensitive) ( 'service', lambda: ServiceFactory(name='UNIQUE EXPORT DEAL'), lambda obj: obj.name.lower(), ), ), ) def test_common_relation_fields(self, kind, field, object_creator, input_transformer): """ Test the looking up of values for relationship fields common to interactions and service deliveries. """ adviser = AdviserFactory(first_name='Neptune', last_name='Doris') service = _random_service() obj = object_creator() resolved_data = { 'kind': kind, 'date': '01/01/2018', 'adviser_1': adviser.name, 'contact_email': '*****@*****.**', 'service': service.name, field: input_transformer(obj), } form = InteractionCSVRowForm(data=resolved_data) assert not form.errors assert form.cleaned_data[field] == obj @pytest.mark.parametrize( 'field,object_creator,input_transformer', ( # communication channel look-up (same case) ( 'communication_channel', lambda: _random_communication_channel(), lambda obj: obj.name, ), # communication channel look-up (case-insensitive) ( 'communication_channel', lambda: _random_communication_channel(), lambda obj: obj.name.upper(), ), ), ) def test_interaction_relation_fields(self, field, object_creator, input_transformer): """Test the looking up of values for relationship fields specific to interactions.""" adviser = AdviserFactory(first_name='Neptune', last_name='Doris') service = _random_service() obj = object_creator() resolved_data = { 'kind': 'interaction', 'date': '01/01/2018', 'adviser_1': adviser.name, 'contact_email': '*****@*****.**', 'service': service.name, field: input_transformer(obj), } form = InteractionCSVRowForm(data=resolved_data) assert not form.errors assert form.cleaned_data[field] == obj @pytest.mark.parametrize( 'field,object_creator,input_transformer,expected_value_transformer', ( # communication channel should be ignored ( 'communication_channel', lambda: _random_communication_channel(), lambda obj: obj.name, lambda obj: None, ), # event look-up ( 'event_id', lambda: EventFactory(), lambda obj: str(obj.pk), lambda obj: obj, ), ), ) def test_service_delivery_relation_fields( self, field, object_creator, input_transformer, expected_value_transformer, ): """Test the looking up of values for relationship fields specific to service deliveries.""" adviser = AdviserFactory(first_name='Neptune', last_name='Doris') service = _random_service() obj = object_creator() resolved_data = { 'kind': 'service_delivery', 'date': '01/01/2018', 'adviser_1': adviser.name, 'contact_email': '*****@*****.**', 'service': service.name, field: input_transformer(obj), } form = InteractionCSVRowForm(data=resolved_data) assert not form.errors assert form.cleaned_data[field] == expected_value_transformer(obj) @pytest.mark.parametrize( 'kind', (Interaction.KINDS.interaction, Interaction.KINDS.service_delivery), ) def test_subject_falls_back_to_service(self, kind): """Test that if subject is not specified, the name of the service is used instead.""" adviser = AdviserFactory(first_name='Neptune', last_name='Doris') service = _random_service() data = { 'kind': kind, 'date': '01/01/2018', 'adviser_1': adviser.name, 'contact_email': '*****@*****.**', 'service': service.name, } form = InteractionCSVRowForm(data=data) assert not form.errors assert form.cleaned_data['subject'] == service.name