Example #1
0
def dnb_company_search_datahub_companies():
    """
    Creates Data Hub companies for hydrating DNB search results with.
    """
    # Company with no interactions
    CompanyFactory(duns_number='1234567', id='6083b732-b07a-42d6-ada4-c8082293285b')
    # Company with two interactions
    company = CompanyFactory(duns_number='7654321', id='6083b732-b07a-42d6-ada4-c99999999999')

    interaction_date = datetime.datetime(year=2019, month=8, day=1, hour=16, minute=0, tzinfo=utc)
    with freeze_time(interaction_date):
        CompanyInteractionFactory(
            id='6083b732-b07a-42d6-ada4-222222222222',
            date=interaction_date,
            subject='Meeting with Joe Bloggs',
            company=company,
        )

    older_interaction_date = datetime.datetime(year=2018, month=8, day=1, tzinfo=utc)
    with freeze_time(older_interaction_date):
        CompanyInteractionFactory(
            id='6083b732-b07a-42d6-ada4-111111111111',
            date=older_interaction_date,
            subject='Meeting with John Smith',
            company=company,
        )
Example #2
0
    def test_filter_by_one_list_tier_group(
        self,
        es_with_collector,
    ):
        """
        Test that we can filter by one list tier group.
        """
        one_list_tier = OneListTier.objects.all().order_by('?')[0]
        company_1 = CompanyFactory(
            name='Global HQ Ltd',
            one_list_tier=one_list_tier,
        )
        company_2 = CompanyFactory(
            name='Regional Subsidiary Ltd',
            global_headquarters=company_1,
        )
        CompanyInteractionFactory(
            subject='Global HQ chat',
            company=company_1,
        )
        CompanyInteractionFactory(
            subject='Regional Subsidiary chat',
            company=company_2,
        )

        es_with_collector.flush_and_refresh()

        url = reverse('api-v3:search:interaction')

        response = self.api_client.post(
            url,
            data={
                'company_one_list_group_tier': one_list_tier.id,
            },
        )

        assert response.status_code == status.HTTP_200_OK
        assert response.data['count'] == 2
        assert len(response.data['results']) == 2
        matched_interaction_subjects = {
            result['subject']
            for result in response.data['results']
        }

        assert matched_interaction_subjects == {
            'Global HQ chat', 'Regional Subsidiary chat'
        }
        assert all(
            result['company_one_list_group_tier']['name'] == one_list_tier.name
            for result in response.data['results'])
Example #3
0
    def test_filter_by_multiple_one_list_tier_groups(
        self,
        es_with_collector,
    ):
        """
        Test that we can filter by multiple one list tier groups.
        """
        one_list_tiers = list(OneListTier.objects.all().order_by('?')[0:2])
        company_1 = CompanyFactory(
            name='Global HQ Ltd',
            one_list_tier=one_list_tiers[0],
        )
        company_2 = CompanyFactory(
            name='Other Company',
            one_list_tier=one_list_tiers[1],
        )
        CompanyInteractionFactory(
            subject='Global HQ chat',
            company=company_1,
        )
        CompanyInteractionFactory(
            subject='Other Company chat',
            company=company_2,
        )
        es_with_collector.flush_and_refresh()

        url = reverse('api-v3:search:interaction')

        response = self.api_client.post(
            url,
            data={
                'company_one_list_group_tier':
                [tier.id for tier in one_list_tiers],
            },
        )

        expected_interaction_subjects = {
            'Global HQ chat',
            'Other Company chat',
        }
        assert response.status_code == status.HTTP_200_OK
        assert response.data['count'] == 2
        assert len(response.data['results']) == 2
        matched_interaction_subjects = {
            result['subject']
            for result in response.data['results']
        }

        assert matched_interaction_subjects == expected_interaction_subjects
Example #4
0
    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'] == []
Example #6
0
    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
Example #7
0
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'
Example #8
0
    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
Example #9
0
 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)
Example #10
0
 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
Example #11
0
    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
Example #12
0
    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
Example #13
0
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_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'
Example #15
0
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
Example #16
0
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)
Example #17
0
    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'
Example #18
0
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()
Example #19
0
    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
        ]
Example #20
0
 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
Example #21
0
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,
    )
Example #22
0
    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
Example #23
0
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,
    )
Example #24
0
 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
Example #25
0
    def test_with_multiple_interactions(self, data_flow_api_client):
        """Test that the correct number of records are returned in the right order"""
        with freeze_time('2019-01-01 12:30:00'):
            interaction1 = CompanyInteractionFactory()
        with freeze_time('2019-01-03 12:00:00'):
            interaction2 = CompanyInteractionFactory()
        with freeze_time('2019-01-01 12:00:00'):
            interaction3 = CompanyInteractionFactory()
            interaction4 = CompanyInteractionFactory()

        response = data_flow_api_client.get(self.view_url)
        assert response.status_code == status.HTTP_200_OK
        response_results = response.json()['results']
        assert len(response_results) == 4
        expected_list = sorted(
            [interaction3, interaction4],
            key=lambda item: item.pk,
        ) + [interaction1, interaction2]
        for index, interaction in enumerate(expected_list):
            assert interaction.get_absolute_url() == response_results[index]['interaction_link']
Example #26
0
    def test_filter_by_company_name(self, setup_es, name_term,
                                    matched_company_name):
        """Tests filtering interaction by company name."""
        matching_company1 = CompanyFactory(
            name='whiskers and tabby',
            trading_names=['Maine Coon', 'Egyptian Mau'],
        )
        matching_company2 = CompanyFactory(
            name='1a',
            trading_names=['3a', '4a'],
        )
        non_matching_company = CompanyFactory(
            name='Pluto and pippo',
            trading_names=['eniam nooc', 'naitpyge uam'],
        )
        CompanyInteractionFactory(company=matching_company1)
        CompanyInteractionFactory(company=matching_company2)
        CompanyInteractionFactory(company=non_matching_company)

        setup_es.indices.refresh()

        url = reverse('api-v3:search:interaction')

        response = self.api_client.post(
            url,
            data={
                'original_query': '',
                'company_name': name_term,
            },
        )

        assert response.status_code == status.HTTP_200_OK
        match = Interaction.objects.filter(
            company__name=matched_company_name).first()
        if match:
            assert response.data['count'] == 1
            assert len(response.data['results']) == 1
            assert response.data['results'][0]['id'] == str(match.id)
        else:
            assert response.data['count'] == 0
            assert len(response.data['results']) == 0
Example #27
0
    def test_cannot_update_service_answers(self, initial_value, new_value,
                                           expected_response):
        """Test that the service answers field cannot be updated with incorrect data."""
        interaction = CompanyInteractionFactory(**initial_value)

        url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk})
        response = self.api_client.patch(
            url,
            data=new_value,
        )
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.json() == expected_response
def test_null_adviser(api_client):
    """
    Test that we can handle dit_participant.adviser being None
    """
    interaction = CompanyInteractionFactory(dit_participants=[])
    InteractionDITParticipantFactory(
        interaction=interaction,
        adviser=None,
        team=TeamFactory(),
    )
    response = hawk.get(api_client, get_url('api-v3:activity-stream:interactions'))
    assert response.status_code == status.HTTP_200_OK
    def test_restricted_user_cannot_get_company_interaction(self):
        """Test that a restricted user cannot get a company interaction."""
        interaction = CompanyInteractionFactory()
        requester = create_test_user(
            permission_codenames=[InteractionPermission.view_associated_investmentproject],
            dit_team=TeamFactory(),
        )
        api_client = self.create_api_client(user=requester)
        url = reverse('api-v3:interaction:item', kwargs={'pk': interaction.pk})
        response = api_client.get(url)

        assert response.status_code == status.HTTP_403_FORBIDDEN
Example #30
0
def test_updated_interaction_synced(opensearch_with_signals):
    """Test that when an interaction is updated it is synced to OpenSearch."""
    interaction = CompanyInteractionFactory()
    new_subject = 'pluto'
    interaction.subject = new_subject
    interaction.save()
    opensearch_with_signals.indices.refresh()

    result = opensearch_with_signals.get(
        index=InteractionSearchApp.search_model.get_write_index(),
        id=interaction.pk,
    )
    assert result['_source']['subject'] == new_subject