def test_updated_interaction_synced(opensearch_with_signals): """Test that when export country history is updated, it is synced to OpenSearch.""" export_country_history = CompanyExportCountryHistoryFactory( history_type=CompanyExportCountryHistory.HistoryType.INSERT, ) history_type = CompanyExportCountryHistory.HistoryType.UPDATE export_country_history.history_type = history_type export_country_history.save() opensearch_with_signals.indices.refresh() result = opensearch_with_signals.get( index=ExportCountryHistoryApp.search_model.get_write_index(), id=export_country_history.pk, ) assert result['_source'] == { '_document_type': ExportCountryHistoryApp.name, 'history_user': { 'id': str(export_country_history.history_user.id), 'name': export_country_history.history_user.name, }, 'country': { 'id': str(export_country_history.country.id), 'name': export_country_history.country.name, }, 'company': { 'id': str(export_country_history.company.id), 'name': export_country_history.company.name, }, 'id': str(export_country_history.pk), 'history_type': export_country_history.history_type, 'history_date': export_country_history.history_date.isoformat(), 'date': export_country_history.history_date.isoformat(), 'status': str(export_country_history.status), }
def test_export_country_history_to_dict(es): """Test for export country history search model""" export_country_history = CompanyExportCountryHistoryFactory() result = ExportCountryHistory.db_object_to_dict(export_country_history) assert result == { '_document_type': ExportCountryHistoryApp.name, 'id': export_country_history.pk, 'company': { 'id': str(export_country_history.company.pk), 'name': export_country_history.company.name, }, 'country': { 'id': str(export_country_history.country.pk), 'name': export_country_history.country.name, }, 'history_date': export_country_history.history_date, 'date': export_country_history.history_date, 'history_type': export_country_history.history_type, 'history_user': { 'id': str(export_country_history.history_user.pk), 'name': export_country_history.history_user.name, }, 'status': export_country_history.status, }
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_merge_allowed_when_source_company_has_export_countries(self): """Test that merging is allowed if the source company has export countries.""" source_company = CompanyFactory() CompanyExportCountryFactory(company=source_company) CompanyExportCountryHistoryFactory(company=source_company) target_company = CompanyFactory() user = AdviserFactory() merge_time = datetime(2011, 2, 1, 14, 0, 10, tzinfo=utc) with freeze_time(merge_time): merge_companies(source_company, target_company, user) source_company.refresh_from_db() assert source_company.archived assert source_company.archived_by == user assert source_company.archived_on == merge_time assert source_company.archived_reason == ( f'This record is no longer in use and its data has been transferred ' f'to {target_company} for the following reason: Duplicate record.' ) assert source_company.modified_by == user assert source_company.modified_on == merge_time assert source_company.transfer_reason == Company.TransferReason.DUPLICATE assert source_company.transferred_by == user assert source_company.transferred_on == merge_time assert source_company.transferred_to == target_company
def test_new_export_country_history_synced(opensearch_with_signals): """Test that new export country history is synced to OpenSearch.""" company_export_country_history = CompanyExportCountryHistoryFactory() opensearch_with_signals.indices.refresh() assert opensearch_with_signals.get( index=ExportCountryHistoryApp.search_model.get_write_index(), id=company_export_country_history.pk, )
def test_new_export_country_history_synced(es_with_signals): """Test that new export country history is synced to ES.""" company_export_country_history = CompanyExportCountryHistoryFactory() es_with_signals.indices.refresh() assert es_with_signals.get( index=ExportCountryHistoryApp.es_model.get_write_index(), doc_type=ExportCountryHistoryApp.name, id=company_export_country_history.pk, )
def test_company_export_country_history_update(self): """ Test that updating an existing CompanyExportCountry record sets up a corresponding history record. """ company = CompanyFactory() country = random_obj_for_model(CountryModel) adviser = AdviserFactory() export_country = CompanyExportCountryFactory( company=company, country=country, status=CompanyExportCountry.Status.FUTURE_INTEREST, created_by=adviser, ) CompanyExportCountryHistoryFactory( id=export_country.id, company=export_country.company, country=export_country.country, status=export_country.status, history_type=CompanyExportCountryHistory.HistoryType.INSERT, history_user=export_country.created_by, ) # update it, by changing status company.add_export_country( country, CompanyExportCountry.Status.CURRENTLY_EXPORTING, now(), adviser, True, ) history = CompanyExportCountryHistory.objects.filter( id=export_country.id, ).order_by('history_date') assert history.count() == 2 assert history[0].id == export_country.id assert history[0].company == export_country.company assert history[0].country == export_country.country assert history[0].status == export_country.status assert history[ 0].history_type == CompanyExportCountryHistory.HistoryType.INSERT assert history[1].id == export_country.id assert history[1].company == export_country.company assert history[1].country == export_country.country assert history[ 1].status == CompanyExportCountry.Status.CURRENTLY_EXPORTING assert history[ 1].history_type == CompanyExportCountryHistory.HistoryType.UPDATE
def test_delete_company_export_countries_check_history_tracks_correct_user( self): """ Check that history correctly tracks the user who deletes the export country """ company = CompanyFactory() country = CountryModel.objects.order_by('name')[0] adviser = AdviserFactory() export_country = CompanyExportCountryFactory( company=company, country=country, status=CompanyExportCountry.Status.FUTURE_INTEREST, created_by=adviser, ) CompanyExportCountryHistoryFactory( id=export_country.id, company=export_country.company, country=export_country.country, status=export_country.status, history_type=CompanyExportCountryHistory.HistoryType.INSERT, history_user=export_country.created_by, ) new_user = create_test_user(permission_codenames=( 'change_company', 'change_companyexportcountry', ), ) api_client = self.create_api_client(user=new_user) url = reverse('api-v4:company:update-export-detail', kwargs={'pk': company.pk}) response = api_client.patch( url, data={ 'export_countries': [], }, ) assert response.status_code == status.HTTP_204_NO_CONTENT company.refresh_from_db() assert company.export_countries.count() == 0 delete_history = CompanyExportCountryHistory.objects.filter( company=company, history_type=CompanyExportCountryHistory.HistoryType.DELETE, ) assert delete_history.count() == 1 assert delete_history[0].country == country assert delete_history[0].history_user == new_user
def test_company_export_country_history_update_with_no_change(self): """ Test that submitting an update for a CompanyExportCountry record that doesn't change any field (i.e status) does not create a history record. """ company = CompanyFactory() country = random_obj_for_model(CountryModel) adviser = AdviserFactory() export_country = CompanyExportCountryFactory( company=company, country=country, status=CompanyExportCountry.Status.FUTURE_INTEREST, created_by=adviser, ) CompanyExportCountryHistoryFactory( id=export_country.id, company=export_country.company, country=export_country.country, status=export_country.status, history_type=CompanyExportCountryHistory.HistoryType.INSERT, history_user=export_country.created_by, ) history = CompanyExportCountryHistory.objects.filter( id=export_country.id) assert history.count() == 1 assert history[ 0].history_type == CompanyExportCountryHistory.HistoryType.INSERT # update it, but don't modify the status company.add_export_country( country, CompanyExportCountry.Status.FUTURE_INTEREST, company.created_on, adviser, True, ) # export country history records should be unchanged history = CompanyExportCountryHistory.objects.filter( id=export_country.id) assert history.count() == 1 assert history[ 0].history_type == CompanyExportCountryHistory.HistoryType.INSERT
def test_export_country_history_response_body(self, opensearch_with_collector): """Test the format of an export country history result in the response body.""" history_object = CompanyExportCountryHistoryFactory( history_type=HistoryType.INSERT) opensearch_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ # The view requires a filter 'company': history_object.company.pk, }, ) assert response.status_code == status.HTTP_200_OK results = response.json()['results'] assert len(results) == 1 assert results[0] == { 'company': { 'id': str(history_object.company.pk), 'name': history_object.company.name, }, 'country': { 'id': str(history_object.country.pk), 'name': history_object.country.name, }, 'date': history_object.history_date.isoformat(), 'history_date': history_object.history_date.isoformat(), 'history_type': history_object.history_type, 'history_user': { 'id': str(history_object.history_user.pk), 'name': history_object.history_user.name, }, 'id': str(history_object.pk), 'status': history_object.status, }
def _make_dated_export_country_history(history_date, **kwargs): with freeze_time(history_date): return CompanyExportCountryHistoryFactory( history_type=HistoryType.INSERT, **kwargs)
class TestSearchExportCountryHistory(APITestMixin): """Tests search views.""" @pytest.mark.parametrize( 'permission_codenames,expected_status', ( ( [], status.HTTP_403_FORBIDDEN, ), ( ['view_companyexportcountry'], status.HTTP_403_FORBIDDEN, ), ( [InteractionPermission.view_all], status.HTTP_403_FORBIDDEN, ), ( ['view_companyexportcountry', InteractionPermission.view_all], status.HTTP_200_OK, ), ), ) @pytest.mark.usefixtures('es') def test_permission_checking(self, permission_codenames, expected_status, api_client): """Test that the expected status is returned for various user permissions.""" user = create_test_user(permission_codenames=permission_codenames, dit_team=None) api_client = self.create_api_client(user=user) response = api_client.post( export_country_history_search_url, data={ 'company': uuid4(), }, ) assert response.status_code == expected_status def test_export_country_history_search_with_empty_request( self, es_with_collector): """Should return 400.""" es_with_collector.flush_and_refresh() error_response = 'Request must include either country or company parameters' response = self.api_client.post(export_country_history_search_url, data={}) assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.json()['non_field_errors'][0] == error_response def test_export_country_history_response_body(self, es_with_collector): """Test the format of an export country history result in the response body.""" history_object = CompanyExportCountryHistoryFactory( history_type=HistoryType.INSERT) es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ # The view requires a filter 'company': history_object.company.pk, }, ) assert response.status_code == status.HTTP_200_OK results = response.json()['results'] assert len(results) == 1 assert results[0] == { 'company': { 'id': str(history_object.company.pk), 'name': history_object.company.name, }, 'country': { 'id': str(history_object.country.pk), 'name': history_object.country.name, }, 'date': history_object.history_date.isoformat(), 'history_date': history_object.history_date.isoformat(), 'history_type': history_object.history_type, 'history_user': { 'id': str(history_object.history_user.pk), 'name': history_object.history_user.name, }, 'id': str(history_object.pk), 'status': history_object.status, } def test_interaction_response_body(self, es_with_collector): """Test the format of an interaction result in the response body.""" interaction = ExportCountriesInteractionFactory() es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ # The view requires a filter 'company': interaction.company.pk, }, ) assert response.status_code == status.HTTP_200_OK results = response.json()['results'] assert len(results) == 1 result = results[0] result['contacts'].sort(key=itemgetter('id')) result['dit_participants'].sort( key=lambda participant: participant['adviser']['id']) result['export_countries'].sort( key=lambda export_country: export_country['country']['id']) assert result == { 'company': { 'id': str(interaction.company.pk), 'name': interaction.company.name, 'trading_names': interaction.company.trading_names, }, 'contacts': [{ 'id': str(contact.pk), 'first_name': contact.first_name, 'name': contact.name, 'last_name': contact.last_name, } for contact in sorted(interaction.contacts.all(), key=attrgetter('id'))], 'date': interaction.date.isoformat(), 'dit_participants': [{ 'adviser': { 'id': str(dit_participant.adviser.pk), 'first_name': dit_participant.adviser.first_name, 'name': dit_participant.adviser.name, 'last_name': dit_participant.adviser.last_name, }, 'team': { 'id': str(dit_participant.team.pk), 'name': dit_participant.team.name, }, } for dit_participant in interaction.dit_participants.order_by( 'adviser__pk')], 'export_countries': [{ 'country': { 'id': str(export_country.country.pk), 'name': export_country.country.name, }, 'status': export_country.status, } for export_country in interaction.export_countries.order_by( 'country__pk')], 'kind': interaction.kind, 'id': str(interaction.pk), 'service': { 'id': str(interaction.service.pk), 'name': interaction.service.name, }, 'subject': interaction.subject, } @pytest.mark.parametrize( 'factory', ( lambda: CompanyExportCountryHistoryFactory(history_type=HistoryType .INSERT), lambda: CompanyExportCountryHistoryFactory(history_type=HistoryType .DELETE), ExportCountriesInteractionFactory, ExportCountriesServiceDeliveryFactory, ), ) def test_filtering_by_company_returns_matches(self, es_with_collector, factory): """Test that filtering by company includes matching objects.""" obj = factory() es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ 'company': obj.company.pk, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 1 assert response_data['results'][0]['id'] == str(obj.pk) def test_filtering_by_company_excludes_non_matches(self, es_with_collector): """Test that filtering by company excludes non-matching objects.""" company = CompanyFactory() # Non-export country interactions should be excluded CompanyInteractionFactory(company=company) # Unrelated companies should be excluded CompanyExportCountryHistoryFactory(history_type=HistoryType.INSERT) ExportCountriesInteractionFactory() es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ 'company': company.pk, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 0 assert response_data['results'] == [] @pytest.mark.parametrize( 'factory', ( lambda: CompanyExportCountryHistoryFactory(history_type=HistoryType .INSERT), lambda: CompanyExportCountryHistoryFactory(history_type=HistoryType .DELETE), ), ) def test_filtering_by_country_returns_matching_history_objects( self, es_with_collector, factory, ): """Test that filtering by country includes matching export country history objects.""" obj = factory() es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ 'country': obj.country.pk, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 1 assert response_data['results'][0]['id'] == str(obj.pk) @pytest.mark.parametrize( 'factory', ( ExportCountriesInteractionFactory, ExportCountriesServiceDeliveryFactory, ), ) def test_filtering_by_country_returns_matching_interactions( self, es_with_collector, factory): """Test that filtering by country includes matching interactions.""" obj = factory() es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ 'country': obj.export_countries.first().country.pk, }, ) assert response.status_code == status.HTTP_200_OK response_data = response.json() assert response_data['count'] == 1 assert response_data['results'][0]['id'] == str(obj.pk) 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'] == [] @pytest.mark.parametrize( 'request_args,is_reversed', ( # default sorting ({}, True), ({ 'sortby': 'date:asc' }, False), ({ 'sortby': 'date:desc' }, True), ), ) def test_sorts_results(self, es_with_collector, request_args, is_reversed): """ Test sorting in various cases. Note that a filter is mandatory in this view, hence the test filters by company. """ datetimes = [ datetime(2001, 1, 22, tzinfo=utc), datetime(2002, 2, 23, 1, 2, 3, tzinfo=utc), datetime(2003, 3, 24, tzinfo=utc), datetime(2004, 4, 25, 1, 2, 3, tzinfo=utc), ] company = CompanyFactory() objects = [ ExportCountriesInteractionFactory(date=datetimes.pop(0), company=company), _make_dated_export_country_history(datetimes.pop(0), company=company), ExportCountriesInteractionFactory(date=datetimes.pop(0), company=company), _make_dated_export_country_history(datetimes.pop(0), company=company), ] if is_reversed: objects.reverse() es_with_collector.flush_and_refresh() response = self.api_client.post( export_country_history_search_url, data={ 'company': company.pk, **request_args, }, ) assert response.status_code == status.HTTP_200_OK expected_result_ids = [str(obj.pk) for obj in objects] actual_result_ids = [ result['id'] for result in response.json()['results'] ] assert actual_result_ids == expected_result_ids
def test_str(self): """Test the human friendly string representation of the object""" history = CompanyExportCountryHistoryFactory() status = f'{history.company} {history.country} {history.status}' assert str(history) == status