def _company_factory( num_interactions=0, num_contacts=0, num_orders=0, num_referrals=0, num_company_list_items=0, ): """Factory for a company that has companies, interactions and OMIS orders.""" company = CompanyFactory() ContactFactory.create_batch(num_contacts, company=company) CompanyInteractionFactory.create_batch(num_interactions, company=company) CompanyReferralFactory.create_batch(num_referrals, company=company, contact=None) OrderFactory.create_batch(num_orders, company=company) CompanyListItemFactory.create_batch(num_company_list_items, company=company) return company
def test_ignores_interactions_without_adviser_and_team( self, monkeypatch, caplog): """Test that the task does not modify interactions without advisers and teams.""" caplog.set_level('INFO', 'datahub') populate_interaction_dit_participant_mock = Mock( wraps=populate_interaction_dit_participant, ) monkeypatch.setattr( 'datahub.dbmaintenance.tasks.populate_interaction_dit_participant', populate_interaction_dit_participant_mock, ) interactions = CompanyInteractionFactory.create_batch( 10, dit_adviser=None, dit_team=None, dit_participants=[], ) result = populate_interaction_dit_participant_mock.apply_async( kwargs={'batch_size': 100}, ) assert result.successful() assert populate_interaction_dit_participant_mock.apply_async.call_count == 1 for interaction in interactions: interaction.refresh_from_db() # These objects should not have been modified assert all([obj.dit_participants.count() == 0 for obj in interactions]) assert len(caplog.records) == 1 assert f'0 InteractionDITParticipant many-to-many objects created' in caplog.text
def _company_factory(num_interactions, num_contacts, num_investment_projects, num_orders): """ Factory for a company that has companies, interactions, investment projects and OMIS orders. """ company = CompanyFactory() ContactFactory.create_batch(num_contacts, company=company) CompanyInteractionFactory.create_batch(num_interactions, company=company) OrderFactory.create_batch(num_orders, company=company) fields_iter = cycle(INVESTMENT_PROJECT_COMPANY_FIELDS) fields = islice(fields_iter, 0, num_investment_projects) InvestmentProjectFactory.create_batch( num_investment_projects, **{field: company for field in fields}, ) return company
def test_unarchive_interaction_non_restricted_user(self, permissions): """ Tests un-archiving an interaction for a non-restricted user. """ requester = create_test_user(permission_codenames=permissions) api_client = self.create_api_client(user=requester) interaction = CompanyInteractionFactory( archived=True, archived_by=requester, archived_reason='just cos', archived_on=now(), ) url = reverse( 'api-v3:interaction:unarchive-item', kwargs={'pk': interaction.pk}, ) response = api_client.post(url) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['archived'] is False assert response_data['archived_by'] is None assert response_data['archived_reason'] == '' assert response_data['archived_on'] is None
def test_realtime_messages_sent( self, monkeypatch, automatic_company_archive_feature_flag, created_on_delta, companies_to_create, expected_message, ): """ Test that appropriate realtime messaging is sent which reflects the archiving actions. """ created_on = timezone.now() - created_on_delta for _ in range(companies_to_create): with freeze_time(created_on): company = CompanyFactory() CompanyInteractionFactory( company=company, date=timezone.now() - relativedelta(years=8, days=1), ) mock_send_realtime_message = mock.Mock() monkeypatch.setattr( 'datahub.company.tasks.company.send_realtime_message', mock_send_realtime_message, ) automatic_company_archive.apply_async(kwargs={'simulate': False}) mock_send_realtime_message.assert_called_once_with(expected_message)
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_filter_by_was_policy_feedback_provided( self, setup_es, was_policy_feedback_provided): """Test filtering interactions by was_policy_feedback_provided.""" interactions_without_policy_feedback = CompanyInteractionFactory.create_batch( 5) interactions_with_policy_feedback = ( CompanyInteractionFactoryWithPolicyFeedback.create_batch(5)) setup_es.indices.refresh() url = reverse('api-v3:search:interaction') request_data = { 'was_policy_feedback_provided': was_policy_feedback_provided, } response = self.api_client.post(url, data=request_data) assert response.status_code == status.HTTP_200_OK response_data = response.json() expected_interactions = (interactions_with_policy_feedback if was_policy_feedback_provided else interactions_without_policy_feedback) assert response_data['count'] == len(expected_interactions) results = response_data['results'] result_ids = {result['id'] for result in results} assert result_ids == { str(interaction.pk) for interaction in expected_interactions }
def test_modified_on( self, automatic_company_archive_feature_flag, modified_on_delta, expected_archived, ): """ Test that a company modified_on dates around the 3m boundary are archived or not as expected. """ gt_3m_ago = timezone.now() - relativedelta(months=3, days=1) with freeze_time(gt_3m_ago): company = CompanyFactory() CompanyInteractionFactory( company=company, date=timezone.now() - relativedelta(years=8, days=1), ) with freeze_time(timezone.now() - modified_on_delta): company.save() task_result = automatic_company_archive.apply_async( kwargs={'simulate': False}) assert task_result.successful() archived_company = Company.objects.get(pk=company.id) assert archived_company.archived == expected_archived assert archived_company.modified_on == company.modified_on
def company_with_multiple_participant_interaction_factory(): """Factory for a company with an interaction that has multiple participants.""" company = CompanyFactory() interaction = CompanyInteractionFactory(company=company, dit_participants=[]) InteractionDITParticipantFactory.create_batch(2, interaction=interaction) return company
def test_archive_interaction_restricted_user_associated_project(self): """ Tests archiving an interaction for a restricted user. """ project_creator = AdviserFactory() project = InvestmentProjectFactory(created_by=project_creator) requester = create_test_user( permission_codenames=[InteractionPermission.change_associated_investmentproject], dit_team=project_creator.dit_team, # same dit team as the project creator ) api_client = self.create_api_client(user=requester) interaction = CompanyInteractionFactory(investment_project=project) url = reverse( 'api-v3:interaction:archive-item', kwargs={'pk': interaction.pk}, ) response = api_client.post( url, data={ 'reason': 'archive reason', }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['archived'] is True assert response_data['archived_by']['id'] == str(requester.pk) assert response_data['archived_reason'] == 'archive reason'
def test_interaction_synced_when_dit_participant_added( opensearch_with_signals): """Test that interactions are synced to OpenSearch if their DIT participants change.""" interaction = CompanyInteractionFactory(dit_participants=[]) opensearch_with_signals.indices.refresh() doc = opensearch_with_signals.get( index=InteractionSearchApp.search_model.get_read_alias(), id=interaction.pk, ) assert doc['_source']['dit_participants'] == [] dit_participant = InteractionDITParticipantFactory(interaction=interaction) opensearch_with_signals.indices.refresh() updated_doc = opensearch_with_signals.get( index=InteractionSearchApp.search_model.get_read_alias(), id=interaction.pk, ) actual_dit_participants = updated_doc['_source']['dit_participants'] assert len(actual_dit_participants) == 1 assert actual_dit_participants[0]['adviser']['id'] == str( dit_participant.adviser.pk) assert actual_dit_participants[0]['team']['id'] == str( dit_participant.team.pk)
def test_cannot_update_read_only_fields(self): """Test updating read-only fields.""" interaction = CompanyInteractionFactory( archived_documents_url_path='old_path', archived=False, archived_by=None, archived_on=None, archived_reason=None, ) url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk}) # TODO: also test `archived` field once we have made it read-only response = self.api_client.patch( url, data={ 'archived_documents_url_path': 'new_path', 'archived': True, 'archived_by': 123, 'archived_on': date.today(), 'archived_reason': 'test', }, ) assert response.status_code == status.HTTP_200_OK assert response.data['archived_documents_url_path'] == 'old_path' assert response.data['archived'] is False assert response.data['archived_by'] is None assert response.data['archived_on'] is None assert response.data['archived_reason'] is None
def test_find_active_contact_by_email_address( email, expected_matching_status, match_on_alternative, match_strategy, ): """Test finding a contact by email address for various scenarios.""" for factory_kwargs in EMAIL_MATCHING_CONTACT_TEST_DATA: interaction_count = 0 if factory_kwargs.get('interactions'): interaction_count = factory_kwargs.pop('interactions') created_contact = ContactFactory(**factory_kwargs) for _ in range(interaction_count): CompanyInteractionFactory(contacts=[created_contact]) contact, actual_matching_status = find_active_contact_by_email_address(email, match_strategy) assert actual_matching_status == expected_matching_status if actual_matching_status == ContactMatchingStatus.matched: assert contact actual_email = contact.email_alternative if match_on_alternative else contact.email assert actual_email.lower() == email.lower() else: assert not contact
def test_unarchive_interaction_restricted_user_non_associated_project(self): """ Test that a restricted user cannot un-archive a non-associated interaction. """ project_creator = AdviserFactory() project = InvestmentProjectFactory(created_by=project_creator) # Ensure the requester is created for a different DIT team requester = create_test_user( permission_codenames=[InteractionPermission.change_associated_investmentproject], ) api_client = self.create_api_client(user=requester) interaction = CompanyInteractionFactory( investment_project=project, archived=True, archived_by=project_creator, archived_on=now(), archived_reason='why not', ) url = reverse( 'api-v3:interaction:unarchive-item', kwargs={'pk': interaction.pk}, ) response = api_client.post( url, ) assert response.status_code == status.HTTP_403_FORBIDDEN
def test_filter_by_dit_participant(self, setup_es, dit_participant_field): """Test filtering interaction by DIT participant adviser and team IDs.""" interactions = CompanyInteractionFactory.create_batch( 10, dit_participants=[]) for interaction in interactions: InteractionDITParticipantFactory.create_batch( 2, interaction=interaction) setup_es.indices.refresh() interaction = choice(interactions) dit_participant = interaction.dit_participants.order_by('?').first() url = reverse('api-v3:search:interaction') request_data = { f'dit_participants__{dit_participant_field}': getattr(dit_participant, dit_participant_field).id, } response = self.api_client.post(url, request_data) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 1 results = response_data['results'] assert len(results) == 1 assert results[0]['id'] == str(interaction.pk)
def test_restricted_user_can_update_associated_investment_project_interaction(self): """ Test that a restricted user can update an interaction for an associated investment project. """ project_creator = AdviserFactory() project = InvestmentProjectFactory(created_by=project_creator) interaction = CompanyInteractionFactory( subject='I am a subject', investment_project=project, ) requester = create_test_user( permission_codenames=[ InteractionPermission.change_associated_investmentproject, ], dit_team=project_creator.dit_team, ) api_client = self.create_api_client(user=requester) url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk}) response = api_client.patch( url, data={ 'subject': 'I am another subject', }, ) assert response.status_code == status.HTTP_200_OK assert response.data['subject'] == 'I am another subject'
def test_adding_interaction_updates_company(es_with_signals): """Test that when an interaction is added, the company is synced to ES.""" test_name = 'very_hard_to_find_company' company = CompanyFactory( name=test_name, ) es_with_signals.indices.refresh() doc = es_with_signals.get( index=CompanySearchApp.es_model.get_read_alias(), doc_type=DEFAULT_MAPPING_TYPE, id=company.pk, ) assert doc['_source']['name'] == test_name assert doc['_source']['latest_interaction_date'] is None CompanyInteractionFactory( date=dateutil_parse('2018-04-05T00:00:00Z'), company=company, ) es_with_signals.indices.refresh() updated_doc = es_with_signals.get( index=CompanySearchApp.es_model.get_read_alias(), doc_type=DEFAULT_MAPPING_TYPE, id=company.pk, ) assert updated_doc['_source']['name'] == test_name assert updated_doc['_source']['latest_interaction_date'] == '2018-04-05T00:00:00+00:00'
def test_basic_search_no_permissions(self, setup_es): """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) InvestmentProjectFactory(created_by=user) CompanyFactory() ContactFactory() EventFactory() CompanyInteractionFactory() OrderFactory() setup_es.indices.refresh() url = reverse('api-v3:search:basic') response = api_client.get( url, data={ 'term': '', 'entity': 'company', }, ) 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_filtering_by_country_excludes_non_matches(self, es_with_collector): """Test that filtering by country excludes non-matching objects.""" countries = list(Country.objects.order_by('?')[:2]) filter_country = countries[0] other_country = countries[1] # Non-export country interactions should be excluded CompanyInteractionFactory() # Unrelated countries should be excluded CompanyExportCountryHistoryFactory(country=other_country, history_type=HistoryType.INSERT) ExportCountriesInteractionFactory( export_countries__country=other_country) es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ 'country': filter_country.pk, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 0 assert response_data['results'] == []
def test_interactions_to_es_documents(setup_es): """Test converting 2 orders to Elasticsearch documents.""" interactions = CompanyInteractionFactory.create_batch(2) result = Interaction.db_objects_to_es_documents(interactions) assert {item['_id'] for item in result} == {str(item.pk) for item in interactions}
def test_sync_outdated_companies_limit_most_recently_interacted_updated( requests_mock, dnb_response_uk, ): """ Test that running sync_outdated_companies_with_dnb with a limit will update the most recently interacted company. """ requests_mock.post( DNB_SEARCH_URL, json=dnb_response_uk, ) company_most_recent_interaction = CompanyFactory( duns_number='123456789', dnb_modified_on=now() - timedelta(days=1), ) CompanyInteractionFactory(company=company_most_recent_interaction, date=now()) company_least_recent_interaction = CompanyFactory( duns_number='123456788', dnb_modified_on=now() - timedelta(days=1), ) CompanyInteractionFactory( company=company_least_recent_interaction, date=now() - timedelta(days=1), ) task_result = sync_outdated_companies_with_dnb.apply_async(kwargs={ 'fields_to_update': ['global_ultimate_duns_number'], 'dnb_modified_on_before': now() + timedelta(days=1), 'simulate': False, 'limit': 1, }, ) company_least_recent_interaction.refresh_from_db() company_most_recent_interaction.refresh_from_db() assert task_result.successful() # We expect the least recently interacted company to be unmodified assert company_least_recent_interaction.dnb_modified_on == now( ) - timedelta(days=1) # We expect most recently interacted company to be modified assert company_most_recent_interaction.dnb_modified_on == now()
def test_can_replace_some_participants(self): """Test that a subset of existing DIT participants can be replaced.""" interaction = CompanyInteractionFactory(dit_participants=[]) dit_participants = InteractionDITParticipantFactory.create_batch( 3, interaction=interaction, ) # Change the first adviser's team so that we can check that the participant's team is # unchanged after the update. dit_participants[0].adviser.dit_team = TeamFactory() dit_participants[0].adviser.save() new_advisers = [ dit_participants[0].adviser, AdviserFactory(), ] request_data = { 'dit_participants': [ { 'adviser': { 'id': adviser.pk, }, } for adviser in new_advisers ], } url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk}) response = self.api_client.patch(url, data=request_data) assert response.status_code == status.HTTP_200_OK response_data = response.json() response_data['dit_participants'].sort( key=lambda dit_participant: dit_participant['adviser']['id'], ) expected_advisers_and_teams = [ (new_advisers[0], dit_participants[0].team), (new_advisers[1], new_advisers[1].dit_team), ] expected_advisers_and_teams.sort(key=lambda adviser_and_team: adviser_and_team[0].pk) assert response_data['dit_participants'] == [ { 'adviser': { 'id': str(adviser.pk), 'first_name': adviser.first_name, 'last_name': adviser.last_name, 'name': adviser.name, }, 'team': { 'id': str(team.pk), 'name': team.name, }, } for adviser, team in expected_advisers_and_teams ]
def test_fails_without_permissions(self): """Should return 403""" interaction = CompanyInteractionFactory() user = create_test_user(dit_team=TeamFactory()) api_client = self.create_api_client(user=user) url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk}) response = api_client.get(url) assert response.status_code == status.HTTP_403_FORBIDDEN
def test_validation(self, data, errors): """Test validation when an invalid date is provided.""" interaction = CompanyInteractionFactory() url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk}) response = self.api_client.patch(url, data=data) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json() == errors
def test_interactions_to_documents(opensearch): """Test converting 2 orders to OpenSearch documents.""" interactions = CompanyInteractionFactory.create_batch(2) result = Interaction.db_objects_to_documents(interactions) assert {item['_id'] for item in result} == {item.pk for item in interactions}
def test_new_interaction_synced(opensearch_with_signals): """Test that new interactions are synced to OpenSearch.""" interaction = CompanyInteractionFactory() opensearch_with_signals.indices.refresh() assert opensearch_with_signals.get( index=InteractionSearchApp.search_model.get_write_index(), id=interaction.pk, )
def test_intelligent_homepage_limit(self, setup_es): """Test the limit param.""" CompanyInteractionFactory.create_batch(15, dit_adviser=self.user) ContactFactory.create_batch(15, created_by=self.user) setup_es.indices.refresh() url = reverse('dashboard:intelligent-homepage') response = self.api_client.get( url, data={ 'limit': 10, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert len(response_data['contacts']) == 10 assert len(response_data['interactions']) == 10
def test_new_interaction_synced(es_with_signals): """Test that new interactions are synced to ES.""" interaction = CompanyInteractionFactory() es_with_signals.indices.refresh() assert es_with_signals.get( index=InteractionSearchApp.es_model.get_write_index(), doc_type=InteractionSearchApp.name, id=interaction.pk, )
def test_get_contact_names(self, num_contacts, expected_display_value): """Test that contact names are formatted as expected.""" interaction = CompanyInteractionFactory( contacts=ContactFactory.create_batch(num_contacts), ) interaction_admin = InteractionAdmin(Interaction, site) first_contact = interaction.contacts.order_by('pk').first() formatted_expected_display_value = expected_display_value.format( first_contact_name=first_contact.name if first_contact else '', ) assert interaction_admin.get_contact_names( interaction) == formatted_expected_display_value
def test_filtered_by_event(self): """List of interactions filtered by event""" contact = ContactFactory() event = EventFactory() CompanyInteractionFactory.create_batch(3, contacts=[contact]) EventServiceDeliveryFactory.create_batch(3) service_deliveries = EventServiceDeliveryFactory.create_batch(3, event=event) url = reverse('api-v3:interaction:collection') response = self.api_client.get(url, data={'event_id': event.id}) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 3 actual_ids = {result['id'] for result in response_data['results']} expected_ids = {str(service_delivery.id) for service_delivery in service_deliveries} assert actual_ids == expected_ids