From 2b8bf3fd4dd926ed1fd0d605bb349981ab839d51 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Fri, 12 Jan 2024 16:44:00 +0000 Subject: [PATCH 01/21] Add factories for various party types --- api/parties/tests/factories.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/api/parties/tests/factories.py b/api/parties/tests/factories.py index c9b531fb5f..2e58f41414 100644 --- a/api/parties/tests/factories.py +++ b/api/parties/tests/factories.py @@ -14,3 +14,23 @@ class PartyFactory(factory.django.DjangoModelFactory): class Meta: model = Party + + +class ConsigneeFactory(PartyFactory): + type = PartyType.CONSIGNEE + sub_type = SubType.GOVERNMENT + + +class EndUserFactory(PartyFactory): + type = PartyType.END_USER + sub_type = SubType.GOVERNMENT + + +class ThirdPartyFactory(PartyFactory): + type = PartyType.THIRD_PARTY + sub_type = SubType.GOVERNMENT + + +class UltimateEndUserFactory(PartyFactory): + type = PartyType.ULTIMATE_END_USER + sub_type = SubType.GOVERNMENT From 4857861865382af0341630ab0d18cee638b1722a Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Fri, 12 Jan 2024 16:44:36 +0000 Subject: [PATCH 02/21] Update application creation helper function to use factories --- api/applications/tests/factories.py | 1 + .../tests/test_edit_good_on_applications.py | 87 ++++++++----------- .../tests/test_finalise_application.py | 4 +- api/applications/tests/test_removing_goods.py | 35 +++++--- .../test_retrieve_good_on_applications.py | 8 +- api/applications/tests/test_third_parties.py | 5 +- api/cases/tests/test_case_search.py | 7 +- api/cases/tests/test_get_case.py | 4 +- api/cases/tests/test_good_precedents_view.py | 9 +- api/parties/tests/factories.py | 1 + test_helpers/clients.py | 66 +++++--------- 11 files changed, 110 insertions(+), 117 deletions(-) diff --git a/api/applications/tests/factories.py b/api/applications/tests/factories.py index 03f6227e60..07c5bc6a9b 100644 --- a/api/applications/tests/factories.py +++ b/api/applications/tests/factories.py @@ -73,6 +73,7 @@ class StandardApplicationFactory(factory.django.DjangoModelFactory): intended_end_use = "this is our intended end use" is_shipped_waybill_or_lading = True non_waybill_or_lading_route_details = None + is_mod_security_approved = False submitted_by = factory.SubFactory(ExporterUserFactory) class Meta: diff --git a/api/applications/tests/test_edit_good_on_applications.py b/api/applications/tests/test_edit_good_on_applications.py index 6971164f08..8c5fecc382 100644 --- a/api/applications/tests/test_edit_good_on_applications.py +++ b/api/applications/tests/test_edit_good_on_applications.py @@ -16,13 +16,14 @@ class EditGoodOnApplicationsTests(DataTestClient): def test_edit_a_good_on_applicaton(self): - self.create_draft_standard_application(self.organisation) - self.good_on_application.firearm_details = FirearmFactory.create() - self.good_on_application.save() + application = self.create_draft_standard_application(self.organisation) + good_on_application = application.goods.first() + good_on_application.firearm_details = FirearmFactory() + good_on_application.save() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.put( @@ -37,15 +38,9 @@ def test_edit_a_good_on_applicaton(self): ) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.good_on_application.refresh_from_db() - self.assertEqual( - self.good_on_application.firearm_details.year_of_manufacture, - 1990, - ) - self.assertEqual( - self.good_on_application.firearm_details.calibre, - "1mm", - ) + good_on_application.refresh_from_db() + self.assertEqual(good_on_application.firearm_details.year_of_manufacture, 1990) + self.assertEqual(good_on_application.firearm_details.calibre, "1mm") def test_edit_a_good_on_applicaton_read_only(self): application = self.create_draft_standard_application(self.organisation) @@ -53,15 +48,16 @@ def test_edit_a_good_on_applicaton_read_only(self): application.status = get_case_status_by_status(uneditable_status) application.save() - self.good_on_application.firearm_details = FirearmFactory.create() - self.good_on_application.save() + good_on_application = application.goods.first() + good_on_application.firearm_details = FirearmFactory() + good_on_application.save() - original_year_of_manufacture = self.good_on_application.firearm_details.year_of_manufacture - original_calibre = self.good_on_application.firearm_details.calibre + original_year_of_manufacture = good_on_application.firearm_details.year_of_manufacture + original_calibre = good_on_application.firearm_details.calibre url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.put( @@ -77,30 +73,25 @@ def test_edit_a_good_on_applicaton_read_only(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.json(), {"errors": [strings.Applications.Generic.READ_ONLY]}) - self.good_on_application.refresh_from_db() - self.assertEqual( - self.good_on_application.firearm_details.year_of_manufacture, - original_year_of_manufacture, - ) - self.assertEqual( - self.good_on_application.firearm_details.calibre, - original_calibre, - ) + good_on_application.refresh_from_db() + self.assertEqual(good_on_application.firearm_details.year_of_manufacture, original_year_of_manufacture) + self.assertEqual(good_on_application.firearm_details.calibre, original_calibre) def test_edit_a_good_on_applicaton_invalid_organisation(self): another_organisation, _ = self.create_organisation_with_exporter_user() application = self.create_draft_standard_application(another_organisation) application.save() - self.good_on_application.firearm_details = FirearmFactory.create() - self.good_on_application.save() + good_on_application = application.goods.first() + good_on_application.firearm_details = FirearmFactory() + good_on_application.save() - original_year_of_manufacture = self.good_on_application.firearm_details.year_of_manufacture - original_calibre = self.good_on_application.firearm_details.calibre + original_year_of_manufacture = good_on_application.firearm_details.year_of_manufacture + original_calibre = good_on_application.firearm_details.calibre url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.put( @@ -116,13 +107,13 @@ def test_edit_a_good_on_applicaton_invalid_organisation(self): self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.json(), {"errors": [strings.Applications.Generic.INVALID_ORGANISATION]}) - self.good_on_application.refresh_from_db() + good_on_application.refresh_from_db() self.assertEqual( - self.good_on_application.firearm_details.year_of_manufacture, + good_on_application.firearm_details.year_of_manufacture, original_year_of_manufacture, ) self.assertEqual( - self.good_on_application.firearm_details.calibre, + good_on_application.firearm_details.calibre, original_calibre, ) @@ -130,13 +121,9 @@ def test_edit_a_good_on_applicaton_invalid_organisation(self): class GovUserEditGoodOnApplicationsTests(DataTestClient): def test_edit_trigger_list_assessment_and_nca(self): "Test updating trigger list assessment on multiple products on application" - draft = self.create_draft_standard_application(self.organisation) + draft = self.create_draft_standard_application(self.organisation, num_products=2) application = self.submit_application(draft, self.exporter_user) - self.good_on_application2 = GoodOnApplicationFactory( - application=application, - good=GoodFactory(organisation=self.organisation, is_good_controlled=True), - ) - good_on_applications = [self.good_on_application, self.good_on_application2] + good_on_applications = application.goods.all() for good_on_application in good_on_applications: self.assertEqual(good_on_application.nsg_list_type, "") @@ -151,18 +138,18 @@ def test_edit_trigger_list_assessment_and_nca(self): data = [ { - "id": self.good_on_application.id, + "id": good_on_applications[0].id, "application": application.id, - "good": self.good_on_application.good.id, + "good": good_on_applications[0].good.id, "nsg_list_type": NSGListType.TRIGGER_LIST, "is_trigger_list_guidelines_applicable": True, "is_nca_applicable": False, "nsg_assessment_note": "Trigger list product1", }, { - "id": self.good_on_application2.id, + "id": good_on_applications[1].id, "application": application.id, - "good": self.good_on_application2.good.id, + "good": good_on_applications[1].good.id, "nsg_list_type": NSGListType.TRIGGER_LIST, "is_trigger_list_guidelines_applicable": False, "is_nca_applicable": True, @@ -195,6 +182,7 @@ def test_edit_trigger_list_assessment_and_nca(self): def test_edit_good_on_terminal_status_application_forbidden(self, terminal_status): draft = self.create_draft_standard_application(self.organisation) application = self.submit_application(draft, self.exporter_user) + good_on_application = application.goods.first() self.good_on_application2 = GoodOnApplicationFactory( application=application, good=GoodFactory(organisation=self.organisation, is_good_controlled=True), @@ -211,9 +199,9 @@ def test_edit_good_on_terminal_status_application_forbidden(self, terminal_statu url, data=[ { - "id": self.good_on_application.id, + "id": good_on_application.id, "application": application.id, - "good": self.good_on_application.good.id, + "good": good_on_application.good.id, "nsg_list_type": NSGListType.TRIGGER_LIST, "is_trigger_list_guidelines_applicable": True, "is_nca_applicable": True, @@ -236,6 +224,7 @@ def test_edit_good_on_terminal_status_application_forbidden(self, terminal_statu def test_edit_good_on_application_bad_request(self): draft = self.create_draft_standard_application(self.organisation) application = self.submit_application(draft, self.exporter_user) + good_on_application = application.goods.first() url = reverse( "applications:good_on_application_update_internal", @@ -246,9 +235,9 @@ def test_edit_good_on_application_bad_request(self): url, data=[ { - "id": self.good_on_application.id, + "id": good_on_application.id, "application": application.id, - "good": self.good_on_application.good.id, + "good": good_on_application.good.id, "nsg_list_type": "INVALID_LIST_TYPE", "is_trigger_list_guidelines_applicable": False, "is_nca_applicable": True, diff --git a/api/applications/tests/test_finalise_application.py b/api/applications/tests/test_finalise_application.py index 1c04681f0f..fcf2662cab 100644 --- a/api/applications/tests/test_finalise_application.py +++ b/api/applications/tests/test_finalise_application.py @@ -638,6 +638,7 @@ class FinaliseApplicationGetApprovedGoodsTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) + self.good_on_application = self.standard_application.goods.first() self.url = reverse("applications:finalise", kwargs={"pk": self.standard_application.id}) def test_get_approved_goods_success(self): @@ -687,7 +688,7 @@ def test_get_approved_goods_success(self): self.assertEqual(data[0]["good"]["id"], str(self.good_on_application.good.id)) self.assertEqual(data[0]["good"]["description"], self.good_on_application.good.description) self.assertEqual(data[0]["quantity"], self.good_on_application.quantity) - self.assertEqual(data[0]["value"].split(".")[0], str(self.good_on_application.value)) + self.assertEqual(data[0]["value"], str(self.good_on_application.value)) self.assertEqual(data[0]["advice"]["type"]["key"], AdviceType.APPROVE) self.assertEqual(data[0]["advice"]["text"], advice_text) @@ -712,6 +713,7 @@ def setUp(self): super().setUp() self.gov_user.role.permissions.set([GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name]) self.standard_application = self.create_standard_application_case(self.organisation) + self.good_on_application = self.standard_application.goods.first() self.url = reverse("applications:finalise", kwargs={"pk": self.standard_application.id}) self.date = timezone.now() self.data = { diff --git a/api/applications/tests/test_removing_goods.py b/api/applications/tests/test_removing_goods.py index fac073285b..21b5ef72e2 100644 --- a/api/applications/tests/test_removing_goods.py +++ b/api/applications/tests/test_removing_goods.py @@ -27,18 +27,19 @@ def test_remove_a_good_from_draft_success(self): And the good status is changed to DRAFT """ draft = self.create_draft_standard_application(self.organisation) - self.submit_application(draft) # This will submit the application and set the good status to SUBMITTED + application = self.submit_application(draft) # This will submit the application and set the good status to SUBMITTED + good_on_application = application.goods.first() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.delete(url, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(GoodOnApplication.objects.filter(application=draft).count(), 0) - self.assertEqual(self.good_on_application.good.status, GoodStatus.DRAFT) + self.assertEqual(good_on_application.good.status, GoodStatus.DRAFT) def test_remove_a_good_from_draft_success_when_good_is_verified(self): """ @@ -50,19 +51,21 @@ def test_remove_a_good_from_draft_success_when_good_is_verified(self): And the good status is not changed """ draft = self.create_draft_standard_application(self.organisation) - self.good_on_application.good.status = GoodStatus.VERIFIED - self.good_on_application.good.save() + application = self.submit_application(draft) + good_on_application = application.goods.first() + good_on_application.good.status = GoodStatus.VERIFIED + good_on_application.good.save() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.delete(url, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(GoodOnApplication.objects.filter(application=draft).count(), 0) - self.assertEqual(self.good_on_application.good.status, GoodStatus.VERIFIED) + self.assertEqual(good_on_application.good.status, GoodStatus.VERIFIED) def test_remove_a_good_from_application_success_when_good_is_on_multiple_applications( self, @@ -126,22 +129,26 @@ def test_remove_a_good_that_does_not_exist_from_draft(self): def test_remove_a_good_from_draft_as_gov_user_failure(self): draft = self.create_draft_standard_application(self.organisation) + application = self.submit_application(draft, self.exporter_user) + good_on_application = application.goods.first() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.delete(url, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(GoodOnApplication.objects.filter(application=draft).count(), 1) + self.assertEqual(GoodOnApplication.objects.filter(application=application).count(), 1) def test_remove_goods_from_application_not_in_users_organisation_failure(self): - self.create_draft_standard_application(self.organisation) + draft = self.create_draft_standard_application(self.organisation) + application = self.submit_application(draft) + good_on_application = application.goods.first() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) other_organisation, _ = self.create_organisation_with_exporter_user() @@ -160,9 +167,10 @@ def test_delete_good_from_application_in_an_editable_status_success(self, editab application = self.create_draft_standard_application(self.organisation) application.status = get_case_status_by_status(editable_status) application.save() + good_on_application = application.goods.first() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.delete(url, **self.exporter_headers) @@ -175,9 +183,10 @@ def test_delete_good_from_application_in_read_only_status_failure(self, read_onl application = self.create_draft_standard_application(self.organisation) application.status = get_case_status_by_status(read_only_status) application.save() + good_on_application = application.goods.first() url = reverse( "applications:good_on_application", - kwargs={"obj_pk": self.good_on_application.id}, + kwargs={"obj_pk": good_on_application.id}, ) response = self.client.delete(url, **self.exporter_headers) diff --git a/api/applications/tests/test_retrieve_good_on_applications.py b/api/applications/tests/test_retrieve_good_on_applications.py index c86b0a7785..fa09803c2b 100644 --- a/api/applications/tests/test_retrieve_good_on_applications.py +++ b/api/applications/tests/test_retrieve_good_on_applications.py @@ -6,8 +6,13 @@ class RetrieveGoodsTests(DataTestClient): + def setUp(self): + super().setUp() + draft = self.create_draft_standard_application(self.organisation) + self.application = self.submit_application(draft) + self.good_on_application = self.application.goods.first() + def test_exporter_retrieve_a_good_on_application(self): - self.create_draft_standard_application(self.organisation) self.good_on_application.good.status = GoodStatus.VERIFIED self.good_on_application.good.save() @@ -26,7 +31,6 @@ def test_exporter_retrieve_a_good_on_application(self): ) def test_caseworker_retrieve_a_good_on_application(self): - self.create_draft_standard_application(self.organisation) self.good_on_application.good.status = GoodStatus.VERIFIED self.good_on_application.good.save() diff --git a/api/applications/tests/test_third_parties.py b/api/applications/tests/test_third_parties.py index 814c97f968..838387fefc 100644 --- a/api/applications/tests/test_third_parties.py +++ b/api/applications/tests/test_third_parties.py @@ -105,7 +105,7 @@ def test_unsuccessful_add_third_party(self): self.assertEqual(response_data, errors) def test_get_third_parties(self): - third_party = self.draft.third_parties.all().first().party + third_party = self.draft.third_parties.first().party response = self.client.get(f"{self.url}?type={PartyType.THIRD_PARTY}", **self.exporter_headers) parties = response.json()["third_parties"] @@ -118,7 +118,8 @@ def test_get_third_parties(self): self.assertEqual(party["country"]["name"], str(third_party.country.name)) self.assertEqual(party["website"], str(third_party.website)) self.assertEqual(party["type"], str(third_party.type)) - self.assertEqual(party["organisation"], str(third_party.organisation.id)) + if third_party.organisation: + self.assertEqual(party["organisation"], str(third_party.organisation.id)) self.assertEqual(party["sub_type"]["key"], str(third_party.sub_type)) self.assertEqual(party["role"]["key"], str(third_party.role)) diff --git a/api/cases/tests/test_case_search.py b/api/cases/tests/test_case_search.py index a2914cee32..bdbc823a31 100644 --- a/api/cases/tests/test_case_search.py +++ b/api/cases/tests/test_case_search.py @@ -141,12 +141,11 @@ def test_get_cases_returns_all_cases_with_end_user_and_ultimate_end_user_data(se cases_with_users = [case for case in response_data["cases"] if case["end_users"]] # All the non clc cases should have users. self.assertEqual(len(cases_with_users), 3) - end_user = {"name": "End User", "type": "end_user"} - ult_end_user = {"name": "Ult End User", "type": "ultimate_end_user"} + # The helper function that creates application uses factories to add parties to application + # so their names will be different hence only check necessary party types are present or not for case in cases_with_users: - self.assertIn(end_user, case["end_users"]) - self.assertIn(ult_end_user, case["end_users"]) + self.assertEqual(sorted([user["type"] for user in case["end_users"]]), ["end_user", "ultimate_end_user"]) def test_get_app_type_cases(self): """ diff --git a/api/cases/tests/test_get_case.py b/api/cases/tests/test_get_case.py index ea1c280699..7b11d28b6b 100644 --- a/api/cases/tests/test_get_case.py +++ b/api/cases/tests/test_get_case.py @@ -79,6 +79,7 @@ def test_case_returns_expected_third_party(self): response_data = response.json() self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response_data["case"]["data"]["third_parties"]), 1) self._assert_party( self.standard_application.third_parties.last().party, @@ -92,7 +93,8 @@ def _assert_party(self, expected, actual): self.assertEqual(str(expected.country.name), actual["country"]["name"]) self.assertEqual(str(expected.website), actual["website"]) self.assertEqual(str(expected.type), actual["type"]) - self.assertEqual(str(expected.organisation.id), actual["organisation"]) + if expected.organisation: + self.assertEqual(str(expected.organisation.id), actual["organisation"]) sub_type = actual["sub_type"] # sub_type is not always a dict. diff --git a/api/cases/tests/test_good_precedents_view.py b/api/cases/tests/test_good_precedents_view.py index f203e0a6da..a91ee5eac8 100644 --- a/api/cases/tests/test_good_precedents_view.py +++ b/api/cases/tests/test_good_precedents_view.py @@ -108,6 +108,13 @@ def test_get_with_matching_precedents(self): json = response.json() + # The helper function that creates application uses factories to add parties to application + # so their destinations will be different hence extract expected values instead of using + # fixed values in expected_data + expected_destinations = sorted( + [party_on_application.party.country.name for party_on_application in self.application.parties.all()] + ) + wassenaar_regime = RegimeEntry.objects.get(name="Wassenaar Arrangement") expected_data = { "count": len(CaseStatusEnum.precedent_statuses), @@ -124,7 +131,7 @@ def test_get_with_matching_precedents(self): "unit": None, "value": None, "control_list_entries": ["ML1a"], - "destinations": ["Great Britain"], + "destinations": expected_destinations, "wassenaar": True, "submitted_at": application.submitted_at.astimezone(timezone("UTC")).strftime( "%Y-%m-%dT%H:%M:%S.%fZ" diff --git a/api/parties/tests/factories.py b/api/parties/tests/factories.py index 2e58f41414..fd6c59873b 100644 --- a/api/parties/tests/factories.py +++ b/api/parties/tests/factories.py @@ -11,6 +11,7 @@ class PartyFactory(factory.django.DjangoModelFactory): country = factory.SubFactory(CountryFactory) sub_type = SubType.OTHER type = PartyType.CONSIGNEE + website = '' class Meta: model = Party diff --git a/test_helpers/clients.py b/test_helpers/clients.py index a21a62ceaf..8afd2a878e 100644 --- a/test_helpers/clients.py +++ b/test_helpers/clients.py @@ -30,6 +30,12 @@ GiftingClearanceApplication, F680ClearanceApplication, ) +from api.applications.tests.factories import ( + GoodOnApplicationFactory, + PartyFactory, + PartyOnApplicationFactory, + StandardApplicationFactory, +) from api.audit_trail import service as audit_trail_service from api.audit_trail.enums import AuditType from api.audit_trail.models import Audit @@ -41,7 +47,6 @@ Case, CaseDocument, CaseAssignment, - GoodCountryDecision, EcjuQuery, CaseType, Advice, @@ -56,12 +61,10 @@ from api.flags.tests.factories import FlagFactory from api.addresses.tests.factories import AddressFactoryGB from api.goods.enums import ( - GoodControlled, GoodPvGraded, PvGrading, ItemCategory, MilitaryUse, - Component, FirearmGoodType, ) from api.goods.models import Good, GoodDocument, PvGradingDetails, FirearmGoodDetails @@ -80,6 +83,7 @@ from api.parties.enums import SubType, PartyType, PartyRole from api.parties.models import Party from api.parties.models import PartyDocument +from api.parties.tests.factories import ConsigneeFactory, EndUserFactory, ThirdPartyFactory, UltimateEndUserFactory from api.picklists.enums import PickListStatus, PicklistType from api.picklists.models import PicklistItem from api.queries.end_user_advisories.models import EndUserAdvisoryQuery @@ -100,7 +104,7 @@ from api.teams.models import Team from api.users.tests.factories import GovUserFactory from test_helpers import colours -from api.users.enums import UserStatuses, SystemUser, UserType +from api.users.enums import SystemUser, UserType from api.users.libraries.user_to_token import user_to_token from api.users.models import ExporterUser, UserOrganisationRelationship, BaseUser, GovUser, Role from api.workflow.flagging_rules_automation import apply_flagging_rules_to_case @@ -722,60 +726,34 @@ def create_draft_standard_application( if not user: user = UserOrganisationRelationship.objects.filter(organisation_id=organisation.id).first().user - application = StandardApplication( + application = StandardApplicationFactory( name=reference_name, - export_type=ApplicationExportType.PERMANENT, case_type_id=case_type_id, - have_you_been_informed=ApplicationExportLicenceOfficialType.YES, - reference_number_on_information_form="", - activity="Trade", - usage="Trade", organisation=organisation, status=get_case_status_by_status(CaseStatusEnum.DRAFT), - is_military_end_use_controls=False, - is_informed_wmd=False, - is_suspected_wmd=False, - is_eu_military=False, - is_compliant_limitations_eu=None, - intended_end_use="this is our intended end use", - is_shipped_waybill_or_lading=True, - is_mod_security_approved=False, - non_waybill_or_lading_route_details=None, - status_id="00000000-0000-0000-0000-000000000000", submitted_by=user, ) - application.save() - if add_a_good: - if num_products == 1: - # Add a good to the standard application - self.good_on_application = GoodOnApplication.objects.create( - good=good if good else GoodFactory(organisation=organisation, is_good_controlled=True), + if reuse_good: + good = GoodFactory(organisation=organisation, is_good_controlled=True) + + for _ in range(num_products): + GoodOnApplicationFactory( + good=good if reuse_good else GoodFactory(organisation=organisation, is_good_controlled=True), application=application, - quantity=10, + quantity=random.randint(1, 50), unit=Units.NAR, - value=500, + value=random.randint(100, 5000), ) - else: - if reuse_good: - good = GoodFactory(organisation=organisation, is_good_controlled=True) - for _ in range(num_products): - GoodOnApplication.objects.create( - good=good if reuse_good else GoodFactory(organisation=organisation, is_good_controlled=True), - application=application, - quantity=random.randint(1, 50), - unit=Units.NAR, - value=random.randint(100, 5000), - ) if parties: - self.create_party("End User", organisation, PartyType.END_USER, application) + PartyOnApplicationFactory(application=application, party=ConsigneeFactory()) + PartyOnApplicationFactory(application=application, party=EndUserFactory()) + PartyOnApplicationFactory(application=application, party=ThirdPartyFactory()) + if ultimate_end_users: - self.create_party("Ult End User", organisation, PartyType.ULTIMATE_END_USER, application) - self.create_party("Consignee", organisation, PartyType.CONSIGNEE, application) - self.create_party("Third party", organisation, PartyType.THIRD_PARTY, application) - # Set the application party documents + PartyOnApplicationFactory(application=application, party=UltimateEndUserFactory()) self.add_party_documents(application, safe_document) From 7ef5d5a931d98bae93b19d5926c10fac8e847cad Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Mon, 15 Jan 2024 10:47:24 +0000 Subject: [PATCH 03/21] Update licence creation helper function to use factory --- api/applications/tests/factories.py | 2 +- .../tests/test_application_status.py | 9 ++++--- .../tests/test_finalise_application.py | 5 ++-- .../tests/test_export_xml.py | 3 ++- .../tests/test_generate_document.py | 7 ++--- api/cases/tests/test_case_ecju_queries.py | 10 +++---- .../tests/test_final_advice_documents.py | 4 +-- api/cases/tests/test_grant_licence.py | 13 ++++----- api/cases/tests/test_notify.py | 4 +-- api/cases/tests/test_status.py | 3 ++- .../tests/test_compliance_creation.py | 6 ++--- api/data_workspace/tests/test_audit_views.py | 3 ++- .../tests/test_license_views.py | 4 +-- api/data_workspace/tests/test_serializers.py | 5 ++-- .../tests/test_context_generation.py | 19 +++++++------ api/licences/tests/factories.py | 27 ++++++++++++++++--- .../tests/test_api_to_hmrc_integration.py | 25 ++++++++--------- api/licences/tests/test_get_licence.py | 15 +++-------- .../tests/test_get_licence_reference_code.py | 3 ++- api/licences/tests/test_get_licences.py | 9 ++++--- api/licences/tests/test_service.py | 5 ++-- test_helpers/clients.py | 22 ++------------- 22 files changed, 104 insertions(+), 99 deletions(-) diff --git a/api/applications/tests/factories.py b/api/applications/tests/factories.py index 07c5bc6a9b..ece1c8c83c 100644 --- a/api/applications/tests/factories.py +++ b/api/applications/tests/factories.py @@ -60,7 +60,7 @@ class StandardApplicationFactory(factory.django.DjangoModelFactory): name = "Application Test Name" export_type = ApplicationExportType.PERMANENT case_type_id = CaseTypeEnum.SIEL.id - have_you_been_informed = (ApplicationExportLicenceOfficialType.YES,) + have_you_been_informed = ApplicationExportLicenceOfficialType.YES reference_number_on_information_form = "" activity = "Trade" usage = "Trade" diff --git a/api/applications/tests/test_application_status.py b/api/applications/tests/test_application_status.py index d1d8ecd189..fb52851a74 100644 --- a/api/applications/tests/test_application_status.py +++ b/api/applications/tests/test_application_status.py @@ -9,6 +9,7 @@ from api.core.constants import GovPermissions from api.cases.models import CaseAssignment from api.licences.enums import LicenceStatus +from api.licences.tests.factories import StandardLicenceFactory from api.teams.models import Team from api.users.models import UserOrganisationRelationship, Permission from api.staticdata.statuses.enums import CaseStatusEnum @@ -102,7 +103,7 @@ def test_gov_user_set_application_to_terminal_status_removes_case_from_queues_us ) if case_status == CaseStatusEnum.REVOKED: self.standard_application.licences.add( - self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) ) data = {"status": case_status} @@ -134,7 +135,7 @@ def test_gov_user_set_application_to_terminal_status_removes_case_from_queues_us ] ) def test_certain_case_statuses_changes_licence_status(self, case_status, licence_status): - licence = self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) data = {"status": case_status} response = self.client.put(self.url, data=data, **self.gov_headers) @@ -195,7 +196,7 @@ def test_exporter_set_application_status_failure(self, new_status): def test_exporter_set_application_status_surrendered_success(self): self.standard_application.status = get_case_status_by_status(CaseStatusEnum.FINALISED) self.standard_application.save() - self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) surrendered_status = get_case_status_by_status("surrendered") data = {"status": CaseStatusEnum.SURRENDERED} @@ -285,7 +286,7 @@ def test_gov_set_status_to_applicant_editing_failure(self): def test_gov_set_status_for_all_except_applicant_editing_and_finalised_success(self, case_status): if case_status == CaseStatusEnum.REVOKED: self.standard_application.licences.add( - self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) ) data = {"status": case_status} diff --git a/api/applications/tests/test_finalise_application.py b/api/applications/tests/test_finalise_application.py index fcf2662cab..8fb3c9b18c 100644 --- a/api/applications/tests/test_finalise_application.py +++ b/api/applications/tests/test_finalise_application.py @@ -21,6 +21,7 @@ from api.flags.tests.factories import FlagFactory from api.licences.enums import LicenceStatus from api.licences.models import Licence, GoodOnLicence +from api.licences.tests.factories import StandardLicenceFactory from lite_content.lite_api import strings from api.staticdata.statuses.models import CaseStatus from api.teams.enums import TeamIdEnum @@ -68,7 +69,7 @@ def test_approve_application_success(self): def test_approve_application_reissue_success(self): self._set_user_permission([GovPermissions.MANAGE_LICENCE_FINAL_ADVICE]) - existing_licence = self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + existing_licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) data = {"action": AdviceType.APPROVE, "duration": existing_licence.duration} data.update(self.post_date) @@ -89,7 +90,7 @@ def test_approve_application_reissue_success(self): def test_approve_application_override_draft_success(self): self._set_user_permission([GovPermissions.MANAGE_LICENCE_FINAL_ADVICE, GovPermissions.MANAGE_LICENCE_DURATION]) - existing_licence = self.create_licence(self.standard_application, status=LicenceStatus.DRAFT) + existing_licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.DRAFT) data = {"action": AdviceType.APPROVE, "duration": existing_licence.duration + 1} data.update(self.post_date) diff --git a/api/cases/enforcement_check/tests/test_export_xml.py b/api/cases/enforcement_check/tests/test_export_xml.py index 549a76f3b7..448ead577f 100644 --- a/api/cases/enforcement_check/tests/test_export_xml.py +++ b/api/cases/enforcement_check/tests/test_export_xml.py @@ -61,7 +61,8 @@ def test_export_xml_with_parties_success(self): stakeholder["SH_TYPE"], party.type.upper() if party.type != PartyType.THIRD_PARTY else "OTHER" ) self.assertEqual(stakeholder["COUNTRY"], party.country.name) - self.assertEqual(stakeholder["ORG_NAME"], party.organisation.name) + if party.organisation: + self.assertEqual(stakeholder["ORG_NAME"], party.organisation.name) self.assertEqual(stakeholder["PD_SURNAME"], party.name) self.assertEqual(stakeholder["ADDRESS1"], party.address) # Ensure the correct EnforcementCheckID object is added for the import xml process diff --git a/api/cases/generated_documents/tests/test_generate_document.py b/api/cases/generated_documents/tests/test_generate_document.py index 20b35af2f1..cadd780869 100644 --- a/api/cases/generated_documents/tests/test_generate_document.py +++ b/api/cases/generated_documents/tests/test_generate_document.py @@ -19,6 +19,7 @@ from api.letter_templates.helpers import generate_preview, DocumentPreviewError from api.cases.generated_documents.models import GeneratedCaseDocument from api.licences.enums import LicenceStatus +from api.licences.tests.factories import StandardLicenceFactory from api.staticdata.decisions.models import Decision from api.staticdata.statuses.enums import CaseStatusEnum from api.staticdata.statuses.models import CaseStatus @@ -151,7 +152,7 @@ def test_generate_decision_document_success( application = self.case licence = None if advice_type == AdviceType.APPROVE: - licence = self.create_licence(self.case, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=self.case, status=LicenceStatus.DRAFT) html_to_pdf_func.return_value = None upload_bytes_file_func.return_value = None @@ -220,7 +221,7 @@ def test_regenerate_decision_document_success( @mock.patch("api.cases.generated_documents.views.html_to_pdf") @mock.patch("api.cases.generated_documents.views.s3_operations.upload_bytes_file") def test_generate_licence_document_success(self, upload_bytes_file_func, html_to_pdf_func): - licence = self.create_licence(self.case, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=self.case, status=LicenceStatus.DRAFT) html_to_pdf_func.return_value = None upload_bytes_file_func.return_value = None self.data["visible_to_exporter"] = True @@ -262,7 +263,7 @@ def test_generate_licence_document_no_licence_failure(self, upload_bytes_file_fu @mock.patch("api.cases.generated_documents.views.html_to_pdf") @mock.patch("api.cases.generated_documents.views.s3_operations.upload_bytes_file") def test_generate_new_licence_document_success(self, upload_bytes_file_func, html_to_pdf_func): - licence = self.create_licence(self.case, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=self.case, status=LicenceStatus.DRAFT) self.create_generated_case_document( self.case, self.letter_template, advice_type=AdviceType.APPROVE, licence=licence, visible_to_exporter=False ) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index dfcb421a39..80e4e98428 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -6,14 +6,14 @@ from parameterized import parameterized from rest_framework import status -from api.applications.models import BaseApplication from api.audit_trail.enums import AuditType from api.audit_trail.models import Audit from api.audit_trail.serializers import AuditSerializer from api.cases.enums import ECJUQueryType from api.cases.models import EcjuQuery -from api.compliance.tests.factories import ComplianceSiteCaseFactory, ComplianceVisitCaseFactory +from api.compliance.tests.factories import ComplianceSiteCaseFactory from api.licences.enums import LicenceStatus +from api.licences.tests.factories import StandardLicenceFactory from api.picklists.enums import PicklistType from api.staticdata.statuses.enums import CaseStatusEnum from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status @@ -216,14 +216,14 @@ def setUp(self): status=get_case_status_by_status(CaseStatusEnum.OPEN), ) - self.licence_1 = self.create_licence( - self.create_open_application_case(self.organisation), status=LicenceStatus.ISSUED + self.licence_1 = StandardLicenceFactory( + case=self.create_open_application_case(self.organisation), status=LicenceStatus.ISSUED ) application = self.create_open_application_case(self.organisation) application.submitted_by = ExporterUserFactory() application.save() - self.licence_2 = self.create_licence(application, status=LicenceStatus.ISSUED) + self.licence_2 = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) self.data = {"question": "Test ECJU Query question?", "query_type": PicklistType.PRE_VISIT_QUESTIONNAIRE} @mock.patch("api.cases.views.views.notify.notify_exporter_ecju_query") diff --git a/api/cases/tests/test_final_advice_documents.py b/api/cases/tests/test_final_advice_documents.py index f0227c3b38..9cae2b4da4 100644 --- a/api/cases/tests/test_final_advice_documents.py +++ b/api/cases/tests/test_final_advice_documents.py @@ -5,7 +5,7 @@ from api.cases.enums import AdviceType, CaseTypeEnum, AdviceLevel from api.cases.tests.factories import GoodCountryDecisionFactory, FinalAdviceFactory from api.goodstype.tests.factories import GoodsTypeFactory -from api.licences.tests.factories import LicenceFactory +from api.licences.tests.factories import StandardLicenceFactory from api.staticdata.countries.models import Country from test_helpers.clients import DataTestClient from api.applications.tests.factories import GoodOnApplicationFactory, GoodFactory @@ -15,7 +15,7 @@ class AdviceDocumentsTests(DataTestClient): def setUp(self): super().setUp() self.case = self.create_standard_application_case(self.organisation) - self.licence = LicenceFactory(case=self.case) + self.licence = StandardLicenceFactory(case=self.case) self.template = self.create_letter_template(name="Template", case_types=[CaseTypeEnum.SIEL.id]) self.url = reverse("cases:final_advice_documents", kwargs={"pk": self.case.id}) diff --git a/api/cases/tests/test_grant_licence.py b/api/cases/tests/test_grant_licence.py index 03624e479f..e882c85fdd 100644 --- a/api/cases/tests/test_grant_licence.py +++ b/api/cases/tests/test_grant_licence.py @@ -9,6 +9,7 @@ from api.cases.generated_documents.models import GeneratedCaseDocument from api.core.constants import GovPermissions from api.core.exceptions import PermissionDeniedError +from api.licences.tests.factories import StandardLicenceFactory from lite_content.lite_api.strings import Cases from api.staticdata.decisions.models import Decision from api.staticdata.statuses.enums import CaseStatusEnum @@ -33,7 +34,7 @@ def setUp(self): @mock.patch("api.cases.generated_documents.models.GeneratedCaseDocument.send_exporter_notifications") def test_grant_standard_application_success(self, send_exporter_notifications_func, mock_notify): self.gov_user.role.permissions.set([GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name]) - licence = self.create_licence(self.standard_case, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=self.standard_case, status=LicenceStatus.DRAFT) self.create_generated_case_document( self.standard_case, self.template, advice_type=AdviceType.APPROVE, licence=licence ) @@ -63,7 +64,7 @@ def test_grant_standard_application_success(self, send_exporter_notifications_fu def test_grant_standard_application_wrong_permission_failure(self): self.gov_user.role.permissions.set([GovPermissions.MANAGE_CLEARANCE_FINAL_ADVICE.name]) - self.create_licence(self.standard_case, status=LicenceStatus.DRAFT) + StandardLicenceFactory(case=self.standard_case, status=LicenceStatus.DRAFT) self.create_generated_case_document(self.standard_case, self.template, advice_type=AdviceType.APPROVE) response = self.client.put(self.url, data={}, **self.gov_headers) @@ -73,7 +74,7 @@ def test_grant_standard_application_wrong_permission_failure(self): def test_missing_advice_document_failure(self): self.gov_user.role.permissions.set([GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name]) - self.create_licence(self.standard_case, status=LicenceStatus.DRAFT) + StandardLicenceFactory(case=self.standard_case, status=LicenceStatus.DRAFT) response = self.client.put(self.url, data={}, **self.gov_headers) @@ -89,7 +90,7 @@ def test_grant_clearance_success(self, send_exporter_notifications_func, mock_no self.url = reverse("cases:finalise", kwargs={"pk": clearance_case.id}) self.gov_user.role.permissions.set([GovPermissions.MANAGE_CLEARANCE_FINAL_ADVICE.name]) - licence = self.create_licence(clearance_case, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=clearance_case, status=LicenceStatus.DRAFT) self.create_generated_case_document( clearance_case, self.template, advice_type=AdviceType.APPROVE, licence=licence ) @@ -120,7 +121,7 @@ def test_grant_clearance_wrong_permission_failure(self): self.url = reverse("cases:finalise", kwargs={"pk": clearance_case.id}) self.gov_user.role.permissions.set([GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name]) - self.create_licence(clearance_case, status=LicenceStatus.DRAFT) + StandardLicenceFactory(case=clearance_case, status=LicenceStatus.DRAFT) self.create_generated_case_document(clearance_case, self.template, advice_type=AdviceType.APPROVE) response = self.client.put(self.url, data={}, **self.gov_headers) @@ -146,7 +147,7 @@ def test_grant_standard_application_licence_and_revoke( self.gov_user.role.permissions.set( [GovPermissions.MANAGE_LICENCE_FINAL_ADVICE.name, GovPermissions.REOPEN_CLOSED_CASES.name] ) - licence = self.create_licence(self.standard_case, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=self.standard_case, status=LicenceStatus.DRAFT) self.create_generated_case_document( self.standard_case, self.template, advice_type=AdviceType.APPROVE, licence=licence ) diff --git a/api/cases/tests/test_notify.py b/api/cases/tests/test_notify.py index 59a4faa72e..976cfd7dcb 100644 --- a/api/cases/tests/test_notify.py +++ b/api/cases/tests/test_notify.py @@ -9,7 +9,7 @@ notify_exporter_inform_letter, notify_exporter_appeal_acknowledgement, ) -from api.licences.tests.factories import LicenceFactory +from api.licences.tests.factories import StandardLicenceFactory from api.users.tests.factories import ExporterUserFactory from gov_notify.enums import TemplateType from gov_notify.payloads import ( @@ -28,7 +28,7 @@ class NotifyTests(DataTestClient): def setUp(self): super().setUp() self.case = self.create_standard_application_case(self.organisation) - self.licence = LicenceFactory(case=self.case) + self.licence = StandardLicenceFactory(case=self.case) @mock.patch("api.cases.notify.send_email") def test_notify_licence_issued(self, mock_send_email): diff --git a/api/cases/tests/test_status.py b/api/cases/tests/test_status.py index c95be12247..eab5c88af4 100644 --- a/api/cases/tests/test_status.py +++ b/api/cases/tests/test_status.py @@ -8,6 +8,7 @@ from api.audit_trail.models import Audit from api.cases.models import CaseAssignment from api.licences.enums import LicenceStatus +from api.licences.tests.factories import StandardLicenceFactory from api.staticdata.statuses.enums import CaseStatusEnum from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status from test_helpers.clients import DataTestClient @@ -49,7 +50,7 @@ def test_optional_note(self): ] ) def test_certain_case_statuses_changes_licence_status(self, case_status, licence_status): - licence = self.create_licence(self.case, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=self.case, status=LicenceStatus.ISSUED) data = {"status": case_status} response = self.client.patch(self.url, data=data, **self.gov_headers) diff --git a/api/compliance/tests/test_compliance_creation.py b/api/compliance/tests/test_compliance_creation.py index 7432f50a5a..e821b54ba0 100644 --- a/api/compliance/tests/test_compliance_creation.py +++ b/api/compliance/tests/test_compliance_creation.py @@ -9,7 +9,7 @@ from api.compliance.helpers import generate_compliance_site_case from api.compliance.models import ComplianceSiteCase from api.goods.tests.factories import GoodFactory -from api.licences.tests.factories import LicenceFactory, GoodOnLicenceFactory +from api.licences.tests.factories import StandardLicenceFactory, GoodOnLicenceFactory from api.open_general_licences.tests.factories import OpenGeneralLicenceCaseFactory, OpenGeneralLicenceFactory from api.organisations.tests.factories import SiteFactory from test_helpers.clients import DataTestClient @@ -72,7 +72,7 @@ def tests_siel_good_control_code(self, control_code, exists): ) GoodOnLicenceFactory( good=GoodOnApplicationFactory(application=case, good=good), - licence=LicenceFactory(case=case), + licence=StandardLicenceFactory(case=case), quantity=100, value=1, ) @@ -92,7 +92,7 @@ def tests_siel_no_compliance_feature_flag_off(self): ) GoodOnLicenceFactory( good=GoodOnApplicationFactory(application=case, good=good), - licence=LicenceFactory(case=case), + licence=StandardLicenceFactory(case=case), quantity=100, value=1, ) diff --git a/api/data_workspace/tests/test_audit_views.py b/api/data_workspace/tests/test_audit_views.py index aae75353d8..40cd9520db 100644 --- a/api/data_workspace/tests/test_audit_views.py +++ b/api/data_workspace/tests/test_audit_views.py @@ -4,6 +4,7 @@ from api.audit_trail.enums import AuditType from api.licences.models import LicenceStatus +from api.licences.tests.factories import StandardLicenceFactory from api.staticdata.statuses.enums import CaseStatusEnum from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status from test_helpers.clients import DataTestClient @@ -96,7 +97,7 @@ def setUp(self): super().setUp() self.url = reverse("data_workspace:dw-audit-licence-updated-status-list") case = self.create_standard_application_case(self.organisation, "Test Application") - licence = self.create_licence(case, LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=case, status=LicenceStatus.ISSUED) def test_audit_updated_status(self): expected_fields = ("created_at", "user", "case", "licence", "status") diff --git a/api/data_workspace/tests/test_license_views.py b/api/data_workspace/tests/test_license_views.py index 7324a7c043..0111078fa0 100644 --- a/api/data_workspace/tests/test_license_views.py +++ b/api/data_workspace/tests/test_license_views.py @@ -11,7 +11,7 @@ from api.cases.tests.factories import FinalAdviceFactory from api.cases.enums import AdviceType from api.goods.tests.factories import GoodFactory -from api.licences.tests.factories import LicenceFactory, GoodOnLicenceFactory +from api.licences.tests.factories import StandardLicenceFactory, GoodOnLicenceFactory from test_helpers.clients import DataTestClient @@ -33,7 +33,7 @@ def setUp(self): ) GoodOnLicenceFactory( good=GoodOnApplicationFactory(application=case, good=good), - licence=LicenceFactory(case=case), + licence=StandardLicenceFactory(case=case), quantity=100, value=1, ) diff --git a/api/data_workspace/tests/test_serializers.py b/api/data_workspace/tests/test_serializers.py index 6e95fbb0fb..842a292703 100644 --- a/api/data_workspace/tests/test_serializers.py +++ b/api/data_workspace/tests/test_serializers.py @@ -9,8 +9,7 @@ LicenceWithoutGoodsSerializer, ) from api.cases.tests.factories import EcjuQueryFactory, CaseAssignmentFactory -from api.licences.tests.factories import LicenceFactory -from api.staticdata.statuses.models import CaseStatus +from api.licences.tests.factories import StandardLicenceFactory def test_EcjuQuerySerializer(db): @@ -55,7 +54,7 @@ def test_AuditUpdatedLicenceStatusSerializer(db): def test_LicenceWithoutGoodsSerializer(db): - licence = LicenceFactory() + licence = StandardLicenceFactory() serialized = LicenceWithoutGoodsSerializer(licence) expected_fields = { "id", diff --git a/api/letter_templates/tests/test_context_generation.py b/api/letter_templates/tests/test_context_generation.py index 139697de73..58c3611378 100644 --- a/api/letter_templates/tests/test_context_generation.py +++ b/api/letter_templates/tests/test_context_generation.py @@ -15,7 +15,7 @@ from api.applications.models import ExternalLocationOnApplication, CountryOnApplication, GoodOnApplication from api.applications.tests.factories import GoodOnApplicationFactory from api.cases.enums import AdviceLevel, AdviceType, CaseTypeEnum -from api.cases.models import Advice +from api.licences.tests.factories import StandardLicenceFactory from api.letter_templates.context_generator import EcjuQuerySerializer from api.cases.tests.factories import GoodCountryDecisionFactory, FinalAdviceFactory from api.compliance.enums import ComplianceVisitTypes, ComplianceRiskValues @@ -31,7 +31,6 @@ MilitaryUse, Component, ItemCategory, - FirearmGoodType, GoodControlled, GoodPvGraded, ) @@ -443,7 +442,7 @@ def test_generate_context_with_goods(self): self.gov_user, application, "good", AdviceType.APPROVE, AdviceLevel.FINAL, good=product.good ) - licence = self.create_licence(application, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for product in application.goods.all(): GoodOnLicenceFactory( good=product, @@ -482,7 +481,7 @@ def test_generate_context_with_goods_proviso_included(self): advice_text="proviso", ) - licence = self.create_licence(application, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for product in application.goods.all(): GoodOnLicenceFactory( good=product, @@ -525,7 +524,7 @@ def test_generate_context_with_goods_proviso_included_and_multiple_goa_for_singl advice_text="proviso", ) - licence = self.create_licence(application, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for product in application.goods.all(): GoodOnLicenceFactory( good=product, @@ -630,7 +629,7 @@ def test_goods_context_approve_has_quantity_and_value(self): good=good, ) - licence = self.create_licence(case, status=LicenceStatus.ISSUED, start_date=date(2023, 10, 5)) + licence = StandardLicenceFactory(case=case, status=LicenceStatus.ISSUED) good_on_licence = GoodOnLicenceFactory(good=good_on_application, quantity=3, value=420, licence=licence) context = get_document_context(case) @@ -662,7 +661,7 @@ def test_goods_context_proviso_has_quantity_and_value(self): good=good, ) - licence = self.create_licence(case, status=LicenceStatus.ISSUED, start_date=date(2023, 10, 5)) + licence = StandardLicenceFactory(case=case, status=LicenceStatus.ISSUED) good_on_licence = GoodOnLicenceFactory(good=good_on_application, quantity=3, value=420, licence=licence) context = get_document_context(case) @@ -743,7 +742,7 @@ def test_generate_context_with_goods_types(self): def test_generate_context_with_licence(self, start_date): case = self.create_standard_application_case(self.organisation, user=self.exporter_user) - licence = self.create_licence(case, status=LicenceStatus.ISSUED, start_date=start_date) + licence = StandardLicenceFactory(case=case, status=LicenceStatus.ISSUED, start_date=start_date) good_on_licence = GoodOnLicenceFactory( good=case.goods.first(), quantity=10, usage=20, value=30, licence=licence ) @@ -983,7 +982,7 @@ def test_generate_context_with_compliance_visit_details(self): application = self.create_open_application_case(self.organisation) - licence = self.create_licence(application, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) olr = OpenLicenceReturnsFactory(organisation=self.organisation) @@ -1018,7 +1017,7 @@ def test_generate_context_with_compliance_site_details(self): application = self.create_open_application_case(self.organisation) - self.create_licence(application, status=LicenceStatus.ISSUED) + StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) olr = OpenLicenceReturnsFactory(organisation=self.organisation) diff --git a/api/licences/tests/factories.py b/api/licences/tests/factories.py index b1092ff23c..20a701c4eb 100644 --- a/api/licences/tests/factories.py +++ b/api/licences/tests/factories.py @@ -1,14 +1,35 @@ import factory from django.utils import timezone +from api.applications.enums import DefaultDuration from api.applications.tests.factories import GoodOnApplicationFactory, StandardApplicationFactory +from api.cases.enums import AdviceType +from api.licences.helpers import get_licence_reference_code from api.licences.models import Licence, GoodOnLicence +from api.staticdata.decisions.models import Decision -class LicenceFactory(factory.django.DjangoModelFactory): +class StandardLicenceFactory(factory.django.DjangoModelFactory): case = factory.SubFactory(StandardApplicationFactory) start_date = timezone.now().date() - duration = 24 + duration = DefaultDuration.PERMANENT_STANDARD.value + hmrc_integration_sent_at = None + + @factory.post_generation + def reference_code(self, create, extracted, **kwargs): + if not create: + return + + self.reference_code = get_licence_reference_code(extracted or self.case.reference_code) + + # https://factoryboy.readthedocs.io/en/latest/recipes.html#simple-many-to-many-relationship + @factory.post_generation + def decisions(self, create, extracted, **kwargs): + if not create: + return + + decisions = extracted or [Decision.objects.get(name=AdviceType.APPROVE)] + self.decisions.add(*decisions) class Meta: model = Licence @@ -16,7 +37,7 @@ class Meta: class GoodOnLicenceFactory(factory.django.DjangoModelFactory): good = factory.SubFactory(GoodOnApplicationFactory) - licence = factory.SubFactory(LicenceFactory) + licence = factory.SubFactory(StandardLicenceFactory) class Meta: model = GoodOnLicence diff --git a/api/licences/tests/test_api_to_hmrc_integration.py b/api/licences/tests/test_api_to_hmrc_integration.py index c5d5fa7946..879bc60e9f 100644 --- a/api/licences/tests/test_api_to_hmrc_integration.py +++ b/api/licences/tests/test_api_to_hmrc_integration.py @@ -24,6 +24,7 @@ ) from api.licences.models import Licence, GoodOnLicence from api.licences.serializers.hmrc_integration import HMRCIntegrationLicenceSerializer +from api.licences.tests.factories import StandardLicenceFactory from api.licences.celery_tasks import ( send_licence_details_to_lite_hmrc, schedule_licence_details_to_lite_hmrc, @@ -76,7 +77,7 @@ def test_standard_application(self, status): action = licence_status_to_hmrc_integration_action.get(status) standard_application = self.create_standard_application_case(self.organisation) self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - standard_licence = self.create_licence(standard_application, status=status) + standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( good=good_on_application, @@ -87,7 +88,7 @@ def test_standard_application(self, status): ) old_licence = None if action == HMRCIntegrationActionEnum.UPDATE: - old_licence = self.create_licence(standard_application, status=LicenceStatus.CANCELLED) + old_licence = StandardLicenceFactory(case=standard_application, status=LicenceStatus.CANCELLED) standard_application.licences.add(old_licence) data = HMRCIntegrationLicenceSerializer(standard_licence).data @@ -115,7 +116,7 @@ def test_standard_application_no_trading_country_code(self, status): end_user.country = trade_country end_user.save() self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - standard_licence = self.create_licence(standard_application, status=status) + standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( good=good_on_application, @@ -126,7 +127,7 @@ def test_standard_application_no_trading_country_code(self, status): ) old_licence = None if action == HMRCIntegrationActionEnum.UPDATE: - old_licence = self.create_licence(standard_application, status=LicenceStatus.CANCELLED) + old_licence = StandardLicenceFactory(case=standard_application, status=LicenceStatus.CANCELLED) standard_application.licences.add(old_licence) data = HMRCIntegrationLicenceSerializer(standard_licence).data @@ -158,7 +159,7 @@ def test_standard_application_translates_to_trading_country_code(self, status): end_user.country = trade_country end_user.save() self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - standard_licence = self.create_licence(standard_application, status=status) + standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( good=good_on_application, @@ -169,7 +170,7 @@ def test_standard_application_translates_to_trading_country_code(self, status): ) old_licence = None if action == HMRCIntegrationActionEnum.UPDATE: - old_licence = self.create_licence(standard_application, status=LicenceStatus.CANCELLED) + old_licence = StandardLicenceFactory(case=standard_application, status=LicenceStatus.CANCELLED) standard_application.licences.add(old_licence) data = HMRCIntegrationLicenceSerializer(standard_licence).data @@ -200,7 +201,7 @@ def test_standard_application_strips_territory_code(self, status): end_user.country = trade_country end_user.save() self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - standard_licence = self.create_licence(standard_application, status=status) + standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( good=good_on_application, @@ -211,7 +212,7 @@ def test_standard_application_strips_territory_code(self, status): ) old_licence = None if action == HMRCIntegrationActionEnum.UPDATE: - old_licence = self.create_licence(standard_application, status=LicenceStatus.CANCELLED) + old_licence = StandardLicenceFactory(case=standard_application, status=LicenceStatus.CANCELLED) standard_application.licences.add(old_licence) data = HMRCIntegrationLicenceSerializer(standard_licence).data @@ -341,7 +342,7 @@ def setUp(self): self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) status = LicenceStatus.ISSUED self.hmrc_integration_status = licence_status_to_hmrc_integration_action.get(status) - self.standard_licence = self.create_licence(self.standard_application, status=status) + self.standard_licence = StandardLicenceFactory(case=self.standard_application, status=status) @mock.patch("api.licences.libraries.hmrc_integration_operations.post") @mock.patch("api.licences.libraries.hmrc_integration_operations.HMRCIntegrationLicenceSerializer") @@ -391,7 +392,7 @@ def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - self.standard_licence = self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + self.standard_licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) @override_settings(LITE_HMRC_INTEGRATION_ENABLED=True) @mock.patch("api.licences.celery_tasks.schedule_licence_details_to_lite_hmrc") @@ -467,7 +468,7 @@ def setUp(self): self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) status = LicenceStatus.ISSUED self.hmrc_integration_status = licence_status_to_hmrc_integration_action.get(status) - self.standard_licence = self.create_licence(self.standard_application, status=status) + self.standard_licence = StandardLicenceFactory(case=self.standard_application, status=status) for product in self.standard_application.goods.all(): GoodOnLicenceFactory( good=product, @@ -684,7 +685,7 @@ def test_reinstate_licence_success(self, request): @override_settings(LITE_HMRC_INTEGRATION_ENABLED=True) def _create_licence_for_submission(self, create_application_case_callback): application = create_application_case_callback(self.organisation) - licence = self.create_licence(application, status=LicenceStatus.DRAFT) + licence = StandardLicenceFactory(case=application, status=LicenceStatus.DRAFT) for product in application.goods.all(): GoodOnLicence.objects.create(good=product, licence=licence, quantity=product.quantity, value=product.value) self.create_advice(self.gov_user, application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) diff --git a/api/licences/tests/test_get_licence.py b/api/licences/tests/test_get_licence.py index db95ab8f40..fecfddd847 100644 --- a/api/licences/tests/test_get_licence.py +++ b/api/licences/tests/test_get_licence.py @@ -1,7 +1,4 @@ -from decimal import Decimal - from django.urls import reverse -from django.utils import timezone from rest_framework import status from api.applications.tests.factories import StandardApplicationFactory, GoodOnApplicationFactory @@ -9,7 +6,7 @@ from api.cases.tests.factories import FinalAdviceFactory from api.goods.tests.factories import GoodFactory from api.licences.enums import LicenceStatus -from api.licences.tests.factories import LicenceFactory, GoodOnLicenceFactory +from api.licences.tests.factories import StandardLicenceFactory, GoodOnLicenceFactory from api.staticdata.units.enums import Units from test_helpers.clients import DataTestClient @@ -17,12 +14,7 @@ class GetLicenceTests(DataTestClient): def test_get_licence_gov_view(self): application = StandardApplicationFactory() - licence = LicenceFactory( - case=application, - start_date=timezone.now().date(), - status=LicenceStatus.ISSUED, - duration=100, - ) + licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) self.url = reverse("cases:licences", kwargs={"pk": application.id}) good = GoodFactory(organisation=application.organisation) @@ -92,7 +84,8 @@ def test_get_licence_exporter_view(self): ] ) licences = { - application: self.create_licence(application, status=LicenceStatus.ISSUED) for application in applications + application: StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) + for application in applications } documents = { application: self.create_generated_case_document(application, template, licence=licences[application]) diff --git a/api/licences/tests/test_get_licence_reference_code.py b/api/licences/tests/test_get_licence_reference_code.py index c4a3c92d60..e47bca1309 100644 --- a/api/licences/tests/test_get_licence_reference_code.py +++ b/api/licences/tests/test_get_licence_reference_code.py @@ -2,6 +2,7 @@ from api.licences.enums import LicenceStatus from api.licences.helpers import get_licence_reference_code +from api.licences.tests.factories import StandardLicenceFactory from test_helpers.clients import DataTestClient @@ -24,6 +25,6 @@ def test_get_amended_licence_reference_code(self): Check all amended licences get suffix /A -> /Z """ for letter in ascii_uppercase: - self.create_licence(self.application, status=LicenceStatus.ISSUED) + StandardLicenceFactory(case=self.application, status=LicenceStatus.ISSUED) reference_code = get_licence_reference_code(self.application.reference_code) self.assertEqual(reference_code, self.application.reference_code + "/" + letter) diff --git a/api/licences/tests/test_get_licences.py b/api/licences/tests/test_get_licences.py index aef02ff6d4..5a1469b9a2 100644 --- a/api/licences/tests/test_get_licences.py +++ b/api/licences/tests/test_get_licences.py @@ -9,6 +9,7 @@ from api.licences.models import Licence from api.licences.tests.factories import GoodOnLicenceFactory from api.licences.views.main import LicenceType +from api.licences.tests.factories import StandardLicenceFactory from api.open_general_licences.tests.factories import OpenGeneralLicenceCaseFactory, OpenGeneralLicenceFactory from api.staticdata.countries.models import Country from api.staticdata.statuses.enums import CaseStatusEnum @@ -50,7 +51,7 @@ def setUp(self): for application in self.applications ] self.licences = { - application: self.create_licence(application, status=LicenceStatus.ISSUED) + application: StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for application in self.applications } @@ -134,7 +135,7 @@ def test_get_clearance_licences_only(self): self.assertTrue(str(self.licences[self.gifting_application].id) in ids) def test_draft_licences_are_not_included(self): - draft_licence = self.create_licence(self.standard_application, status=LicenceStatus.DRAFT) + draft_licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.DRAFT) response = self.client.get(self.url, **self.exporter_headers) response_data = response.json()["results"] @@ -164,7 +165,9 @@ def setUp(self): self.url = reverse("licences:licences") self.standard_application = self.create_standard_application_case(self.organisation) self.open_application = self.create_open_application_case(self.organisation) - self.standard_application_licence = self.create_licence(self.standard_application, status=LicenceStatus.ISSUED) + self.standard_application_licence = StandardLicenceFactory( + case=self.standard_application, status=LicenceStatus.ISSUED + ) self.open_application_licence = self.create_licence(self.open_application, status=LicenceStatus.ISSUED) def test_only_my_organisations_licences_are_returned(self): diff --git a/api/licences/tests/test_service.py b/api/licences/tests/test_service.py index 1b08060b6e..258343819c 100644 --- a/api/licences/tests/test_service.py +++ b/api/licences/tests/test_service.py @@ -11,7 +11,7 @@ from api.staticdata.control_list_entries.helpers import get_control_list_entry from api.licences.enums import LicenceStatus from api.licences.service import get_case_licences -from api.licences.tests.factories import LicenceFactory, GoodOnLicenceFactory +from api.licences.tests.factories import StandardLicenceFactory, GoodOnLicenceFactory from test_helpers.clients import DataTestClient @@ -19,9 +19,8 @@ class GetCaseLicenceTests(DataTestClient): def setUp(self): super().setUp() self.application = StandardApplicationFactory() - self.licence = LicenceFactory( + self.licence = StandardLicenceFactory( case=self.application, - start_date=timezone.now().date(), status=LicenceStatus.REVOKED, duration=100, reference_code="reference", diff --git a/test_helpers/clients.py b/test_helpers/clients.py index 8afd2a878e..9ad331a70e 100644 --- a/test_helpers/clients.py +++ b/test_helpers/clients.py @@ -76,7 +76,7 @@ from api.letter_templates.models import LetterTemplate from api.licences.enums import LicenceStatus from api.licences.helpers import get_licence_reference_code -from api.licences.models import GoodOnLicence, Licence +from api.licences.tests.factories import StandardLicenceFactory from api.organisations.enums import OrganisationType from api.organisations.models import Organisation, ExternalLocation from api.organisations.tests.factories import OrganisationFactory, SiteFactory @@ -92,7 +92,6 @@ from api.staticdata.control_list_entries.models import ControlListEntry from api.staticdata.countries.helpers import get_country from api.staticdata.countries.models import Country -from api.staticdata.decisions.models import Decision from api.staticdata.f680_clearance_types.models import F680ClearanceType from api.staticdata.letter_layouts.models import LetterLayout from api.staticdata.management.commands import seedall @@ -1083,28 +1082,11 @@ def create_ecju_query(self, case, question="ECJU question", gov_user=None, creat def create_licence( application: Case, status: LicenceStatus, - reference_code=None, - decisions=None, - hmrc_integration_sent_at=None, - start_date=None, ): - if not decisions: - decisions = [Decision.objects.get(name=AdviceType.APPROVE)] - if not reference_code: - reference_code = get_licence_reference_code(application.reference_code) - if not start_date: - start_date = django.utils.timezone.now().date() - - licence = Licence.objects.create( + return StandardLicenceFactory( case=application, - reference_code=reference_code, - start_date=start_date, - duration=get_default_duration(application), status=status, - hmrc_integration_sent_at=hmrc_integration_sent_at, ) - licence.decisions.set(decisions) - return licence def create_routing_rule( self, team_id, queue_id, tier, status_id, additional_rules: list, is_python_criteria=False, active=True From 35c67a0d2a7d786592f89fd48f5640b339d8d70f Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Mon, 15 Jan 2024 15:55:42 +0000 Subject: [PATCH 04/21] Update Good creation helper to use factories --- api/applications/tests/test_adding_goods.py | 18 ++-- api/applications/tests/test_documents.py | 2 - .../tests/test_finalise_application.py | 21 +++-- api/applications/tests/test_goods.py | 4 +- api/applications/tests/test_removing_goods.py | 4 +- api/cases/tests/libraries/test_advice.py | 9 +- api/cases/tests/test_case_search.py | 4 +- api/cases/tests/test_good_precedents_view.py | 3 +- api/data_workspace/tests/test_good_views.py | 3 +- api/goods/tests/factories.py | 53 +++++++---- api/goods/tests/test_document_reasons.py | 5 +- api/goods/tests/test_edit.py | 84 +++++++---------- api/goods/tests/test_flags.py | 5 +- api/goods/tests/test_goods_documents.py | 2 +- api/goods/tests/test_views.py | 12 +-- api/parties/tests/factories.py | 2 +- .../goods_query/tests/test_goods_queries.py | 21 +++-- api/search/tests/test_signals.py | 3 +- .../management/commands/seedapplications.py | 5 +- test_helpers/clients.py | 90 ++----------------- 20 files changed, 143 insertions(+), 207 deletions(-) diff --git a/api/applications/tests/test_adding_goods.py b/api/applications/tests/test_adding_goods.py index e906256b6e..a8f30fc52d 100644 --- a/api/applications/tests/test_adding_goods.py +++ b/api/applications/tests/test_adding_goods.py @@ -8,9 +8,9 @@ from api.audit_trail.models import Audit from api.cases.enums import CaseTypeEnum from api.goods.enums import ItemType -from lite_content.lite_api import strings -from api.staticdata.missing_document_reasons.enums import GoodMissingDocumentReasons +from api.goods.tests.factories import GoodFactory from api.staticdata.units.enums import Units +from lite_content.lite_api import strings from test_helpers.clients import DataTestClient from test_helpers.decorators import none_param_tester @@ -19,7 +19,7 @@ class AddingGoodsOnApplicationTests(DataTestClient): def setUp(self): super().setUp() self.draft = self.create_draft_standard_application(self.organisation) - self.good = self.create_good("A good", self.organisation) + self.good = GoodFactory(name="A good", organisation=self.organisation) def test_add_a_good_to_a_draft(self): self.create_good_document( @@ -56,7 +56,7 @@ def test_add_a_good_to_a_draft(self): def test_user_cannot_add_another_organisations_good_to_a_draft(self): good_name = "A good" organisation_2, _ = self.create_organisation_with_exporter_user() - good = self.create_good(good_name, organisation_2) + good = GoodFactory(name=good_name, organisation=organisation_2) self.create_good_document( good, user=self.exporter_user, @@ -242,7 +242,7 @@ def test_adding_good_validate_only(self, data): self.assertEqual(response.status_code, data["response"]) def test_adding_good_without_document_or_reason_success(self): - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) good.is_document_available = False good.save() data = { @@ -259,7 +259,7 @@ def test_adding_good_without_document_or_reason_success(self): self.assertEqual(response.status_code, status.HTTP_201_CREATED) def test_adding_good_with_reason_official_sensitive_success(self): - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) good.is_document_sensitive = True good.save() data = { @@ -281,7 +281,7 @@ def test_add_a_good_to_a_draft_failure(self, quantity, unit, value, is_good_inco Ensure all params have to be sent otherwise fail """ self.create_draft_standard_application(self.organisation) - self.create_good("A good", self.organisation) + GoodFactory(organisation=self.organisation) self.create_good_document( self.good, user=self.exporter_user, @@ -308,7 +308,7 @@ class AddingGoodsOnApplicationFirearmsTests(DataTestClient): def setUp(self): super().setUp() self.draft = self.create_draft_standard_application(self.organisation) - self.good = self.create_good("A good", self.organisation, create_firearm_details=True) + self.good = GoodFactory(organisation=self.organisation) @parameterized.expand( [ @@ -410,7 +410,7 @@ class AddingGoodsOnApplicationExhibitionTests(DataTestClient): def setUp(self): super().setUp() self.draft = self.create_mod_clearance_application(self.organisation, CaseTypeEnum.EXHIBITION) - self.good = self.create_good("A good", self.organisation) + self.good = GoodFactory(organisation=self.organisation) def test_add_a_good_to_a_exhibition_draft_choice(self): self.create_good_document( diff --git a/api/applications/tests/test_documents.py b/api/applications/tests/test_documents.py index 41e696020d..d604fb729e 100644 --- a/api/applications/tests/test_documents.py +++ b/api/applications/tests/test_documents.py @@ -2,7 +2,6 @@ from django.urls import reverse -from api.applications.serializers import good as serializers from api.audit_trail.enums import AuditType from api.audit_trail.models import Audit from test_helpers.clients import DataTestClient @@ -15,7 +14,6 @@ class ApplicationDocumentViewTests(DataTestClient): def test_audit_trail_create(self, upload_bytes_func, mock_virus_scan, mock_s3_operations_get_object): mock_virus_scan.return_value = False application = self.create_draft_standard_application(organisation=self.organisation, user=self.exporter_user) - good = self.create_good("A good", self.organisation) url = reverse("applications:application_documents", kwargs={"pk": application.pk}) diff --git a/api/applications/tests/test_finalise_application.py b/api/applications/tests/test_finalise_application.py index 8fb3c9b18c..321d15a599 100644 --- a/api/applications/tests/test_finalise_application.py +++ b/api/applications/tests/test_finalise_application.py @@ -9,6 +9,7 @@ from api.applications.enums import LicenceDuration from api.applications.views.helpers.advice import CounterSignatureIncompleteError from api.applications.libraries.licence import get_default_duration +from api.applications.tests.factories import GoodOnApplicationFactory from api.audit_trail.enums import AuditType from api.audit_trail.models import Audit from api.audit_trail.serializers import AuditSerializer @@ -19,11 +20,13 @@ from api.flags.enums import FlagLevels from api.flags.models import Flag from api.flags.tests.factories import FlagFactory +from api.goods.tests.factories import GoodFactory from api.licences.enums import LicenceStatus from api.licences.models import Licence, GoodOnLicence from api.licences.tests.factories import StandardLicenceFactory from lite_content.lite_api import strings from api.staticdata.statuses.models import CaseStatus +from api.staticdata.units.enums import Units from api.teams.enums import TeamIdEnum from api.teams.models import Team from test_helpers.clients import DataTestClient @@ -254,8 +257,12 @@ def test_reissue_after_product_assessment_changes(self): # Add few more products for i in range(3): - good_on_app = self.create_good_on_application( - self.standard_application, self.create_good(f"product{i+2}", self.organisation) + good_on_app = GoodOnApplicationFactory( + application=self.standard_application, + good=GoodFactory(name=f"product{i+2}", organisation=self.organisation), + quantity=10, + unit=Units.NAR, + value=500, ) self.create_advice( self.gov_user, @@ -655,8 +662,9 @@ def test_get_approved_goods_success(self): ) # Refuse a second good - second_good_on_app = self.create_good_on_application( - self.standard_application, self.create_good("a thing", self.organisation) + second_good_on_app = GoodOnApplicationFactory( + application=self.standard_application, + good=GoodFactory(organisation=self.organisation), ) self.create_advice( self.gov_user, @@ -668,8 +676,9 @@ def test_get_approved_goods_success(self): ) # NLR a third good - third_good_on_app = self.create_good_on_application( - self.standard_application, self.create_good("a thing", self.organisation) + third_good_on_app = GoodOnApplicationFactory( + application=self.standard_application, + good=GoodFactory(organisation=self.organisation), ) self.create_advice( self.gov_user, diff --git a/api/applications/tests/test_goods.py b/api/applications/tests/test_goods.py index 728cd23dc4..4a6daa537e 100644 --- a/api/applications/tests/test_goods.py +++ b/api/applications/tests/test_goods.py @@ -1,9 +1,9 @@ from django.urls import reverse from unittest import mock -from api.applications.serializers import good as serializers from api.audit_trail.enums import AuditType from api.audit_trail.models import Audit +from api.goods.tests.factories import GoodFactory from test_helpers.clients import DataTestClient @@ -14,7 +14,7 @@ class ApplicationGoodOnApplicationDocumentViewTests(DataTestClient): def test_audit_trail_create(self, upload_bytes_func, mock_virus_scan, mock_s3_operations_get_object): mock_virus_scan.return_value = False application = self.create_draft_standard_application(organisation=self.organisation, user=self.exporter_user) - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) url = reverse( "applications:application-goods-documents", diff --git a/api/applications/tests/test_removing_goods.py b/api/applications/tests/test_removing_goods.py index 21b5ef72e2..e56d2e5dd9 100644 --- a/api/applications/tests/test_removing_goods.py +++ b/api/applications/tests/test_removing_goods.py @@ -27,7 +27,9 @@ def test_remove_a_good_from_draft_success(self): And the good status is changed to DRAFT """ draft = self.create_draft_standard_application(self.organisation) - application = self.submit_application(draft) # This will submit the application and set the good status to SUBMITTED + application = self.submit_application( + draft + ) # This will submit the application and set the good status to SUBMITTED good_on_application = application.goods.first() url = reverse( diff --git a/api/cases/tests/libraries/test_advice.py b/api/cases/tests/libraries/test_advice.py index 16d0413a47..835c6b5cd2 100644 --- a/api/cases/tests/libraries/test_advice.py +++ b/api/cases/tests/libraries/test_advice.py @@ -1,6 +1,7 @@ from api.cases.enums import AdviceType from api.cases.libraries import advice from api.cases.tests import factories +from api.goods.tests.factories import GoodFactory from api.users.tests.factories import GovUserFactory from api.teams.tests.factories import TeamFactory @@ -11,7 +12,7 @@ class TestAdviceHelpers(DataTestClient): def test_group_advice_single_approve(self): # given there is a case case = self.create_standard_application_case(self.organisation) - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) # and the case has a single approve factories.UserAdviceFactory.create(type=AdviceType.APPROVE, case=case, user=self.gov_user, good=good) @@ -35,7 +36,7 @@ def test_group_advice_multiple_approve_and_proviso(self): # given there is a case case = self.create_standard_application_case(self.organisation) - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) # and the case has an approve factories.UserAdviceFactory.create(type=AdviceType.APPROVE, case=case, user=self.gov_user, good=good) @@ -60,7 +61,7 @@ def test_group_advice_multiple_approve_and_reject(self): # given there is a case case = self.create_standard_application_case(self.organisation) - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) # and the case has an approve factories.UserAdviceFactory.create(type=AdviceType.APPROVE, case=case, user=self.gov_user, good=good) # and the case has an reject @@ -86,7 +87,7 @@ def test_users_from_two_teams_can_give_advice(self): # given there is a case case = self.create_standard_application_case(self.organisation) - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) # and the case has collated advice from one team factories.UserAdviceFactory.create(type=AdviceType.APPROVE, case=case, user=self.gov_user, good=good) diff --git a/api/cases/tests/test_case_search.py b/api/cases/tests/test_case_search.py index bdbc823a31..52b81e6a31 100644 --- a/api/cases/tests/test_case_search.py +++ b/api/cases/tests/test_case_search.py @@ -794,7 +794,7 @@ def test_get_cases_filter_by_country_no_match(self, countries_on_application, fi def test_get_cases_filter_by_includes_refusal_recommendation(self): case = self.create_standard_application_case(self.organisation) team_ogd = Team.objects.filter(is_ogd=True).first() - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) self.gov_user.team = team_ogd self.gov_user.save() @@ -810,7 +810,7 @@ def test_get_cases_filter_by_includes_refusal_recommendation(self): def test_get_cases_filter_by_includes_refusal_recommendation_not_met(self): case = self.create_standard_application_case(self.organisation) - good = self.create_good("A good", self.organisation) + good = GoodFactory(organisation=self.organisation) factories.UserAdviceFactory(user=self.gov_user, case=case, good=good, type=AdviceType.REFUSE) diff --git a/api/cases/tests/test_good_precedents_view.py b/api/cases/tests/test_good_precedents_view.py index a91ee5eac8..7b39af6759 100644 --- a/api/cases/tests/test_good_precedents_view.py +++ b/api/cases/tests/test_good_precedents_view.py @@ -6,6 +6,7 @@ from api.flags.enums import SystemFlags from parameterized import parameterized from api.goods.enums import GoodStatus +from api.goods.tests.factories import GoodFactory from api.staticdata.control_list_entries.models import ControlListEntry from api.staticdata.regimes.models import RegimeEntry from api.staticdata.report_summaries.models import ReportSummarySubject, ReportSummaryPrefix @@ -21,7 +22,7 @@ def setUp(self): super().setUp() # Create a common good - self.good = self.create_good("A good", self.organisation) + self.good = GoodFactory(organisation=self.organisation) self.good.flags.add(SystemFlags.WASSENAAR) # Create an application self.application = self.create_draft_standard_application(self.organisation) diff --git a/api/data_workspace/tests/test_good_views.py b/api/data_workspace/tests/test_good_views.py index 63678956c7..cb4771dbb4 100644 --- a/api/data_workspace/tests/test_good_views.py +++ b/api/data_workspace/tests/test_good_views.py @@ -1,5 +1,6 @@ from django.test import override_settings from api.goods.models import GoodControlListEntry +from api.goods.tests.factories import GoodFactory from api.staticdata.control_list_entries.models import ControlListEntry from django.urls import reverse from rest_framework import status @@ -10,7 +11,7 @@ class DataWorkspaceTests(DataTestClient): def setUp(self): super().setUp() - self.good = DataTestClient.create_good(description="Test good", organisation=self.organisation) + self.good = GoodFactory(name="Test good", organisation=self.organisation) @override_settings(HAWK_AUTHENTICATION_ENABLED=False) def test_goods(self): diff --git a/api/goods/tests/factories.py b/api/goods/tests/factories.py index 10abd1c655..dc354154fa 100644 --- a/api/goods/tests/factories.py +++ b/api/goods/tests/factories.py @@ -4,6 +4,35 @@ from api.goods import models from api.goods.enums import ItemCategory, Component, MilitaryUse, FirearmGoodType, GoodPvGraded from api.staticdata.control_list_entries.helpers import get_control_list_entry +from api.staticdata.control_list_entries.models import ControlListEntry + + +class FirearmFactory(factory.django.DjangoModelFactory): + type = FirearmGoodType.AMMUNITION + category = [] + year_of_manufacture = 2019 + calibre = "5.56x45mm" + is_covered_by_firearm_act_section_one_two_or_five = "Yes" + section_certificate_number = "section certificate number?" + section_certificate_date_of_expiry = factory.LazyFunction(timezone.now().date) + serial_numbers_available = models.FirearmGoodDetails.SerialNumberAvailability.AVAILABLE + no_identification_markings_details = "" + + class Meta: + model = models.FirearmGoodDetails + + +class PvGradingDetailsFactory(factory.django.DjangoModelFactory): + grading = None + custom_grading = "Custom grading" + prefix = "Prefix" + suffix = "Suffix" + issuing_authority = "Government organisation" + reference = "reference" + date_of_issue = "2024-01-01" + + class Meta: + model = models.PvGradingDetails class GoodFactory(factory.django.DjangoModelFactory): @@ -13,6 +42,8 @@ class GoodFactory(factory.django.DjangoModelFactory): part_number = factory.Faker("ean13") organisation = None item_category = ItemCategory.GROUP2_FIREARMS + firearm_details = factory.SubFactory(FirearmFactory) + pv_grading_details = factory.SubFactory(PvGradingDetailsFactory) is_military_use = MilitaryUse.NO is_component = Component.NO is_pv_graded = GoodPvGraded.NO @@ -25,6 +56,7 @@ class GoodFactory(factory.django.DjangoModelFactory): report_summary_prefix = None report_summary_subject = None report_summary = None + comment = None class Meta: model = models.Good @@ -35,9 +67,11 @@ def control_list_entries(self, create, extracted, **kwargs): # Simple build, do nothing. return - control_list_entries = extracted or ["ML1a"] - for control_list_entry in control_list_entries: - self.control_list_entries.add(get_control_list_entry(control_list_entry)) + if self.is_good_controlled: + control_list_entries = extracted or ["ML1a"] + self.control_list_entries.add( + *list(ControlListEntry.objects.filter(rating__in=control_list_entries)), + ) @factory.post_generation def flags(self, create, extracted, **kwargs): @@ -46,16 +80,3 @@ def flags(self, create, extracted, **kwargs): return self.flags.set(extracted) - - -class FirearmFactory(factory.django.DjangoModelFactory): - type = FirearmGoodType.AMMUNITION - year_of_manufacture = 2019 - calibre = "5.56x45mm" - is_covered_by_firearm_act_section_one_two_or_five = True - section_certificate_number = "section certificate number?" - section_certificate_date_of_expiry = factory.LazyFunction(timezone.now().date) - serial_numbers_available = models.FirearmGoodDetails.SerialNumberAvailability.AVAILABLE - - class Meta: - model = models.FirearmGoodDetails diff --git a/api/goods/tests/test_document_reasons.py b/api/goods/tests/test_document_reasons.py index 9ba2e32138..9cd2e8ea06 100644 --- a/api/goods/tests/test_document_reasons.py +++ b/api/goods/tests/test_document_reasons.py @@ -4,15 +4,14 @@ from django.urls import reverse from rest_framework import status -from lite_content.lite_api import strings -from api.staticdata.missing_document_reasons.enums import GoodMissingDocumentReasons +from api.goods.tests.factories import GoodFactory from test_helpers.clients import DataTestClient class GoodDocumentAvaiabilityandSensitivityTests(DataTestClient): def setUp(self): super().setUp() - self.good = self.create_good("a good", self.organisation) + self.good = GoodFactory(organisation=self.organisation) self.document_availability_url = reverse("goods:good_document_availability", kwargs={"pk": self.good.id}) self.document_sensitivity_url = reverse("goods:good_document_sensitivity", kwargs={"pk": self.good.id}) diff --git a/api/goods/tests/test_edit.py b/api/goods/tests/test_edit.py index a5b2750f4a..5bea1f82f9 100644 --- a/api/goods/tests/test_edit.py +++ b/api/goods/tests/test_edit.py @@ -18,6 +18,7 @@ from api.goods.models import Good, PvGradingDetails from api.goods.tests.factories import GoodFactory from api.goods.views import GOOD_ON_APP_BAD_REPORT_SUMMARY_SUBJECT, GOOD_ON_APP_BAD_REPORT_SUMMARY_PREFIX +from api.goods.tests.factories import GoodFactory from api.staticdata.report_summaries.models import ReportSummaryPrefix, ReportSummarySubject from lite_content.lite_api import strings from api.staticdata.control_list_entries.helpers import get_control_list_entry @@ -28,7 +29,7 @@ class GoodsEditDraftGoodTests(DataTestClient): def setUp(self): super().setUp() - self.good = self.create_good(description="This is a good", organisation=self.organisation) + self.good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP1_COMPONENTS) self.url = reverse("goods:good", kwargs={"pk": str(self.good.id)}) self.edit_details_url = reverse("goods:good_details", kwargs={"pk": str(self.good.id)}) @@ -152,9 +153,9 @@ def test_edit_military_use_to_modified_and_details_set_success(self): self.assertEqual(Good.objects.all().count(), 1) def test_edit_military_use_to_selection_without_details_clears_the_field_success(self): - good = self.create_good( - "a good", - self.organisation, + good = GoodFactory( + organisation=self.organisation, + item_category=ItemCategory.GROUP1_MATERIALS, is_military_use=MilitaryUse.YES_MODIFIED, modified_military_use_details="modified details", ) @@ -190,8 +191,12 @@ def test_edit_component_to_yes_selection_with_details_success(self, component, d self.assertEqual(Good.objects.all().count(), 1) def test_edit_component_to_no_clears_details_field_success(self): - good = self.create_good( - "a good", self.organisation, is_component=Component.YES_MODIFIED, component_details="modified details" + good = GoodFactory( + organisation=self.organisation, + item_category=ItemCategory.GROUP1_COMPONENTS, + is_component=Component.YES_MODIFIED, + component_details="modified details", + firearm_details=None, ) request_data = {"is_component": Component.NO} @@ -236,8 +241,8 @@ def test_edit_information_security_also_edits_the_details(self, uses_information ] ) def test_edit_software_or_technology_details_success(self, category, details): - good = self.create_good( - "a good", self.organisation, item_category=category, software_or_technology_details="initial details" + good = GoodFactory( + organisation=self.organisation, item_category=category, software_or_technology_details="initial details" ) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = {"is_software_or_technology_step": True, "software_or_technology_details": details} @@ -251,11 +256,9 @@ def test_edit_software_or_technology_details_success(self, category, details): self.assertEqual(Good.objects.all().count(), 2) def test_cannot_edit_component_and_component_details_of_non_category_one_good_failure(self): - good = self.create_good( - "a good", - self.organisation, + good = GoodFactory( + organisation=self.organisation, item_category=ItemCategory.GROUP3_TECHNOLOGY, - software_or_technology_details="initial details", ) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { @@ -271,7 +274,7 @@ def test_cannot_edit_component_and_component_details_of_non_category_one_good_fa self.assertEqual(errors["non_field_errors"], [strings.Goods.CANNOT_SET_DETAILS_ERROR]) def test_cannot_edit_software_technology_details_non_category_three_good_failure(self): - good = self.create_good("a good", self.organisation, item_category=ItemCategory.GROUP1_PLATFORM) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP1_PLATFORM) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = {"software_or_technology_details": "some details"} @@ -282,9 +285,7 @@ def test_cannot_edit_software_technology_details_non_category_three_good_failure self.assertEqual(errors["non_field_errors"], [strings.Goods.CANNOT_SET_DETAILS_ERROR]) def test_edit_category_two_product_type_success(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = {"firearm_details": {"type": FirearmGoodType.FIREARMS}} @@ -299,9 +300,7 @@ def test_edit_category_two_product_type_success(self): self.assertEqual(Good.objects.all().count(), 2) def test_edit_category_two_product_category_success(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) expected = [FirearmCategory.NON_AUTOMATIC_SHOTGUN, FirearmCategory.NON_AUTOMATIC_RIM_FIRED_RIFLE] @@ -314,9 +313,7 @@ def test_edit_category_two_product_category_success(self): self.assertEqual(actual, expected) def test_update_firearm_type_invalidates_notapplicable_fields(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) firearm_details = good.firearm_details firearm_details.is_covered_by_firearm_act_section_one_two_or_five = "Unknown" firearm_details.is_covered_by_firearm_act_section_one_two_or_five_explanation = "Explanation" @@ -339,9 +336,7 @@ def test_update_firearm_type_invalidates_notapplicable_fields(self): self.assertEqual(Good.objects.all().count(), 2) def test_edit_category_two_calibre_and_year_of_manufacture_success(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = {"firearm_details": {"calibre": "1.0", "year_of_manufacture": "2019"}} @@ -357,9 +352,7 @@ def test_edit_category_two_calibre_and_year_of_manufacture_success(self): self.assertEqual(Good.objects.all().count(), 2) def test_edit_category_two_firearm_replica(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { @@ -383,9 +376,7 @@ def test_edit_category_two_firearm_replica(self): self.assertEqual(Good.objects.all().count(), 2) def test_edit_category_two_section_question_and_details_success(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) future_expiry_date = (now() + timedelta(days=365)).date().isoformat() @@ -410,9 +401,7 @@ def test_edit_category_two_section_question_and_details_success(self): self.assertEqual(Good.objects.all().count(), 2) def test_edit_category_two_section_question_and_no_certificate_number_failure(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { @@ -433,9 +422,11 @@ def test_edit_category_two_section_question_and_no_certificate_number_failure(se def test_edit_category_two_section_question_and_invalid_expiry_date_failure(self): """Test editing section of firearms question failure by providing an expiry date not in the future.""" - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) + good.firearm_details.is_covered_by_firearm_act_section_one_two_or_five = "No" + good.firearm_details.section_certificate_number = None + good.firearm_details.section_certificate_date_of_expiry = None + good.firearm_details.save() url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { @@ -462,9 +453,7 @@ def test_edit_category_two_section_question_and_invalid_expiry_date_failure(self self.assertEqual(errors["section_certificate_date_of_expiry"], [strings.Goods.FIREARM_GOOD_INVALID_EXPIRY_DATE]) def test_edit_category_two_identification_markings_details_success(self): - good = self.create_good( - "a good", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { @@ -482,9 +471,7 @@ def test_edit_category_two_identification_markings_details_success(self): self.assertEqual(good["firearm_details"]["no_identification_markings_details"], "") def test_edit_firearm_year_made_before_1938(self): - good = self.create_good( - "Rifle", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = {"firearm_details": {"is_made_before_1938": False}} @@ -495,9 +482,7 @@ def test_edit_firearm_year_made_before_1938(self): self.assertEqual(good["firearm_details"]["is_made_before_1938"], False) def test_edit_firearm_is_deactivated(self): - good = self.create_good( - "Rifle", self.organisation, item_category=ItemCategory.GROUP2_FIREARMS, create_firearm_details=True - ) + good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { @@ -596,11 +581,8 @@ class GoodsAttachingTests(DataTestClient): def setUp(self): super().setUp() - self.good = self.create_good( - "Rifle", - self.organisation, - item_category=ItemCategory.GROUP2_FIREARMS, - create_firearm_details=True, + self.good = GoodFactory( + name="Rifle", organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS ) self.url = reverse("goods:good_attaching", kwargs={"pk": str(self.good.id)}) diff --git a/api/goods/tests/test_flags.py b/api/goods/tests/test_flags.py index aa5ac79368..b4b723bf8c 100644 --- a/api/goods/tests/test_flags.py +++ b/api/goods/tests/test_flags.py @@ -4,6 +4,7 @@ from api.audit_trail.models import Audit from api.cases.models import Case +from api.goods.tests.factories import GoodFactory from test_helpers.clients import DataTestClient @@ -11,8 +12,8 @@ class GoodFlagsManagementTests(DataTestClient): def setUp(self): super().setUp() # Goods - self.good = self.create_good("a good", self.organisation) - self.good_2 = self.create_good("a second good", self.organisation) + self.good = GoodFactory(organisation=self.organisation) + self.good_2 = GoodFactory(organisation=self.organisation) # Teams self.other_team = self.create_team("Team") diff --git a/api/goods/tests/test_goods_documents.py b/api/goods/tests/test_goods_documents.py index bf7cfe0ec0..b9e70a7550 100644 --- a/api/goods/tests/test_goods_documents.py +++ b/api/goods/tests/test_goods_documents.py @@ -16,7 +16,7 @@ class GoodDocumentsTests(DataTestClient): def setUp(self): super().setUp() - self.good = self.create_good("this is a good", self.organisation) + self.good = GoodFactory(organisation=self.organisation) self.url = reverse("goods:documents", kwargs={"pk": self.good.id}) def test_can_view_all_documents_on_a_good(self): diff --git a/api/goods/tests/test_views.py b/api/goods/tests/test_views.py index 8f22c11b05..81a9b56451 100644 --- a/api/goods/tests/test_views.py +++ b/api/goods/tests/test_views.py @@ -36,20 +36,20 @@ def test_fail_view_other_organisations_goods_details(self): ) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - def test_view_good__query_filter_by_description(self): + def test_view_good__query_filter_by_name(self): org = self.organisation - self.create_good("thing1", org) - self.create_good("Thing2", org) - self.create_good("item3", org) + GoodFactory(name="thing1", organisation=org) + GoodFactory(name="Thing2", organisation=org) + GoodFactory(name="item3", organisation=org) - url = reverse("goods:goods") + "?description=thing" + url = reverse("goods:goods") + "?name=thing" response = self.client.get(url, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) response_data = response.json()["results"] self.assertEqual(len(response_data), 2) - url = reverse("goods:goods") + "?description=item" + url = reverse("goods:goods") + "?name=item" response = self.client.get(url, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) response_data = response.json()["results"] diff --git a/api/parties/tests/factories.py b/api/parties/tests/factories.py index fd6c59873b..c16dd2defe 100644 --- a/api/parties/tests/factories.py +++ b/api/parties/tests/factories.py @@ -11,7 +11,7 @@ class PartyFactory(factory.django.DjangoModelFactory): country = factory.SubFactory(CountryFactory) sub_type = SubType.OTHER type = PartyType.CONSIGNEE - website = '' + website = "" class Meta: model = Party diff --git a/api/queries/goods_query/tests/test_goods_queries.py b/api/queries/goods_query/tests/test_goods_queries.py index 3d2826109a..530e7e6952 100644 --- a/api/queries/goods_query/tests/test_goods_queries.py +++ b/api/queries/goods_query/tests/test_goods_queries.py @@ -1,17 +1,16 @@ from django.contrib.contenttypes.models import ContentType from django.urls import reverse -from parameterized import parameterized from rest_framework import status from api.audit_trail.models import Audit from api.audit_trail.enums import AuditType from api.cases.enums import CaseTypeEnum -from api.cases.models import CaseAssignment from api.core import constants from api.flags.enums import SystemFlags from api.flags.models import Flag from api.goods.enums import GoodStatus, GoodPvGraded, PvGrading from api.goods.models import Good +from api.goods.tests.factories import GoodFactory from api.users.tests.factories import GovUserFactory from lite_content.lite_api import strings from api.picklists.enums import PicklistType, PickListStatus @@ -22,7 +21,7 @@ from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status from api.staticdata.statuses.models import CaseStatus from test_helpers.clients import DataTestClient -from api.users.models import Role, GovUser +from api.users.models import Role class ControlListClassificationsQueryCreateTests(DataTestClient): @@ -208,8 +207,8 @@ def test_user_cannot_respond_to_clc_with_case_in_terminal_state(self): class PvGradingQueryCreateTests(DataTestClient): def test_given_an_unsure_pv_graded_good_exists_when_creating_pv_grading_query_then_201_created_is_returned(self): - pv_graded_good = self.create_good( - description="This is a good", + pv_graded_good = GoodFactory( + name="This is a good", organisation=self.organisation, is_good_controlled=False, is_pv_graded=GoodPvGraded.GRADING_REQUIRED, @@ -236,8 +235,8 @@ def test_given_an_unsure_pv_graded_good_exists_when_creating_pv_grading_query_th ) def test_given_a_pv_graded_good_exists_when_creating_pv_grading_query_then_400_bad_request_is_returned(self): - pv_graded_good = self.create_good( - description="This is a good", + pv_graded_good = GoodFactory( + name="This is a good", organisation=self.organisation, is_good_controlled=False, is_pv_graded=GoodPvGraded.YES, @@ -259,8 +258,8 @@ def test_given_a_pv_graded_good_exists_when_creating_pv_grading_query_then_400_b self.assertEqual(GoodsQuery.objects.count(), 0) def test_given_good_doesnt_require_pv_grading_when_creating_pv_grading_query_then_400_bad_request_is_returned(self): - pv_graded_good = self.create_good( - description="This is a good", + pv_graded_good = GoodFactory( + name="This is a good", organisation=self.organisation, is_good_controlled=False, is_pv_graded=GoodPvGraded.NO, @@ -298,8 +297,8 @@ def setUp(self): "Report Summary", self.team, PicklistType.REPORT_SUMMARY, PickListStatus.ACTIVE ) - self.pv_graded_and_controlled_good = self.create_good( - description="This is a good", + self.pv_graded_and_controlled_good = GoodFactory( + name="This is a good", organisation=self.organisation, is_good_controlled=None, is_pv_graded=GoodPvGraded.GRADING_REQUIRED, diff --git a/api/search/tests/test_signals.py b/api/search/tests/test_signals.py index 71ba78a339..7e0bef8894 100644 --- a/api/search/tests/test_signals.py +++ b/api/search/tests/test_signals.py @@ -4,6 +4,7 @@ from django.test import override_settings +from api.goods.tests.factories import GoodFactory from api.parties.models import PartyType from test_helpers.clients import DataTestClient @@ -58,7 +59,7 @@ def test_case(self, mock_task): @patch("api.search.signals.update_search_index") def test_good(self, mock_task): application = self.create_standard_application_case(self.organisation) - good = self.create_good("test good", self.organisation) + good = GoodFactory(organisation=self.organisation) good_on_app = self.create_good_on_application(application, good) application.goods.add(good_on_app) application.save() diff --git a/api/staticdata/management/commands/seedapplications.py b/api/staticdata/management/commands/seedapplications.py index e83e058bae..6a2e90a8ce 100644 --- a/api/staticdata/management/commands/seedapplications.py +++ b/api/staticdata/management/commands/seedapplications.py @@ -6,6 +6,7 @@ GoodOnApplication, ) from api.goods.enums import GoodPvGraded, GoodStatus +from api.goods.tests.factories import GoodFactory from api.goods.models import Good from api.organisations.enums import OrganisationType from api.organisations.models import Organisation @@ -111,8 +112,8 @@ def ensure_verified_goods_exist(cls, number_of_goods, organisation, tc=DataTestC goods_added = [ verify_good( - tc.create_good( - description=name, + GoodFactory( + name=name, organisation=organisation, is_good_controlled=random.choice([True, False, None]), control_list_entries=["ML1a"], diff --git a/test_helpers/clients.py b/test_helpers/clients.py index 9ad331a70e..6c1c949187 100644 --- a/test_helpers/clients.py +++ b/test_helpers/clients.py @@ -13,7 +13,7 @@ from rest_framework.test import APITestCase, URLPatternsTestCase, APIClient import pytest -from api.applications.enums import ApplicationExportType, ApplicationExportLicenceOfficialType +from api.applications.enums import ApplicationExportType from api.applications.libraries.edit_applications import set_case_flags_on_submitted_standard_or_open_application from api.applications.libraries.goods_on_applications import add_goods_flags_to_submitted_application from api.applications.libraries.licence import get_default_duration @@ -32,7 +32,6 @@ ) from api.applications.tests.factories import ( GoodOnApplicationFactory, - PartyFactory, PartyOnApplicationFactory, StandardApplicationFactory, ) @@ -69,7 +68,7 @@ ) from api.goods.models import Good, GoodDocument, PvGradingDetails, FirearmGoodDetails from api.applications.models import GoodOnApplicationInternalDocument -from api.goods.tests.factories import GoodFactory +from api.goods.tests.factories import FirearmFactory, GoodFactory, PvGradingDetailsFactory from api.goodstype.document.models import GoodsTypeDocument from api.goodstype.models import GoodsType from api.goodstype.tests.factories import GoodsTypeFactory @@ -479,85 +478,8 @@ def create_picklist_item(name, team: Team, picklist_type, status): picklist_item.save() return picklist_item - @staticmethod - def create_good( - description: str, - organisation: Organisation, - is_good_controlled: str = False, - control_list_entries: Optional[List[str]] = None, - is_pv_graded: str = GoodPvGraded.YES, - pv_grading_details: PvGradingDetails = None, - item_category=ItemCategory.GROUP1_DEVICE, - is_military_use=MilitaryUse.NO, - modified_military_use_details=None, - component_details=None, - is_component=None, - uses_information_security=True, - information_security_details=None, - software_or_technology_details=None, - create_firearm_details=False, - ) -> Good: - warnings.warn( - "create_good is a deprecated function. Use a GoodFactory instead", category=DeprecationWarning, stacklevel=2 - ) - - if is_pv_graded == GoodPvGraded.YES and not pv_grading_details: - pv_grading_details = PvGradingDetails.objects.create( - grading=None, - custom_grading="Custom Grading", - prefix="Prefix", - suffix="Suffix", - issuing_authority="Issuing Authority", - reference="ref123", - date_of_issue="2019-12-25", - ) - - firearm_details = None - if create_firearm_details: - firearm_details = FirearmGoodDetails.objects.create( - category=[], - type=FirearmGoodType.AMMUNITION, - calibre="0.5", - year_of_manufacture="1991", - is_covered_by_firearm_act_section_one_two_or_five="No", - section_certificate_number=None, - section_certificate_date_of_expiry=None, - serial_numbers_available=FirearmGoodDetails.SerialNumberAvailability.AVAILABLE, - no_identification_markings_details="", - ) - - good = Good( - description=description, - is_good_controlled=is_good_controlled, - part_number="123456", - organisation=organisation, - comment=None, - report_summary=None, - is_pv_graded=is_pv_graded, - pv_grading_details=pv_grading_details, - item_category=item_category, - is_military_use=is_military_use, - is_component=is_component, - uses_information_security=uses_information_security, - information_security_details=information_security_details, - modified_military_use_details=modified_military_use_details, - component_details=component_details, - software_or_technology_details=software_or_technology_details, - firearm_details=firearm_details, - ) - good.save() - - if good.is_good_controlled == True: - if not control_list_entries: - raise Exception("You need to set control list entries if the good is controlled") - - control_list_entries = ControlListEntry.objects.filter(rating__in=control_list_entries) - good.control_list_entries.set(control_list_entries) - - return good - def create_goods_query(self, description, organisation, clc_reason, pv_reason) -> GoodsQuery: - good = DataTestClient.create_good(description=description, organisation=organisation) + good = GoodFactory(name=description, organisation=organisation) goods_query = GoodsQuery.objects.create( clc_raised_reasons=clc_reason, @@ -575,7 +497,7 @@ def create_goods_query(self, description, organisation, clc_reason, pv_reason) - return goods_query def create_clc_query(self, description, organisation) -> GoodsQuery: - good = DataTestClient.create_good(description=description, organisation=organisation) + good = GoodFactory(name=description, organisation=organisation) clc_query = GoodsQuery.objects.create( clc_raised_reasons="this is a test text", @@ -592,9 +514,7 @@ def create_clc_query(self, description, organisation) -> GoodsQuery: @staticmethod def create_pv_grading_query(description, organisation) -> GoodsQuery: - good = DataTestClient.create_good( - description=description, organisation=organisation, is_pv_graded=GoodPvGraded.GRADING_REQUIRED - ) + good = GoodFactory(name=description, organisation=organisation, is_pv_graded=GoodPvGraded.GRADING_REQUIRED) pv_grading_query = GoodsQuery.objects.create( clc_raised_reasons=None, From 87c40c6f75abab4cb7567e3bed60e1b8c594b787 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Tue, 16 Jan 2024 12:22:32 +0000 Subject: [PATCH 05/21] Use Advice factories to create advice instances in tests Replaces the use of helper function with Advice factories. --- .../tests/test_finalise_application.py | 81 ++++------- api/applications/tests/test_notify.py | 21 +-- api/cases/tests/factories.py | 33 +++-- api/cases/tests/test_case_search.py | 19 +-- api/cases/tests/test_delete_user_advice.py | 14 +- api/cases/tests/test_edit_advice.py | 48 ++++--- .../tests/test_final_advice_documents.py | 27 ++-- api/cases/tests/test_finalise_advice.py | 10 +- api/cases/tests/test_grant_licence.py | 5 +- api/data_workspace/tests/test_advice_views.py | 19 +-- api/goods/tests/factories.py | 8 ++ .../tests/test_context_generation.py | 126 +++++------------- .../tests/test_api_to_hmrc_integration.py | 26 ++-- .../tests/test_hmrc_integration_to_api.py | 4 +- 14 files changed, 177 insertions(+), 264 deletions(-) diff --git a/api/applications/tests/test_finalise_application.py b/api/applications/tests/test_finalise_application.py index 321d15a599..8e37413b6b 100644 --- a/api/applications/tests/test_finalise_application.py +++ b/api/applications/tests/test_finalise_application.py @@ -15,7 +15,7 @@ from api.audit_trail.serializers import AuditSerializer from api.cases.enums import AdviceType, CaseTypeEnum, AdviceLevel, CountersignOrder from api.cases.models import Advice, Case -from api.cases.tests.factories import CountersignAdviceFactory +from api.cases.tests.factories import CountersignAdviceFactory, FinalAdviceFactory, UserAdviceFactory from api.core.constants import GovPermissions from api.flags.enums import FlagLevels from api.flags.models import Flag @@ -246,14 +246,8 @@ def test_reissue_after_product_assessment_changes(self): changes are picked up and only products that require licence are associated to the licence. """ # Approve existing product on the application - self.create_advice( - self.gov_user, - self.standard_application, - "good", - AdviceType.APPROVE, - AdviceLevel.FINAL, - advice_text="approve", - ) + good = self.standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=good) # Add few more products for i in range(3): @@ -264,14 +258,7 @@ def test_reissue_after_product_assessment_changes(self): unit=Units.NAR, value=500, ) - self.create_advice( - self.gov_user, - self.standard_application, - "", - AdviceType.APPROVE, - AdviceLevel.FINAL, - good=good_on_app.good, - ) + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=good_on_app.good) # get the finalise form response = self.client.get(self.url, **self.gov_headers) @@ -357,21 +344,20 @@ def test_finalise_application_failure_with_countersigning_flags_but_no_countersi def _setup_advice_for_application(self, application, advice_type, advice_level): # Create Advice objects for all entities for good_on_application in application.goods.all(): - self.create_advice( - self.gov_user, - application, - "", - advice_type, - advice_level, + UserAdviceFactory( + user=self.gov_user, + case=application, + type=advice_type, + level=advice_level, good=good_on_application.good, ) for party_on_application in application.parties.all(): - self.create_advice( - self.gov_user, - application, - party_on_application.party.type, - advice_type, - advice_level, + UserAdviceFactory( + user=self.gov_user, + case=application, + type=advice_type, + level=advice_level, + end_user=party_on_application.party, ) @parameterized.expand( @@ -652,26 +638,18 @@ def setUp(self): def test_get_approved_goods_success(self): # Approve the existing good advice_text = "looks good to me" - self.create_advice( - self.gov_user, - self.standard_application, - "good", - AdviceType.APPROVE, - AdviceLevel.FINAL, - advice_text=advice_text, - ) + good = self.standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=good, text=advice_text) # Refuse a second good second_good_on_app = GoodOnApplicationFactory( application=self.standard_application, good=GoodFactory(organisation=self.organisation), ) - self.create_advice( - self.gov_user, - self.standard_application, - "", - AdviceType.REFUSE, - AdviceLevel.FINAL, + FinalAdviceFactory( + user=self.gov_user, + case=self.standard_application, + type=AdviceType.REFUSE, good=second_good_on_app.good, ) @@ -680,12 +658,10 @@ def test_get_approved_goods_success(self): application=self.standard_application, good=GoodFactory(organisation=self.organisation), ) - self.create_advice( - self.gov_user, - self.standard_application, - "", - AdviceType.NO_LICENCE_REQUIRED, - AdviceLevel.FINAL, + FinalAdviceFactory( + user=self.gov_user, + case=self.standard_application, + type=AdviceType.NO_LICENCE_REQUIRED, good=third_good_on_app.good, ) @@ -704,8 +680,9 @@ def test_get_approved_goods_success(self): def test_get_proviso_goods_success(self): # Proviso the existing good - advice = self.create_advice( - self.gov_user, self.standard_application, "good", AdviceType.PROVISO, AdviceLevel.FINAL + good = self.standard_application.goods.first().good + advice = FinalAdviceFactory( + user=self.gov_user, case=self.standard_application, good=good, type=AdviceType.PROVISO ) response = self.client.get(self.url, **self.gov_headers) @@ -733,7 +710,7 @@ def setUp(self): "month": self.date.month, "day": self.date.day, } - self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=self.good_on_application.good) def test_approve_success(self): good_value = 1 diff --git a/api/applications/tests/test_notify.py b/api/applications/tests/test_notify.py index eea0c20ed1..e1ef7c9537 100644 --- a/api/applications/tests/test_notify.py +++ b/api/applications/tests/test_notify.py @@ -2,7 +2,7 @@ from api.applications import notify from api.cases.enums import AdviceLevel, AdviceType, CountersignOrder -from api.cases.tests.factories import CountersignAdviceFactory +from api.cases.tests.factories import CountersignAdviceFactory, FinalAdviceFactory from api.staticdata.statuses.enums import CaseStatusEnum from api.staticdata.statuses.models import CaseStatus from api.teams.enums import TeamIdEnum @@ -43,12 +43,12 @@ def test_notify_countersign_case_returned(self, mock_send_email): self.case.status = CaseStatus.objects.get(status=CaseStatusEnum.UNDER_FINAL_REVIEW) self.case._previous_status = CaseStatus.objects.get(status=CaseStatusEnum.FINAL_REVIEW_COUNTERSIGN) self.case.save() - advice = self.create_advice( - self.gov_user, - self.case, - "good", - AdviceType.REFUSE, - AdviceLevel.FINAL, + good_on_application = self.case.goods.first() + advice = FinalAdviceFactory( + user=self.gov_user, + case=self.case, + type=AdviceType.REFUSE, + good=good_on_application.good, ) countersign_advice = CountersignAdviceFactory( order=CountersignOrder.FIRST_COUNTERSIGN, @@ -84,13 +84,6 @@ def test_notify_countersign_case_returned_no_advice(self, mock_send_email): self.case.status = CaseStatus.objects.get(status=CaseStatusEnum.UNDER_FINAL_REVIEW) self.case._previous_status = CaseStatus.objects.get(status=CaseStatusEnum.FINAL_REVIEW_COUNTERSIGN) self.case.save() - advice = self.create_advice( - self.gov_user, - self.case, - "good", - AdviceType.REFUSE, - AdviceLevel.FINAL, - ) notify.notify_caseworker_countersign_return(self.case) mock_send_email.assert_not_called() diff --git a/api/cases/tests/factories.py b/api/cases/tests/factories.py index 36461fc8ee..306962ea6e 100644 --- a/api/cases/tests/factories.py +++ b/api/cases/tests/factories.py @@ -20,34 +20,34 @@ from api.users.tests.factories import GovUserFactory -class UserAdviceFactory(factory.django.DjangoModelFactory): +class BaseAdviceFactory(factory.django.DjangoModelFactory): text = factory.Faker("word") note = factory.Faker("word") type = AdviceType.APPROVE - level = AdviceLevel.USER + + @factory.post_generation + def denial_reasons(self, create, extracted, **kwargs): + if not create: + return + + if self.type == AdviceType.REFUSE: + denial_reasons = extracted or ["1a", "1b", "1c"] + self.denial_reasons.set(denial_reasons) class Meta: model = Advice -class TeamAdviceFactory(factory.django.DjangoModelFactory): - text = factory.Faker("word") - note = factory.Faker("word") - type = AdviceType.APPROVE - level = AdviceLevel.TEAM +class UserAdviceFactory(BaseAdviceFactory): + level = AdviceLevel.USER - class Meta: - model = Advice +class TeamAdviceFactory(BaseAdviceFactory): + level = AdviceLevel.TEAM -class FinalAdviceFactory(factory.django.DjangoModelFactory): - text = factory.Faker("word") - note = factory.Faker("word") - type = AdviceType.APPROVE - level = AdviceLevel.FINAL - class Meta: - model = Advice +class FinalAdviceFactory(BaseAdviceFactory): + level = AdviceLevel.FINAL class CountersignAdviceFactory(factory.django.DjangoModelFactory): @@ -98,7 +98,6 @@ class Meta: class CaseAssignmentFactory(factory.django.DjangoModelFactory): - case = factory.SubFactory(CaseFactory) user = factory.SubFactory(GovUserFactory) queue = factory.SubFactory(QueueFactory) diff --git a/api/cases/tests/test_case_search.py b/api/cases/tests/test_case_search.py index 52b81e6a31..f7f1e6a2c9 100644 --- a/api/cases/tests/test_case_search.py +++ b/api/cases/tests/test_case_search.py @@ -2,7 +2,6 @@ from django.urls import reverse from django import utils as django_utils -from api.staticdata.statuses.factories import CaseStatusFactory from parameterized import parameterized from rest_framework import status @@ -16,12 +15,12 @@ SanctionMatchFactory, PartyOnApplicationFactory, ) -from api.cases.enums import AdviceLevel, AdviceType, CaseTypeEnum +from api.cases.enums import AdviceType, CaseTypeEnum from api.cases.models import Case, CaseAssignment, EcjuQuery, CaseType from api.flags.models import Flag from api.goods.tests.factories import GoodFactory from api.picklists.enums import PicklistType -from api.cases.tests.factories import CaseSIELFactory, FinalAdviceFactory +from api.cases.tests.factories import FinalAdviceFactory from api.queues.constants import ( UPDATED_CASES_QUEUE_ID, SYSTEM_QUEUES, @@ -33,7 +32,7 @@ from api.staticdata.regimes.models import RegimeEntry from api.staticdata.report_summaries.tests.factories import ReportSummaryPrefixFactory, ReportSummarySubjectFactory from api.staticdata.statuses.libraries.get_case_status import get_case_status_by_status -from api.staticdata.statuses.models import CaseStatus, CaseSubStatus +from api.staticdata.statuses.models import CaseSubStatus from api.users.tests.factories import GovUserFactory from test_helpers.clients import DataTestClient from api.users.enums import UserStatuses @@ -1056,18 +1055,14 @@ def _create_data(self): def test_api_success(self): self._create_data() - self.advice = self.create_advice( - self.gov_user, self.case, "good", AdviceType.APPROVE, AdviceLevel.FINAL, good=self.good - ) + self.advice = FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) self.gov_user2 = self.create_gov_user( team=self.create_team(name="other_team"), email="new_user2@digital.trade.gov.uk" ) - self.group_advice = self.create_advice( - self.gov_user2, self.case, "good", AdviceType.REFUSE, AdviceLevel.FINAL, good=self.good - ) - self.create_advice( - self.gov_user2, self.group_advice.case, "good", AdviceType.REFUSE, AdviceLevel.FINAL, good=self.good + self.group_advice = FinalAdviceFactory( + user=self.gov_user2, case=self.case, type=AdviceType.REFUSE, good=self.good ) + FinalAdviceFactory(user=self.gov_user2, case=self.case, type=AdviceType.REFUSE, good=self.good) response = self.client.get(self.url, **self.gov_headers) response_data = response.json()["results"] diff --git a/api/cases/tests/test_delete_user_advice.py b/api/cases/tests/test_delete_user_advice.py index 15438730d9..8952f54e82 100644 --- a/api/cases/tests/test_delete_user_advice.py +++ b/api/cases/tests/test_delete_user_advice.py @@ -1,5 +1,6 @@ from api.cases.models import Advice -from api.cases.enums import AdviceLevel, AdviceType +from api.cases.enums import AdviceType +from api.cases.tests.factories import UserAdviceFactory from api.users.tests.factories import GovUserFactory from test_helpers.clients import DataTestClient @@ -12,13 +13,14 @@ def setUp(self): self.application = self.create_draft_standard_application(self.organisation) self.case = self.submit_application(self.application) self.gov_user_2 = GovUserFactory(baseuser_ptr__email="user@email.com", team=self.team) - + self.good = self.case.goods.first().good + self.end_user = self.case.end_user self.standard_case_url = reverse("cases:user_advice", kwargs={"pk": self.case.id}) def test_delete_current_user_advice(self): - self.create_advice(self.gov_user, self.application, "end_user", AdviceType.APPROVE, AdviceLevel.USER) - self.create_advice(self.gov_user_2, self.application, "good", AdviceType.REFUSE, AdviceLevel.USER) - self.create_advice(self.gov_user, self.application, "good", AdviceType.PROVISO, AdviceLevel.USER) + UserAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) + UserAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.PROVISO, end_user=self.end_user.party) + UserAdviceFactory(user=self.gov_user_2, case=self.case, type=AdviceType.REFUSE, good=self.good) resp = self.client.delete(self.standard_case_url, **self.gov_headers) @@ -28,7 +30,7 @@ def test_delete_current_user_advice(self): self.assertEqual(remaining_records[0].user, self.gov_user_2) def test_creates_audit_trail(self): - self.create_advice(self.gov_user, self.application, "end_user", AdviceType.APPROVE, AdviceLevel.USER) + UserAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, end_user=self.end_user.party) self.client.delete(self.standard_case_url, **self.gov_headers) diff --git a/api/cases/tests/test_edit_advice.py b/api/cases/tests/test_edit_advice.py index 1fbd17a033..fa66c5ab6b 100644 --- a/api/cases/tests/test_edit_advice.py +++ b/api/cases/tests/test_edit_advice.py @@ -7,7 +7,7 @@ from api.applications.models import StandardApplication from api.cases.enums import AdviceType, AdviceLevel, CountersignOrder from api.cases.models import Advice, CountersignAdvice -from api.cases.tests.factories import CountersignAdviceFactory +from api.cases.tests.factories import CountersignAdviceFactory, UserAdviceFactory from api.core.constants import GovPermissions from api.flags.enums import FlagLevels from api.flags.models import Flag @@ -93,21 +93,20 @@ def setUp(self): def _setup_advice_for_application(self, application, advice_type, advice_level): # Create Advice objects for all entities for good_on_application in application.goods.all(): - self.create_advice( - self.gov_user, - application, - "", - advice_type, - advice_level, + UserAdviceFactory( + user=self.gov_user, + case=application, + type=advice_type, + level=advice_level, good=good_on_application.good, ) for party_on_application in application.parties.all(): - self.create_advice( - self.gov_user, - application, - party_on_application.party.type, - advice_type, - advice_level, + UserAdviceFactory( + user=self.gov_user, + case=application, + type=advice_type, + level=advice_level, + end_user=party_on_application.party, ) @parameterized.expand(CaseStatusEnum._terminal_statuses) @@ -277,21 +276,20 @@ def setUp(self): def _setup_advice_for_application(self, application, advice_type, advice_level): # Create Advice objects for all entities for good_on_application in application.goods.all(): - self.create_advice( - self.gov_user, - application, - "", - advice_type, - advice_level, + UserAdviceFactory( + user=self.gov_user, + case=application, + type=advice_type, + level=advice_level, good=good_on_application.good, ) for party_on_application in application.parties.all(): - self.create_advice( - self.gov_user, - application, - party_on_application.party.type, - advice_type, - advice_level, + UserAdviceFactory( + user=self.gov_user, + case=application, + type=advice_type, + level=advice_level, + end_user=party_on_application.party, ) @parameterized.expand( diff --git a/api/cases/tests/test_final_advice_documents.py b/api/cases/tests/test_final_advice_documents.py index 9cae2b4da4..e05b2848a9 100644 --- a/api/cases/tests/test_final_advice_documents.py +++ b/api/cases/tests/test_final_advice_documents.py @@ -1,8 +1,7 @@ from django.urls import reverse from rest_framework import status -from django.test import override_settings -from api.cases.enums import AdviceType, CaseTypeEnum, AdviceLevel +from api.cases.enums import AdviceType, CaseTypeEnum from api.cases.tests.factories import GoodCountryDecisionFactory, FinalAdviceFactory from api.goodstype.tests.factories import GoodsTypeFactory from api.licences.tests.factories import StandardLicenceFactory @@ -15,13 +14,15 @@ class AdviceDocumentsTests(DataTestClient): def setUp(self): super().setUp() self.case = self.create_standard_application_case(self.organisation) + self.good = self.case.goods.first().good + self.end_user = self.case.end_user.party self.licence = StandardLicenceFactory(case=self.case) self.template = self.create_letter_template(name="Template", case_types=[CaseTypeEnum.SIEL.id]) self.url = reverse("cases:final_advice_documents", kwargs={"pk": self.case.id}) def test_get_final_advice_no_documents(self): - self.create_advice(self.gov_user, self.case, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - self.create_advice(self.gov_user, self.case, "end_user", AdviceType.REFUSE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.REFUSE, end_user=self.end_user) expected_format = { AdviceType.APPROVE: {"value": AdviceType.get_text(AdviceType.APPROVE)}, AdviceType.REFUSE: {"value": AdviceType.get_text(AdviceType.REFUSE)}, @@ -34,8 +35,8 @@ def test_get_final_advice_no_documents(self): self.assertEqual(response.json()["documents"], expected_format) def test_get_final_advice_with_document(self): - self.create_advice(self.gov_user, self.case, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - self.create_advice(self.gov_user, self.case, "end_user", AdviceType.REFUSE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.REFUSE, end_user=self.end_user) document_one = self.create_generated_case_document( self.case, self.template, advice_type=AdviceType.APPROVE, licence=self.licence @@ -63,8 +64,10 @@ def test_get_final_advice_with_document(self): self.assertEqual(response_data[AdviceType.REFUSE]["document"]["id"], str(document_two.pk)) def test_get_final_advice_with_no_licence_required_document_no_controlled_goods(self): - self.create_advice(self.gov_user, self.case, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - self.create_advice(self.gov_user, self.case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) + FinalAdviceFactory( + user=self.gov_user, case=self.case, type=AdviceType.NO_LICENCE_REQUIRED, end_user=self.end_user + ) document_no_license = self.create_generated_case_document( self.case, self.template, advice_type=AdviceType.NO_LICENCE_REQUIRED, licence=self.licence @@ -95,8 +98,10 @@ def test_get_final_advice_with_no_licence_required_document_no_controlled_goods( self.assertEqual(response_data[AdviceType.NO_LICENCE_REQUIRED]["document"]["id"], str(document_no_license.pk)) def test_get_final_advice_with_no_licence_required_document_controlled_goods(self): - self.create_advice(self.gov_user, self.case, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - self.create_advice(self.gov_user, self.case, "end_user", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.APPROVE, good=self.good) + FinalAdviceFactory( + user=self.gov_user, case=self.case, type=AdviceType.NO_LICENCE_REQUIRED, end_user=self.end_user + ) good = GoodFactory( organisation=self.organisation, @@ -135,7 +140,7 @@ def test_get_final_advice_with_no_licence_required_document_controlled_goods(sel def test_get_final_advice_with_document_proviso(self): # Proviso advice should match up with approve document - self.create_advice(self.gov_user, self.case, "good", AdviceType.PROVISO, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.case, type=AdviceType.PROVISO, good=self.good) document = self.create_generated_case_document( self.case, self.template, advice_type=AdviceType.APPROVE, licence=self.licence ) diff --git a/api/cases/tests/test_finalise_advice.py b/api/cases/tests/test_finalise_advice.py index f2ee8a0ef3..112c6e8c66 100644 --- a/api/cases/tests/test_finalise_advice.py +++ b/api/cases/tests/test_finalise_advice.py @@ -1,10 +1,10 @@ from unittest import mock from django.urls import reverse from rest_framework import status -from django.test import override_settings from api.audit_trail.models import Audit -from api.cases.enums import AdviceType, CaseTypeEnum, AdviceLevel +from api.cases.enums import AdviceType, CaseTypeEnum +from api.cases.tests.factories import FinalAdviceFactory from api.cases.libraries.get_case import get_case from api.cases.generated_documents.models import GeneratedCaseDocument from api.core.constants import GovPermissions @@ -19,7 +19,7 @@ def setUp(self): super().setUp() self.application = self.create_standard_application_case(self.organisation) self.url = reverse("cases:finalise", kwargs={"pk": self.application.id}) - self.create_advice(self.gov_user, self.application, "good", AdviceType.REFUSE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.application, type=AdviceType.REFUSE) self.template = self.create_letter_template( name="Template", case_types=[CaseTypeEnum.SIEL.id], @@ -81,7 +81,7 @@ def setUp(self): super().setUp() self.application = self.create_standard_application_case(self.organisation) self.url = reverse("cases:finalise", kwargs={"pk": self.application.id}) - self.create_advice(self.gov_user, self.application, "good", AdviceType.NO_LICENCE_REQUIRED, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.application, type=AdviceType.NO_LICENCE_REQUIRED) self.template = self.create_letter_template( name="Template", case_types=[CaseTypeEnum.SIEL.id], @@ -122,7 +122,7 @@ def setUp(self): super().setUp() self.application = self.create_standard_application_case(self.organisation) self.url = reverse("cases:finalise", kwargs={"pk": self.application.id}) - self.create_advice(self.gov_user, self.application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.application, type=AdviceType.APPROVE) self.template = self.create_letter_template( name="Template", case_types=[CaseTypeEnum.SIEL.id], diff --git a/api/cases/tests/test_grant_licence.py b/api/cases/tests/test_grant_licence.py index e882c85fdd..f34de69ae0 100644 --- a/api/cases/tests/test_grant_licence.py +++ b/api/cases/tests/test_grant_licence.py @@ -7,6 +7,7 @@ from api.audit_trail.models import Audit from api.cases.enums import AdviceType, CaseTypeEnum, AdviceLevel from api.cases.generated_documents.models import GeneratedCaseDocument +from api.cases.tests.factories import FinalAdviceFactory from api.core.constants import GovPermissions from api.core.exceptions import PermissionDeniedError from api.licences.tests.factories import StandardLicenceFactory @@ -23,7 +24,7 @@ def setUp(self): super().setUp() self.standard_case = self.create_standard_application_case(self.organisation) self.url = reverse("cases:finalise", kwargs={"pk": self.standard_case.id}) - self.create_advice(self.gov_user, self.standard_case, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=self.standard_case, type=AdviceType.APPROVE) self.template = self.create_letter_template( name="Template", case_types=[CaseTypeEnum.SIEL.id], @@ -86,7 +87,7 @@ def test_missing_advice_document_failure(self): def test_grant_clearance_success(self, send_exporter_notifications_func, mock_notify): clearance_case = self.create_mod_clearance_application(self.organisation, CaseTypeEnum.EXHIBITION) self.submit_application(clearance_case) - self.create_advice(self.gov_user, clearance_case, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + FinalAdviceFactory(user=self.gov_user, case=clearance_case, type=AdviceType.APPROVE) self.url = reverse("cases:finalise", kwargs={"pk": clearance_case.id}) self.gov_user.role.permissions.set([GovPermissions.MANAGE_CLEARANCE_FINAL_ADVICE.name]) diff --git a/api/data_workspace/tests/test_advice_views.py b/api/data_workspace/tests/test_advice_views.py index 0cefa8a486..e4f46fda18 100644 --- a/api/data_workspace/tests/test_advice_views.py +++ b/api/data_workspace/tests/test_advice_views.py @@ -1,6 +1,7 @@ from django.urls import reverse from api.cases.enums import AdviceType, AdviceLevel +from api.cases.tests.factories import FinalAdviceFactory from test_helpers.clients import DataTestClient @@ -8,14 +9,7 @@ class AdviceDataWorkspaceTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) - self.create_advice( - self.gov_user, - self.standard_application, - "good", - AdviceType.APPROVE, - AdviceLevel.FINAL, - advice_text="advice_text", - ) + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, type=AdviceType.APPROVE) def test_advice(self): url = reverse("data_workspace:dw-advice-list") @@ -59,14 +53,7 @@ class AdviceDenialReasonsDataWorkspaceTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) - self.create_advice( - self.gov_user, - self.standard_application, - "good", - AdviceType.REFUSE, - AdviceLevel.FINAL, - advice_text="advice_text", - ) + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, type=AdviceType.REFUSE) def test_advice_denial_reason(self): url = reverse("data_workspace:dw-advice-denial-reasons-list") diff --git a/api/goods/tests/factories.py b/api/goods/tests/factories.py index dc354154fa..6fc322c2ba 100644 --- a/api/goods/tests/factories.py +++ b/api/goods/tests/factories.py @@ -80,3 +80,11 @@ def flags(self, create, extracted, **kwargs): return self.flags.set(extracted) + + @factory.post_generation + def pv_grading_details(self, create, extracted, **kwargs): + if not create: + return + + if self.is_pv_graded == GoodPvGraded.NO: + self.pv_grading_details = None diff --git a/api/letter_templates/tests/test_context_generation.py b/api/letter_templates/tests/test_context_generation.py index 58c3611378..bdde2894b1 100644 --- a/api/letter_templates/tests/test_context_generation.py +++ b/api/letter_templates/tests/test_context_generation.py @@ -14,7 +14,7 @@ ) from api.applications.models import ExternalLocationOnApplication, CountryOnApplication, GoodOnApplication from api.applications.tests.factories import GoodOnApplicationFactory -from api.cases.enums import AdviceLevel, AdviceType, CaseTypeEnum +from api.cases.enums import AdviceType, CaseTypeEnum from api.licences.tests.factories import StandardLicenceFactory from api.letter_templates.context_generator import EcjuQuerySerializer from api.cases.tests.factories import GoodCountryDecisionFactory, FinalAdviceFactory @@ -438,9 +438,7 @@ def test_generate_context_with_custom_addressee(self): def test_generate_context_with_goods(self): application = self.create_standard_application_case(self.organisation, user=self.exporter_user, num_products=10) for product in application.goods.all(): - self.create_advice( - self.gov_user, application, "good", AdviceType.APPROVE, AdviceLevel.FINAL, good=product.good - ) + FinalAdviceFactory(user=self.gov_user, case=application, good=product.good) licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for product in application.goods.all(): @@ -466,20 +464,8 @@ def test_generate_context_with_goods_proviso_included(self): # mixup approvals and proviso's to check the order later for index, product in enumerate(application.goods.all()): - if index % 2 == 0: - self.create_advice( - self.gov_user, application, "good", AdviceType.APPROVE, AdviceLevel.FINAL, good=product.good - ) - else: - self.create_advice( - self.gov_user, - application, - "good", - AdviceType.PROVISO, - AdviceLevel.FINAL, - good=product.good, - advice_text="proviso", - ) + advice_type = AdviceType.PROVISO if index % 2 else AdviceType.APPROVE + FinalAdviceFactory(user=self.gov_user, case=application, good=product.good, type=advice_type) licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for product in application.goods.all(): @@ -506,23 +492,8 @@ def test_generate_context_with_goods_proviso_included_and_multiple_goa_for_singl # mixup approvals and proviso's to check the order later for index, product in enumerate(application.goods.all()): - product.good = single_good - product.save() - - if index % 2 == 0: - self.create_advice( - self.gov_user, application, "good", AdviceType.APPROVE, AdviceLevel.FINAL, good=product.good - ) - else: - self.create_advice( - self.gov_user, - application, - "good", - AdviceType.PROVISO, - AdviceLevel.FINAL, - good=product.good, - advice_text="proviso", - ) + advice_type = AdviceType.PROVISO if index % 2 else AdviceType.APPROVE + FinalAdviceFactory(user=self.gov_user, case=application, good=product.good, type=advice_type) licence = StandardLicenceFactory(case=application, status=LicenceStatus.ISSUED) for product in application.goods.all(): @@ -543,18 +514,13 @@ def test_generate_context_with_goods_proviso_included_and_multiple_goa_for_singl def test_generate_context_with_advice_on_goods(self): case = self.create_standard_application_case(self.organisation, user=self.exporter_user) - final_advice = self.create_advice( - self.gov_user, - case, - "good", - AdviceType.REFUSE, - AdviceLevel.FINAL, - advice_text="abc", - ) good = case.goods.first() - good.licenced_quantity = 10 - good.licenced_value = 15 - good.save() + final_advice = FinalAdviceFactory( + user=self.gov_user, + case=case, + type=AdviceType.REFUSE, + good=good.good, + ) context = get_document_context(case) render_to_string(template_name="letter_templates/case_context_test.html", context=context) @@ -568,21 +534,26 @@ def test_generate_context_with_advice_on_goods_missing(self): good was subsequently removed from the application. """ case = self.create_standard_application_case(self.organisation, user=self.exporter_user) - good = self.create_good_on_application( - case, GoodFactory(organisation=self.organisation, is_good_controlled=True) + another_good_on_application = GoodOnApplicationFactory( + application=case, + good=GoodFactory(organisation=self.organisation, is_good_controlled=True), + quantity=10, + unit=Units.NAR, + value=500, ) - self.create_advice( - self.gov_user, - case, - "good", - AdviceType.REFUSE, - AdviceLevel.FINAL, - advice_text="abc", + FinalAdviceFactory( + user=self.gov_user, + case=case, + type=AdviceType.REFUSE, good=case.goods.first().good, ) - final_advice = self.create_advice( - self.gov_user, case, "good", AdviceType.REFUSE, AdviceLevel.FINAL, advice_text="abc", good=good.good + final_advice = FinalAdviceFactory( + user=self.gov_user, + case=case, + type=AdviceType.REFUSE, + good=another_good_on_application.good, ) + case.goods.first().delete() # Remove the first good from the application context = get_document_context(case) @@ -594,18 +565,11 @@ def test_generate_context_with_advice_on_goods_missing(self): def test_generate_context_with_proviso_advice_on_goods(self): case = self.create_standard_application_case(self.organisation, user=self.exporter_user) - final_advice = self.create_advice( - self.gov_user, - case, - "good", - AdviceType.PROVISO, - AdviceLevel.FINAL, - advice_text="abc", - ) good = case.goods.first() good.licenced_quantity = 15 good.licenced_value = 20 good.save() + final_advice = FinalAdviceFactory(user=self.gov_user, case=case, type=AdviceType.PROVISO, good=good.good) context = get_document_context(case) render_to_string(template_name="letter_templates/case_context_test.html", context=context) @@ -619,18 +583,10 @@ def test_goods_context_approve_has_quantity_and_value(self): case = self.create_standard_application_case(self.organisation, user=self.exporter_user) good_on_application = case.goods.first() good = good_on_application.good - approve_advice = self.create_advice( - self.gov_user, - case, - "good", - AdviceType.APPROVE, - AdviceLevel.FINAL, - advice_text="some advice text", - good=good, - ) + FinalAdviceFactory(user=self.gov_user, case=case, good=good) licence = StandardLicenceFactory(case=case, status=LicenceStatus.ISSUED) - good_on_licence = GoodOnLicenceFactory(good=good_on_application, quantity=3, value=420, licence=licence) + GoodOnLicenceFactory(good=good_on_application, quantity=3, value=420, licence=licence) context = get_document_context(case) @@ -642,24 +598,8 @@ def test_goods_context_proviso_has_quantity_and_value(self): case = self.create_standard_application_case(self.organisation, user=self.exporter_user) good_on_application = case.goods.first() good = good_on_application.good - approve_advice = self.create_advice( - self.gov_user, - case, - "good", - AdviceType.APPROVE, - AdviceLevel.FINAL, - advice_text="some advice text", - good=good, - ) - proviso_advice = self.create_advice( - self.gov_user, - case, - "good", - AdviceType.PROVISO, - AdviceLevel.FINAL, - advice_text="some advice text", - good=good, - ) + FinalAdviceFactory(user=self.gov_user, case=case, good=good) + FinalAdviceFactory(user=self.gov_user, case=case, good=good, type=AdviceType.PROVISO) licence = StandardLicenceFactory(case=case, status=LicenceStatus.ISSUED) good_on_licence = GoodOnLicenceFactory(good=good_on_application, quantity=3, value=420, licence=licence) diff --git a/api/licences/tests/test_api_to_hmrc_integration.py b/api/licences/tests/test_api_to_hmrc_integration.py index 879bc60e9f..a5c858407d 100644 --- a/api/licences/tests/test_api_to_hmrc_integration.py +++ b/api/licences/tests/test_api_to_hmrc_integration.py @@ -11,7 +11,7 @@ from django.conf import settings from api.cases.enums import AdviceType, CaseTypeSubTypeEnum, AdviceLevel, CaseTypeEnum -from api.cases.tests.factories import GoodCountryDecisionFactory +from api.cases.tests.factories import GoodCountryDecisionFactory, FinalAdviceFactory from api.core.constants import GovPermissions from api.core.helpers import add_months from api.conf.settings import MAX_ATTEMPTS, LITE_HMRC_REQUEST_TIMEOUT @@ -76,7 +76,8 @@ class HMRCIntegrationSerializersTests(DataTestClient): def test_standard_application(self, status): action = licence_status_to_hmrc_integration_action.get(status) standard_application = self.create_standard_application_case(self.organisation) - self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=standard_application, good=good) standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( @@ -115,7 +116,8 @@ def test_standard_application_no_trading_country_code(self, status): end_user = standard_application.parties.get(party__type="end_user").party end_user.country = trade_country end_user.save() - self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=standard_application, good=good) standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( @@ -158,7 +160,8 @@ def test_standard_application_translates_to_trading_country_code(self, status): end_user = standard_application.parties.get(party__type="end_user").party end_user.country = trade_country end_user.save() - self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=standard_application, good=good) standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( @@ -200,7 +203,8 @@ def test_standard_application_strips_territory_code(self, status): end_user = standard_application.parties.get(party__type="end_user").party end_user.country = trade_country end_user.save() - self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=standard_application, good=good) standard_licence = StandardLicenceFactory(case=standard_application, status=status) good_on_application = standard_application.goods.first() GoodOnLicenceFactory( @@ -339,7 +343,8 @@ class HMRCIntegrationOperationsTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) - self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = self.standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=good) status = LicenceStatus.ISSUED self.hmrc_integration_status = licence_status_to_hmrc_integration_action.get(status) self.standard_licence = StandardLicenceFactory(case=self.standard_application, status=status) @@ -391,7 +396,8 @@ class HMRCIntegrationLicenceTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) - self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = self.standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=good) self.standard_licence = StandardLicenceFactory(case=self.standard_application, status=LicenceStatus.ISSUED) @override_settings(LITE_HMRC_INTEGRATION_ENABLED=True) @@ -465,7 +471,8 @@ class HMRCIntegrationTasksTests(DataTestClient): def setUp(self): super().setUp() self.standard_application = self.create_standard_application_case(self.organisation) - self.create_advice(self.gov_user, self.standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) + good = self.standard_application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=self.standard_application, good=good) status = LicenceStatus.ISSUED self.hmrc_integration_status = licence_status_to_hmrc_integration_action.get(status) self.standard_licence = StandardLicenceFactory(case=self.standard_application, status=status) @@ -686,9 +693,10 @@ def test_reinstate_licence_success(self, request): def _create_licence_for_submission(self, create_application_case_callback): application = create_application_case_callback(self.organisation) licence = StandardLicenceFactory(case=application, status=LicenceStatus.DRAFT) + good = application.goods.first().good + FinalAdviceFactory(user=self.gov_user, case=application, good=good) for product in application.goods.all(): GoodOnLicence.objects.create(good=product, licence=licence, quantity=product.quantity, value=product.value) - self.create_advice(self.gov_user, application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) template = self.create_letter_template( name=f"{timezone.now()}", case_types=[application.case_type], diff --git a/api/licences/tests/test_hmrc_integration_to_api.py b/api/licences/tests/test_hmrc_integration_to_api.py index 7ed04f3adb..813a32a277 100644 --- a/api/licences/tests/test_hmrc_integration_to_api.py +++ b/api/licences/tests/test_hmrc_integration_to_api.py @@ -14,7 +14,7 @@ from api.goodstype.models import GoodsType from api.licences.enums import LicenceStatus, HMRCIntegrationActionEnum from api.licences.models import HMRCIntegrationUsageData, Licence -from api.licences.tests.factories import GoodOnLicenceFactory +from api.licences.tests.factories import GoodOnLicenceFactory, StandardLicenceFactory from api.open_general_licences.tests.factories import OpenGeneralLicenceFactory, OpenGeneralLicenceCaseFactory from api.staticdata.countries.models import Country from test_helpers.clients import DataTestClient @@ -28,7 +28,7 @@ def setUp(self): def create_siel_licence(self): standard_application = self.create_standard_application_case(self.organisation) self.create_advice(self.gov_user, standard_application, "good", AdviceType.APPROVE, AdviceLevel.FINAL) - licence = self.create_licence(standard_application, status=LicenceStatus.ISSUED) + licence = StandardLicenceFactory(case=standard_application, status=LicenceStatus.ISSUED) self._create_good_on_licence(licence, standard_application.goods.first()) return licence From 4ffbd8fb9f2e47f4117a6b331a0c1c87ac06d60d Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Tue, 30 Jan 2024 13:57:15 +0000 Subject: [PATCH 06/21] Exclude source country from Country factory As exports always start from GB exclude this from the factory. This will trigger some flagging rules unintentionally and causes assertions to fail. --- api/staticdata/countries/factories.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/staticdata/countries/factories.py b/api/staticdata/countries/factories.py index e29ac33ed1..3df89da541 100644 --- a/api/staticdata/countries/factories.py +++ b/api/staticdata/countries/factories.py @@ -4,8 +4,8 @@ class CountryFactory(factory.django.DjangoModelFactory): - id = factory.Iterator(["UK", "IT", "ES"]) - name = factory.Iterator(["United Kingdom", "Italy", "Spain"]) + id = factory.Iterator(["IT", "ES"]) + name = factory.Iterator(["Italy", "Spain"]) is_eu = False type = factory.Iterator(["1", "2", "3"]) From 9bdb65f4d227570fec187f63f30f297dd9954c81 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Wed, 31 Jan 2024 12:23:23 +0000 Subject: [PATCH 07/21] Fix last failures --- .../tests/test_existing_parties.py | 5 ++--- .../tests/test_export_xml.py | 5 ++++- api/cases/tests/test_good_precedents_view.py | 2 +- api/goods/tests/test_edit.py | 21 ++++++------------- api/licences/tests/test_service.py | 2 +- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/api/applications/tests/test_existing_parties.py b/api/applications/tests/test_existing_parties.py index 83f20fae9e..57ed49df02 100644 --- a/api/applications/tests/test_existing_parties.py +++ b/api/applications/tests/test_existing_parties.py @@ -31,7 +31,7 @@ def test_get_existing_parties(self): response = self.client.get(self.url, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data["results"]), Party.objects.count()) + self.assertEqual(len(response.data["results"]), 2) def test_get_existing_parties_only_returns_parties_from_own_organisation_success(self): second_organisation, _ = self.create_organisation_with_exporter_user(name="Second organisation") @@ -124,11 +124,10 @@ def test_get_existing_parties_contains_no_duplicates_without_filters_success(sel second_expected_copy_id = Party.objects.filter(name="Mr Copy").get().id # Party table data contains one duplicate, so results returned is 1 less than all parties - self.assertEqual(Party.objects.count() - 1, len(response_data)) + self.assertEqual(len(response_data), 4) self.assertIn(str(expected_copy_id), response_data_ids) self.assertIn(str(second_expected_copy_id), response_data_ids) - self.assertIn(str(self.draft.end_user.party.id), response_data_ids) self.assertNotIn(str(original_party.id), response_data_ids) def test_get_existing_parties_contains_no_duplicates_with_filters_success(self): diff --git a/api/cases/enforcement_check/tests/test_export_xml.py b/api/cases/enforcement_check/tests/test_export_xml.py index 448ead577f..840988be33 100644 --- a/api/cases/enforcement_check/tests/test_export_xml.py +++ b/api/cases/enforcement_check/tests/test_export_xml.py @@ -64,7 +64,10 @@ def test_export_xml_with_parties_success(self): if party.organisation: self.assertEqual(stakeholder["ORG_NAME"], party.organisation.name) self.assertEqual(stakeholder["PD_SURNAME"], party.name) - self.assertEqual(stakeholder["ADDRESS1"], party.address) + # When address is exported to xml, newlines are replaced with space + # so do the same when comparing the address here + expected_address = party.address.replace("\n", " ") + self.assertEqual(stakeholder["ADDRESS1"], expected_address) # Ensure the correct EnforcementCheckID object is added for the import xml process self._assert_enforcement_type_recorded(stakeholder["SH_ID"], entity_uuid, party.type) diff --git a/api/cases/tests/test_good_precedents_view.py b/api/cases/tests/test_good_precedents_view.py index 7b39af6759..c8c8d84bf8 100644 --- a/api/cases/tests/test_good_precedents_view.py +++ b/api/cases/tests/test_good_precedents_view.py @@ -113,7 +113,7 @@ def test_get_with_matching_precedents(self): # so their destinations will be different hence extract expected values instead of using # fixed values in expected_data expected_destinations = sorted( - [party_on_application.party.country.name for party_on_application in self.application.parties.all()] + list(set(self.application.parties.values_list("party__country__name", flat=True))) ) wassenaar_regime = RegimeEntry.objects.get(name="Wassenaar Arrangement") diff --git a/api/goods/tests/test_edit.py b/api/goods/tests/test_edit.py index 5bea1f82f9..6ecef42e24 100644 --- a/api/goods/tests/test_edit.py +++ b/api/goods/tests/test_edit.py @@ -29,21 +29,14 @@ class GoodsEditDraftGoodTests(DataTestClient): def setUp(self): super().setUp() - self.good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP1_COMPONENTS) + self.good = GoodFactory( + organisation=self.organisation, + item_category=ItemCategory.GROUP1_COMPONENTS, + is_good_controlled=True, + ) self.url = reverse("goods:good", kwargs={"pk": str(self.good.id)}) self.edit_details_url = reverse("goods:good_details", kwargs={"pk": str(self.good.id)}) - def test_when_updating_is_good_controlled_to_no_then_control_list_entries_is_deleted(self): - request_data = {"is_good_controlled": False} - - response = self.client.put(self.url, request_data, **self.exporter_headers) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.json()["good"]["is_good_controlled"]["key"], "False") - self.assertEqual(response.json()["good"]["control_list_entries"], []) - - self.assertEqual(Good.objects.all().count(), 1) - def test_when_updating_non_clc_the_clc_is_not_overwritten(self): ratings = ["ML1a", "ML1b"] request_data = {"is_good_controlled": True, "control_list_entries": ratings} @@ -113,9 +106,7 @@ def test_when_updating_is_pv_graded_to_no_then_pv_grading_details_are_deleted(se self.assertEqual(PvGradingDetails.objects.all().count(), 0) def test_when_updating_pv_grading_details_then_new_details_are_returned(self): - pv_grading_details = self.good.pv_grading_details.__dict__ - pv_grading_details.pop("_state") - pv_grading_details.pop("id") + pv_grading_details = {} pv_grading_details["grading"] = PvGrading.UK_OFFICIAL pv_grading_details["date_of_issue"] = "2020-01-01" request_data = {"is_pv_graded": GoodPvGraded.YES, "pv_grading_details": pv_grading_details} diff --git a/api/licences/tests/test_service.py b/api/licences/tests/test_service.py index 258343819c..902cd88f65 100644 --- a/api/licences/tests/test_service.py +++ b/api/licences/tests/test_service.py @@ -25,7 +25,7 @@ def setUp(self): duration=100, reference_code="reference", ) - self.good = GoodFactory(organisation=self.application.organisation) + self.good = GoodFactory(organisation=self.application.organisation, is_good_controlled=True) self.good_on_application = GoodOnApplicationFactory( application=self.application, good=self.good, quantity=100.0, value=Decimal("1000.00") ) From 48f424a39fccef05dc6ff3f3d4bbf14db1326cdd Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Thu, 1 Feb 2024 14:06:10 +0000 Subject: [PATCH 08/21] Update lite-routing sha - Fixes tests affected by the changes in this PR --- lite_routing | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lite_routing b/lite_routing index cf93856ab6..d42fa8ac7f 160000 --- a/lite_routing +++ b/lite_routing @@ -1 +1 @@ -Subproject commit cf93856ab684b24a9be90929c379d587544616e7 +Subproject commit d42fa8ac7fdd9f0db36fb1a2c48b8e3f611d33d5 From 6d8fd24434e5ae63ee669cf0864397d34f9b069e Mon Sep 17 00:00:00 2001 From: Tomos Williams Date: Thu, 1 Feb 2024 13:43:02 +0000 Subject: [PATCH 09/21] trial run --- .circleci/config.yml | 115 +++++++++++++++++++++++++------------------ .coveragerc | 3 -- 2 files changed, 68 insertions(+), 50 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d8e1c340d5..5c8e6a07f4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -90,10 +90,24 @@ commands: key: dependencies-v1-{{ .Branch }}-{{ checksum "Pipfile.lock" }} upload_code_coverage: + parameters: + alias: + type: string steps: - run: - name: Upload code coverage - command: ./codecov + name: Rename coverage file + command: mkdir coverage-output && cp .coverage coverage-output/.coverage.<> + - persist_to_workspace: + root: coverage-output + paths: + - .coverage.* + - run: + name: Code coverage report + command: | + pipenv run coverage xml + curl -Os https://uploader.codecov.io/latest/linux/codecov + chmod +x codecov + ./codecov jobs: tests: @@ -109,17 +123,15 @@ jobs: parallelism: 10 steps: - setup - - run: - name: Create report directories - command: | - mkdir test_results - run: name: Run tests command: | - pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc --ignore lite_routing -k "not seeding and not elasticsearch and not performance and not migration" --junitxml=test_results/output.xml - - upload_code_coverage + pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc --ignore lite_routing -k "not seeding and not elasticsearch and not performance and not migration" + - upload_code_coverage: + alias: tests - store_test_results: - path: test_results + path: tests + seeding_tests: docker: @@ -132,17 +144,12 @@ jobs: LITE_API_ENABLE_ES: True steps: - setup - - run: - name: Create report directories - command: | - mkdir test_results - run: name: Run seeding tests command: | - pipenv run pytest --cov=. --cov-report xml --cov-config=.coveragerc -k seeding --junitxml=test_results/output.xml - - upload_code_coverage - - store_test_results: - path: test_results + pipenv run pytest --cov=. --cov-report xml --cov-config=.coveragerc -k seeding + - upload_code_coverage: + alias: seeding_tests migration_tests: docker: @@ -156,17 +163,14 @@ jobs: LITE_API_ENABLE_ES: True steps: - setup - - run: - name: Create report directories - command: | - mkdir test_results - run: name: Run migration tests command: | - pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc -k migration --junitxml=test_results/output.xml - - upload_code_coverage + pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc -k migration + - upload_code_coverage: + alias: migration_tests - store_test_results: - path: test_results + path: migration_tests lite_routing_tests: docker: @@ -181,17 +185,14 @@ jobs: parallelism: 5 steps: - setup - - run: - name: Create report directories - command: | - mkdir test_results - run: name: Run lite_routing tests command: | - pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc -k "not migration" --ignore lite_routing/routing_rules_internal/tests/bdd lite_routing --junitxml=test_results/output.xml - - upload_code_coverage + pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc -k "not migration" --ignore lite_routing/routing_rules_internal/tests/bdd lite_routing + - upload_code_coverage: + alias: lite_routing_tests - store_test_results: - path: test_results + path: lite_routing_tests lite_routing_bdd_tests: docker: @@ -212,19 +213,14 @@ jobs: - run: name: Create report directories command: | - mkdir test_results mkdir cucumber_results - mkdir cucumber_html - run: name: Run lite_routing tests - command: pipenv run pytest --circleci-parallelize --gherkin-terminal-reporter -vv lite_routing/routing_rules_internal/tests/bdd --junitxml=test_results/output.xml --cucumberjson=cucumber_results/cuc.json + command: pipenv run pytest --circleci-parallelize --gherkin-terminal-reporter -vv lite_routing/routing_rules_internal/tests/bdd --cucumberjson=cucumber_results/cuc.json - run: name: Generate html cucumber report command: node generate_cucumber_report.js when: always - - upload_code_coverage - - store_test_results: - path: test_results - store_artifacts: path: cucumber_html @@ -239,17 +235,12 @@ jobs: LITE_API_ENABLE_ES: True steps: - setup - - run: - name: Create report directories - command: | - mkdir test_results - run: name: Run elasticsearch tests command: | - pipenv run pytest --cov=. --cov-report xml --cov-config=.coveragerc -k elasticsearch --junitxml=test_results/output.xml - - upload_code_coverage - - store_test_results: - path: test_results + pipenv run pytest --cov=. --cov-report xml --cov-config=.coveragerc -k elasticsearch + - upload_code_coverage: + alias: elastic_search_tests check_migrations: docker: @@ -266,6 +257,28 @@ jobs: name: Check migrations are made command: pipenv run ./manage.py makemigrations --check + check_coverage: + working_directory: ~/repo + docker: + - <<: *image_python + steps: + - checkout + - attach_workspace: + at: ~/repo/tmp + - run: pip install coverage diff_cover + - run: coverage combine tmp + - run: coverage xml + - run: coverage html + - store_artifacts: + path: htmlcov + - run: diff-cover coverage.xml --compare-branch=origin/dev --html-report coverage-report.html + - store_artifacts: + path: coverage-report.html + - run: zip -r coverage.zip htmlcov coverage-report.html + - store_artifacts: + path: coverage.zip + - run: diff-cover coverage.xml --compare-branch=origin/dev --fail-under=100 + linting: docker: - <<: *image_python @@ -362,8 +375,9 @@ jobs: fi workflows: - test: + tests: jobs: + - linting - tests - seeding_tests - lite_routing_tests @@ -371,6 +385,13 @@ workflows: - elastic_search_tests - migration_tests - check_migrations - - linting + - check_coverage: + requires: + - tests + - seeding_tests + - elastic_search_tests + - migration_tests + - lite_routing_bdd_tests + - lite_routing_tests - check-lite-routing-sha - e2e_tests diff --git a/.coveragerc b/.coveragerc index e732bb5f41..fa0434254d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -18,6 +18,3 @@ omit = ./lite_routing/management/commands/generate_rules_docs.py branch = True - -plugins = - django_coverage_plugin From a6106db3dd35f529c1b674b19fcb4b2ce10352e5 Mon Sep 17 00:00:00 2001 From: Tomos Williams Date: Thu, 1 Feb 2024 18:03:35 +0000 Subject: [PATCH 10/21] testing ae --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5c8e6a07f4..c5067ab885 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -267,7 +267,7 @@ jobs: at: ~/repo/tmp - run: pip install coverage diff_cover - run: coverage combine tmp - - run: coverage xml + - run: coverage xml ae - run: coverage html - store_artifacts: path: htmlcov From d400530d3a64c3f05d4b42822c21c0a1cbffe562 Mon Sep 17 00:00:00 2001 From: Tomos Williams Date: Fri, 2 Feb 2024 08:25:08 +0000 Subject: [PATCH 11/21] change working_directory --- .circleci/config.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c5067ab885..5dfcfd1199 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -129,9 +129,6 @@ jobs: pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc --ignore lite_routing -k "not seeding and not elasticsearch and not performance and not migration" - upload_code_coverage: alias: tests - - store_test_results: - path: tests - seeding_tests: docker: @@ -169,8 +166,6 @@ jobs: pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc -k migration - upload_code_coverage: alias: migration_tests - - store_test_results: - path: migration_tests lite_routing_tests: docker: @@ -191,8 +186,6 @@ jobs: pipenv run pytest --circleci-parallelize --cov=. --cov-report xml --cov-config=.coveragerc -k "not migration" --ignore lite_routing/routing_rules_internal/tests/bdd lite_routing - upload_code_coverage: alias: lite_routing_tests - - store_test_results: - path: lite_routing_tests lite_routing_bdd_tests: docker: @@ -223,6 +216,8 @@ jobs: when: always - store_artifacts: path: cucumber_html + - upload_code_coverage: + alias: lite_routing_bdd_tests elastic_search_tests: docker: @@ -258,16 +253,16 @@ jobs: command: pipenv run ./manage.py makemigrations --check check_coverage: - working_directory: ~/repo + working_directory: ~/lite-api docker: - <<: *image_python steps: - checkout - attach_workspace: - at: ~/repo/tmp + at: ~/lite-api/tmp - run: pip install coverage diff_cover - run: coverage combine tmp - - run: coverage xml ae + - run: coverage xml - run: coverage html - store_artifacts: path: htmlcov From dbbaf5323c805a4d07ef1f7ef8f1cfb4f922d649 Mon Sep 17 00:00:00 2001 From: Tomos Williams Date: Fri, 2 Feb 2024 08:52:41 +0000 Subject: [PATCH 12/21] trial run --- .circleci/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5dfcfd1199..4ffce43ab4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -216,8 +216,6 @@ jobs: when: always - store_artifacts: path: cucumber_html - - upload_code_coverage: - alias: lite_routing_bdd_tests elastic_search_tests: docker: @@ -386,7 +384,6 @@ workflows: - seeding_tests - elastic_search_tests - migration_tests - - lite_routing_bdd_tests - lite_routing_tests - check-lite-routing-sha - e2e_tests From f095fa38704a03f89d4e0ee3645e95ae91b92408 Mon Sep 17 00:00:00 2001 From: Kevin Carrogan Date: Fri, 2 Feb 2024 11:54:14 +0000 Subject: [PATCH 13/21] Checkout submodules when checking coverage --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4ffce43ab4..2107954cbb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -256,6 +256,7 @@ jobs: - <<: *image_python steps: - checkout + - run: git submodule sync --recursive && git submodule update --recursive --init - attach_workspace: at: ~/lite-api/tmp - run: pip install coverage diff_cover From 201beb1c186f14e0edad4a7d12f3d655bf34f770 Mon Sep 17 00:00:00 2001 From: Kevin Carrogan Date: Fri, 2 Feb 2024 11:56:00 +0000 Subject: [PATCH 14/21] Add random file extension to coverage This is so that parallel test runs don't create clashing coverage file names --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2107954cbb..7810682a57 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -96,7 +96,7 @@ commands: steps: - run: name: Rename coverage file - command: mkdir coverage-output && cp .coverage coverage-output/.coverage.<> + command: mkdir coverage-output && cp .coverage coverage-output/.coverage.<>.$(cat /proc/sys/kernel/random/uuid) - persist_to_workspace: root: coverage-output paths: From 1920039d44cc7e3014f35b7f15148bd265fbbae5 Mon Sep 17 00:00:00 2001 From: Kevin Carrogan Date: Fri, 2 Feb 2024 12:00:01 +0000 Subject: [PATCH 15/21] Temporarily remove jobs --- .circleci/config.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7810682a57..d3ed810f3b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -371,20 +371,20 @@ jobs: workflows: tests: jobs: - - linting + # - linting - tests - - seeding_tests - - lite_routing_tests - - lite_routing_bdd_tests - - elastic_search_tests - - migration_tests - - check_migrations + # - seeding_tests + # - lite_routing_tests + # - lite_routing_bdd_tests + # - elastic_search_tests + # - migration_tests + # - check_migrations - check_coverage: requires: - tests - - seeding_tests - - elastic_search_tests - - migration_tests - - lite_routing_tests - - check-lite-routing-sha - - e2e_tests + # - seeding_tests + # - elastic_search_tests + # - migration_tests + # - lite_routing_tests + # - check-lite-routing-sha + # - e2e_tests From 01cc1906e4b74719d569dee16d7ebe89ee94ff28 Mon Sep 17 00:00:00 2001 From: Kevin Carrogan Date: Fri, 2 Feb 2024 12:18:00 +0000 Subject: [PATCH 16/21] Add back all tests --- .circleci/config.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d3ed810f3b..7810682a57 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -371,20 +371,20 @@ jobs: workflows: tests: jobs: - # - linting + - linting - tests - # - seeding_tests - # - lite_routing_tests - # - lite_routing_bdd_tests - # - elastic_search_tests - # - migration_tests - # - check_migrations + - seeding_tests + - lite_routing_tests + - lite_routing_bdd_tests + - elastic_search_tests + - migration_tests + - check_migrations - check_coverage: requires: - tests - # - seeding_tests - # - elastic_search_tests - # - migration_tests - # - lite_routing_tests - # - check-lite-routing-sha - # - e2e_tests + - seeding_tests + - elastic_search_tests + - migration_tests + - lite_routing_tests + - check-lite-routing-sha + - e2e_tests From 93f88c7acf55b36c39fcd7e592d1d3396d29a5b3 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Mon, 29 Jan 2024 15:11:07 +0000 Subject: [PATCH 17/21] Update model and serializer to allow blank response --- api/cases/models.py | 2 +- api/cases/serializers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/cases/models.py b/api/cases/models.py index a0c70d545b..d54dc7b9c2 100644 --- a/api/cases/models.py +++ b/api/cases/models.py @@ -600,7 +600,7 @@ class EcjuQuery(TimestampableModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) question = models.CharField(null=False, blank=False, max_length=5000) - response = models.CharField(null=True, blank=False, max_length=2200) + response = models.CharField(null=True, blank=True, max_length=2200) case = models.ForeignKey(Case, related_name="case_ecju_query", on_delete=models.CASCADE) team = models.ForeignKey(Team, null=True, on_delete=models.CASCADE) responded_at = models.DateTimeField(auto_now_add=False, blank=True, null=True) diff --git a/api/cases/serializers.py b/api/cases/serializers.py index de07bc1a29..6f34b67fd0 100644 --- a/api/cases/serializers.py +++ b/api/cases/serializers.py @@ -635,7 +635,7 @@ class EcjuQueryUserResponseSerializer(serializers.ModelSerializer): responded_by_user = PrimaryKeyRelatedSerializerField( queryset=BaseUser.objects.all(), serializer=BaseUserViewSerializer ) - response = serializers.CharField(max_length=2200, allow_blank=False, allow_null=True) + response = serializers.CharField(max_length=2200, allow_blank=True, allow_null=True) documents = serializers.SerializerMethodField() class Meta: From 6e890db14463c8d65e80dd3ff889dc497897e0b5 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Thu, 1 Feb 2024 09:58:17 +0000 Subject: [PATCH 18/21] Update existing tests for optional response --- api/cases/tests/test_case_ecju_queries.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index 4230a3147c..33134dff28 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -514,12 +514,12 @@ def test_close_query_has_optional_response_exporter(self): query_response_url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) - data = {} + data = {"response": ""} response = self.client.put(query_response_url, data, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_ecju_query = response.json()["ecju_query"] - self.assertIsNone(response_ecju_query["response"]) + self.assertEqual(response_ecju_query["response"], "") self.assertIsNotNone(response_ecju_query["responded_at"]) def test_close_query_has_optional_response_govuser(self): @@ -530,13 +530,13 @@ def test_close_query_has_optional_response_govuser(self): query_response_url = reverse("cases:case_ecju_query", kwargs={"pk": case.id, "ecju_pk": ecju_query.id}) - data = {"response": None} + data = {"response": ""} response = self.client.put(query_response_url, data, **self.gov_headers) self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_ecju_query = response.json()["ecju_query"] - self.assertIsNone(response_ecju_query["response"]) + self.assertEqual(response_ecju_query["response"], "") self.assertIsNotNone(response_ecju_query["responded_at"]) response_get = self.client.get(query_response_url, **self.gov_headers) From 20671655a74cfe5066104be93b26189b5110355d Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Mon, 29 Jan 2024 16:45:02 +0000 Subject: [PATCH 19/21] Add auto migration --- .../0062_alter_ecjuquery_response.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 api/cases/migrations/0062_alter_ecjuquery_response.py diff --git a/api/cases/migrations/0062_alter_ecjuquery_response.py b/api/cases/migrations/0062_alter_ecjuquery_response.py new file mode 100644 index 0000000000..ad32339867 --- /dev/null +++ b/api/cases/migrations/0062_alter_ecjuquery_response.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.23 on 2024-01-29 16:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("cases", "0061_auto_20240105_1523"), + ] + + operations = [ + migrations.AlterField( + model_name="ecjuquery", + name="response", + field=models.CharField(blank=True, max_length=2200, null=True), + ), + ] From ceb35ca0e1b683f3640ca925df9fefa4b4c17c58 Mon Sep 17 00:00:00 2001 From: Henry Cooksley Date: Thu, 1 Feb 2024 11:57:58 +0000 Subject: [PATCH 20/21] Update test assertion --- api/cases/tests/test_case_ecju_queries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/cases/tests/test_case_ecju_queries.py b/api/cases/tests/test_case_ecju_queries.py index 33134dff28..3fbfb2b0f7 100644 --- a/api/cases/tests/test_case_ecju_queries.py +++ b/api/cases/tests/test_case_ecju_queries.py @@ -519,7 +519,7 @@ def test_close_query_has_optional_response_exporter(self): self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_ecju_query = response.json()["ecju_query"] - self.assertEqual(response_ecju_query["response"], "") + self.assertIsNone(response_ecju_query["response"]) self.assertIsNotNone(response_ecju_query["responded_at"]) def test_close_query_has_optional_response_govuser(self): @@ -536,7 +536,7 @@ def test_close_query_has_optional_response_govuser(self): self.assertEqual(response.status_code, status.HTTP_201_CREATED) response_ecju_query = response.json()["ecju_query"] - self.assertEqual(response_ecju_query["response"], "") + self.assertIsNone(response_ecju_query["response"]) self.assertIsNotNone(response_ecju_query["responded_at"]) response_get = self.client.get(query_response_url, **self.gov_headers) From 3fc53ce3e600353c17dc076b27e4074c54a8d934 Mon Sep 17 00:00:00 2001 From: Arun Siluvery Date: Thu, 1 Feb 2024 22:18:58 +0000 Subject: [PATCH 21/21] Address review comments --- .../tests/test_existing_parties.py | 13 +++++++------ api/applications/tests/test_third_parties.py | 4 ++-- .../tests/test_export_xml.py | 3 +-- api/goods/tests/test_edit.py | 19 +++++++++++-------- test_helpers/clients.py | 6 +++--- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/api/applications/tests/test_existing_parties.py b/api/applications/tests/test_existing_parties.py index 57ed49df02..01581db0b9 100644 --- a/api/applications/tests/test_existing_parties.py +++ b/api/applications/tests/test_existing_parties.py @@ -31,7 +31,8 @@ def test_get_existing_parties(self): response = self.client.get(self.url, **self.exporter_headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data["results"]), 2) + # parties of this organisation + self.assertEqual(len(response.data["results"]), Party.objects.filter(organisation=self.organisation).count()) def test_get_existing_parties_only_returns_parties_from_own_organisation_success(self): second_organisation, _ = self.create_organisation_with_exporter_user(name="Second organisation") @@ -123,18 +124,18 @@ def test_get_existing_parties_contains_no_duplicates_without_filters_success(sel expected_copy_id = Party.objects.filter(name="Mr Original", address="456 abc st.").get().id second_expected_copy_id = Party.objects.filter(name="Mr Copy").get().id - # Party table data contains one duplicate, so results returned is 1 less than all parties - self.assertEqual(len(response_data), 4) + # Party table data contains one duplicate, so results returned is 1 less than all parties of this organisation + self.assertEqual(len(response_data), Party.objects.filter(organisation=self.organisation).count() - 1) self.assertIn(str(expected_copy_id), response_data_ids) self.assertIn(str(second_expected_copy_id), response_data_ids) self.assertNotIn(str(original_party.id), response_data_ids) def test_get_existing_parties_contains_no_duplicates_with_filters_success(self): - params = f"?name=Mr" + params = "?name=Unique" original_party = Party.objects.create( - name="Mr Original", + name="Unique name", address="123 abc st.", website="https://www.gov.py", country=self.country, @@ -144,7 +145,7 @@ def test_get_existing_parties_contains_no_duplicates_with_filters_success(self): ) copied_party = Party.objects.create( - name="Mr Original", + name="Unique name", address="123 abc st.", website="https://www.gov.py", country=self.country, diff --git a/api/applications/tests/test_third_parties.py b/api/applications/tests/test_third_parties.py index 838387fefc..484c609940 100644 --- a/api/applications/tests/test_third_parties.py +++ b/api/applications/tests/test_third_parties.py @@ -118,8 +118,8 @@ def test_get_third_parties(self): self.assertEqual(party["country"]["name"], str(third_party.country.name)) self.assertEqual(party["website"], str(third_party.website)) self.assertEqual(party["type"], str(third_party.type)) - if third_party.organisation: - self.assertEqual(party["organisation"], str(third_party.organisation.id)) + self.assertIsNotNone(party["organisation"]) + self.assertEqual(party["organisation"], str(third_party.organisation.id)) self.assertEqual(party["sub_type"]["key"], str(third_party.sub_type)) self.assertEqual(party["role"]["key"], str(third_party.role)) diff --git a/api/cases/enforcement_check/tests/test_export_xml.py b/api/cases/enforcement_check/tests/test_export_xml.py index 840988be33..bf00bfd452 100644 --- a/api/cases/enforcement_check/tests/test_export_xml.py +++ b/api/cases/enforcement_check/tests/test_export_xml.py @@ -61,8 +61,7 @@ def test_export_xml_with_parties_success(self): stakeholder["SH_TYPE"], party.type.upper() if party.type != PartyType.THIRD_PARTY else "OTHER" ) self.assertEqual(stakeholder["COUNTRY"], party.country.name) - if party.organisation: - self.assertEqual(stakeholder["ORG_NAME"], party.organisation.name) + self.assertIsNotNone(stakeholder["ORG_NAME"]) self.assertEqual(stakeholder["PD_SURNAME"], party.name) # When address is exported to xml, newlines are replaced with space # so do the same when comparing the address here diff --git a/api/goods/tests/test_edit.py b/api/goods/tests/test_edit.py index 6ecef42e24..38da0931e3 100644 --- a/api/goods/tests/test_edit.py +++ b/api/goods/tests/test_edit.py @@ -17,9 +17,7 @@ ) from api.goods.models import Good, PvGradingDetails from api.goods.tests.factories import GoodFactory -from api.goods.views import GOOD_ON_APP_BAD_REPORT_SUMMARY_SUBJECT, GOOD_ON_APP_BAD_REPORT_SUMMARY_PREFIX -from api.goods.tests.factories import GoodFactory -from api.staticdata.report_summaries.models import ReportSummaryPrefix, ReportSummarySubject +from api.goods.tests.factories import FirearmFactory, GoodFactory from lite_content.lite_api import strings from api.staticdata.control_list_entries.helpers import get_control_list_entry from test_helpers.clients import DataTestClient @@ -413,11 +411,16 @@ def test_edit_category_two_section_question_and_no_certificate_number_failure(se def test_edit_category_two_section_question_and_invalid_expiry_date_failure(self): """Test editing section of firearms question failure by providing an expiry date not in the future.""" - good = GoodFactory(organisation=self.organisation, item_category=ItemCategory.GROUP2_FIREARMS) - good.firearm_details.is_covered_by_firearm_act_section_one_two_or_five = "No" - good.firearm_details.section_certificate_number = None - good.firearm_details.section_certificate_date_of_expiry = None - good.firearm_details.save() + firearm_details = FirearmFactory( + is_covered_by_firearm_act_section_one_two_or_five="No", + section_certificate_number=None, + section_certificate_date_of_expiry=None, + ) + good = GoodFactory( + organisation=self.organisation, + item_category=ItemCategory.GROUP2_FIREARMS, + firearm_details=firearm_details, + ) url = reverse("goods:good_details", kwargs={"pk": str(good.id)}) request_data = { diff --git a/test_helpers/clients.py b/test_helpers/clients.py index 6c1c949187..e2236aaa64 100644 --- a/test_helpers/clients.py +++ b/test_helpers/clients.py @@ -667,9 +667,9 @@ def create_draft_standard_application( ) if parties: - PartyOnApplicationFactory(application=application, party=ConsigneeFactory()) - PartyOnApplicationFactory(application=application, party=EndUserFactory()) - PartyOnApplicationFactory(application=application, party=ThirdPartyFactory()) + PartyOnApplicationFactory(application=application, party=ConsigneeFactory(organisation=self.organisation)) + PartyOnApplicationFactory(application=application, party=EndUserFactory(organisation=self.organisation)) + PartyOnApplicationFactory(application=application, party=ThirdPartyFactory(organisation=self.organisation)) if ultimate_end_users: PartyOnApplicationFactory(application=application, party=UltimateEndUserFactory())